Compare commits
21 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
7bb8b4631f | ||
|
|
9f43170708 | ||
|
|
71112dbe87 | ||
|
|
4a8093609f | ||
|
|
7865c90737 | ||
|
|
e4e3d11772 | ||
|
|
a5242cbb9e | ||
|
|
c4d1bf5029 | ||
|
|
e003140c6e | ||
|
|
6b8c94e6c4 | ||
|
|
143e4a4559 | ||
|
|
dfeddbe823 | ||
|
|
021824930d | ||
|
|
b8301f10a8 | ||
|
|
2e8808317f | ||
|
|
79747f3d6f | ||
|
|
63825e7201 | ||
|
|
03ccda1a69 | ||
|
|
e02621577f | ||
|
|
ab869c8d20 | ||
|
|
b2aa636ea0 |
2
.github/workflows/go-cross.yml
vendored
2
.github/workflows/go-cross.yml
vendored
@@ -44,7 +44,7 @@ jobs:
|
||||
|
||||
# https://github.com/marketplace/actions/cache
|
||||
- name: Cache Go modules
|
||||
uses: actions/cache@v2
|
||||
uses: actions/cache@v3
|
||||
with:
|
||||
# In order:
|
||||
# * Module download cache
|
||||
|
||||
2
.github/workflows/main.yml
vendored
2
.github/workflows/main.yml
vendored
@@ -93,7 +93,7 @@ jobs:
|
||||
|
||||
# https://github.com/marketplace/actions/cache
|
||||
- name: Cache Go modules
|
||||
uses: actions/cache@v2
|
||||
uses: actions/cache@v3
|
||||
with:
|
||||
path: ./_test/tmp
|
||||
key: ${{ runner.os }}-yaegi-${{ hashFiles('**//_test/tmp/') }}
|
||||
|
||||
2
.github/workflows/release.yml
vendored
2
.github/workflows/release.yml
vendored
@@ -26,7 +26,7 @@ jobs:
|
||||
fetch-depth: 0
|
||||
|
||||
- name: Cache Go modules
|
||||
uses: actions/cache@v2
|
||||
uses: actions/cache@v3
|
||||
with:
|
||||
path: ~/go/pkg/mod
|
||||
key: ${{ runner.os }}-go-${{ hashFiles('**/go.sum') }}
|
||||
|
||||
@@ -5,8 +5,8 @@ import (
|
||||
"sync"
|
||||
)
|
||||
|
||||
// Defined an interface of stringBuilder that compatible with
|
||||
// strings.Builder(go 1.10) and bytes.Buffer(< go 1.10)
|
||||
// Define an interface of stringBuilder that is compatible with
|
||||
// strings.Builder(go 1.10) and bytes.Buffer(< go 1.10).
|
||||
type stringBuilder interface {
|
||||
WriteRune(r rune) (n int, err error)
|
||||
WriteString(s string) (int, error)
|
||||
|
||||
21
_test/assign17.go
Normal file
21
_test/assign17.go
Normal file
@@ -0,0 +1,21 @@
|
||||
package main
|
||||
|
||||
func main() {
|
||||
s := make([]map[string]string, 0)
|
||||
m := make(map[string]string)
|
||||
m["m1"] = "m1"
|
||||
m["m2"] = "m2"
|
||||
s = append(s, m)
|
||||
tmpStr := "start"
|
||||
println(tmpStr)
|
||||
for _, v := range s {
|
||||
tmpStr, ok := v["m1"]
|
||||
println(tmpStr, ok)
|
||||
}
|
||||
println(tmpStr)
|
||||
}
|
||||
|
||||
// Output:
|
||||
// start
|
||||
// m1 true
|
||||
// start
|
||||
21
_test/assign18.go
Normal file
21
_test/assign18.go
Normal file
@@ -0,0 +1,21 @@
|
||||
package main
|
||||
|
||||
func main() {
|
||||
s := make([]map[string]string, 0)
|
||||
m := make(map[string]string)
|
||||
m["m1"] = "m1"
|
||||
m["m2"] = "m2"
|
||||
s = append(s, m)
|
||||
tmpStr := "start"
|
||||
println(tmpStr)
|
||||
for _, v := range s {
|
||||
tmpStr, _ := v["m1"]
|
||||
println(tmpStr)
|
||||
}
|
||||
println(tmpStr)
|
||||
}
|
||||
|
||||
// Output:
|
||||
// start
|
||||
// m1
|
||||
// start
|
||||
56
_test/cli7.go
Normal file
56
_test/cli7.go
Normal file
@@ -0,0 +1,56 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"io"
|
||||
"log"
|
||||
"net/http"
|
||||
"net/http/httptest"
|
||||
)
|
||||
|
||||
type T struct {
|
||||
http.ResponseWriter
|
||||
}
|
||||
|
||||
type mw1 struct {
|
||||
}
|
||||
|
||||
var obj = map[string]interface{}{}
|
||||
|
||||
func (m *mw1) ServeHTTP(rw http.ResponseWriter, rq *http.Request) {
|
||||
t := &T{
|
||||
ResponseWriter: rw,
|
||||
}
|
||||
x := t.Header()
|
||||
i := obj["m1"].(*mw1)
|
||||
fmt.Fprint(rw, "Welcome to my website!", x, i)
|
||||
}
|
||||
|
||||
func main() {
|
||||
m1 := &mw1{}
|
||||
|
||||
obj["m1"] = m1
|
||||
|
||||
mux := http.NewServeMux()
|
||||
mux.HandleFunc("/", m1.ServeHTTP)
|
||||
|
||||
server := httptest.NewServer(mux)
|
||||
defer server.Close()
|
||||
|
||||
client(server.URL)
|
||||
}
|
||||
|
||||
func client(uri string) {
|
||||
resp, err := http.Get(uri)
|
||||
if err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
body, err := io.ReadAll(resp.Body)
|
||||
if err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
fmt.Println(string(body))
|
||||
}
|
||||
|
||||
// Output:
|
||||
// Welcome to my website!map[] &{}
|
||||
41
_test/cli8.go
Normal file
41
_test/cli8.go
Normal file
@@ -0,0 +1,41 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"net/http"
|
||||
"net/http/httptest"
|
||||
)
|
||||
|
||||
type T struct {
|
||||
name string
|
||||
next http.Handler
|
||||
}
|
||||
|
||||
func (t *T) ServeHTTP(rw http.ResponseWriter, req *http.Request) {
|
||||
println("in T.ServeHTTP")
|
||||
if t.next != nil {
|
||||
t.next.ServeHTTP(rw, req)
|
||||
}
|
||||
}
|
||||
|
||||
func New(name string, next http.Handler) (http.Handler, error) { return &T{name, next}, nil }
|
||||
|
||||
func main() {
|
||||
next := func(rw http.ResponseWriter, req *http.Request) {
|
||||
println("in next")
|
||||
}
|
||||
|
||||
t, err := New("test", http.HandlerFunc(next))
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
|
||||
recorder := httptest.NewRecorder()
|
||||
req := httptest.NewRequest(http.MethodGet, "/", nil)
|
||||
t.ServeHTTP(recorder, req)
|
||||
println(recorder.Result().Status)
|
||||
}
|
||||
|
||||
// Output:
|
||||
// in T.ServeHTTP
|
||||
// in next
|
||||
// 200 OK
|
||||
18
_test/convert3.go
Normal file
18
_test/convert3.go
Normal file
@@ -0,0 +1,18 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"net/http"
|
||||
)
|
||||
|
||||
func main() {
|
||||
next := func(rw http.ResponseWriter, req *http.Request) {
|
||||
rw.Header().Set("Cache-Control", "max-age=20")
|
||||
rw.WriteHeader(http.StatusOK)
|
||||
}
|
||||
f := http.HandlerFunc(next)
|
||||
fmt.Printf("%T\n", f.ServeHTTP)
|
||||
}
|
||||
|
||||
// Output:
|
||||
// func(http.ResponseWriter, *http.Request)
|
||||
17
_test/issue-1416.go
Normal file
17
_test/issue-1416.go
Normal file
@@ -0,0 +1,17 @@
|
||||
package main
|
||||
|
||||
type Number int32
|
||||
|
||||
type Number1 = Number
|
||||
|
||||
type Number2 = Number1
|
||||
|
||||
func (n Number2) IsValid() bool { return true }
|
||||
|
||||
func main() {
|
||||
a := Number(5)
|
||||
println(a.IsValid())
|
||||
}
|
||||
|
||||
// Output:
|
||||
// true
|
||||
44
_test/issue-1425.go
Normal file
44
_test/issue-1425.go
Normal file
@@ -0,0 +1,44 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"io"
|
||||
"log"
|
||||
"os"
|
||||
"strings"
|
||||
)
|
||||
|
||||
type WrappedReader struct {
|
||||
reader io.Reader
|
||||
}
|
||||
|
||||
func (wr WrappedReader) Read(p []byte) (n int, err error) {
|
||||
return wr.reader.Read(p)
|
||||
}
|
||||
|
||||
// Of course, this implementation is completely stupid because it does not write
|
||||
// to the intended writer, as any honest WriteTo implementation should. its
|
||||
// implemtion is just to make obvious the divergence of behaviour with yaegi.
|
||||
func (wr WrappedReader) WriteTo(w io.Writer) (n int64, err error) {
|
||||
// Ignore w, send to Stdout to prove whether this WriteTo is used.
|
||||
data, err := io.ReadAll(wr)
|
||||
if err != nil {
|
||||
return 0, err
|
||||
}
|
||||
nn, err := os.Stdout.Write(data)
|
||||
return int64(nn), err
|
||||
}
|
||||
|
||||
func main() {
|
||||
f := strings.NewReader("hello world")
|
||||
wr := WrappedReader{reader: f}
|
||||
|
||||
// behind the scenes, io.Copy is supposed to use wr.WriteTo if the implementation exists.
|
||||
// With Go, it works as expected, i.e. the output is sent to os.Stdout.
|
||||
// With Yaegi, it doesn't, i.e. the output is sent to io.Discard.
|
||||
if _, err := io.Copy(io.Discard, wr); err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
}
|
||||
|
||||
// Output:
|
||||
// hello world
|
||||
25
_test/issue-1439.go
Normal file
25
_test/issue-1439.go
Normal file
@@ -0,0 +1,25 @@
|
||||
package main
|
||||
|
||||
type Transformer interface {
|
||||
Reset()
|
||||
}
|
||||
|
||||
type Encoder struct {
|
||||
Transformer
|
||||
}
|
||||
|
||||
type nop struct{}
|
||||
|
||||
func (nop) Reset() { println("Reset") }
|
||||
|
||||
func f(e Transformer) {
|
||||
e.Reset()
|
||||
}
|
||||
|
||||
func main() {
|
||||
e := Encoder{Transformer: nop{}}
|
||||
f(e)
|
||||
}
|
||||
|
||||
// Output:
|
||||
// Reset
|
||||
41
_test/issue-1442.go
Normal file
41
_test/issue-1442.go
Normal file
@@ -0,0 +1,41 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"context"
|
||||
)
|
||||
|
||||
func main() {
|
||||
ctx, _ := context.WithCancel(context.Background())
|
||||
ch := make(chan string, 20)
|
||||
defer close(ch)
|
||||
|
||||
go func(ctx context.Context, ch <-chan string) {
|
||||
for {
|
||||
select {
|
||||
case <-ctx.Done():
|
||||
return
|
||||
case tmp := <-ch:
|
||||
_ = tmp
|
||||
}
|
||||
}
|
||||
}(ctx, ch)
|
||||
|
||||
for _, i := range "abcdef" {
|
||||
for _, j := range "0123456789" {
|
||||
// i, j := "a", "0"
|
||||
for _, k := range "ABCDEF" {
|
||||
select {
|
||||
case <-ctx.Done():
|
||||
return
|
||||
default:
|
||||
tmp := string(i) + string(j) + string(k)
|
||||
ch <- tmp
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
// Output:
|
||||
//
|
||||
20
_test/issue-1447.go
Normal file
20
_test/issue-1447.go
Normal file
@@ -0,0 +1,20 @@
|
||||
package main
|
||||
|
||||
import "fmt"
|
||||
|
||||
type I interface {
|
||||
Name() string
|
||||
}
|
||||
|
||||
type S struct {
|
||||
iMap map[string]I
|
||||
}
|
||||
|
||||
func main() {
|
||||
s := S{}
|
||||
s.iMap = map[string]I{}
|
||||
fmt.Println(s)
|
||||
}
|
||||
|
||||
// Output:
|
||||
// {map[]}
|
||||
19
_test/issue-1451.go
Normal file
19
_test/issue-1451.go
Normal file
@@ -0,0 +1,19 @@
|
||||
package main
|
||||
|
||||
type t1 uint8
|
||||
|
||||
const (
|
||||
n1 t1 = iota
|
||||
n2
|
||||
)
|
||||
|
||||
type T struct {
|
||||
elem [n2 + 1]int
|
||||
}
|
||||
|
||||
func main() {
|
||||
println(len(T{}.elem))
|
||||
}
|
||||
|
||||
// Output:
|
||||
// 2
|
||||
22
_test/issue-1454.go
Normal file
22
_test/issue-1454.go
Normal file
@@ -0,0 +1,22 @@
|
||||
package main
|
||||
|
||||
type I2 interface {
|
||||
I2() string
|
||||
}
|
||||
|
||||
type I interface {
|
||||
I2
|
||||
}
|
||||
|
||||
type S struct{}
|
||||
|
||||
func (*S) I2() string { return "foo" }
|
||||
|
||||
func main() {
|
||||
var i I
|
||||
_, ok := i.(*S)
|
||||
println(ok)
|
||||
}
|
||||
|
||||
// Output:
|
||||
// false
|
||||
22
_test/issue-1459.go
Normal file
22
_test/issue-1459.go
Normal file
@@ -0,0 +1,22 @@
|
||||
package main
|
||||
|
||||
import "fmt"
|
||||
|
||||
type funclistItem func()
|
||||
|
||||
type funclist struct {
|
||||
list []funclistItem
|
||||
}
|
||||
|
||||
func main() {
|
||||
funcs := funclist{}
|
||||
|
||||
funcs.list = append(funcs.list, func() { fmt.Println("first") })
|
||||
|
||||
for _, f := range funcs.list {
|
||||
f()
|
||||
}
|
||||
}
|
||||
|
||||
// Output:
|
||||
// first
|
||||
79
_test/issue-1460.go
Normal file
79
_test/issue-1460.go
Normal file
@@ -0,0 +1,79 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"encoding/json"
|
||||
"errors"
|
||||
"reflect"
|
||||
)
|
||||
|
||||
func unmarshalJSON[T any](b []byte, x *[]T) error {
|
||||
if *x != nil {
|
||||
return errors.New("already initialized")
|
||||
}
|
||||
if len(b) == 0 {
|
||||
return nil
|
||||
}
|
||||
return json.Unmarshal(b, x)
|
||||
}
|
||||
|
||||
type StructView[T any] interface {
|
||||
Valid() bool
|
||||
AsStruct() T
|
||||
}
|
||||
|
||||
type SliceView[T ViewCloner[T, V], V StructView[T]] struct {
|
||||
ж []T
|
||||
}
|
||||
|
||||
type ViewCloner[T any, V StructView[T]] interface {
|
||||
View() V
|
||||
Clone() T
|
||||
}
|
||||
|
||||
func SliceOfViews[T ViewCloner[T, V], V StructView[T]](x []T) SliceView[T, V] {
|
||||
return SliceView[T, V]{x}
|
||||
}
|
||||
|
||||
func (v SliceView[T, V]) MarshalJSON() ([]byte, error) { return json.Marshal(v.ж) }
|
||||
|
||||
func (v *SliceView[T, V]) UnmarshalJSON(b []byte) error { return unmarshalJSON(b, &v.ж) }
|
||||
|
||||
type Slice[T any] struct {
|
||||
ж []T
|
||||
}
|
||||
|
||||
func (v Slice[T]) MarshalJSON() ([]byte, error) { return json.Marshal(v.ж) }
|
||||
|
||||
func (v *Slice[T]) UnmarshalJSON(b []byte) error { return unmarshalJSON(b, &v.ж) }
|
||||
|
||||
func SliceOf[T any](x []T) Slice[T] {
|
||||
return Slice[T]{x}
|
||||
}
|
||||
|
||||
type viewStruct struct {
|
||||
Int int
|
||||
Strings Slice[string]
|
||||
StringsPtr *Slice[string] `json:",omitempty"`
|
||||
}
|
||||
|
||||
func main() {
|
||||
ss := SliceOf([]string{"bar"})
|
||||
in := viewStruct{
|
||||
Int: 1234,
|
||||
Strings: ss,
|
||||
StringsPtr: &ss,
|
||||
}
|
||||
|
||||
var buf bytes.Buffer
|
||||
encoder := json.NewEncoder(&buf)
|
||||
encoder.SetIndent("", "")
|
||||
err1 := encoder.Encode(&in)
|
||||
b := buf.Bytes()
|
||||
var got viewStruct
|
||||
err2 := json.Unmarshal(b, &got)
|
||||
println(err1 == nil, err2 == nil, reflect.DeepEqual(got, in))
|
||||
}
|
||||
|
||||
// Output:
|
||||
// true true true
|
||||
24
_test/issue-1465.go
Normal file
24
_test/issue-1465.go
Normal file
@@ -0,0 +1,24 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
)
|
||||
|
||||
func SomeFunc[T int | string](defaultValue T) T {
|
||||
switch v := any(&defaultValue).(type) {
|
||||
case *string:
|
||||
*v = *v + " abc"
|
||||
case *int:
|
||||
*v -= 234
|
||||
}
|
||||
return defaultValue
|
||||
}
|
||||
|
||||
func main() {
|
||||
fmt.Println(SomeFunc("test"))
|
||||
fmt.Println(SomeFunc(1234))
|
||||
}
|
||||
|
||||
// Output:
|
||||
// test abc
|
||||
// 1000
|
||||
24
_test/issue-1466.go
Normal file
24
_test/issue-1466.go
Normal file
@@ -0,0 +1,24 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
)
|
||||
|
||||
func SomeFunc(defaultValue interface{}) interface{} {
|
||||
switch v := defaultValue.(type) {
|
||||
case string:
|
||||
return v + " abc"
|
||||
case int:
|
||||
return v - 234
|
||||
}
|
||||
panic("whoops")
|
||||
}
|
||||
|
||||
func main() {
|
||||
fmt.Println(SomeFunc(1234))
|
||||
fmt.Println(SomeFunc("test"))
|
||||
}
|
||||
|
||||
// Output:
|
||||
// 1000
|
||||
// test abc
|
||||
15
_test/issue-1470.go
Normal file
15
_test/issue-1470.go
Normal file
@@ -0,0 +1,15 @@
|
||||
package main
|
||||
|
||||
type T struct {
|
||||
num [tnum + 2]int
|
||||
}
|
||||
|
||||
const tnum = 23
|
||||
|
||||
func main() {
|
||||
t := T{}
|
||||
println(len(t.num))
|
||||
}
|
||||
|
||||
// Output:
|
||||
// 25
|
||||
12
_test/issue-1475.go
Normal file
12
_test/issue-1475.go
Normal file
@@ -0,0 +1,12 @@
|
||||
package main
|
||||
|
||||
type T uint16
|
||||
|
||||
func f() T { return 0 }
|
||||
|
||||
func main() {
|
||||
println(f())
|
||||
}
|
||||
|
||||
// Output:
|
||||
// 0
|
||||
@@ -1,6 +1,6 @@
|
||||
package main
|
||||
|
||||
import "github.com/traefik/yaegi/_test/alias3"
|
||||
import "github.com/traefik/yaegi/_test/named3"
|
||||
|
||||
var globalT *T
|
||||
|
||||
@@ -8,10 +8,10 @@ func init() {
|
||||
globalT = &T{A: "test"}
|
||||
}
|
||||
|
||||
type T alias3.T
|
||||
type T named3.T
|
||||
|
||||
func (t *T) PrintT() {
|
||||
(*alias3.T)(t).Print()
|
||||
(*named3.T)(t).Print()
|
||||
}
|
||||
|
||||
func main() {
|
||||
@@ -1,4 +1,4 @@
|
||||
package alias3
|
||||
package named3
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
3
_test/p4/p4.go
Normal file
3
_test/p4/p4.go
Normal file
@@ -0,0 +1,3 @@
|
||||
package p4
|
||||
|
||||
var Value1 = "value1"
|
||||
10
_test/p5.go
Normal file
10
_test/p5.go
Normal file
@@ -0,0 +1,10 @@
|
||||
package main
|
||||
|
||||
import "github.com/traefik/yaegi/_test/p5"
|
||||
|
||||
func main() {
|
||||
println(*p5.Value1)
|
||||
}
|
||||
|
||||
// Output:
|
||||
// value1
|
||||
8
_test/p5/p5.go
Normal file
8
_test/p5/p5.go
Normal file
@@ -0,0 +1,8 @@
|
||||
package p5
|
||||
|
||||
import "github.com/traefik/yaegi/_test/p4"
|
||||
|
||||
var (
|
||||
Value1 = &val1
|
||||
val1 = p4.Value1
|
||||
)
|
||||
@@ -10,11 +10,11 @@ func main() {
|
||||
c2 := make(chan string)
|
||||
|
||||
go func() {
|
||||
time.Sleep(1e7)
|
||||
time.Sleep(1e8)
|
||||
c1 <- "one"
|
||||
}()
|
||||
go func() {
|
||||
time.Sleep(2e7)
|
||||
time.Sleep(2e8)
|
||||
c2 <- "two"
|
||||
}()
|
||||
|
||||
|
||||
@@ -6,8 +6,8 @@ import (
|
||||
)
|
||||
|
||||
const (
|
||||
period = 100 * time.Millisecond
|
||||
precision = 7 * time.Millisecond
|
||||
period = 300 * time.Millisecond
|
||||
precision = 30 * time.Millisecond
|
||||
)
|
||||
|
||||
func main() {
|
||||
|
||||
@@ -25,7 +25,7 @@ func main() {
|
||||
}
|
||||
s.ts["test"] = append(s.ts["test"], &T{s: s})
|
||||
|
||||
t , ok:= s.getT("test")
|
||||
t, ok := s.getT("test")
|
||||
println(t != nil, ok)
|
||||
}
|
||||
|
||||
|
||||
17
_test/switch39.go
Normal file
17
_test/switch39.go
Normal file
@@ -0,0 +1,17 @@
|
||||
package main
|
||||
|
||||
func f(params ...interface{}) {
|
||||
switch p0 := params[0].(type) {
|
||||
case string:
|
||||
println("string:", p0)
|
||||
default:
|
||||
println("not a string")
|
||||
}
|
||||
}
|
||||
|
||||
func main() {
|
||||
f("Hello")
|
||||
}
|
||||
|
||||
// Output:
|
||||
// string: Hello
|
||||
17
_test/switch40.go
Normal file
17
_test/switch40.go
Normal file
@@ -0,0 +1,17 @@
|
||||
package main
|
||||
|
||||
func f(params ...interface{}) {
|
||||
switch params[0].(type) {
|
||||
case string:
|
||||
println("a string")
|
||||
default:
|
||||
println("not a string")
|
||||
}
|
||||
}
|
||||
|
||||
func main() {
|
||||
f("Hello")
|
||||
}
|
||||
|
||||
// Output:
|
||||
// a string
|
||||
@@ -43,6 +43,6 @@ func assertValue() {
|
||||
}
|
||||
|
||||
// Output:
|
||||
// interface conversion: interface {} is int, not string
|
||||
// interface conversion: interface {} is nil, not string
|
||||
// interface conversion: *httptest.ResponseRecorder is not http.Pusher: missing method Push
|
||||
// 22:10: interface conversion: interface {} is int, not string
|
||||
// 32:10: interface conversion: interface {} is nil, not string
|
||||
// 42:10: interface conversion: *httptest.ResponseRecorder is not http.Pusher: missing method Push
|
||||
|
||||
@@ -1,3 +1,4 @@
|
||||
// Package yaegi provides a Go interpreter.
|
||||
package yaegi
|
||||
|
||||
//go:generate go generate github.com/traefik/yaegi/internal/cmd/extract
|
||||
|
||||
@@ -11,7 +11,7 @@ Output files are written in the current directory, and prefixed with the go vers
|
||||
|
||||
Usage:
|
||||
|
||||
extract package...
|
||||
extract package...
|
||||
|
||||
The same program is used for all target operating systems and architectures.
|
||||
The GOOS and GOARCH environment variables set the desired target.
|
||||
|
||||
@@ -539,7 +539,7 @@ func {{$name}}(n *node) {
|
||||
|
||||
{{- if or (eq $op.Name "==") (eq $op.Name "!=") }}
|
||||
|
||||
if c0.typ.cat == aliasT || c1.typ.cat == aliasT {
|
||||
if c0.typ.cat == linkedT || c1.typ.cat == linkedT {
|
||||
switch {
|
||||
case isInterface:
|
||||
v0 := genValue(c0)
|
||||
|
||||
@@ -1,3 +1,4 @@
|
||||
// Package unsafe2 provides helpers to generate recursive struct types.
|
||||
package unsafe2
|
||||
|
||||
import (
|
||||
|
||||
@@ -147,10 +147,22 @@ func skipFile(ctx *build.Context, p string, skipTest bool) bool {
|
||||
}
|
||||
a := strings.Split(p[i+1:], "_")
|
||||
last := len(a) - 1
|
||||
if last1 := last - 1; last1 >= 0 && a[last1] == ctx.GOOS && a[last] == ctx.GOARCH {
|
||||
return false
|
||||
if last-1 >= 0 {
|
||||
switch x, y := a[last-1], a[last]; {
|
||||
case x == ctx.GOOS:
|
||||
if knownArch[y] {
|
||||
return y != ctx.GOARCH
|
||||
}
|
||||
return false
|
||||
case knownOs[x] && knownArch[y]:
|
||||
return true
|
||||
case knownArch[y] && y != ctx.GOARCH:
|
||||
return true
|
||||
default:
|
||||
return false
|
||||
}
|
||||
}
|
||||
if s := a[last]; s != ctx.GOOS && s != ctx.GOARCH && knownOs[s] || knownArch[s] {
|
||||
if x := a[last]; knownOs[x] && x != ctx.GOOS || knownArch[x] && x != ctx.GOARCH {
|
||||
return true
|
||||
}
|
||||
return false
|
||||
|
||||
@@ -51,7 +51,7 @@ func TestBuildTag(t *testing.T) {
|
||||
}
|
||||
}
|
||||
|
||||
func TestBuildFile(t *testing.T) {
|
||||
func TestSkipFile(t *testing.T) {
|
||||
// Assume a specific OS, arch and go pattern no matter the real underlying system
|
||||
ctx := build.Context{
|
||||
GOARCH: "amd64",
|
||||
@@ -65,10 +65,18 @@ func TestBuildFile(t *testing.T) {
|
||||
{"bar_linux.go", false},
|
||||
{"bar_maix.go", false},
|
||||
{"bar_mlinux.go", false},
|
||||
|
||||
{"bar_aix_foo.go", false},
|
||||
{"bar_linux_foo.go", false},
|
||||
{"bar_foo_amd64.go", false},
|
||||
{"bar_foo_arm.go", true},
|
||||
|
||||
{"bar_aix_s390x.go", true},
|
||||
{"bar_aix_amd64.go", true},
|
||||
{"bar_linux_arm.go", true},
|
||||
|
||||
{"bar_amd64.go", false},
|
||||
{"bar_arm.go", true},
|
||||
}
|
||||
|
||||
for _, test := range tests {
|
||||
|
||||
111
interp/cfg.go
111
interp/cfg.go
@@ -145,9 +145,9 @@ func (interp *Interpreter) cfg(root *node, sc *scope, importPath, pkgName string
|
||||
}
|
||||
|
||||
switch o.typ.cat {
|
||||
case valueT, aliasT:
|
||||
case valueT, linkedT:
|
||||
typ := o.typ.rtype
|
||||
if o.typ.cat == aliasT {
|
||||
if o.typ.cat == linkedT {
|
||||
typ = o.typ.val.TypeOf()
|
||||
}
|
||||
switch typ.Kind() {
|
||||
@@ -221,6 +221,14 @@ func (interp *Interpreter) cfg(root *node, sc *scope, importPath, pkgName string
|
||||
sc.sym[label] = sym
|
||||
c.sym = sym
|
||||
}
|
||||
// If block is the body of a function, get declared variables in current scope.
|
||||
// This is done in order to add the func signature symbols into sc.sym,
|
||||
// as we will need them in post-processing.
|
||||
if n.anc != nil && n.anc.kind == funcDecl {
|
||||
for k, v := range sc.anc.sym {
|
||||
sc.sym[k] = v
|
||||
}
|
||||
}
|
||||
|
||||
case breakStmt, continueStmt, gotoStmt:
|
||||
if len(n.child) == 0 {
|
||||
@@ -571,6 +579,10 @@ func (interp *Interpreter) cfg(root *node, sc *scope, importPath, pkgName string
|
||||
var sym *symbol
|
||||
var level int
|
||||
|
||||
if dest.rval.IsValid() && isConstType(dest.typ) {
|
||||
err = n.cfgErrorf("cannot assign to %s (%s constant)", dest.rval, dest.typ.str)
|
||||
break
|
||||
}
|
||||
if isBlank(src) {
|
||||
err = n.cfgErrorf("cannot use _ as value")
|
||||
break
|
||||
@@ -671,7 +683,7 @@ func (interp *Interpreter) cfg(root *node, sc *scope, importPath, pkgName string
|
||||
n.gen = nop
|
||||
src.findex = dest.findex
|
||||
src.level = level
|
||||
case len(n.child) < 4 && isArithmeticAction(src):
|
||||
case len(n.child) < 4 && isArithmeticAction(src) && !isInterface(dest.typ):
|
||||
// Optimize single assignments from some arithmetic operations.
|
||||
src.typ = dest.typ
|
||||
src.findex = dest.findex
|
||||
@@ -820,9 +832,7 @@ func (interp *Interpreter) cfg(root *node, sc *scope, importPath, pkgName string
|
||||
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.
|
||||
pos := childPos(n)
|
||||
n.typ = sc.def.typ.ret[pos]
|
||||
n.findex = pos
|
||||
n.findex = childPos(n)
|
||||
default:
|
||||
// Allocate a new location in frame, and store the result here.
|
||||
n.findex = sc.add(n.typ)
|
||||
@@ -835,7 +845,7 @@ func (interp *Interpreter) cfg(root *node, sc *scope, importPath, pkgName string
|
||||
}
|
||||
wireChild(n)
|
||||
t := n.child[0].typ
|
||||
for t.cat == aliasT {
|
||||
for t.cat == linkedT {
|
||||
t = t.val
|
||||
}
|
||||
switch t.cat {
|
||||
@@ -952,7 +962,14 @@ func (interp *Interpreter) cfg(root *node, sc *scope, importPath, pkgName string
|
||||
}
|
||||
wireChild(n)
|
||||
|
||||
case declStmt, exprStmt, sendStmt:
|
||||
case sendStmt:
|
||||
if !isChan(n.child[0].typ) {
|
||||
err = n.cfgErrorf("invalid operation: cannot send to non-channel %s", n.child[0].typ.id())
|
||||
break
|
||||
}
|
||||
fallthrough
|
||||
|
||||
case declStmt, exprStmt:
|
||||
wireChild(n)
|
||||
l := n.lastChild()
|
||||
n.findex = l.findex
|
||||
@@ -1052,7 +1069,7 @@ func (interp *Interpreter) cfg(root *node, sc *scope, importPath, pkgName string
|
||||
}
|
||||
|
||||
n.gen = c0.sym.builtin
|
||||
c0.typ = &itype{cat: builtinT}
|
||||
c0.typ = &itype{cat: builtinT, name: bname}
|
||||
if n.typ, err = nodeType(interp, sc, n); err != nil {
|
||||
return
|
||||
}
|
||||
@@ -1692,12 +1709,7 @@ func (interp *Interpreter) cfg(root *node, sc *scope, importPath, pkgName string
|
||||
}
|
||||
if c.typ.cat == nilT {
|
||||
// nil: Set node value to zero of return type
|
||||
if typ.cat == funcT {
|
||||
// Wrap the typed nil value in a node, as per other interpreter functions
|
||||
c.rval = reflect.ValueOf(&node{kind: basicLit, rval: reflect.New(typ.TypeOf()).Elem()})
|
||||
} else {
|
||||
c.rval = reflect.New(typ.TypeOf()).Elem()
|
||||
}
|
||||
c.rval = reflect.New(typ.TypeOf()).Elem()
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1912,9 +1924,13 @@ func (interp *Interpreter) cfg(root *node, sc *scope, importPath, pkgName string
|
||||
wireChild(n)
|
||||
// Move action to block statement, so select node can be an exit point.
|
||||
n.child[0].gen = _select
|
||||
// Chain channel init actions in commClauses prior to invoke select.
|
||||
// Chain channel init actions in commClauses prior to invoking select.
|
||||
var cur *node
|
||||
for _, c := range n.child[0].child {
|
||||
if c.kind == commClauseDefault {
|
||||
// No channel init in this case.
|
||||
continue
|
||||
}
|
||||
var an, pn *node // channel init action nodes
|
||||
if len(c.child) > 0 {
|
||||
switch c0 := c.child[0]; {
|
||||
@@ -2051,7 +2067,14 @@ func (interp *Interpreter) cfg(root *node, sc *scope, importPath, pkgName string
|
||||
}
|
||||
sbn.start = clauses[0].start
|
||||
n.start = n.child[0].start
|
||||
n.child[0].tnext = sbn.start
|
||||
if n.kind == typeSwitch {
|
||||
// Handle the typeSwitch init (the type assert expression).
|
||||
init := n.child[1].lastChild().child[0]
|
||||
init.tnext = sbn.start
|
||||
n.child[0].tnext = init.start
|
||||
} else {
|
||||
n.child[0].tnext = sbn.start
|
||||
}
|
||||
|
||||
case switchIfStmt: // like an if-else chain
|
||||
sc = sc.pop()
|
||||
@@ -2281,11 +2304,36 @@ func compDefineX(sc *scope, n *node) error {
|
||||
return n.cfgErrorf("unsupported assign expression")
|
||||
}
|
||||
|
||||
// Handle redeclarations: find out new symbols vs existing ones.
|
||||
symIsNew := map[string]bool{}
|
||||
hasNewSymbol := false
|
||||
for i := range types {
|
||||
id := n.child[i].ident
|
||||
if id == "_" || id == "" {
|
||||
continue
|
||||
}
|
||||
if _, found := symIsNew[id]; found {
|
||||
return n.cfgErrorf("%s repeated on left side of :=", id)
|
||||
}
|
||||
// A new symbol doesn't exist in current scope. Upper scopes are not
|
||||
// taken into accout here, as a new symbol can shadow an existing one.
|
||||
if _, found := sc.sym[id]; found {
|
||||
symIsNew[id] = false
|
||||
} else {
|
||||
symIsNew[id] = true
|
||||
hasNewSymbol = true
|
||||
}
|
||||
}
|
||||
|
||||
for i, t := range types {
|
||||
var index int
|
||||
id := n.child[i].ident
|
||||
if sym, level, ok := sc.lookup(id); ok && level == n.child[i].level && sym.kind == varSym && sym.typ.id() == t.id() {
|
||||
// Reuse symbol in case of a variable redeclaration with the same type.
|
||||
// A variable can be redeclared if at least one other not blank variable is created.
|
||||
// The redeclared variable must be of same type (it is reassigned, not created).
|
||||
// Careful to not reuse a variable which has been shadowed (it must not be a newSym).
|
||||
sym, level, ok := sc.lookup(id)
|
||||
canRedeclare := hasNewSymbol && len(symIsNew) > 1 && !symIsNew[id] && ok
|
||||
if canRedeclare && level == n.child[i].level && sym.kind == varSym && sym.typ.id() == t.id() {
|
||||
index = sym.index
|
||||
} else {
|
||||
index = sc.add(t)
|
||||
@@ -2420,15 +2468,22 @@ func genGlobalVarDecl(nodes []*node, sc *scope) (*node, error) {
|
||||
|
||||
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)
|
||||
}
|
||||
if n.kind != identExpr {
|
||||
return true
|
||||
}
|
||||
return true
|
||||
// Process ident nodes, and avoid false dependencies.
|
||||
if n.anc.kind == selectorExpr && childPos(n) == 1 {
|
||||
return false
|
||||
}
|
||||
sym, _, ok := sc.lookup(n.ident)
|
||||
if !ok {
|
||||
return false
|
||||
}
|
||||
if sym.kind != varSym || !sym.global || sym.node == nod {
|
||||
return false
|
||||
}
|
||||
deps = append(deps, sym.node)
|
||||
return false
|
||||
}, nil)
|
||||
return deps
|
||||
}
|
||||
@@ -2839,7 +2894,7 @@ func typeSwichAssign(n *node) bool {
|
||||
|
||||
func compositeGenerator(n *node, typ *itype, rtyp reflect.Type) (gen bltnGenerator) {
|
||||
switch typ.cat {
|
||||
case aliasT, ptrT:
|
||||
case linkedT, ptrT:
|
||||
gen = compositeGenerator(n, typ.val, rtyp)
|
||||
case arrayT, sliceT:
|
||||
gen = arrayLit
|
||||
|
||||
@@ -4,7 +4,7 @@ Package interp provides a complete Go interpreter.
|
||||
For the Go language itself, refer to the official Go specification
|
||||
https://golang.org/ref/spec.
|
||||
|
||||
Importing packages
|
||||
# Importing packages
|
||||
|
||||
Packages can be imported in source or binary form, using the standard
|
||||
Go import statement. In source form, packages are searched first in the
|
||||
@@ -16,7 +16,7 @@ Binary form packages are compiled and linked with the interpreter
|
||||
executable, and exposed to scripts with the Use method. The extract
|
||||
subcommand of yaegi can be used to generate package wrappers.
|
||||
|
||||
Custom build tags
|
||||
# Custom build tags
|
||||
|
||||
Custom build tags allow to control which files in imported source
|
||||
packages are interpreted, in the same way as the "-tags" option of the
|
||||
|
||||
@@ -161,8 +161,9 @@ func (interp *Interpreter) gta(root *node, rpath, importPath, pkgName string) ([
|
||||
switch rtn.kind {
|
||||
case starExpr:
|
||||
typPtr = true
|
||||
if rtn.child[0].kind == indexExpr {
|
||||
typName = rtn.child[0].child[0].ident
|
||||
switch c := rtn.child[0]; c.kind {
|
||||
case indexExpr, indexListExpr:
|
||||
typName = c.child[0].ident
|
||||
genericMethod = true
|
||||
}
|
||||
case indexExpr:
|
||||
@@ -175,7 +176,7 @@ func (interp *Interpreter) gta(root *node, rpath, importPath, pkgName string) ([
|
||||
revisit = append(revisit, n)
|
||||
return false
|
||||
}
|
||||
if sym.kind != typeSym || (sym.node != nil && sym.node.kind == typeSpecAssign) {
|
||||
if sym.typ.path != pkgName {
|
||||
err = n.cfgErrorf("cannot define new methods on non-local type %s", baseType(sym.typ).id())
|
||||
return false
|
||||
}
|
||||
@@ -297,7 +298,7 @@ func (interp *Interpreter) gta(root *node, rpath, importPath, pkgName string) ([
|
||||
typeName := n.child[0].ident
|
||||
if len(n.child) > 2 {
|
||||
// Handle a generic type: skip definition as parameter is not instantiated yet.
|
||||
n.typ = genericOf(nil, typeName, withNode(n.child[0]), withScope(sc))
|
||||
n.typ = genericOf(nil, typeName, pkgName, withNode(n.child[0]), withScope(sc))
|
||||
if _, exists := sc.sym[typeName]; !exists {
|
||||
sc.sym[typeName] = &symbol{kind: typeSym, node: n}
|
||||
}
|
||||
@@ -311,6 +312,15 @@ func (interp *Interpreter) gta(root *node, rpath, importPath, pkgName string) ([
|
||||
return false
|
||||
}
|
||||
|
||||
if n.kind == typeSpecAssign {
|
||||
// Create an aliased type in the current scope
|
||||
sc.sym[typeName] = &symbol{kind: typeSym, node: n, typ: typ}
|
||||
n.typ = typ
|
||||
break
|
||||
}
|
||||
|
||||
// else we are not an alias (typeSpec)
|
||||
|
||||
switch n.child[1].kind {
|
||||
case identExpr, selectorExpr:
|
||||
n.typ = namedOf(typ, pkgName, typeName, withNode(n.child[0]), withScope(sc))
|
||||
@@ -332,24 +342,15 @@ func (interp *Interpreter) gta(root *node, rpath, importPath, pkgName string) ([
|
||||
}
|
||||
sym, exists := sc.sym[typeName]
|
||||
if !exists {
|
||||
sc.sym[typeName] = &symbol{kind: typeSym, node: n}
|
||||
} else {
|
||||
if sym.typ != nil && (len(sym.typ.method) > 0) {
|
||||
if n.kind == typeSpecAssign {
|
||||
err = n.cfgErrorf("cannot define new methods on non-local type %s", baseType(typ).id())
|
||||
return false
|
||||
}
|
||||
// Type has already been seen as a receiver in a method function
|
||||
for _, m := range sym.typ.method {
|
||||
n.typ.addMethod(m)
|
||||
}
|
||||
} else {
|
||||
// TODO(mpl): figure out how to detect redeclarations without breaking type aliases.
|
||||
// Allow redeclarations for now.
|
||||
sc.sym[typeName] = &symbol{kind: typeSym, node: n}
|
||||
sym = &symbol{kind: typeSym, node: n}
|
||||
sc.sym[typeName] = sym
|
||||
} else if sym.typ != nil && (len(sym.typ.method) > 0) {
|
||||
// Type has already been seen as a receiver in a method function
|
||||
for _, m := range sym.typ.method {
|
||||
n.typ.addMethod(m)
|
||||
}
|
||||
}
|
||||
sc.sym[typeName].typ = n.typ
|
||||
sym.typ = n.typ
|
||||
if !n.typ.isComplete() {
|
||||
revisit = append(revisit, n)
|
||||
}
|
||||
@@ -367,7 +368,7 @@ func (interp *Interpreter) gta(root *node, rpath, importPath, pkgName string) ([
|
||||
func baseType(t *itype) *itype {
|
||||
for {
|
||||
switch t.cat {
|
||||
case ptrT, aliasT:
|
||||
case ptrT, linkedT:
|
||||
t = t.val
|
||||
default:
|
||||
return t
|
||||
@@ -439,7 +440,7 @@ func definedType(typ *itype) error {
|
||||
return err
|
||||
}
|
||||
fallthrough
|
||||
case aliasT, arrayT, chanT, chanSendT, chanRecvT, ptrT, variadicT:
|
||||
case linkedT, arrayT, chanT, chanSendT, chanRecvT, ptrT, variadicT:
|
||||
if err := definedType(typ.val); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
221
interp/interp.go
221
interp/interp.go
@@ -4,16 +4,12 @@ import (
|
||||
"bufio"
|
||||
"context"
|
||||
"errors"
|
||||
"flag"
|
||||
"fmt"
|
||||
"go/build"
|
||||
"go/constant"
|
||||
"go/scanner"
|
||||
"go/token"
|
||||
"io"
|
||||
"io/fs"
|
||||
"log"
|
||||
"math/bits"
|
||||
"os"
|
||||
"os/signal"
|
||||
"path"
|
||||
@@ -547,62 +543,6 @@ func (interp *Interpreter) EvalTest(path string) error {
|
||||
return err
|
||||
}
|
||||
|
||||
// Symbols returns a map of interpreter exported symbol values for the given
|
||||
// import path. If the argument is the empty string, all known symbols are
|
||||
// returned.
|
||||
func (interp *Interpreter) Symbols(importPath string) Exports {
|
||||
m := map[string]map[string]reflect.Value{}
|
||||
interp.mutex.RLock()
|
||||
defer interp.mutex.RUnlock()
|
||||
|
||||
for k, v := range interp.srcPkg {
|
||||
if importPath != "" && k != importPath {
|
||||
continue
|
||||
}
|
||||
syms := map[string]reflect.Value{}
|
||||
for n, s := range v {
|
||||
if !canExport(n) {
|
||||
// Skip private non-exported symbols.
|
||||
continue
|
||||
}
|
||||
switch s.kind {
|
||||
case constSym:
|
||||
syms[n] = s.rval
|
||||
case funcSym:
|
||||
syms[n] = genFunctionWrapper(s.node)(interp.frame)
|
||||
case varSym:
|
||||
syms[n] = interp.frame.data[s.index]
|
||||
case typeSym:
|
||||
syms[n] = reflect.New(s.typ.TypeOf())
|
||||
}
|
||||
}
|
||||
|
||||
if len(syms) > 0 {
|
||||
m[k] = syms
|
||||
}
|
||||
|
||||
if importPath != "" {
|
||||
return m
|
||||
}
|
||||
}
|
||||
|
||||
if importPath != "" && len(m) > 0 {
|
||||
return m
|
||||
}
|
||||
|
||||
for k, v := range interp.binPkg {
|
||||
if importPath != "" && k != importPath {
|
||||
continue
|
||||
}
|
||||
m[k] = v
|
||||
if importPath != "" {
|
||||
return m
|
||||
}
|
||||
}
|
||||
|
||||
return m
|
||||
}
|
||||
|
||||
func isFile(filesystem fs.FS, path string) bool {
|
||||
fi, err := fs.Stat(filesystem, path)
|
||||
return err == nil && fi.Mode().IsRegular()
|
||||
@@ -664,167 +604,6 @@ func (interp *Interpreter) stop() {
|
||||
|
||||
func (interp *Interpreter) runid() uint64 { return atomic.LoadUint64(&interp.id) }
|
||||
|
||||
// getWrapper returns the wrapper type of the corresponding interface, or nil if not found.
|
||||
func (interp *Interpreter) getWrapper(t reflect.Type) reflect.Type {
|
||||
if p, ok := interp.binPkg[t.PkgPath()]; ok {
|
||||
return p["_"+t.Name()].Type().Elem()
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// Use loads binary runtime symbols in the interpreter context so
|
||||
// they can be used in interpreted code.
|
||||
func (interp *Interpreter) Use(values Exports) error {
|
||||
for k, v := range values {
|
||||
importPath := path.Dir(k)
|
||||
packageName := path.Base(k)
|
||||
|
||||
if k == "." && v["MapTypes"].IsValid() {
|
||||
// Use mapping for special interface wrappers.
|
||||
for kk, vv := range v["MapTypes"].Interface().(map[reflect.Value][]reflect.Type) {
|
||||
interp.mapTypes[kk] = vv
|
||||
}
|
||||
continue
|
||||
}
|
||||
|
||||
if importPath == "." {
|
||||
return fmt.Errorf("export path %[1]q is missing a package name; did you mean '%[1]s/%[1]s'?", k)
|
||||
}
|
||||
|
||||
if importPath == selfPrefix {
|
||||
interp.hooks.Parse(v)
|
||||
continue
|
||||
}
|
||||
|
||||
if interp.binPkg[importPath] == nil {
|
||||
interp.binPkg[importPath] = make(map[string]reflect.Value)
|
||||
interp.pkgNames[importPath] = packageName
|
||||
}
|
||||
|
||||
for s, sym := range v {
|
||||
interp.binPkg[importPath][s] = sym
|
||||
}
|
||||
if k == selfPath {
|
||||
interp.binPkg[importPath]["Self"] = reflect.ValueOf(interp)
|
||||
}
|
||||
}
|
||||
|
||||
// Checks if input values correspond to stdlib packages by looking for one
|
||||
// well known stdlib package path.
|
||||
if _, ok := values["fmt/fmt"]; ok {
|
||||
fixStdlib(interp)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// fixStdlib redefines interpreter stdlib symbols to use the standard input,
|
||||
// output and errror assigned to the interpreter. The changes are limited to
|
||||
// the interpreter only.
|
||||
// Note that it is possible to escape the virtualized stdio by
|
||||
// read/write directly to file descriptors 0, 1, 2.
|
||||
func fixStdlib(interp *Interpreter) {
|
||||
p := interp.binPkg["fmt"]
|
||||
if p == nil {
|
||||
return
|
||||
}
|
||||
|
||||
stdin, stdout, stderr := interp.stdin, interp.stdout, interp.stderr
|
||||
|
||||
p["Print"] = reflect.ValueOf(func(a ...interface{}) (n int, err error) { return fmt.Fprint(stdout, a...) })
|
||||
p["Printf"] = reflect.ValueOf(func(f string, a ...interface{}) (n int, err error) { return fmt.Fprintf(stdout, f, a...) })
|
||||
p["Println"] = reflect.ValueOf(func(a ...interface{}) (n int, err error) { return fmt.Fprintln(stdout, a...) })
|
||||
|
||||
p["Scan"] = reflect.ValueOf(func(a ...interface{}) (n int, err error) { return fmt.Fscan(stdin, a...) })
|
||||
p["Scanf"] = reflect.ValueOf(func(f string, a ...interface{}) (n int, err error) { return fmt.Fscanf(stdin, f, a...) })
|
||||
p["Scanln"] = reflect.ValueOf(func(a ...interface{}) (n int, err error) { return fmt.Fscanln(stdin, a...) })
|
||||
|
||||
// Update mapTypes to virtualized symbols as well.
|
||||
interp.mapTypes[p["Print"]] = interp.mapTypes[reflect.ValueOf(fmt.Print)]
|
||||
interp.mapTypes[p["Printf"]] = interp.mapTypes[reflect.ValueOf(fmt.Printf)]
|
||||
interp.mapTypes[p["Println"]] = interp.mapTypes[reflect.ValueOf(fmt.Println)]
|
||||
interp.mapTypes[p["Scan"]] = interp.mapTypes[reflect.ValueOf(fmt.Scan)]
|
||||
interp.mapTypes[p["Scanf"]] = interp.mapTypes[reflect.ValueOf(fmt.Scanf)]
|
||||
interp.mapTypes[p["Scanln"]] = interp.mapTypes[reflect.ValueOf(fmt.Scanln)]
|
||||
|
||||
if p = interp.binPkg["flag"]; p != nil {
|
||||
c := flag.NewFlagSet(os.Args[0], flag.PanicOnError)
|
||||
c.SetOutput(stderr)
|
||||
p["CommandLine"] = reflect.ValueOf(&c).Elem()
|
||||
}
|
||||
|
||||
if p = interp.binPkg["log"]; p != nil {
|
||||
l := log.New(stderr, "", log.LstdFlags)
|
||||
// Restrict Fatal symbols to panic instead of exit.
|
||||
p["Fatal"] = reflect.ValueOf(l.Panic)
|
||||
p["Fatalf"] = reflect.ValueOf(l.Panicf)
|
||||
p["Fatalln"] = reflect.ValueOf(l.Panicln)
|
||||
|
||||
p["Flags"] = reflect.ValueOf(l.Flags)
|
||||
p["Output"] = reflect.ValueOf(l.Output)
|
||||
p["Panic"] = reflect.ValueOf(l.Panic)
|
||||
p["Panicf"] = reflect.ValueOf(l.Panicf)
|
||||
p["Panicln"] = reflect.ValueOf(l.Panicln)
|
||||
p["Prefix"] = reflect.ValueOf(l.Prefix)
|
||||
p["Print"] = reflect.ValueOf(l.Print)
|
||||
p["Printf"] = reflect.ValueOf(l.Printf)
|
||||
p["Println"] = reflect.ValueOf(l.Println)
|
||||
p["SetFlags"] = reflect.ValueOf(l.SetFlags)
|
||||
p["SetOutput"] = reflect.ValueOf(l.SetOutput)
|
||||
p["SetPrefix"] = reflect.ValueOf(l.SetPrefix)
|
||||
p["Writer"] = reflect.ValueOf(l.Writer)
|
||||
|
||||
// Update mapTypes to virtualized symbols as well.
|
||||
interp.mapTypes[p["Print"]] = interp.mapTypes[reflect.ValueOf(log.Print)]
|
||||
interp.mapTypes[p["Printf"]] = interp.mapTypes[reflect.ValueOf(log.Printf)]
|
||||
interp.mapTypes[p["Println"]] = interp.mapTypes[reflect.ValueOf(log.Println)]
|
||||
interp.mapTypes[p["Panic"]] = interp.mapTypes[reflect.ValueOf(log.Panic)]
|
||||
interp.mapTypes[p["Panicf"]] = interp.mapTypes[reflect.ValueOf(log.Panicf)]
|
||||
interp.mapTypes[p["Panicln"]] = interp.mapTypes[reflect.ValueOf(log.Panicln)]
|
||||
}
|
||||
|
||||
if p = interp.binPkg["os"]; p != nil {
|
||||
p["Args"] = reflect.ValueOf(&interp.args).Elem()
|
||||
if interp.specialStdio {
|
||||
// Inherit streams from interpreter even if they do not have a file descriptor.
|
||||
p["Stdin"] = reflect.ValueOf(&stdin).Elem()
|
||||
p["Stdout"] = reflect.ValueOf(&stdout).Elem()
|
||||
p["Stderr"] = reflect.ValueOf(&stderr).Elem()
|
||||
} else {
|
||||
// Inherits streams from interpreter only if they have a file descriptor and preserve original type.
|
||||
if s, ok := stdin.(*os.File); ok {
|
||||
p["Stdin"] = reflect.ValueOf(&s).Elem()
|
||||
}
|
||||
if s, ok := stdout.(*os.File); ok {
|
||||
p["Stdout"] = reflect.ValueOf(&s).Elem()
|
||||
}
|
||||
if s, ok := stderr.(*os.File); ok {
|
||||
p["Stderr"] = reflect.ValueOf(&s).Elem()
|
||||
}
|
||||
}
|
||||
if !interp.unrestricted {
|
||||
// In restricted mode, scripts can only access to a passed virtualized env, and can not write the real one.
|
||||
getenv := func(key string) string { return interp.env[key] }
|
||||
p["Clearenv"] = reflect.ValueOf(func() { interp.env = map[string]string{} })
|
||||
p["ExpandEnv"] = reflect.ValueOf(func(s string) string { return os.Expand(s, getenv) })
|
||||
p["Getenv"] = reflect.ValueOf(getenv)
|
||||
p["LookupEnv"] = reflect.ValueOf(func(key string) (s string, ok bool) { s, ok = interp.env[key]; return })
|
||||
p["Setenv"] = reflect.ValueOf(func(key, value string) error { interp.env[key] = value; return nil })
|
||||
p["Unsetenv"] = reflect.ValueOf(func(key string) error { delete(interp.env, key); return nil })
|
||||
p["Environ"] = reflect.ValueOf(func() (a []string) {
|
||||
for k, v := range interp.env {
|
||||
a = append(a, k+"="+v)
|
||||
}
|
||||
return
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
if p = interp.binPkg["math/bits"]; p != nil {
|
||||
// Do not trust extracted value maybe from another arch.
|
||||
p["UintSize"] = reflect.ValueOf(constant.MakeInt64(bits.UintSize))
|
||||
}
|
||||
}
|
||||
|
||||
// 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 {
|
||||
|
||||
@@ -109,6 +109,7 @@ func TestInterpConsistencyBuild(t *testing.T) {
|
||||
file.Name() == "range9.go" || // expect error
|
||||
file.Name() == "unsafe6.go" || // needs go.mod to be 1.17
|
||||
file.Name() == "unsafe7.go" || // needs go.mod to be 1.17
|
||||
file.Name() == "type24.go" || // expect error
|
||||
file.Name() == "type27.go" || // expect error
|
||||
file.Name() == "type28.go" || // expect error
|
||||
file.Name() == "type29.go" || // expect error
|
||||
|
||||
@@ -133,6 +133,9 @@ func TestEvalAssign(t *testing.T) {
|
||||
{src: "j := true || _", err: "1:33: cannot use _ as value"},
|
||||
{src: "j := true && _", err: "1:33: cannot use _ as value"},
|
||||
{src: "j := interface{}(int(1)); j.(_)", err: "1:54: cannot use _ as value"},
|
||||
{src: "ff := func() (a, b, c int) {return 1, 2, 3}; x, y, x := ff()", err: "1:73: x repeated on left side of :="},
|
||||
{src: "xx := 1; xx, _ := 2, 3", err: "1:37: no new variables on left side of :="},
|
||||
{src: "1 = 2", err: "1:28: cannot assign to 1 (untyped int constant)"},
|
||||
})
|
||||
}
|
||||
|
||||
@@ -216,6 +219,8 @@ func TestEvalTypeSpec(t *testing.T) {
|
||||
runTests(t, i, []testCase{
|
||||
{src: `type _ struct{}`, err: "1:19: cannot use _ as value"},
|
||||
{src: `a := struct{a, _ int}{32, 0}`, res: "{32 0}"},
|
||||
{src: "type A int; type A = string", err: "1:31: A redeclared in this block"},
|
||||
{src: "type B int; type B string", err: "1:31: B redeclared in this block"},
|
||||
})
|
||||
}
|
||||
|
||||
@@ -640,6 +645,8 @@ func TestEvalChan(t *testing.T) {
|
||||
return ok && msg == "ping"
|
||||
})()`, res: "true",
|
||||
},
|
||||
{src: `a :=5; a <- 4`, err: "cannot send to non-channel int"},
|
||||
{src: `a :=5; b := <-a`, err: "cannot receive from non-channel int"},
|
||||
})
|
||||
}
|
||||
|
||||
|
||||
@@ -33,7 +33,6 @@ func Hi(h Helloer) {
|
||||
//
|
||||
// Only the Wrap type definition needs to be exported to the interpreter (not
|
||||
// the interfaces and methods definitions).
|
||||
//
|
||||
type Wrap struct {
|
||||
DoHello func() // related to the Hello() method.
|
||||
// Other interface method wrappers...
|
||||
|
||||
@@ -7,6 +7,7 @@ import (
|
||||
"go/token"
|
||||
"os"
|
||||
"path/filepath"
|
||||
"regexp"
|
||||
"strings"
|
||||
"testing"
|
||||
|
||||
@@ -76,7 +77,9 @@ func runCheck(t *testing.T, p string) {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
if res := strings.TrimSpace(stdout.String()); res != wanted {
|
||||
// Remove path in output, to have results independent of location.
|
||||
re := regexp.MustCompile(p + ":")
|
||||
if res := re.ReplaceAllString(strings.TrimSpace(stdout.String()), ""); res != wanted {
|
||||
t.Errorf("\ngot: %q,\nwant: %q", res, wanted)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -2626,7 +2626,7 @@ func equal(n *node) {
|
||||
c0, c1 := n.child[0], n.child[1]
|
||||
t0, t1 := c0.typ.TypeOf(), c1.typ.TypeOf()
|
||||
|
||||
if c0.typ.cat == aliasT || c1.typ.cat == aliasT {
|
||||
if c0.typ.cat == linkedT || c1.typ.cat == linkedT {
|
||||
switch {
|
||||
case isInterface:
|
||||
v0 := genValue(c0)
|
||||
@@ -4538,7 +4538,7 @@ func notEqual(n *node) {
|
||||
c0, c1 := n.child[0], n.child[1]
|
||||
t0, t1 := c0.typ.TypeOf(), c1.typ.TypeOf()
|
||||
|
||||
if c0.typ.cat == aliasT || c1.typ.cat == aliasT {
|
||||
if c0.typ.cat == linkedT || c1.typ.cat == linkedT {
|
||||
switch {
|
||||
case isInterface:
|
||||
v0 := genValue(c0)
|
||||
|
||||
295
interp/run.go
295
interp/run.go
@@ -401,7 +401,7 @@ func typeAssert(n *node, withResult, withOk bool) {
|
||||
ok = v.IsValid()
|
||||
if !ok {
|
||||
if !withOk {
|
||||
panic(fmt.Sprintf("interface conversion: interface {} is nil, not %s", rtype.String()))
|
||||
panic(n.cfgErrorf("interface conversion: interface {} is nil, not %s", rtype.String()))
|
||||
}
|
||||
return next
|
||||
}
|
||||
@@ -409,7 +409,7 @@ func typeAssert(n *node, withResult, withOk bool) {
|
||||
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))
|
||||
panic(n.cfgErrorf("interface conversion: %s is not %s: missing method %s", leftType.String(), rtype.String(), method))
|
||||
}
|
||||
return next
|
||||
}
|
||||
@@ -430,14 +430,18 @@ func typeAssert(n *node, withResult, withOk bool) {
|
||||
concrete := val.Interface()
|
||||
ctyp := reflect.TypeOf(concrete)
|
||||
|
||||
if vv, ok := concrete.(valueInterface); ok {
|
||||
ctyp = vv.value.Type()
|
||||
concrete = vv.value.Interface()
|
||||
}
|
||||
ok = canAssertTypes(ctyp, rtype)
|
||||
if !ok {
|
||||
if !withOk {
|
||||
// TODO(mpl): think about whether this should ever happen.
|
||||
if ctyp == nil {
|
||||
panic(fmt.Sprintf("interface conversion: interface {} is nil, not %s", rtype.String()))
|
||||
panic(n.cfgErrorf("interface conversion: interface {} is nil, not %s", rtype.String()))
|
||||
}
|
||||
panic(fmt.Sprintf("interface conversion: interface {} is %s, not %s", ctyp.String(), rtype.String()))
|
||||
panic(n.cfgErrorf("interface conversion: interface {} is %s, not %s", ctyp.String(), rtype.String()))
|
||||
}
|
||||
return next
|
||||
}
|
||||
@@ -462,7 +466,7 @@ func typeAssert(n *node, withResult, withOk bool) {
|
||||
}
|
||||
if !ok {
|
||||
if !withOk {
|
||||
panic(fmt.Sprintf("interface conversion: interface {} is nil, not %s", rtype.String()))
|
||||
panic(n.cfgErrorf("interface conversion: interface {} is nil, not %s", rtype.String()))
|
||||
}
|
||||
return next
|
||||
}
|
||||
@@ -475,7 +479,7 @@ func typeAssert(n *node, withResult, withOk bool) {
|
||||
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))
|
||||
panic(n.cfgErrorf("interface conversion: %s is not %s: missing method %s", v.Type().String(), rtype.String(), method))
|
||||
}
|
||||
return next
|
||||
}
|
||||
@@ -495,7 +499,7 @@ func typeAssert(n *node, withResult, withOk bool) {
|
||||
if !ok || !v.value.IsValid() {
|
||||
ok = false
|
||||
if !withOk {
|
||||
panic(fmt.Sprintf("interface conversion: interface {} is nil, not %s", rtype.String()))
|
||||
panic(n.cfgErrorf("interface conversion: interface {} is nil, not %s", rtype.String()))
|
||||
}
|
||||
return next
|
||||
}
|
||||
@@ -503,7 +507,7 @@ func typeAssert(n *node, withResult, withOk bool) {
|
||||
ok = canAssertTypes(v.value.Type(), rtype)
|
||||
if !ok {
|
||||
if !withOk {
|
||||
panic(fmt.Sprintf("interface conversion: interface {} is %s, not %s", v.value.Type().String(), rtype.String()))
|
||||
panic(n.cfgErrorf("interface conversion: interface {} is %s, not %s", v.value.Type().String(), rtype.String()))
|
||||
}
|
||||
return next
|
||||
}
|
||||
@@ -553,7 +557,7 @@ func convert(n *node) {
|
||||
if c.isNil() { // convert nil to type
|
||||
// TODO(mpl): Try to completely remove, as maybe frameType already does the job for interfaces.
|
||||
if isInterfaceSrc(n.child[0].typ) && !isEmptyInterface(n.child[0].typ) {
|
||||
typ = reflect.TypeOf((*valueInterface)(nil)).Elem()
|
||||
typ = valueInterfaceType
|
||||
}
|
||||
n.exec = func(f *frame) bltn {
|
||||
dest(f).Set(reflect.New(typ).Elem())
|
||||
@@ -562,29 +566,11 @@ func convert(n *node) {
|
||||
return
|
||||
}
|
||||
|
||||
if isFuncSrc(n.child[0].typ) && isFuncSrc(c.typ) {
|
||||
value := genValue(c)
|
||||
n.exec = func(f *frame) bltn {
|
||||
n, ok := value(f).Interface().(*node)
|
||||
if !ok || !n.typ.convertibleTo(c.typ) {
|
||||
panic("cannot convert")
|
||||
}
|
||||
n1 := *n
|
||||
n1.typ = c.typ
|
||||
dest(f).Set(reflect.ValueOf(&n1))
|
||||
return next
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
doConvert := true
|
||||
var value func(*frame) reflect.Value
|
||||
switch {
|
||||
case isFuncSrc(c.typ):
|
||||
value = genFunctionWrapper(c)
|
||||
case isFuncSrc(n.child[0].typ) && c.typ.cat == valueT:
|
||||
doConvert = false
|
||||
value = genValueNode(c)
|
||||
default:
|
||||
value = genValue(c)
|
||||
}
|
||||
@@ -655,8 +641,8 @@ func assign(n *node) {
|
||||
|
||||
for i := 0; i < n.nleft; i++ {
|
||||
dest, src := n.child[i], n.child[sbase+i]
|
||||
if isFuncSrc(src.typ) && isField(dest) {
|
||||
svalue[i] = genFunctionWrapper(src)
|
||||
if isNamedFuncSrc(src.typ) {
|
||||
svalue[i] = genFuncValue(src)
|
||||
} else {
|
||||
svalue[i] = genDestValue(dest.typ, src)
|
||||
}
|
||||
@@ -710,10 +696,8 @@ func assign(n *node) {
|
||||
for i := range types {
|
||||
var t reflect.Type
|
||||
switch typ := n.child[sbase+i].typ; {
|
||||
case isFuncSrc(typ):
|
||||
t = reflect.TypeOf((*node)(nil))
|
||||
case isInterfaceSrc(typ):
|
||||
t = reflect.TypeOf((*valueInterface)(nil)).Elem()
|
||||
t = valueInterfaceType
|
||||
default:
|
||||
t = typ.TypeOf()
|
||||
}
|
||||
@@ -948,9 +932,6 @@ func genFunctionWrapper(n *node) func(*frame) reflect.Value {
|
||||
var def *node
|
||||
var ok bool
|
||||
|
||||
if n.kind == basicLit {
|
||||
return func(f *frame) reflect.Value { return n.rval }
|
||||
}
|
||||
if def, ok = n.val.(*node); !ok {
|
||||
return genValueAsFunctionWrapper(n)
|
||||
}
|
||||
@@ -959,11 +940,7 @@ func genFunctionWrapper(n *node) func(*frame) reflect.Value {
|
||||
var rcvr func(*frame) reflect.Value
|
||||
|
||||
if n.recv != nil {
|
||||
if n.recv.node.typ.cat != defRecvType(def).cat {
|
||||
rcvr = genValueRecvIndirect(n)
|
||||
} else {
|
||||
rcvr = genValueRecv(n)
|
||||
}
|
||||
rcvr = genValueRecv(n)
|
||||
}
|
||||
funcType := n.typ.TypeOf()
|
||||
|
||||
@@ -985,15 +962,20 @@ func genFunctionWrapper(n *node) func(*frame) reflect.Value {
|
||||
// Copy method receiver as first argument.
|
||||
src, dest := rcvr(f), d[numRet]
|
||||
sk, dk := src.Kind(), dest.Kind()
|
||||
for {
|
||||
vs, ok := src.Interface().(valueInterface)
|
||||
if !ok {
|
||||
break
|
||||
}
|
||||
src = vs.value
|
||||
sk = src.Kind()
|
||||
}
|
||||
switch {
|
||||
case sk == reflect.Ptr && dk != reflect.Ptr:
|
||||
dest.Set(src.Elem())
|
||||
case sk != reflect.Ptr && dk == reflect.Ptr:
|
||||
dest.Set(src.Addr())
|
||||
default:
|
||||
if wrappedSrc, ok := src.Interface().(valueInterface); ok {
|
||||
src = wrappedSrc.value
|
||||
}
|
||||
dest.Set(src)
|
||||
}
|
||||
d = d[numRet+1:]
|
||||
@@ -1007,12 +989,10 @@ func genFunctionWrapper(n *node) func(*frame) reflect.Value {
|
||||
}
|
||||
typ := def.typ.arg[i]
|
||||
switch {
|
||||
case isEmptyInterface(typ):
|
||||
case isEmptyInterface(typ) || typ.TypeOf() == valueInterfaceType:
|
||||
d[i].Set(arg)
|
||||
case isInterfaceSrc(typ):
|
||||
d[i].Set(reflect.ValueOf(valueInterface{value: arg.Elem()}))
|
||||
case isFuncSrc(typ) && arg.Kind() == reflect.Func:
|
||||
d[i].Set(reflect.ValueOf(genFunctionNode(arg)))
|
||||
default:
|
||||
d[i].Set(arg)
|
||||
}
|
||||
@@ -1021,21 +1001,11 @@ func genFunctionWrapper(n *node) func(*frame) reflect.Value {
|
||||
// Interpreter code execution.
|
||||
runCfg(start, fr, def, n)
|
||||
|
||||
result := fr.data[:numRet]
|
||||
for i, r := range result {
|
||||
if v, ok := r.Interface().(*node); ok {
|
||||
result[i] = genFunctionWrapper(v)(f)
|
||||
}
|
||||
}
|
||||
return result
|
||||
return fr.data[:numRet]
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func genFunctionNode(v reflect.Value) *node {
|
||||
return &node{kind: funcType, action: aNop, rval: v, typ: valueTOf(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 {
|
||||
@@ -1050,19 +1020,24 @@ func genInterfaceWrapper(n *node, typ reflect.Type) func(*frame) reflect.Value {
|
||||
return value
|
||||
}
|
||||
}
|
||||
mn := typ.NumMethod()
|
||||
|
||||
// Retrieve methods from the interface wrapper, which is a struct where all fields
|
||||
// except the first define the methods to implement.
|
||||
// As the field name was generated with a prefixed first character (in order to avoid
|
||||
// collisions with method names), this first character is ignored in comparisons.
|
||||
wrap := getWrapper(n, typ)
|
||||
mn := wrap.NumField() - 1
|
||||
names := make([]string, mn)
|
||||
methods := make([]*node, mn)
|
||||
indexes := make([][]int, mn)
|
||||
for i := 0; i < mn; i++ {
|
||||
names[i] = typ.Method(i).Name
|
||||
names[i] = wrap.Field(i + 1).Name[1:]
|
||||
methods[i], indexes[i] = n.typ.lookupMethod(names[i])
|
||||
if methods[i] == nil && n.typ.cat != nilT {
|
||||
// interpreted method not found, look for binary method, possibly embedded
|
||||
_, indexes[i], _, _ = n.typ.lookupBinMethod(names[i])
|
||||
}
|
||||
}
|
||||
wrap := n.interp.getWrapper(typ)
|
||||
|
||||
return func(f *frame) reflect.Value {
|
||||
v := value(f)
|
||||
@@ -1169,11 +1144,6 @@ func call(n *node) {
|
||||
// Compute method receiver value.
|
||||
values = append(values, genValueRecv(c0))
|
||||
method = true
|
||||
case len(c0.child) > 0 && c0.child[0].typ != nil && isInterfaceSrc(c0.child[0].typ):
|
||||
recvIndexLater = true
|
||||
values = append(values, genValueBinRecv(c0, &receiver{node: c0.child[0]}))
|
||||
value = genValueBinMethodOnInterface(n, value)
|
||||
method = true
|
||||
case c0.action == aMethod:
|
||||
// Add a place holder for interface method receiver.
|
||||
values = append(values, nil)
|
||||
@@ -1235,7 +1205,7 @@ func call(n *node) {
|
||||
case isInterfaceBin(arg):
|
||||
values = append(values, genInterfaceWrapper(c, arg.rtype))
|
||||
case isFuncSrc(arg):
|
||||
values = append(values, genValueNode(c))
|
||||
values = append(values, genFuncValue(c))
|
||||
default:
|
||||
values = append(values, genValue(c))
|
||||
}
|
||||
@@ -1300,21 +1270,37 @@ func call(n *node) {
|
||||
var ok bool
|
||||
|
||||
bf := value(f)
|
||||
|
||||
if def, ok = bf.Interface().(*node); ok {
|
||||
bf = def.rval
|
||||
}
|
||||
|
||||
// Call bin func if defined
|
||||
if bf.IsValid() {
|
||||
var callf func([]reflect.Value) []reflect.Value
|
||||
|
||||
// Lambda definitions are necessary here. Due to reflect internals,
|
||||
// having `callf = bf.Call` or `callf = bf.CallSlice` does not work.
|
||||
//nolint:gocritic
|
||||
if hasVariadicArgs {
|
||||
callf = func(in []reflect.Value) []reflect.Value { return bf.CallSlice(in) }
|
||||
} else {
|
||||
callf = func(in []reflect.Value) []reflect.Value { return bf.Call(in) }
|
||||
}
|
||||
|
||||
if method && len(values) > bf.Type().NumIn() {
|
||||
// The receiver is already passed in the function wrapper, skip it.
|
||||
values = values[1:]
|
||||
}
|
||||
in := make([]reflect.Value, len(values))
|
||||
for i, v := range values {
|
||||
in[i] = v(f)
|
||||
}
|
||||
if goroutine {
|
||||
go bf.Call(in)
|
||||
go callf(in)
|
||||
return tnext
|
||||
}
|
||||
out := bf.Call(in)
|
||||
out := callf(in)
|
||||
for i, v := range rvalues {
|
||||
if v != nil {
|
||||
v(f).Set(out[i])
|
||||
@@ -1548,19 +1534,14 @@ func callBin(n *node) {
|
||||
}
|
||||
|
||||
switch {
|
||||
case isFuncSrc(c.typ):
|
||||
values = append(values, genFunctionWrapper(c))
|
||||
case isEmptyInterface(c.typ):
|
||||
values = append(values, genValue(c))
|
||||
case isInterfaceSrc(c.typ):
|
||||
values = append(values, genValueInterfaceValue(c))
|
||||
case c.typ.cat == arrayT || c.typ.cat == variadicT:
|
||||
switch {
|
||||
case isEmptyInterface(c.typ.val):
|
||||
if isEmptyInterface(c.typ.val) {
|
||||
values = append(values, genValueArray(c))
|
||||
case isInterfaceSrc(c.typ.val):
|
||||
values = append(values, genValueInterfaceArray(c))
|
||||
default:
|
||||
} else {
|
||||
values = append(values, genInterfaceWrapper(c, defType))
|
||||
}
|
||||
case isPtrSrc(c.typ):
|
||||
@@ -1904,8 +1885,10 @@ func getIndexMap2(n *node) {
|
||||
|
||||
const fork = true // Duplicate frame in frame.clone().
|
||||
|
||||
// getFunc compiles a closure function generator for anonymous functions.
|
||||
func getFunc(n *node) {
|
||||
dest := genValue(n)
|
||||
i := n.findex
|
||||
l := n.level
|
||||
next := getExec(n.tnext)
|
||||
|
||||
n.exec = func(f *frame) bltn {
|
||||
@@ -1913,7 +1896,42 @@ func getFunc(n *node) {
|
||||
nod := *n
|
||||
nod.val = &nod
|
||||
nod.frame = fr
|
||||
dest(f).Set(reflect.ValueOf(&nod))
|
||||
def := &nod
|
||||
numRet := len(def.typ.ret)
|
||||
|
||||
fct := reflect.MakeFunc(nod.typ.TypeOf(), func(in []reflect.Value) []reflect.Value {
|
||||
// Allocate and init local frame. All values to be settable and addressable.
|
||||
fr2 := newFrame(fr, len(def.types), fr.runid())
|
||||
d := fr2.data
|
||||
for i, t := range def.types {
|
||||
d[i] = reflect.New(t).Elem()
|
||||
}
|
||||
d = d[numRet:]
|
||||
|
||||
// Copy function input arguments in local frame.
|
||||
for i, arg := range in {
|
||||
if i >= len(d) {
|
||||
// In case of unused arg, there may be not even a frame entry allocated, just skip.
|
||||
break
|
||||
}
|
||||
typ := def.typ.arg[i]
|
||||
switch {
|
||||
case isEmptyInterface(typ) || typ.TypeOf() == valueInterfaceType:
|
||||
d[i].Set(arg)
|
||||
case isInterfaceSrc(typ):
|
||||
d[i].Set(reflect.ValueOf(valueInterface{value: arg.Elem()}))
|
||||
default:
|
||||
d[i].Set(arg)
|
||||
}
|
||||
}
|
||||
|
||||
// Interpreter code execution.
|
||||
runCfg(def.child[3].start, fr2, def, n)
|
||||
|
||||
return fr2.data[:numRet]
|
||||
})
|
||||
|
||||
getFrame(f, l).data[i] = fct
|
||||
return next
|
||||
}
|
||||
}
|
||||
@@ -1929,7 +1947,7 @@ func getMethod(n *node) {
|
||||
nod.val = &nod
|
||||
nod.recv = n.recv
|
||||
nod.frame = fr
|
||||
getFrame(f, l).data[i] = reflect.ValueOf(&nod)
|
||||
getFrame(f, l).data[i] = genFuncValue(&nod)(f)
|
||||
return next
|
||||
}
|
||||
}
|
||||
@@ -1950,10 +1968,12 @@ func getMethodByName(n *node) {
|
||||
}
|
||||
val = v
|
||||
}
|
||||
|
||||
if met := val.value.MethodByName(name); met.IsValid() {
|
||||
getFrame(f, l).data[i] = met
|
||||
return next
|
||||
}
|
||||
|
||||
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
|
||||
@@ -1963,16 +1983,38 @@ func getMethodByName(n *node) {
|
||||
}
|
||||
return next
|
||||
}
|
||||
|
||||
m, li := typ.lookupMethod(name)
|
||||
|
||||
// Try harder to find a matching embedded valueInterface.
|
||||
// TODO (marc): make sure it works for arbitrary depth and breadth.
|
||||
if m == nil && isStruct(val.node.typ) {
|
||||
v := val.value
|
||||
for v.Type().Kind() == reflect.Ptr {
|
||||
v = v.Elem()
|
||||
}
|
||||
nf := v.NumField()
|
||||
for i := 0; i < nf; i++ {
|
||||
var ok bool
|
||||
if val, ok = v.Field(i).Interface().(valueInterface); !ok {
|
||||
continue
|
||||
}
|
||||
if m, li = val.node.typ.lookupMethod(name); m != nil {
|
||||
break
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if m == nil {
|
||||
panic(n.cfgErrorf("method not found: %s", name))
|
||||
}
|
||||
|
||||
fr := f.clone(!fork)
|
||||
nod := *m
|
||||
nod.val = &nod
|
||||
nod.recv = &receiver{nil, val.value, li}
|
||||
nod.frame = fr
|
||||
getFrame(f, l).data[i] = reflect.ValueOf(&nod)
|
||||
getFrame(f, l).data[i] = genFuncValue(&nod)(f)
|
||||
return next
|
||||
}
|
||||
}
|
||||
@@ -2354,27 +2396,12 @@ func _return(n *node) {
|
||||
switch t := def.typ.ret[i]; t.cat {
|
||||
case errorT:
|
||||
values[i] = genInterfaceWrapper(c, t.TypeOf())
|
||||
case aliasT:
|
||||
if isInterfaceSrc(t) {
|
||||
values[i] = genValueInterface(c)
|
||||
} else {
|
||||
values[i] = genValue(c)
|
||||
}
|
||||
case funcT:
|
||||
values[i] = genValue(c)
|
||||
case interfaceT:
|
||||
if len(t.field) == 0 {
|
||||
// empty interface case.
|
||||
// we can't let genValueInterface deal with it, because we call on c,
|
||||
// not on n, which means that the interfaceT knowledge is lost.
|
||||
values[i] = genValue(c)
|
||||
break
|
||||
}
|
||||
values[i] = genValueInterface(c)
|
||||
case valueT:
|
||||
switch t.rtype.Kind() {
|
||||
case reflect.Interface:
|
||||
values[i] = genInterfaceWrapper(c, t.rtype)
|
||||
values[i] = genInterfaceWrapper(c, t.TypeOf())
|
||||
continue
|
||||
case reflect.Func:
|
||||
values[i] = genFunctionWrapper(c)
|
||||
@@ -2382,9 +2409,19 @@ func _return(n *node) {
|
||||
}
|
||||
fallthrough
|
||||
default:
|
||||
if c.typ.untyped {
|
||||
values[i] = genValueAs(c, def.typ.ret[i].TypeOf())
|
||||
} else {
|
||||
switch {
|
||||
case isInterfaceSrc(t):
|
||||
if len(t.field) == 0 {
|
||||
// empty interface case.
|
||||
// we can't let genValueInterface deal with it, because we call on c,
|
||||
// not on n, which means that the interfaceT knowledge is lost.
|
||||
values[i] = genValue(c)
|
||||
break
|
||||
}
|
||||
values[i] = genValueInterface(c)
|
||||
case c.typ.untyped:
|
||||
values[i] = genValueAs(c, t.TypeOf())
|
||||
default:
|
||||
values[i] = genValue(c)
|
||||
}
|
||||
}
|
||||
@@ -2579,7 +2616,7 @@ func doCompositeBinStruct(n *node, hasType bool) {
|
||||
next := getExec(n.tnext)
|
||||
value := valueGenerator(n, n.findex)
|
||||
typ := n.typ.rtype
|
||||
if n.typ.cat == ptrT || n.typ.cat == aliasT {
|
||||
if n.typ.cat == ptrT || n.typ.cat == linkedT {
|
||||
typ = n.typ.val.rtype
|
||||
}
|
||||
child := n.child
|
||||
@@ -2646,7 +2683,7 @@ func doComposite(n *node, hasType bool, keyed bool) {
|
||||
value := valueGenerator(n, n.findex)
|
||||
next := getExec(n.tnext)
|
||||
typ := n.typ
|
||||
if typ.cat == ptrT || typ.cat == aliasT {
|
||||
if typ.cat == ptrT || typ.cat == linkedT {
|
||||
typ = typ.val
|
||||
}
|
||||
child := n.child
|
||||
@@ -2672,10 +2709,8 @@ func doComposite(n *node, hasType bool, keyed bool) {
|
||||
switch {
|
||||
case val.typ.cat == nilT:
|
||||
values[fieldIndex] = func(*frame) reflect.Value { return reflect.New(rft).Elem() }
|
||||
case isFuncSrc(val.typ):
|
||||
case isNamedFuncSrc(val.typ):
|
||||
values[fieldIndex] = genValueAsFunctionWrapper(val)
|
||||
case isArray(val.typ) && val.typ.val != nil && isInterfaceSrc(val.typ.val) && !isEmptyInterface(val.typ.val):
|
||||
values[fieldIndex] = genValueInterfaceArray(val)
|
||||
case isInterfaceSrc(ft) && (!isEmptyInterface(ft) || len(val.typ.method) > 0):
|
||||
values[fieldIndex] = genValueInterface(val)
|
||||
case isInterface(ft):
|
||||
@@ -2972,15 +3007,17 @@ func _case(n *node) {
|
||||
}
|
||||
return fnext
|
||||
}
|
||||
vi := v.Interface().(valueInterface)
|
||||
if vi.node == nil {
|
||||
if typ.cat == nilT {
|
||||
return tnext
|
||||
if vi, ok := v.Interface().(valueInterface); ok {
|
||||
if vi.node != nil {
|
||||
if vi.node.typ.id() == typ.id() {
|
||||
destValue(f).Set(vi.value)
|
||||
return tnext
|
||||
}
|
||||
}
|
||||
return fnext
|
||||
}
|
||||
if vi.node.typ.id() == typ.id() {
|
||||
destValue(f).Set(vi.value)
|
||||
if v.Type() == typ.TypeOf() {
|
||||
destValue(f).Set(v)
|
||||
return tnext
|
||||
}
|
||||
return fnext
|
||||
@@ -3015,13 +3052,23 @@ func _case(n *node) {
|
||||
}
|
||||
return fnext
|
||||
}
|
||||
if v := val.Interface().(valueInterface).node; v != nil {
|
||||
for _, typ := range types {
|
||||
if v.typ.id() == typ.id() {
|
||||
destValue(f).Set(val)
|
||||
return tnext
|
||||
if vi, ok := val.Interface().(valueInterface); ok {
|
||||
if v := vi.node; v != nil {
|
||||
for _, typ := range types {
|
||||
if v.typ.id() == typ.id() {
|
||||
destValue(f).Set(val)
|
||||
return tnext
|
||||
}
|
||||
}
|
||||
}
|
||||
return fnext
|
||||
}
|
||||
vt := val.Type()
|
||||
for _, typ := range types {
|
||||
if vt == typ.TypeOf() {
|
||||
destValue(f).Set(val)
|
||||
return tnext
|
||||
}
|
||||
}
|
||||
return fnext
|
||||
}
|
||||
@@ -3556,7 +3603,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 || t.Kind() == reflect.Slice && t.Elem().Kind() == reflect.Interface:
|
||||
case !(n.kind == basicLit || n.rval.IsValid()) || t == nil || t.Kind() == reflect.Interface || t == valueInterfaceType || 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.
|
||||
@@ -3587,7 +3634,7 @@ func convertConstantValue(n *node) {
|
||||
case constant.Int:
|
||||
i, x := constant.Int64Val(c)
|
||||
if !x {
|
||||
panic(fmt.Sprintf("constant %s overflows int64", c.ExactString()))
|
||||
panic(n.cfgErrorf("constant %s overflows int64", c.ExactString()))
|
||||
}
|
||||
v = reflect.ValueOf(int(i))
|
||||
case constant.Float:
|
||||
@@ -3817,11 +3864,7 @@ func slice0(n *node) {
|
||||
func isNil(n *node) {
|
||||
var value func(*frame) reflect.Value
|
||||
c0 := n.child[0]
|
||||
if isFuncSrc(c0.typ) {
|
||||
value = genValueAsFunctionWrapper(c0)
|
||||
} else {
|
||||
value = genValue(c0)
|
||||
}
|
||||
value = genValue(c0)
|
||||
typ := n.typ.concrete().TypeOf()
|
||||
isInterface := n.typ.TypeOf().Kind() == reflect.Interface
|
||||
tnext := getExec(n.tnext)
|
||||
@@ -3906,18 +3949,14 @@ func isNil(n *node) {
|
||||
func isNotNil(n *node) {
|
||||
var value func(*frame) reflect.Value
|
||||
c0 := n.child[0]
|
||||
if isFuncSrc(c0.typ) {
|
||||
value = genValueAsFunctionWrapper(c0)
|
||||
} else {
|
||||
value = genValue(c0)
|
||||
}
|
||||
value = genValue(c0)
|
||||
typ := n.typ.concrete().TypeOf()
|
||||
isInterface := n.typ.TypeOf().Kind() == reflect.Interface
|
||||
tnext := getExec(n.tnext)
|
||||
dest := genValue(n)
|
||||
|
||||
if n.fnext == nil {
|
||||
if isInterfaceSrc(c0.typ) {
|
||||
if isInterfaceSrc(c0.typ) && c0.typ.TypeOf() != valueInterfaceType {
|
||||
if isInterface {
|
||||
n.exec = func(f *frame) bltn {
|
||||
dest(f).Set(reflect.ValueOf(!value(f).IsNil()).Convert(typ))
|
||||
@@ -3962,7 +4001,7 @@ func isNotNil(n *node) {
|
||||
|
||||
fnext := getExec(n.fnext)
|
||||
|
||||
if isInterfaceSrc(c0.typ) {
|
||||
if isInterfaceSrc(c0.typ) && c0.typ.TypeOf() != valueInterfaceType {
|
||||
n.exec = func(f *frame) bltn {
|
||||
if value(f).IsNil() {
|
||||
dest(f).SetBool(false)
|
||||
|
||||
@@ -72,7 +72,6 @@ type symbol struct {
|
||||
//
|
||||
// In symbols, the index value corresponds to the index in scope.types, and at
|
||||
// execution to the index in frame, created exactly from the types layout.
|
||||
//
|
||||
type scope struct {
|
||||
anc *scope // ancestor upper scope
|
||||
child []*scope // included scopes
|
||||
|
||||
264
interp/type.go
264
interp/type.go
@@ -17,7 +17,6 @@ type tcat uint
|
||||
// Types for go language.
|
||||
const (
|
||||
nilT tcat = iota
|
||||
aliasT
|
||||
arrayT
|
||||
binT
|
||||
binPkgT
|
||||
@@ -41,6 +40,7 @@ const (
|
||||
int16T
|
||||
int32T
|
||||
int64T
|
||||
linkedT
|
||||
mapT
|
||||
ptrT
|
||||
sliceT
|
||||
@@ -60,7 +60,6 @@ const (
|
||||
|
||||
var cats = [...]string{
|
||||
nilT: "nilT",
|
||||
aliasT: "aliasT",
|
||||
arrayT: "arrayT",
|
||||
binT: "binT",
|
||||
binPkgT: "binPkgT",
|
||||
@@ -75,14 +74,15 @@ var cats = [...]string{
|
||||
float32T: "float32",
|
||||
float64T: "float64T",
|
||||
funcT: "funcT",
|
||||
genericT: "genericT",
|
||||
interfaceT: "interfaceT",
|
||||
intT: "intT",
|
||||
int8T: "int8T",
|
||||
int16T: "int16T",
|
||||
int32T: "int32T",
|
||||
int64T: "int64T",
|
||||
linkedT: "linkedT",
|
||||
mapT: "mapT",
|
||||
genericT: "genericT",
|
||||
ptrT: "ptrT",
|
||||
sliceT: "sliceT",
|
||||
srcPkgT: "srcPkgT",
|
||||
@@ -118,7 +118,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, sliceT, variadicT or genericT
|
||||
val *itype // Type of value element if chanT, chanSendT, chanRecvT, mapT, ptrT, linkedT, arrayT, sliceT, variadicT or genericT
|
||||
recv *itype // Receiver type for funcT or nil
|
||||
arg []*itype // Argument types if funcT or nil
|
||||
ret []*itype // Return types if funcT or nil
|
||||
@@ -248,7 +248,7 @@ func namedOf(val *itype, path, name string, opts ...itypeOption) *itype {
|
||||
if path != "" {
|
||||
str = path + "." + name
|
||||
}
|
||||
t := &itype{cat: aliasT, val: val, path: path, name: name, str: str}
|
||||
t := &itype{cat: linkedT, val: val, path: path, name: name, str: str}
|
||||
for _, opt := range opts {
|
||||
opt(t)
|
||||
}
|
||||
@@ -373,8 +373,8 @@ func structOf(t *itype, fields []structField, opts ...itypeOption) *itype {
|
||||
}
|
||||
|
||||
// genericOf returns a generic type.
|
||||
func genericOf(val *itype, name string, opts ...itypeOption) *itype {
|
||||
t := &itype{cat: genericT, name: name, str: name, val: val}
|
||||
func genericOf(val *itype, name, path string, opts ...itypeOption) *itype {
|
||||
t := &itype{cat: genericT, name: name, path: path, str: name, val: val}
|
||||
for _, opt := range opts {
|
||||
opt(t)
|
||||
}
|
||||
@@ -446,12 +446,19 @@ func nodeType2(interp *Interpreter, sc *scope, n *node, seen []*node) (t *itype,
|
||||
)
|
||||
switch v := c0.rval; {
|
||||
case v.IsValid():
|
||||
// Size if defined by a constant litteral value.
|
||||
// Size if defined by a constant literal value.
|
||||
if isConstantValue(v.Type()) {
|
||||
c := v.Interface().(constant.Value)
|
||||
length = constToInt(c)
|
||||
} else {
|
||||
length = int(v.Int())
|
||||
switch v.Type().Kind() {
|
||||
case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64:
|
||||
length = int(v.Int())
|
||||
case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64, reflect.Uintptr:
|
||||
length = int(v.Uint())
|
||||
default:
|
||||
return nil, c0.cfgErrorf("non integer constant %v", v)
|
||||
}
|
||||
}
|
||||
case c0.kind == ellipsisExpr:
|
||||
// [...]T expression, get size from the length of composite array.
|
||||
@@ -476,7 +483,11 @@ func nodeType2(interp *Interpreter, sc *scope, n *node, seen []*node) (t *itype,
|
||||
length = int(vInt(sym.rval))
|
||||
default:
|
||||
// Size is defined by a numeric constant expression.
|
||||
if _, err = interp.cfg(c0, sc, sc.pkgID, sc.pkgName); err != nil {
|
||||
if _, err := interp.cfg(c0, sc, sc.pkgID, sc.pkgName); err != nil {
|
||||
if strings.Contains(err.Error(), " undefined: ") {
|
||||
incomplete = true
|
||||
break
|
||||
}
|
||||
return nil, err
|
||||
}
|
||||
v, ok := c0.rval.Interface().(constant.Value)
|
||||
@@ -587,7 +598,10 @@ func nodeType2(interp *Interpreter, sc *scope, n *node, seen []*node) (t *itype,
|
||||
}
|
||||
|
||||
if isInterfaceSrc(dt) {
|
||||
dt.val = t
|
||||
// Set a new interface type preserving the concrete type (.val field).
|
||||
t2 := *dt
|
||||
t2.val = t
|
||||
dt = &t2
|
||||
}
|
||||
t = dt
|
||||
|
||||
@@ -768,11 +782,11 @@ func nodeType2(interp *Interpreter, sc *scope, n *node, seen []*node) (t *itype,
|
||||
}
|
||||
}
|
||||
if sym.kind == varTypeSym {
|
||||
t = genericOf(sym.typ, n.ident, withNode(n), withScope(sc))
|
||||
t = genericOf(sym.typ, n.ident, sc.pkgName, withNode(n), withScope(sc))
|
||||
} else {
|
||||
t = sym.typ
|
||||
}
|
||||
if t.incomplete && t.cat == aliasT && t.val != nil && t.val.cat != nilT {
|
||||
if t.incomplete && t.cat == linkedT && t.val != nil && t.val.cat != nilT {
|
||||
t.incomplete = false
|
||||
}
|
||||
if t.incomplete && t.node != n {
|
||||
@@ -814,46 +828,50 @@ func nodeType2(interp *Interpreter, sc *scope, n *node, seen []*node) (t *itype,
|
||||
break
|
||||
}
|
||||
// A generic type is being instantiated. Generate it.
|
||||
g, err := genAST(sc, lt.node.anc, []*node{t1.node})
|
||||
t, err = genType(interp, sc, name, lt, []*node{t1.node}, seen)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
t, err = nodeType2(interp, sc, g.lastChild(), seen)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
sc.sym[name] = &symbol{index: -1, kind: typeSym, typ: t, node: g}
|
||||
|
||||
// Instantiate type methods (if any).
|
||||
var pt *itype
|
||||
if len(lt.method) > 0 {
|
||||
pt = ptrOf(t, withNode(g), withScope(sc))
|
||||
}
|
||||
for _, nod := range lt.method {
|
||||
gm, err := genAST(sc, nod, []*node{t1.node})
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if gm.typ, err = nodeType(interp, sc, gm.child[2]); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
t.addMethod(gm)
|
||||
if rtn := gm.child[0].child[0].lastChild(); rtn.kind == starExpr {
|
||||
// The receiver is a pointer on a generic type.
|
||||
pt.addMethod(gm)
|
||||
rtn.typ = pt
|
||||
}
|
||||
// Compile method CFG.
|
||||
if _, err = interp.cfg(gm, sc, sc.pkgID, sc.pkgName); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
// Generate closures for function body.
|
||||
if err = genRun(gm); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
case indexListExpr:
|
||||
// Similar to above indexExpr for generic types, but handle multiple type parameters.
|
||||
var lt *itype
|
||||
if lt, err = nodeType2(interp, sc, n.child[0], seen); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
// Index list expressions can be used only in context of generic types.
|
||||
if lt.cat != genericT {
|
||||
err = n.cfgErrorf("not a generic type: %s", lt.id())
|
||||
return nil, err
|
||||
}
|
||||
name := lt.id() + "["
|
||||
out := false
|
||||
tnodes := []*node{}
|
||||
for _, c := range n.child[1:] {
|
||||
t1, err := nodeType2(interp, sc, c, seen)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if t1.cat == genericT || t1.incomplete {
|
||||
t = lt
|
||||
out = true
|
||||
break
|
||||
}
|
||||
tnodes = append(tnodes, t1.node)
|
||||
name += t1.id() + ","
|
||||
}
|
||||
if out {
|
||||
break
|
||||
}
|
||||
name += "]"
|
||||
if sym, _, found := sc.lookup(name); found {
|
||||
t = sym.typ
|
||||
break
|
||||
}
|
||||
// A generic type is being instantiated. Generate it.
|
||||
t, err = genType(interp, sc, name, lt, tnodes, seen)
|
||||
|
||||
case interfaceType:
|
||||
if sname := typeName(n); sname != "" {
|
||||
if sym, _, found := sc.lookup(sname); found && sym.kind == typeSym {
|
||||
@@ -1076,6 +1094,49 @@ func nodeType2(interp *Interpreter, sc *scope, n *node, seen []*node) (t *itype,
|
||||
return t, err
|
||||
}
|
||||
|
||||
func genType(interp *Interpreter, sc *scope, name string, lt *itype, tnodes, seen []*node) (t *itype, err error) {
|
||||
// A generic type is being instantiated. Generate it.
|
||||
g, err := genAST(sc, lt.node.anc, tnodes)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
t, err = nodeType2(interp, sc, g.lastChild(), seen)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
sc.sym[name] = &symbol{index: -1, kind: typeSym, typ: t, node: g}
|
||||
|
||||
// Instantiate type methods (if any).
|
||||
var pt *itype
|
||||
if len(lt.method) > 0 {
|
||||
pt = ptrOf(t, withNode(g), withScope(sc))
|
||||
}
|
||||
for _, nod := range lt.method {
|
||||
gm, err := genAST(sc, nod, tnodes)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if gm.typ, err = nodeType(interp, sc, gm.child[2]); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
t.addMethod(gm)
|
||||
if rtn := gm.child[0].child[0].lastChild(); rtn.kind == starExpr {
|
||||
// The receiver is a pointer on a generic type.
|
||||
pt.addMethod(gm)
|
||||
rtn.typ = pt
|
||||
}
|
||||
// Compile method CFG.
|
||||
if _, err = interp.cfg(gm, sc, sc.pkgID, sc.pkgName); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
// Generate closures for function body.
|
||||
if err = genRun(gm); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
}
|
||||
return t, err
|
||||
}
|
||||
|
||||
// findPackageType searches the top level scope for a package type.
|
||||
func findPackageType(interp *Interpreter, sc *scope, n *node) *itype {
|
||||
// Find the root scope, the package symbols will exist there.
|
||||
@@ -1242,8 +1303,13 @@ func (t *itype) numOut() int {
|
||||
if t.rtype.Kind() == reflect.Func {
|
||||
return t.rtype.NumOut()
|
||||
}
|
||||
case builtinT:
|
||||
switch t.name {
|
||||
case "append", "cap", "complex", "copy", "imag", "len", "make", "new", "real", "recover":
|
||||
return 1
|
||||
}
|
||||
}
|
||||
return 1
|
||||
return 0
|
||||
}
|
||||
|
||||
func (t *itype) out(i int) *itype {
|
||||
@@ -1266,7 +1332,7 @@ func (t *itype) concrete() *itype {
|
||||
}
|
||||
|
||||
func (t *itype) underlying() *itype {
|
||||
if t.cat == aliasT {
|
||||
if t.cat == linkedT {
|
||||
return t.val.underlying()
|
||||
}
|
||||
return t
|
||||
@@ -1274,10 +1340,10 @@ func (t *itype) underlying() *itype {
|
||||
|
||||
// typeDefined returns true if type t1 is defined from type t2 or t2 from t1.
|
||||
func typeDefined(t1, t2 *itype) bool {
|
||||
if t1.cat == aliasT && t1.val == t2 {
|
||||
if t1.cat == linkedT && t1.val == t2 {
|
||||
return true
|
||||
}
|
||||
if t2.cat == aliasT && t2.val == t1 {
|
||||
if t2.cat == linkedT && t2.val == t1 {
|
||||
return true
|
||||
}
|
||||
return false
|
||||
@@ -1313,7 +1379,7 @@ func isComplete(t *itype, visited map[string]bool) bool {
|
||||
visited[name] = true
|
||||
}
|
||||
switch t.cat {
|
||||
case aliasT:
|
||||
case linkedT:
|
||||
if t.val != nil && t.val.cat != nilT {
|
||||
// A type aliased to a partially defined type is considered complete, to allow recursivity.
|
||||
return true
|
||||
@@ -1357,7 +1423,7 @@ func (t *itype) assignableTo(o *itype) bool {
|
||||
return true
|
||||
}
|
||||
|
||||
if t.cat == aliasT && o.cat == aliasT && (t.underlying().id() != o.underlying().id() || !typeDefined(t, o)) {
|
||||
if t.cat == linkedT && o.cat == linkedT && (t.underlying().id() != o.underlying().id() || !typeDefined(t, o)) {
|
||||
return false
|
||||
}
|
||||
|
||||
@@ -1472,7 +1538,7 @@ func (t *itype) methods() methodSet {
|
||||
seen[typ] = true
|
||||
|
||||
switch typ.cat {
|
||||
case aliasT:
|
||||
case linkedT:
|
||||
for k, v := range getMethods(typ.val) {
|
||||
res[k] = v
|
||||
}
|
||||
@@ -1563,7 +1629,7 @@ func (t *itype) zero() (v reflect.Value, err error) {
|
||||
return v, err
|
||||
}
|
||||
switch t.cat {
|
||||
case aliasT:
|
||||
case linkedT:
|
||||
v, err = t.val.zero()
|
||||
|
||||
case arrayT, ptrT, structT, sliceT:
|
||||
@@ -1581,7 +1647,7 @@ func (t *itype) zero() (v reflect.Value, err error) {
|
||||
// fieldIndex returns the field index from name in a struct, or -1 if not found.
|
||||
func (t *itype) fieldIndex(name string) int {
|
||||
switch t.cat {
|
||||
case aliasT, ptrT:
|
||||
case linkedT, ptrT:
|
||||
return t.val.fieldIndex(name)
|
||||
}
|
||||
for i, field := range t.field {
|
||||
@@ -1616,7 +1682,7 @@ func (t *itype) lookupField(name string) []int {
|
||||
seen[typ] = true
|
||||
|
||||
switch typ.cat {
|
||||
case aliasT, ptrT:
|
||||
case linkedT, ptrT:
|
||||
return lookup(typ.val)
|
||||
}
|
||||
if fi := typ.fieldIndex(name); fi >= 0 {
|
||||
@@ -1625,7 +1691,7 @@ func (t *itype) lookupField(name string) []int {
|
||||
|
||||
for i, f := range typ.field {
|
||||
switch f.typ.cat {
|
||||
case ptrT, structT, interfaceT, aliasT:
|
||||
case ptrT, structT, interfaceT, linkedT:
|
||||
if index2 := lookup(f.typ); len(index2) > 0 {
|
||||
return append([]int{i}, index2...)
|
||||
}
|
||||
@@ -1684,7 +1750,7 @@ func (t *itype) methodCallType() reflect.Type {
|
||||
}
|
||||
|
||||
func (t *itype) resolveAlias() *itype {
|
||||
for t.cat == aliasT {
|
||||
for t.cat == linkedT {
|
||||
t = t.val
|
||||
}
|
||||
return t
|
||||
@@ -1728,7 +1794,7 @@ func (t *itype) lookupMethod2(name string, seen map[*itype]bool) (*node, []int)
|
||||
}
|
||||
}
|
||||
}
|
||||
if t.cat == aliasT || isInterfaceSrc(t) && t.val != nil {
|
||||
if t.cat == linkedT || isInterfaceSrc(t) && t.val != nil {
|
||||
return t.val.lookupMethod2(name, seen)
|
||||
}
|
||||
}
|
||||
@@ -1817,8 +1883,9 @@ func exportName(s string) string {
|
||||
|
||||
var (
|
||||
// TODO(mpl): generators.
|
||||
interf = reflect.TypeOf((*interface{})(nil)).Elem()
|
||||
constVal = reflect.TypeOf((*constant.Value)(nil)).Elem()
|
||||
emptyInterfaceType = reflect.TypeOf((*interface{})(nil)).Elem()
|
||||
valueInterfaceType = reflect.TypeOf((*valueInterface)(nil)).Elem()
|
||||
constVal = reflect.TypeOf((*constant.Value)(nil)).Elem()
|
||||
)
|
||||
|
||||
type fieldRebuild struct {
|
||||
@@ -1841,6 +1908,7 @@ type refTypeContext struct {
|
||||
// "top-level" point.
|
||||
rect *itype
|
||||
rebuilding bool
|
||||
slevel int
|
||||
}
|
||||
|
||||
// Clone creates a copy of the ref type context.
|
||||
@@ -1944,7 +2012,7 @@ func (t *itype) refType(ctx *refTypeContext) reflect.Type {
|
||||
return reflect.TypeOf((*generic)(nil)).Elem()
|
||||
}
|
||||
switch t.cat {
|
||||
case aliasT:
|
||||
case linkedT:
|
||||
t.rtype = t.val.refType(ctx)
|
||||
case arrayT:
|
||||
t.rtype = reflect.ArrayOf(t.length, t.val.refType(ctx))
|
||||
@@ -1971,15 +2039,28 @@ func (t *itype) refType(ctx *refTypeContext) reflect.Type {
|
||||
}
|
||||
t.rtype = reflect.FuncOf(in, out, variadic)
|
||||
case interfaceT:
|
||||
t.rtype = interf
|
||||
if len(t.field) == 0 {
|
||||
// empty interface, do not wrap it
|
||||
t.rtype = emptyInterfaceType
|
||||
break
|
||||
}
|
||||
t.rtype = valueInterfaceType
|
||||
case mapT:
|
||||
t.rtype = reflect.MapOf(t.key.refType(ctx), t.val.refType(ctx))
|
||||
case ptrT:
|
||||
t.rtype = reflect.PtrTo(t.val.refType(ctx))
|
||||
rt := t.val.refType(ctx)
|
||||
if rt == unsafe2.DummyType && ctx.slevel > 1 {
|
||||
// We have a pointer to a recursive struct which is not yet fully computed.
|
||||
// Return it but do not yet store it in rtype, so the complete version can
|
||||
// be stored in future.
|
||||
return reflect.PtrTo(rt)
|
||||
}
|
||||
t.rtype = reflect.PtrTo(rt)
|
||||
case structT:
|
||||
if t.name != "" {
|
||||
ctx.defined[name] = t
|
||||
}
|
||||
ctx.slevel++
|
||||
var fields []reflect.StructField
|
||||
for i, f := range t.field {
|
||||
field := reflect.StructField{
|
||||
@@ -1996,6 +2077,7 @@ func (t *itype) refType(ctx *refTypeContext) reflect.Type {
|
||||
}
|
||||
}
|
||||
}
|
||||
ctx.slevel--
|
||||
fieldFix := []int{} // Slice of field indices to fix for recursivity.
|
||||
t.rtype = reflect.StructOf(fields)
|
||||
if ctx.isComplete() {
|
||||
@@ -2045,21 +2127,19 @@ func (t *itype) frameType() (r reflect.Type) {
|
||||
panic(err)
|
||||
}
|
||||
switch t.cat {
|
||||
case aliasT:
|
||||
case linkedT:
|
||||
r = t.val.frameType()
|
||||
case arrayT:
|
||||
r = reflect.ArrayOf(t.length, t.val.frameType())
|
||||
case sliceT, variadicT:
|
||||
r = reflect.SliceOf(t.val.frameType())
|
||||
case funcT:
|
||||
r = reflect.TypeOf((*node)(nil))
|
||||
case interfaceT:
|
||||
if len(t.field) == 0 {
|
||||
// empty interface, do not wrap it
|
||||
r = reflect.TypeOf((*interface{})(nil)).Elem()
|
||||
r = emptyInterfaceType
|
||||
break
|
||||
}
|
||||
r = reflect.TypeOf((*valueInterface)(nil)).Elem()
|
||||
r = valueInterfaceType
|
||||
case mapT:
|
||||
r = reflect.MapOf(t.key.frameType(), t.val.frameType())
|
||||
case ptrT:
|
||||
@@ -2072,6 +2152,14 @@ func (t *itype) frameType() (r reflect.Type) {
|
||||
|
||||
func (t *itype) implements(it *itype) bool {
|
||||
if isBin(t) {
|
||||
// Note: in case of a valueInterfaceType, we
|
||||
// miss required data which will be available
|
||||
// later, so we optimistically return true to progress,
|
||||
// and additional checks will be hopefully performed at
|
||||
// runtime.
|
||||
if rt := it.TypeOf(); rt == valueInterfaceType {
|
||||
return true
|
||||
}
|
||||
return t.TypeOf().Implements(it.TypeOf())
|
||||
}
|
||||
return t.methods().contains(it.methods())
|
||||
@@ -2127,11 +2215,15 @@ func (t *itype) defaultType(v reflect.Value, sc *scope) *itype {
|
||||
func (t *itype) isNil() bool { return t.cat == nilT }
|
||||
|
||||
func (t *itype) hasNil() bool {
|
||||
switch t.TypeOf().Kind() {
|
||||
switch rt := t.TypeOf(); rt.Kind() {
|
||||
case reflect.UnsafePointer:
|
||||
return true
|
||||
case reflect.Slice, reflect.Ptr, reflect.Func, reflect.Interface, reflect.Map, reflect.Chan:
|
||||
return true
|
||||
case reflect.Struct:
|
||||
if rt == valueInterfaceType {
|
||||
return true
|
||||
}
|
||||
}
|
||||
return false
|
||||
}
|
||||
@@ -2164,16 +2256,6 @@ func constToString(v reflect.Value) string {
|
||||
return constant.StringVal(c)
|
||||
}
|
||||
|
||||
func defRecvType(n *node) *itype {
|
||||
if n.kind != funcDecl || len(n.child[0].child) == 0 {
|
||||
return nil
|
||||
}
|
||||
if r := n.child[0].child[0].lastChild(); r != nil {
|
||||
return r.typ
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func wrappedType(n *node) *itype {
|
||||
if n.typ.cat != valueT {
|
||||
return nil
|
||||
@@ -2192,7 +2274,7 @@ func isShiftNode(n *node) bool {
|
||||
// chanElement returns the channel element type.
|
||||
func chanElement(t *itype) *itype {
|
||||
switch t.cat {
|
||||
case aliasT:
|
||||
case linkedT:
|
||||
return chanElement(t.val)
|
||||
case chanT, chanSendT, chanRecvT:
|
||||
return t.val
|
||||
@@ -2216,12 +2298,16 @@ func isGeneric(t *itype) bool {
|
||||
return t.cat == funcT && t.node != nil && len(t.node.child) > 0 && len(t.node.child[0].child) > 0
|
||||
}
|
||||
|
||||
func isNamedFuncSrc(t *itype) bool {
|
||||
return isFuncSrc(t) && t.node.anc.kind == funcDecl
|
||||
}
|
||||
|
||||
func isFuncSrc(t *itype) bool {
|
||||
return t.cat == funcT || (t.cat == aliasT && isFuncSrc(t.val))
|
||||
return t.cat == funcT || (t.cat == linkedT && isFuncSrc(t.val))
|
||||
}
|
||||
|
||||
func isPtrSrc(t *itype) bool {
|
||||
return t.cat == ptrT || (t.cat == aliasT && isPtrSrc(t.val))
|
||||
return t.cat == ptrT || (t.cat == linkedT && isPtrSrc(t.val))
|
||||
}
|
||||
|
||||
func isSendChan(t *itype) bool {
|
||||
@@ -2238,7 +2324,7 @@ func isArray(t *itype) bool {
|
||||
}
|
||||
|
||||
func isInterfaceSrc(t *itype) bool {
|
||||
return t.cat == interfaceT || (t.cat == aliasT && isInterfaceSrc(t.val))
|
||||
return t.cat == interfaceT || (t.cat == linkedT && isInterfaceSrc(t.val))
|
||||
}
|
||||
|
||||
func isInterfaceBin(t *itype) bool {
|
||||
@@ -2246,14 +2332,14 @@ func isInterfaceBin(t *itype) bool {
|
||||
}
|
||||
|
||||
func isInterface(t *itype) bool {
|
||||
return isInterfaceSrc(t) || t.TypeOf() != nil && t.TypeOf().Kind() == reflect.Interface
|
||||
return isInterfaceSrc(t) || t.TypeOf() == valueInterfaceType || t.TypeOf() != nil && t.TypeOf().Kind() == reflect.Interface
|
||||
}
|
||||
|
||||
func isBin(t *itype) bool {
|
||||
switch t.cat {
|
||||
case valueT:
|
||||
return true
|
||||
case aliasT, ptrT:
|
||||
case linkedT, ptrT:
|
||||
return isBin(t.val)
|
||||
default:
|
||||
return false
|
||||
@@ -2266,7 +2352,7 @@ func isStruct(t *itype) bool {
|
||||
switch t.cat {
|
||||
case structT:
|
||||
return true
|
||||
case aliasT, ptrT:
|
||||
case linkedT, ptrT:
|
||||
return isStruct(t.val)
|
||||
case valueT:
|
||||
k := t.rtype.Kind()
|
||||
|
||||
@@ -590,7 +590,7 @@ func (check typecheck) typeAssertionExpr(n *node, typ *itype) error {
|
||||
// https://github.com/golang/go/issues/39717 lands. It is currently impractical to
|
||||
// type check Named types as they cannot be asserted.
|
||||
|
||||
if n.typ.TypeOf().Kind() != reflect.Interface {
|
||||
if rt := n.typ.TypeOf(); rt.Kind() != reflect.Interface && rt != valueInterfaceType {
|
||||
return n.cfgErrorf("invalid type assertion: non-interface type %s on left", n.typ.id())
|
||||
}
|
||||
ims := n.typ.methods()
|
||||
@@ -625,6 +625,11 @@ func (check typecheck) typeAssertionExpr(n *node, typ *itype) error {
|
||||
return n.cfgErrorf("impossible type assertion: %s does not implement %s as %q method has a pointer receiver", typ.id(), n.typ.id(), name)
|
||||
}
|
||||
|
||||
if im.cat != funcT || tm.cat != funcT {
|
||||
// It only makes sense to compare in/out parameter types if both types are functions.
|
||||
continue
|
||||
}
|
||||
|
||||
err := n.cfgErrorf("impossible type assertion: %s does not implement %s", typ.id(), n.typ.id())
|
||||
if im.numIn() != tm.numIn() || im.numOut() != tm.numOut() {
|
||||
return err
|
||||
|
||||
252
interp/use.go
Normal file
252
interp/use.go
Normal file
@@ -0,0 +1,252 @@
|
||||
package interp
|
||||
|
||||
import (
|
||||
"flag"
|
||||
"fmt"
|
||||
"go/constant"
|
||||
"log"
|
||||
"math/bits"
|
||||
"os"
|
||||
"path"
|
||||
"reflect"
|
||||
)
|
||||
|
||||
// Symbols returns a map of interpreter exported symbol values for the given
|
||||
// import path. If the argument is the empty string, all known symbols are
|
||||
// returned.
|
||||
func (interp *Interpreter) Symbols(importPath string) Exports {
|
||||
m := map[string]map[string]reflect.Value{}
|
||||
interp.mutex.RLock()
|
||||
defer interp.mutex.RUnlock()
|
||||
|
||||
for k, v := range interp.srcPkg {
|
||||
if importPath != "" && k != importPath {
|
||||
continue
|
||||
}
|
||||
syms := map[string]reflect.Value{}
|
||||
for n, s := range v {
|
||||
if !canExport(n) {
|
||||
// Skip private non-exported symbols.
|
||||
continue
|
||||
}
|
||||
switch s.kind {
|
||||
case constSym:
|
||||
syms[n] = s.rval
|
||||
case funcSym:
|
||||
syms[n] = genFunctionWrapper(s.node)(interp.frame)
|
||||
case varSym:
|
||||
syms[n] = interp.frame.data[s.index]
|
||||
case typeSym:
|
||||
syms[n] = reflect.New(s.typ.TypeOf())
|
||||
}
|
||||
}
|
||||
|
||||
if len(syms) > 0 {
|
||||
m[k] = syms
|
||||
}
|
||||
|
||||
if importPath != "" {
|
||||
return m
|
||||
}
|
||||
}
|
||||
|
||||
if importPath != "" && len(m) > 0 {
|
||||
return m
|
||||
}
|
||||
|
||||
for k, v := range interp.binPkg {
|
||||
if importPath != "" && k != importPath {
|
||||
continue
|
||||
}
|
||||
m[k] = v
|
||||
if importPath != "" {
|
||||
return m
|
||||
}
|
||||
}
|
||||
|
||||
return m
|
||||
}
|
||||
|
||||
// getWrapper returns the wrapper type of the corresponding interface, trying
|
||||
// first the composed ones, or nil if not found.
|
||||
func getWrapper(n *node, t reflect.Type) reflect.Type {
|
||||
p, ok := n.interp.binPkg[t.PkgPath()]
|
||||
if !ok {
|
||||
return nil
|
||||
}
|
||||
w := p["_"+t.Name()]
|
||||
lm := n.typ.methods()
|
||||
|
||||
// mapTypes may contain composed interfaces wrappers to test against, from
|
||||
// most complex to simplest (guaranteed by construction of mapTypes). Find the
|
||||
// first for which the interpreter type has all the methods.
|
||||
for _, rt := range n.interp.mapTypes[w] {
|
||||
match := true
|
||||
for i := 1; i < rt.NumField(); i++ {
|
||||
// The interpreter type must have all required wrapper methods.
|
||||
if _, ok := lm[rt.Field(i).Name[1:]]; !ok {
|
||||
match = false
|
||||
break
|
||||
}
|
||||
}
|
||||
if match {
|
||||
return rt
|
||||
}
|
||||
}
|
||||
|
||||
// Otherwise return the direct "non-composed" interface.
|
||||
return w.Type().Elem()
|
||||
}
|
||||
|
||||
// Use loads binary runtime symbols in the interpreter context so
|
||||
// they can be used in interpreted code.
|
||||
func (interp *Interpreter) Use(values Exports) error {
|
||||
for k, v := range values {
|
||||
importPath := path.Dir(k)
|
||||
packageName := path.Base(k)
|
||||
|
||||
if k == "." && v["MapTypes"].IsValid() {
|
||||
// Use mapping for special interface wrappers.
|
||||
for kk, vv := range v["MapTypes"].Interface().(map[reflect.Value][]reflect.Type) {
|
||||
interp.mapTypes[kk] = vv
|
||||
}
|
||||
continue
|
||||
}
|
||||
|
||||
if importPath == "." {
|
||||
return fmt.Errorf("export path %[1]q is missing a package name; did you mean '%[1]s/%[1]s'?", k)
|
||||
}
|
||||
|
||||
if importPath == selfPrefix {
|
||||
interp.hooks.Parse(v)
|
||||
continue
|
||||
}
|
||||
|
||||
if interp.binPkg[importPath] == nil {
|
||||
interp.binPkg[importPath] = make(map[string]reflect.Value)
|
||||
interp.pkgNames[importPath] = packageName
|
||||
}
|
||||
|
||||
for s, sym := range v {
|
||||
interp.binPkg[importPath][s] = sym
|
||||
}
|
||||
if k == selfPath {
|
||||
interp.binPkg[importPath]["Self"] = reflect.ValueOf(interp)
|
||||
}
|
||||
}
|
||||
|
||||
// Checks if input values correspond to stdlib packages by looking for one
|
||||
// well known stdlib package path.
|
||||
if _, ok := values["fmt/fmt"]; ok {
|
||||
fixStdlib(interp)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// fixStdlib redefines interpreter stdlib symbols to use the standard input,
|
||||
// output and errror assigned to the interpreter. The changes are limited to
|
||||
// the interpreter only.
|
||||
// Note that it is possible to escape the virtualized stdio by
|
||||
// read/write directly to file descriptors 0, 1, 2.
|
||||
func fixStdlib(interp *Interpreter) {
|
||||
p := interp.binPkg["fmt"]
|
||||
if p == nil {
|
||||
return
|
||||
}
|
||||
|
||||
stdin, stdout, stderr := interp.stdin, interp.stdout, interp.stderr
|
||||
|
||||
p["Print"] = reflect.ValueOf(func(a ...interface{}) (n int, err error) { return fmt.Fprint(stdout, a...) })
|
||||
p["Printf"] = reflect.ValueOf(func(f string, a ...interface{}) (n int, err error) { return fmt.Fprintf(stdout, f, a...) })
|
||||
p["Println"] = reflect.ValueOf(func(a ...interface{}) (n int, err error) { return fmt.Fprintln(stdout, a...) })
|
||||
|
||||
p["Scan"] = reflect.ValueOf(func(a ...interface{}) (n int, err error) { return fmt.Fscan(stdin, a...) })
|
||||
p["Scanf"] = reflect.ValueOf(func(f string, a ...interface{}) (n int, err error) { return fmt.Fscanf(stdin, f, a...) })
|
||||
p["Scanln"] = reflect.ValueOf(func(a ...interface{}) (n int, err error) { return fmt.Fscanln(stdin, a...) })
|
||||
|
||||
// Update mapTypes to virtualized symbols as well.
|
||||
interp.mapTypes[p["Print"]] = interp.mapTypes[reflect.ValueOf(fmt.Print)]
|
||||
interp.mapTypes[p["Printf"]] = interp.mapTypes[reflect.ValueOf(fmt.Printf)]
|
||||
interp.mapTypes[p["Println"]] = interp.mapTypes[reflect.ValueOf(fmt.Println)]
|
||||
interp.mapTypes[p["Scan"]] = interp.mapTypes[reflect.ValueOf(fmt.Scan)]
|
||||
interp.mapTypes[p["Scanf"]] = interp.mapTypes[reflect.ValueOf(fmt.Scanf)]
|
||||
interp.mapTypes[p["Scanln"]] = interp.mapTypes[reflect.ValueOf(fmt.Scanln)]
|
||||
|
||||
if p = interp.binPkg["flag"]; p != nil {
|
||||
c := flag.NewFlagSet(os.Args[0], flag.PanicOnError)
|
||||
c.SetOutput(stderr)
|
||||
p["CommandLine"] = reflect.ValueOf(&c).Elem()
|
||||
}
|
||||
|
||||
if p = interp.binPkg["log"]; p != nil {
|
||||
l := log.New(stderr, "", log.LstdFlags)
|
||||
// Restrict Fatal symbols to panic instead of exit.
|
||||
p["Fatal"] = reflect.ValueOf(l.Panic)
|
||||
p["Fatalf"] = reflect.ValueOf(l.Panicf)
|
||||
p["Fatalln"] = reflect.ValueOf(l.Panicln)
|
||||
|
||||
p["Flags"] = reflect.ValueOf(l.Flags)
|
||||
p["Output"] = reflect.ValueOf(l.Output)
|
||||
p["Panic"] = reflect.ValueOf(l.Panic)
|
||||
p["Panicf"] = reflect.ValueOf(l.Panicf)
|
||||
p["Panicln"] = reflect.ValueOf(l.Panicln)
|
||||
p["Prefix"] = reflect.ValueOf(l.Prefix)
|
||||
p["Print"] = reflect.ValueOf(l.Print)
|
||||
p["Printf"] = reflect.ValueOf(l.Printf)
|
||||
p["Println"] = reflect.ValueOf(l.Println)
|
||||
p["SetFlags"] = reflect.ValueOf(l.SetFlags)
|
||||
p["SetOutput"] = reflect.ValueOf(l.SetOutput)
|
||||
p["SetPrefix"] = reflect.ValueOf(l.SetPrefix)
|
||||
p["Writer"] = reflect.ValueOf(l.Writer)
|
||||
|
||||
// Update mapTypes to virtualized symbols as well.
|
||||
interp.mapTypes[p["Print"]] = interp.mapTypes[reflect.ValueOf(log.Print)]
|
||||
interp.mapTypes[p["Printf"]] = interp.mapTypes[reflect.ValueOf(log.Printf)]
|
||||
interp.mapTypes[p["Println"]] = interp.mapTypes[reflect.ValueOf(log.Println)]
|
||||
interp.mapTypes[p["Panic"]] = interp.mapTypes[reflect.ValueOf(log.Panic)]
|
||||
interp.mapTypes[p["Panicf"]] = interp.mapTypes[reflect.ValueOf(log.Panicf)]
|
||||
interp.mapTypes[p["Panicln"]] = interp.mapTypes[reflect.ValueOf(log.Panicln)]
|
||||
}
|
||||
|
||||
if p = interp.binPkg["os"]; p != nil {
|
||||
p["Args"] = reflect.ValueOf(&interp.args).Elem()
|
||||
if interp.specialStdio {
|
||||
// Inherit streams from interpreter even if they do not have a file descriptor.
|
||||
p["Stdin"] = reflect.ValueOf(&stdin).Elem()
|
||||
p["Stdout"] = reflect.ValueOf(&stdout).Elem()
|
||||
p["Stderr"] = reflect.ValueOf(&stderr).Elem()
|
||||
} else {
|
||||
// Inherits streams from interpreter only if they have a file descriptor and preserve original type.
|
||||
if s, ok := stdin.(*os.File); ok {
|
||||
p["Stdin"] = reflect.ValueOf(&s).Elem()
|
||||
}
|
||||
if s, ok := stdout.(*os.File); ok {
|
||||
p["Stdout"] = reflect.ValueOf(&s).Elem()
|
||||
}
|
||||
if s, ok := stderr.(*os.File); ok {
|
||||
p["Stderr"] = reflect.ValueOf(&s).Elem()
|
||||
}
|
||||
}
|
||||
if !interp.unrestricted {
|
||||
// In restricted mode, scripts can only access to a passed virtualized env, and can not write the real one.
|
||||
getenv := func(key string) string { return interp.env[key] }
|
||||
p["Clearenv"] = reflect.ValueOf(func() { interp.env = map[string]string{} })
|
||||
p["ExpandEnv"] = reflect.ValueOf(func(s string) string { return os.Expand(s, getenv) })
|
||||
p["Getenv"] = reflect.ValueOf(getenv)
|
||||
p["LookupEnv"] = reflect.ValueOf(func(key string) (s string, ok bool) { s, ok = interp.env[key]; return })
|
||||
p["Setenv"] = reflect.ValueOf(func(key, value string) error { interp.env[key] = value; return nil })
|
||||
p["Unsetenv"] = reflect.ValueOf(func(key string) error { delete(interp.env, key); return nil })
|
||||
p["Environ"] = reflect.ValueOf(func() (a []string) {
|
||||
for k, v := range interp.env {
|
||||
a = append(a, k+"="+v)
|
||||
}
|
||||
return
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
if p = interp.binPkg["math/bits"]; p != nil {
|
||||
// Do not trust extracted value maybe from another arch.
|
||||
p["UintSize"] = reflect.ValueOf(constant.MakeInt64(bits.UintSize))
|
||||
}
|
||||
}
|
||||
134
interp/value.go
134
interp/value.go
@@ -40,59 +40,13 @@ func valueOf(data []reflect.Value, i int) reflect.Value {
|
||||
return data[i]
|
||||
}
|
||||
|
||||
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
|
||||
}
|
||||
c0 := n.child[0]
|
||||
if c0.child[1] == nil || c0.child[1].ident == "" {
|
||||
return defaultGen
|
||||
}
|
||||
value0 := genValue(c0.child[0])
|
||||
|
||||
return func(f *frame) reflect.Value {
|
||||
v := value0(f)
|
||||
var nod *node
|
||||
|
||||
for v.IsValid() {
|
||||
// Traverse interface indirections to find out concrete type.
|
||||
vi, ok := v.Interface().(valueInterface)
|
||||
if !ok {
|
||||
break
|
||||
}
|
||||
v = vi.value
|
||||
nod = vi.node
|
||||
}
|
||||
|
||||
if nod == nil || nod.typ.rtype == nil {
|
||||
return defaultGen(f)
|
||||
}
|
||||
|
||||
// Try to get the bin method, if it doesnt exist, fall back to
|
||||
// the default generator function.
|
||||
meth, ok := nod.typ.rtype.MethodByName(c0.child[1].ident)
|
||||
if !ok {
|
||||
return defaultGen(f)
|
||||
}
|
||||
|
||||
return meth.Func
|
||||
}
|
||||
}
|
||||
|
||||
func genValueRecvIndirect(n *node) func(*frame) reflect.Value {
|
||||
vr := genValueRecv(n)
|
||||
return func(f *frame) reflect.Value {
|
||||
v := vr(f)
|
||||
if vi, ok := v.Interface().(valueInterface); ok {
|
||||
return vi.value
|
||||
}
|
||||
return v.Elem()
|
||||
}
|
||||
}
|
||||
|
||||
func genValueRecv(n *node) func(*frame) reflect.Value {
|
||||
v := genValue(n.recv.node)
|
||||
var v func(*frame) reflect.Value
|
||||
if n.recv.node == nil {
|
||||
v = func(*frame) reflect.Value { return n.recv.val }
|
||||
} else {
|
||||
v = genValue(n.recv.node)
|
||||
}
|
||||
fi := n.recv.index
|
||||
|
||||
if len(fi) == 0 {
|
||||
@@ -108,35 +62,6 @@ 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 genValueAsFunctionWrapper(n *node) func(*frame) reflect.Value {
|
||||
value := genValue(n)
|
||||
typ := n.typ.TypeOf()
|
||||
@@ -146,6 +71,9 @@ func genValueAsFunctionWrapper(n *node) func(*frame) reflect.Value {
|
||||
if v.IsNil() {
|
||||
return reflect.New(typ).Elem()
|
||||
}
|
||||
if v.Kind() == reflect.Func {
|
||||
return v
|
||||
}
|
||||
vn, ok := v.Interface().(*node)
|
||||
if ok && vn.rval.Kind() == reflect.Func {
|
||||
// The node value is already a callable func, no need to wrap it.
|
||||
@@ -176,7 +104,7 @@ func genValue(n *node) func(*frame) reflect.Value {
|
||||
convertConstantValue(n)
|
||||
v := n.rval
|
||||
if !v.IsValid() {
|
||||
v = reflect.New(interf).Elem()
|
||||
v = reflect.New(emptyInterfaceType).Elem()
|
||||
}
|
||||
return func(f *frame) reflect.Value { return v }
|
||||
case funcDecl:
|
||||
@@ -221,9 +149,7 @@ func genDestValue(typ *itype, n *node) func(*frame) reflect.Value {
|
||||
switch {
|
||||
case isInterfaceSrc(typ) && (!isEmptyInterface(typ) || len(n.typ.method) > 0):
|
||||
return genValueInterface(n)
|
||||
case isFuncSrc(typ) && (n.typ.cat == valueT || n.typ.cat == nilT):
|
||||
return genValueNode(n)
|
||||
case typ.cat == valueT && isFuncSrc(n.typ):
|
||||
case isNamedFuncSrc(n.typ):
|
||||
return genFunctionWrapper(n)
|
||||
case isInterfaceBin(typ):
|
||||
return genInterfaceWrapper(n, typ.rtype)
|
||||
@@ -237,6 +163,17 @@ func genDestValue(typ *itype, n *node) func(*frame) reflect.Value {
|
||||
return genValue(n)
|
||||
}
|
||||
|
||||
func genFuncValue(n *node) func(*frame) reflect.Value {
|
||||
value := genValue(n)
|
||||
return func(f *frame) reflect.Value {
|
||||
v := value(f)
|
||||
if nod, ok := v.Interface().(*node); ok {
|
||||
return genFunctionWrapper(nod)(f)
|
||||
}
|
||||
return v
|
||||
}
|
||||
}
|
||||
|
||||
func genValueArray(n *node) func(*frame) reflect.Value {
|
||||
value := genValue(n)
|
||||
// dereference array pointer, to support array operations on array pointer
|
||||
@@ -287,19 +224,6 @@ func genValueRangeArray(n *node) func(*frame) reflect.Value {
|
||||
}
|
||||
}
|
||||
|
||||
func genValueInterfaceArray(n *node) func(*frame) reflect.Value {
|
||||
value := genValue(n)
|
||||
return func(f *frame) reflect.Value {
|
||||
vi := value(f).Interface().([]valueInterface)
|
||||
v := reflect.MakeSlice(reflect.TypeOf([]interface{}{}), len(vi), len(vi))
|
||||
for i, vv := range vi {
|
||||
v.Index(i).Set(vv.value)
|
||||
}
|
||||
|
||||
return v
|
||||
}
|
||||
}
|
||||
|
||||
func genValueInterface(n *node) func(*frame) reflect.Value {
|
||||
value := genValue(n)
|
||||
|
||||
@@ -356,7 +280,7 @@ func getConcreteValue(val reflect.Value) reflect.Value {
|
||||
|
||||
func zeroInterfaceValue() reflect.Value {
|
||||
n := &node{kind: basicLit, typ: &itype{cat: nilT, untyped: true, str: "nil"}}
|
||||
v := reflect.New(interf).Elem()
|
||||
v := reflect.New(emptyInterfaceType).Elem()
|
||||
return reflect.ValueOf(valueInterface{n, v})
|
||||
}
|
||||
|
||||
@@ -432,18 +356,6 @@ func genValueInterfaceValue(n *node) func(*frame) reflect.Value {
|
||||
}
|
||||
}
|
||||
|
||||
func genValueNode(n *node) func(*frame) reflect.Value {
|
||||
value := genValue(n)
|
||||
|
||||
return func(f *frame) reflect.Value {
|
||||
v := value(f)
|
||||
if _, ok := v.Interface().(*node); ok {
|
||||
return v
|
||||
}
|
||||
return reflect.ValueOf(&node{rval: v})
|
||||
}
|
||||
}
|
||||
|
||||
func vInt(v reflect.Value) (i int64) {
|
||||
if c := vConstantValue(v); c != nil {
|
||||
i, _ = constant.Int64Val(constant.ToInt(c))
|
||||
|
||||
88
stdlib/wrapper-composed.go
Normal file
88
stdlib/wrapper-composed.go
Normal file
@@ -0,0 +1,88 @@
|
||||
package stdlib
|
||||
|
||||
import (
|
||||
"bufio"
|
||||
"io"
|
||||
"net"
|
||||
"net/http"
|
||||
"reflect"
|
||||
)
|
||||
|
||||
// Wrappers for composed interfaces which trigger a special behavior in stdlib.
|
||||
// Note: it may become useless to pre-compile composed interface wrappers
|
||||
// once golang/go#15924 is resolved.
|
||||
|
||||
// In net/http, a ResponseWriter may also implement a Hijacker.
|
||||
|
||||
type _netHTTPResponseWriterHijacker struct {
|
||||
IValue interface{}
|
||||
WHeader func() http.Header
|
||||
WWrite func(a0 []byte) (int, error)
|
||||
WWriteHeader func(statusCode int)
|
||||
|
||||
WHijack func() (net.Conn, *bufio.ReadWriter, error)
|
||||
}
|
||||
|
||||
func (w _netHTTPResponseWriterHijacker) Header() http.Header {
|
||||
return w.WHeader()
|
||||
}
|
||||
|
||||
func (w _netHTTPResponseWriterHijacker) Write(a0 []byte) (int, error) {
|
||||
return w.WWrite(a0)
|
||||
}
|
||||
|
||||
func (w _netHTTPResponseWriterHijacker) WriteHeader(statusCode int) {
|
||||
w.WWriteHeader(statusCode)
|
||||
}
|
||||
|
||||
func (w _netHTTPResponseWriterHijacker) Hijack() (net.Conn, *bufio.ReadWriter, error) {
|
||||
return w.WHijack()
|
||||
}
|
||||
|
||||
// In io, a Reader may implement WriteTo, used by io.Copy().
|
||||
|
||||
type _ioReaderWriteTo struct {
|
||||
IValue interface{}
|
||||
WRead func(p []byte) (n int, err error)
|
||||
|
||||
WWriteTo func(w io.Writer) (n int64, err error)
|
||||
}
|
||||
|
||||
func (w _ioReaderWriteTo) Read(p []byte) (n int, err error) {
|
||||
return w.WRead(p)
|
||||
}
|
||||
|
||||
func (w _ioReaderWriteTo) WriteTo(wr io.Writer) (n int64, err error) {
|
||||
return w.WWriteTo(wr)
|
||||
}
|
||||
|
||||
// In io, a Writer may implement ReadFrom, used by io.Copy().
|
||||
|
||||
type _ioWriterReadFrom struct {
|
||||
IValue interface{}
|
||||
WWrite func(p []byte) (n int, err error)
|
||||
|
||||
WReadFrom func(r io.Reader) (n int64, err error)
|
||||
}
|
||||
|
||||
func (w _ioWriterReadFrom) Write(p []byte) (n int, err error) {
|
||||
return w.WWrite(p)
|
||||
}
|
||||
|
||||
func (w _ioWriterReadFrom) ReadFrom(r io.Reader) (n int64, err error) {
|
||||
return w.WReadFrom(r)
|
||||
}
|
||||
|
||||
// Each MapType value (each slice) must be sorted by complexity, i.e. by number
|
||||
// of interface methods.
|
||||
func init() {
|
||||
MapTypes[reflect.ValueOf((*_net_http_ResponseWriter)(nil))] = []reflect.Type{
|
||||
reflect.ValueOf((*_netHTTPResponseWriterHijacker)(nil)).Type().Elem(),
|
||||
}
|
||||
MapTypes[reflect.ValueOf((*_io_Reader)(nil))] = []reflect.Type{
|
||||
reflect.ValueOf((*_ioReaderWriteTo)(nil)).Type().Elem(),
|
||||
}
|
||||
MapTypes[reflect.ValueOf((*_io_Writer)(nil))] = []reflect.Type{
|
||||
reflect.ValueOf((*_ioWriterReadFrom)(nil)).Type().Elem(),
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user