Compare commits

..

29 Commits

Author SHA1 Message Date
Marc Vertes
7bb8b4631f interp: fix processing of aliased types
For a long time, there was a confusion between aliased types and named types (due to my misunderstanding of alias types in Go at that time). The type category `aliasT` has been renamed into `linkedT`, which is correct semantically. 

Aliased types are only declared using `typeSpecAssign`, and its processing is now distinct from `typeSpec` statement, used for named types.

A `linkedT` type is obtained by a statement like `type A uint32`, where `A` type category is therefore `linkedT`.

An aliased type is obtained by a statement like `type A = uint32` (notice the `=` sign, translating into `typeSpecAssign`).

The semantic difference is that in the first linkedT form, `A` and `uint32` are 2 distinct types, instead of being strictly equivalent in the `typeSpecAssign` form (the 2 names lead to one type definition).


Fixes #1416.
2022-10-26 17:00:07 +02:00
Marc Vertes
9f43170708 interp: error instead of panic when assigning to a constant
Add early detection of assigning to a constant during compiling instead of panicking at runtime.
2022-10-25 18:16:10 +02:00
Marc Vertes
71112dbe87 interp: fix return of untyped values for defined types
Fixes #1475
2022-10-25 17:20:06 +02:00
Marc Vertes
4a8093609f interp: fix handling interface in operators
In case of interface values, make sure that the concrete type is preserved during type inference.

Fixes #1466.
2022-10-25 14:02:05 +02:00
Marc Vertes
7865c90737 interp: fix case behavior for values converted to empty interface
Fixes #1465.
2022-10-25 09:38:05 +02:00
Marc Vertes
e4e3d11772 interp: fix the logic to skip source files based on OS or CPU arch
For example, on architecture GOARCH=amd64, a file named `foobar_amd64.go` would be skipped instead of being read and parsed. The function `skipFile` is fixed and missing tests are added.
2022-10-24 15:48:04 +02:00
Marc Vertes
a5242cbb9e interp: retry type definition if an array size is undefined
In case of forward definition of a constant, a symbol may be undefined when attempting to compute the array size in type analysis. Just mark the type as incomplete instead of aborting directly, to allow resolution at a second pass.

Fixes #1470.
2022-10-24 10:44:06 +02:00
Fernandez Ludovic
c4d1bf5029 chore: update actions/cache to v3 2022-10-21 16:21:21 +02:00
Marc Vertes
e003140c6e interp: improve internal handling of functions
Up to now functions could be stored as node values in frame (as for interpreter defined functions) or function values, directly callable by the Go runtime. We now always store functions in the later form, making the processing of functions, anonymous closures and methods simpler and more robust. All functions, once compiled are always directly callable, with no further wrapping necessary.

Fixes #1459.
2022-10-19 17:54:08 +02:00
Marc Vertes
6b8c94e6c4 interp: check that send operate on channel value
Not performing this check was leading to a panic at run-time. It now fails early with a compile error.

Fixes #1453.
2022-10-04 12:00:08 +02:00
Marc Vertes
143e4a4559 interp: fix type assertion for wrapped empty interface
Although empty interfaces are usually not wrapped, for compatibility with the runtime, we may have to wrap them sometime into `valueInterface` type.

It allows to preserve interpreter type metadata for interface values exchanged with the runtime. It is necessary to resolve methods and receivers in the absence of reflect support.

During type assertions on empty interfaces, we now handle a possible valueInterface and dereference the original value to pursue the type assertion.

In the same change, we have improved the format of some panic messages at runtime to give location of offending source at interpreter level.

This change will allow to fix traefik/traefik#9362.
2022-10-03 17:50:09 +02:00
Marc Vertes
dfeddbe823 interp: fix handling generic types with multiple type parameters
Those declarations involve the indexListExpr AST token, which was not handled in type.go. The same processing as for a single type parameter is applied.

Fixes #1460.
2022-09-22 13:50:09 +02:00
Marc Vertes
021824930d interp: improve type assertions
In type assertion at compile time, compare signatures between function types only.

Make `itype.numOut()` return the correct value for Go builtins (this was not strictly necessary due to above fix, but it is correct and improves maintainability).

Fixes #1454.
2022-09-12 22:30:08 +02:00
Marc Vertes
b8301f10a8 interp: add missing conversion for non integer array dimension
Fixes #1451.
2022-09-12 19:40:08 +02:00
Marc Vertes
2e8808317f interp: fix default comm clause in select
Do not attempt to init a non-existent channel setting when in
default communication clause in select.

Fixes #1442.
2022-09-12 15:32:08 +02:00
Marc Vertes
79747f3d6f interp: fix redeclarations containing a blank variable
In [short variable declarations](https://go.dev/ref/spec#Short_variable_declarations),
The reuse of existing symbols is possible only if a new variable is defined,
otherwise a new symbol must be created, which was not the case in the issue.

Search for new symbols and correctly ignore blank variables.

Fixes #1434.
2022-09-02 16:44:07 +02:00
Marc Vertes
63825e7201 interp: fix use of interfaces in composite types
The representation of non empty interfaces defined in the interpreter is now identical between refType() and frameType() functions, which are used to generate interpreter objects.

Fixes #1447 and #1426.
2022-09-01 12:18:08 +02:00
Marc Vertes
03ccda1a69 interp: fix type switch on arbitrary expressions
If the value on which to type-switch was already set (i.e. a variable),
there was no problem. But if it had to be obtained through a complex
expression (func call, array index, etc...), then the code to retrieve
the value prior type-switch was not scheduled. This is now fixed.

This issue is nasty because the behavior is silently changed,
leading potentially to further unrelated issues or runtime panics.

Fixes #1444.
2022-08-25 12:04:08 +02:00
Marc Vertes
e02621577f interp: improve handling of composed interfaces wrappers
This change implements a workaround to better support composed
interfaces in yaegi and let the interpreter define objects which
implement multiple interfaces at once.

We use the existing MapTypes to store what possible composed interface
wrapper could be used for some interfaces. When generating an interface
wrapper, the wrapper with the highest number of implemented methods is
chosen.

This is still an imperfect solution but it improves the accuracy of
interpreter in some critical cases.

This workaround could be removed in future if/when golang/go#15924
is resolved.

Fixes #1425.
2022-08-25 10:44:11 +02:00
Marc Vertes
ab869c8d20 interp: improve method resolution for embedded interfaces
The function `getMethodByName()` is now able to look for
embedded `valueInterface` field for matching methods in interface
struct fields.
    
Fixes #1439 and #1427.
2022-08-17 18:14:10 +02:00
Marc Vertes
b2aa636ea0 interp: fix spurious variable declaration loop
The variable dependency check function was confused by a dependency
variable with the same name but in an external package.

This change is necessary to address #1427.
2022-08-10 16:10:08 +02:00
Marc Vertes
ae725fb3d9 interp: fix generic check on nil function
Related to issue https://github.com/traefik/traefik/issues/9231
2022-08-05 18:20:08 +02:00
Marc Vertes
14bc3b56b8 interp: add support of Go generics in interpreter
Status:
* [x] parsing code with generics
* [x] instantiate generics from concrete types
* [x] automatic type inference
* [x] support of generic recursive types 
* [x] support of generic methods
* [x] support of generic receivers in methods
* [x] support of multiple type parameters
* [x] support of generic constraints
* [x] tests (see _test/gen*.go)

Fixes #1363.
2022-08-03 15:18:08 +02:00
Marc Vertes
255b1cf1de interp: do not allow function declaration without body
Such function declaration denotes either a linkname (an access to
an arbitrary, typically unexported symbol, solved by go compiler),
or a foreign C or assembly implementation of the body.

Those cases are not supported (or planned to be) by the interpreter.

Fixes #1431.
2022-08-03 10:06:06 +02:00
Marc Vertes
d3fc5e990e chore: upgrade to go1.19
* chore: upgrade to go1.19

* review

Co-authored-by: Fernandez Ludovic <ldez@users.noreply.github.com>
2022-08-03 09:44:07 +02:00
Marc Vertes
dc082b5ded stdlib: support of go1.18 and go1.19, remove go1.16 and go1.17
In addition:
- extract commmand now skips exported generics in runtime wrappers
- interp_consistent_test.go fixed for go1.18 and go1.19
- move minimal version of go compiler to go1.18

Note that this version is incompatible with go1.17 and before due
to the major changes in the stdlib go parser.

To be merged once go1.19 is officially released (not before).
2022-07-20 17:10:08 +02:00
Marc Vertes
d9c402e20d interp: fix unit testing for go1.18
Some tests are not passing when using go1.18, due to a change of
content in error messages compared to go1.17. We simply skip them
while we support go1.17. It concerns a small number of tests
regarding error detection.
2022-07-20 11:04:09 +02:00
ttoad
09a1617640 interp: improve support of alias types
I expect the following code to be supported.
```go
type TT http.Header

func (t TT) Set(key, val string) {

}

func (t TT) Get(key string) string {

}
```
So, I pushed this PR. 
Do I need to add some test cases?  I don't see the relevant test files ....
2022-07-14 19:38:07 +02:00
Luo Peng
cb642c44ba interp: improve type checking when comparing aliased types
Fixes #1421.
2022-06-30 10:22:12 +02:00
620 changed files with 13306 additions and 7150 deletions

View File

@@ -17,7 +17,7 @@ jobs:
strategy:
matrix:
go-version: [ 1.16, 1.17 ]
go-version: [ 1.18, 1.19 ]
os: [ubuntu-latest, macos-latest, windows-latest]
include:
@@ -34,6 +34,7 @@ jobs:
uses: actions/setup-go@v2
with:
go-version: ${{ matrix.go-version }}
stable: true
# https://github.com/marketplace/actions/checkout
- name: Checkout code
@@ -43,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

View File

@@ -7,8 +7,8 @@ on:
pull_request:
env:
GO_VERSION: 1.17
GOLANGCI_LINT_VERSION: v1.42.1
GO_VERSION: 1.18
GOLANGCI_LINT_VERSION: v1.47.1
jobs:
@@ -45,12 +45,13 @@ jobs:
needs: linting
strategy:
matrix:
go-version: [ 1.16, 1.17 ]
go-version: [ 1.18, 1.19 ]
steps:
- name: Set up Go ${{ matrix.go-version }}
uses: actions/setup-go@v2
with:
go-version: ${{ matrix.go-version }}
stable: true
- name: Check out code
uses: actions/checkout@v2
@@ -75,13 +76,14 @@ jobs:
working-directory: ${{ github.workspace }}/go/src/github.com/traefik/yaegi
strategy:
matrix:
go-version: [ 1.16, 1.17 ]
go-version: [ 1.18, 1.19 ]
steps:
- name: Set up Go ${{ matrix.go-version }}
uses: actions/setup-go@v2
with:
go-version: ${{ matrix.go-version }}
stable: true
- name: Check out code
uses: actions/checkout@v2
@@ -91,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/') }}

View File

@@ -6,7 +6,7 @@ on:
- v[0-9]+.[0-9]+*
env:
GO_VERSION: 1.17
GO_VERSION: 1.19
jobs:
@@ -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') }}

View File

@@ -27,6 +27,7 @@
"scopelint", # deprecated
"interfacer", # deprecated
"maligned", # deprecated
"exhaustivestruct", # deprecated
"lll",
"gas",
"dupl",
@@ -49,10 +50,15 @@
"wrapcheck",
"nestif",
"exhaustive",
"exhaustivestruct",
"exhaustruct",
"forbidigo",
"ifshort",
"forcetypeassert",
"varnamelen",
"nosnakecase",
"nonamedreturns",
"nilnil",
"maintidx",
"errorlint", # TODO: must be reactivate before fixes
]
@@ -81,3 +87,6 @@
[[issues.exclude-rules]]
path = "interp/interp_eval_test.go"
linters = ["thelper"]
[[issues.exclude-rules]]
path = "interp/debugger.go"
linters = ["containedctx"]

View File

@@ -18,7 +18,7 @@ It powers executable Go scripts and plugins, in embedded interpreters or interac
* Works everywhere Go works
* All Go & runtime resources accessible from script (with control)
* Security: `unsafe` and `syscall` packages neither used nor exported by default
* Support Go 1.16 and Go 1.17 (the latest 2 major releases)
* Support Go 1.18 and Go 1.19 (the latest 2 major releases)
## Install

View File

@@ -1,9 +0,0 @@
package alias3
type T struct {
A string
}
func (t *T) Print() {
println(t.A)
}

View File

@@ -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
View 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
View 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
View 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
View 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
View 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)

39
_test/gen1.go Normal file
View File

@@ -0,0 +1,39 @@
package main
import "fmt"
// SumInts adds together the values of m.
func SumInts(m map[string]int64) int64 {
var s int64
for _, v := range m {
s += v
}
return s
}
// SumFloats adds together the values of m.
func SumFloats(m map[string]float64) float64 {
var s float64
for _, v := range m {
s += v
}
return s
}
func main() {
// Initialize a map for the integer values
ints := map[string]int64{
"first": 34,
"second": 12,
}
// Initialize a map for the float values
floats := map[string]float64{
"first": 35.98,
"second": 26.99,
}
fmt.Printf("Non-Generic Sums: %v and %v\n",
SumInts(ints),
SumFloats(floats))
}

12
_test/gen10.go Normal file
View File

@@ -0,0 +1,12 @@
package main
func genFunc() (f func()) {
return f
}
func main() {
println(genFunc() == nil)
}
// Output:
// true

34
_test/gen2.go Normal file
View File

@@ -0,0 +1,34 @@
package main
import "fmt"
// SumIntsOrFloats sums the values of map m. It supports both int64 and float64
// as types for map values.
func SumIntsOrFloats[K comparable, V int64 | float64](m map[K]V) V {
var s V
for _, v := range m {
s += v
}
return s
}
func main() {
// Initialize a map for the integer values
ints := map[string]int64{
"first": 34,
"second": 12,
}
// Initialize a map for the float values
floats := map[string]float64{
"first": 35.98,
"second": 26.99,
}
fmt.Printf("Generic Sums: %v and %v\n",
SumIntsOrFloats[string, int64](ints),
SumIntsOrFloats[string, float64](floats))
}
// Output:
// Generic Sums: 46 and 62.97

22
_test/gen3.go Normal file
View File

@@ -0,0 +1,22 @@
package main
type Number interface {
int | int64 | ~float64
}
func Sum[T Number](numbers []T) T {
var total T
for _, x := range numbers {
total += x
}
return total
}
func main() {
xs := []int{3, 5, 10}
total := Sum(xs)
println(total)
}
// Output:
// 18

42
_test/gen4.go Normal file
View File

@@ -0,0 +1,42 @@
package main
import "fmt"
type List[T any] struct {
head, tail *element[T]
}
// A recursive generic type.
type element[T any] struct {
next *element[T]
val T
}
func (lst *List[T]) Push(v T) {
if lst.tail == nil {
lst.head = &element[T]{val: v}
lst.tail = lst.head
} else {
lst.tail.next = &element[T]{val: v}
lst.tail = lst.tail.next
}
}
func (lst *List[T]) GetAll() []T {
var elems []T
for e := lst.head; e != nil; e = e.next {
elems = append(elems, e.val)
}
return elems
}
func main() {
lst := List[int]{}
lst.Push(10)
lst.Push(13)
lst.Push(23)
fmt.Println("list:", lst.GetAll())
}
// Output:
// list: [10 13 23]

24
_test/gen5.go Normal file
View File

@@ -0,0 +1,24 @@
package main
import "fmt"
type Set[Elem comparable] struct {
m map[Elem]struct{}
}
func Make[Elem comparable]() Set[Elem] {
return Set[Elem]{m: make(map[Elem]struct{})}
}
func (s Set[Elem]) Add(v Elem) {
s.m[v] = struct{}{}
}
func main() {
s := Make[int]()
s.Add(1)
fmt.Println(s)
}
// Output:
// {map[1:{}]}

19
_test/gen6.go Normal file
View File

@@ -0,0 +1,19 @@
package main
func MapKeys[K comparable, V any](m map[K]V) []K {
r := make([]K, 0, len(m))
for k := range m {
r = append(r, k)
}
return r
}
func main() {
var m = map[int]string{1: "2", 2: "4", 4: "8"}
// Test type inference
println(len(MapKeys(m)))
}
// Output:
// 3

19
_test/gen7.go Normal file
View File

@@ -0,0 +1,19 @@
package main
func MapKeys[K comparable, V any](m map[K]V) []K {
r := make([]K, 0, len(m))
for k := range m {
r = append(r, k)
}
return r
}
func main() {
var m = map[int]string{1: "2", 2: "4", 4: "8"}
// Test type inference
println(len(MapKeys))
}
// Error:
// invalid argument for len

15
_test/gen8.go Normal file
View File

@@ -0,0 +1,15 @@
package main
type Float interface {
~float32 | ~float64
}
func add[T Float](a, b T) float64 { return float64(a) + float64(b) }
func main() {
var x, y int = 1, 2
println(add(x, y))
}
// Error:
// int does not implement main.Float

14
_test/gen9.go Normal file
View File

@@ -0,0 +1,14 @@
package main
type Float interface {
~float32 | ~float64
}
func add[T Float](a, b T) float64 { return float64(a) + float64(b) }
func main() {
println(add(1, 2))
}
// Error:
// untyped int does not implement main.Float

17
_test/issue-1416.go Normal file
View 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

9
_test/issue-1421.go Normal file
View File

@@ -0,0 +1,9 @@
package main
type Number = int
func main() {
println(Number(1) < int(2))
}
// Output: true

44
_test/issue-1425.go Normal file
View 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
View 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
View 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
View 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
View 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
View 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
View 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
View 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
View 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
View 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
View 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
View File

@@ -0,0 +1,12 @@
package main
type T uint16
func f() T { return 0 }
func main() {
println(f())
}
// Output:
// 0

View File

@@ -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() {

31
_test/named3/named3.go Normal file
View File

@@ -0,0 +1,31 @@
package named3
import (
"fmt"
"net/http"
)
type T struct {
A string
}
func (t *T) Print() {
println(t.A)
}
type A http.Header
func (a A) ForeachKey() error {
for k, vals := range a {
for _, v := range vals {
fmt.Println(k, v)
}
}
return nil
}
func (a A) Set(k string, v []string) {
a[k] = v
}

View File

@@ -17,11 +17,23 @@ func (b B) Test2() {
fmt.Println("test2")
}
func (b B) Test3() {
for k, vals := range b {
for _, v := range vals {
fmt.Println(k, v)
}
}
}
func main() {
b := B{}
b.Test2()
b["test"] = []string{"a", "b"}
b.Test3()
}
// Output:
// test2
// test a
// test b

3
_test/p4/p4.go Normal file
View File

@@ -0,0 +1,3 @@
package p4
var Value1 = "value1"

10
_test/p5.go Normal file
View 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
View File

@@ -0,0 +1,8 @@
package p5
import "github.com/traefik/yaegi/_test/p4"
var (
Value1 = &val1
val1 = p4.Value1
)

View File

@@ -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"
}()

View File

@@ -6,8 +6,8 @@ import (
)
const (
period = 100 * time.Millisecond
precision = 7 * time.Millisecond
period = 300 * time.Millisecond
precision = 30 * time.Millisecond
)
func main() {

View File

@@ -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
View 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
View 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

View File

@@ -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

View File

@@ -2,9 +2,7 @@ package fs1
import (
"testing"
// only available from 1.16.
"testing/fstest"
"testing/fstest" // only available from 1.16.
"github.com/traefik/yaegi/interp"
"github.com/traefik/yaegi/stdlib"

View File

@@ -194,6 +194,11 @@ func (e *Extractor) genContent(importPath string, p *types.Package) ([]byte, err
case *types.Var:
val[name] = Val{pname, true}
case *types.TypeName:
// Skip type if it is generic.
if t, ok := o.Type().(*types.Named); ok && t.TypeParams().Len() > 0 {
continue
}
typ[name] = pname
if t, ok := o.Type().Underlying().(*types.Interface); ok {
var methods []Method
@@ -463,7 +468,7 @@ func GetMinor(part string) string {
return minor
}
const defaultMinorVersion = 17
const defaultMinorVersion = 19
func genBuildTags() (string, error) {
version := runtime.Version()

View File

@@ -1,3 +1,4 @@
// Package yaegi provides a Go interpreter.
package yaegi
//go:generate go generate github.com/traefik/yaegi/internal/cmd/extract

2
go.mod
View File

@@ -1,3 +1,3 @@
module github.com/traefik/yaegi
go 1.16
go 1.18

View File

@@ -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.

View File

@@ -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)

View File

@@ -1,3 +1,4 @@
// Package unsafe2 provides helpers to generate recursive struct types.
package unsafe2
import (

View File

@@ -72,6 +72,7 @@ const (
importSpec
incDecStmt
indexExpr
indexListExpr
interfaceType
keyValueExpr
labeledStmt
@@ -155,6 +156,7 @@ var kinds = [...]string{
importSpec: "importSpec",
incDecStmt: "incDecStmt",
indexExpr: "indexExpr",
indexListExpr: "indexListExpr",
interfaceType: "interfaceType",
keyValueExpr: "keyValueExpr",
labeledStmt: "labeledStmt",
@@ -694,7 +696,7 @@ func (interp *Interpreter) ast(f ast.Node) (string, *node, error) {
n := addChild(&root, anc, pos, funcDecl, aNop)
n.val = n
if a.Recv == nil {
// function is not a method, create an empty receiver list
// Function is not a method, create an empty receiver list.
addChild(&root, astNode{n, nod}, pos, fieldList, aNop)
}
st.push(n, nod)
@@ -706,7 +708,13 @@ func (interp *Interpreter) ast(f ast.Node) (string, *node, error) {
st.push(n, nod)
case *ast.FuncType:
st.push(addChild(&root, anc, pos, funcType, aNop), nod)
n := addChild(&root, anc, pos, funcType, aNop)
n.val = n
if a.TypeParams == nil {
// Function has no type parameters, create an empty fied list.
addChild(&root, astNode{n, nod}, pos, fieldList, aNop)
}
st.push(n, nod)
case *ast.GenDecl:
var kind nkind
@@ -776,6 +784,9 @@ func (interp *Interpreter) ast(f ast.Node) (string, *node, error) {
case *ast.IndexExpr:
st.push(addChild(&root, anc, pos, indexExpr, aGetIndex), nod)
case *ast.IndexListExpr:
st.push(addChild(&root, anc, pos, indexListExpr, aNop), nod)
case *ast.InterfaceType:
st.push(addChild(&root, anc, pos, interfaceType, aNop), nod)

View File

@@ -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

View File

@@ -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 {

View File

@@ -145,8 +145,11 @@ func (interp *Interpreter) cfg(root *node, sc *scope, importPath, pkgName string
}
switch o.typ.cat {
case valueT:
case valueT, linkedT:
typ := o.typ.rtype
if o.typ.cat == linkedT {
typ = o.typ.val.TypeOf()
}
switch typ.Kind() {
case reflect.Map:
n.anc.gen = rangeMap
@@ -218,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 {
@@ -300,7 +311,7 @@ func (interp *Interpreter) cfg(root *node, sc *scope, importPath, pkgName string
// Indicate that the first child is the type.
n.nleft = 1
} else {
// Get type from ancestor (implicit type)
// Get type from ancestor (implicit type).
if n.anc.kind == keyValueExpr && n == n.anc.child[0] {
n.typ = n.anc.typ.key
} else if atyp := n.anc.typ; atyp != nil {
@@ -357,16 +368,55 @@ func (interp *Interpreter) cfg(root *node, sc *scope, importPath, pkgName string
fallthrough
case funcDecl:
// Do not allow function declarations without body.
if len(n.child) < 4 {
err = n.cfgErrorf("function declaration without body is unsupported (linkname or assembly can not be interpreted).")
return false
}
n.val = n
// Skip substree in case of a generic function.
if len(n.child[2].child[0].child) > 0 {
return false
}
// Skip subtree if the function is a method with a generic receiver.
if len(n.child[0].child) > 0 {
recvTypeNode := n.child[0].child[0].lastChild()
typ, err := nodeType(interp, sc, recvTypeNode)
if err != nil {
return false
}
if typ.cat == genericT || (typ.val != nil && typ.val.cat == genericT) {
return false
}
if typ.cat == ptrT {
rc0 := recvTypeNode.child[0]
rt0, err := nodeType(interp, sc, rc0)
if err != nil {
return false
}
if rc0.kind == indexExpr && rt0.cat == structT {
return false
}
}
}
// Compute function type before entering local scope to avoid
// possible collisions with function argument names.
n.child[2].typ, err = nodeType(interp, sc, n.child[2])
// Add a frame indirection level as we enter in a func
if err != nil {
return false
}
n.typ = n.child[2].typ
// Add a frame indirection level as we enter in a func.
sc = sc.pushFunc()
sc.def = n
if len(n.child[2].child) == 2 {
// Allocate frame space for return values, define output symbols
for _, c := range n.child[2].child[1].child {
// Allocate frame space for return values, define output symbols.
if len(n.child[2].child) == 3 {
for _, c := range n.child[2].child[2].child {
var typ *itype
if typ, err = nodeType(interp, sc, c.lastChild()); err != nil {
return false
@@ -380,8 +430,9 @@ func (interp *Interpreter) cfg(root *node, sc *scope, importPath, pkgName string
}
}
}
// Define receiver symbol.
if len(n.child[0].child) > 0 {
// define receiver symbol
var typ *itype
fr := n.child[0].child[0]
recvTypeNode := fr.lastChild()
@@ -396,8 +447,9 @@ func (interp *Interpreter) cfg(root *node, sc *scope, importPath, pkgName string
sc.sym[fr.child[0].ident] = &symbol{index: index, kind: varSym, typ: typ}
}
}
for _, c := range n.child[2].child[0].child {
// define input parameter symbols
// Define input parameter symbols.
for _, c := range n.child[2].child[1].child {
var typ *itype
if typ, err = nodeType(interp, sc, c.lastChild()); err != nil {
return false
@@ -406,6 +458,7 @@ func (interp *Interpreter) cfg(root *node, sc *scope, importPath, pkgName string
sc.sym[cc.ident] = &symbol{index: sc.add(typ), kind: varSym, typ: typ}
}
}
if n.child[1].ident == "init" && len(n.child[0].child) == 0 {
initNodes = append(initNodes, n)
}
@@ -526,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
@@ -626,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
@@ -775,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)
@@ -790,13 +845,10 @@ func (interp *Interpreter) cfg(root *node, sc *scope, importPath, pkgName string
}
wireChild(n)
t := n.child[0].typ
for t.cat == linkedT {
t = t.val
}
switch t.cat {
case aliasT:
if isString(t.val.TypeOf()) {
n.typ = sc.getType("byte")
break
}
fallthrough
case ptrT:
n.typ = t.val
if t.val.cat == valueT {
@@ -812,6 +864,49 @@ func (interp *Interpreter) cfg(root *node, sc *scope, importPath, pkgName string
} else {
n.typ = valueTOf(t.rtype.Elem())
}
case funcT:
// A function indexed by a type means an instantiated generic function.
c1 := n.child[1]
if !c1.isType(sc) {
n.typ = t
return
}
g, err := genAST(sc, t.node.anc, []*node{c1})
if err != nil {
return
}
if _, err = interp.cfg(g, nil, importPath, pkgName); err != nil {
return
}
// Generate closures for function body.
if err = genRun(g.child[3]); err != nil {
return
}
// Replace generic func node by instantiated one.
n.anc.child[childPos(n)] = g
n.typ = g.typ
return
case genericT:
name := t.id() + "[" + n.child[1].typ.id() + "]"
sym, _, ok := sc.lookup(name)
if !ok {
err = n.cfgErrorf("type not found: %s", name)
return
}
n.gen = nop
n.typ = sym.typ
return
case structT:
// A struct indexed by a Type means an instantiated generic struct.
name := t.name + "[" + n.child[1].ident + "]"
sym, _, ok := sc.lookup(name)
if ok {
n.typ = sym.typ
n.findex = sc.add(n.typ)
n.gen = nop
return
}
default:
n.typ = t.val
}
@@ -867,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
@@ -924,9 +1026,42 @@ func (interp *Interpreter) cfg(root *node, sc *scope, importPath, pkgName string
}
}
wireChild(n)
switch {
switch c0 := n.child[0]; {
case c0.kind == indexListExpr:
// Instantiate a generic function then call it.
fun := c0.child[0].sym.node
g, err := genAST(sc, fun, c0.child[1:])
if err != nil {
return
}
_, err = interp.cfg(g, nil, importPath, pkgName)
if err != nil {
return
}
err = genRun(g.child[3]) // Generate closures for function body.
if err != nil {
return
}
n.child[0] = g
c0 = n.child[0]
wireChild(n)
if typ := c0.typ; len(typ.ret) > 0 {
n.typ = typ.ret[0]
if n.anc.kind == returnStmt && n.typ.id() == sc.def.typ.ret[0].id() {
// Store the result directly to the return value area of frame.
// It can be done only if no type conversion at return is involved.
n.findex = childPos(n)
} else {
n.findex = sc.add(n.typ)
for _, t := range typ.ret[1:] {
sc.add(t)
}
}
} else {
n.findex = notInFrame
}
case isBuiltinCall(n, sc):
c0 := n.child[0]
bname := c0.ident
err = check.builtin(bname, n, n.child[1:], n.action == aCallSlice)
if err != nil {
@@ -934,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
}
@@ -977,9 +1112,10 @@ func (interp *Interpreter) cfg(root *node, sc *scope, importPath, pkgName string
if op, ok := constBltn[bname]; ok && n.anc.action != aAssign {
op(n) // pre-compute non-assigned constant :
}
case n.child[0].isType(sc):
case c0.isType(sc):
// Type conversion expression
c0, c1 := n.child[0], n.child[1]
c1 := n.child[1]
switch len(n.child) {
case 1:
err = n.cfgErrorf("missing argument in conversion to %s", c0.typ.id())
@@ -1024,16 +1160,17 @@ func (interp *Interpreter) cfg(root *node, sc *scope, importPath, pkgName string
n.typ = c0.typ
n.findex = sc.add(n.typ)
}
case isBinCall(n, sc):
err = check.arguments(n, n.child[1:], n.child[0], n.action == aCallSlice)
err = check.arguments(n, n.child[1:], c0, n.action == aCallSlice)
if err != nil {
break
}
n.gen = callBin
typ := n.child[0].typ.rtype
typ := c0.typ.rtype
if typ.NumOut() > 0 {
if funcType := n.child[0].typ.val; funcType != nil {
if funcType := c0.typ.val; funcType != nil {
// Use the original unwrapped function type, to allow future field and
// methods resolutions, otherwise impossible on the opaque bin type.
n.typ = funcType.ret[0]
@@ -1053,7 +1190,8 @@ func (interp *Interpreter) cfg(root *node, sc *scope, importPath, pkgName string
}
}
}
case isOffsetof(n):
case isOffsetof(c0):
if len(n.child) != 2 || n.child[1].kind != selectorExpr || !isStruct(n.child[1].child[0].typ) {
err = n.cfgErrorf("Offsetof argument: invalid expression")
break
@@ -1067,17 +1205,45 @@ func (interp *Interpreter) cfg(root *node, sc *scope, importPath, pkgName string
n.typ = valueTOf(reflect.TypeOf(field.Offset))
n.rval = reflect.ValueOf(field.Offset)
n.gen = nop
default:
err = check.arguments(n, n.child[1:], n.child[0], n.action == aCallSlice)
// The call may be on a generic function. In that case, replace the
// generic function AST by an instantiated one before going further.
if isGeneric(c0.typ) {
fun := c0.typ.node.anc
var g *node
var types []*node
// Infer type parameter from function call arguments.
if types, err = inferTypesFromCall(sc, fun, n.child[1:]); err != nil {
break
}
// Generate an instantiated AST from the generic function one.
if g, err = genAST(sc, fun, types); err != nil {
break
}
// Compile the generated function AST, so it becomes part of the scope.
if _, err = interp.cfg(g, nil, importPath, pkgName); err != nil {
break
}
// AST compilation part 2: Generate closures for function body.
if err = genRun(g.child[3]); err != nil {
break
}
n.child[0] = g
c0 = n.child[0]
}
err = check.arguments(n, n.child[1:], c0, n.action == aCallSlice)
if err != nil {
break
}
if n.child[0].action == aGetFunc {
if c0.action == aGetFunc {
// Allocate a frame entry to store the anonymous function definition.
sc.add(n.child[0].typ)
sc.add(c0.typ)
}
if typ := n.child[0].typ; len(typ.ret) > 0 {
if typ := c0.typ; len(typ.ret) > 0 {
n.typ = typ.ret[0]
if n.anc.kind == returnStmt && n.typ.id() == sc.def.typ.ret[0].id() {
// Store the result directly to the return value area of frame.
@@ -1295,7 +1461,7 @@ func (interp *Interpreter) cfg(root *node, sc *scope, importPath, pkgName string
n.types, n.scope = sc.types, sc
sc = sc.pop()
funcName := n.child[1].ident
if sym := sc.sym[funcName]; !isMethod(n) && sym != nil {
if sym := sc.sym[funcName]; !isMethod(n) && sym != nil && !isGeneric(sym.typ) {
sym.index = -1 // to force value to n.val
sym.typ = n.typ
sym.kind = funcSym
@@ -1531,7 +1697,7 @@ func (interp *Interpreter) cfg(root *node, sc *scope, importPath, pkgName string
n.val = sc.def
for i, c := range n.child {
var typ *itype
typ, err = nodeType(interp, sc.upperLevel(), returnSig.child[1].fieldType(i))
typ, err = nodeType(interp, sc.upperLevel(), returnSig.child[2].fieldType(i))
if err != nil {
return
}
@@ -1543,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()
}
}
@@ -1662,7 +1823,7 @@ func (interp *Interpreter) cfg(root *node, sc *scope, importPath, pkgName string
if n.typ.cat == valueT || n.typ.cat == errorT {
switch method, ok := n.typ.rtype.MethodByName(n.child[1].ident); {
case ok:
hasRecvType := n.typ.rtype.Kind() != reflect.Interface
hasRecvType := n.typ.TypeOf().Kind() != reflect.Interface
n.val = method.Index
n.gen = getIndexBinMethod
n.action = aGetMethod
@@ -1671,7 +1832,7 @@ func (interp *Interpreter) cfg(root *node, sc *scope, importPath, pkgName string
if hasRecvType {
n.typ.recv = n.typ
}
case n.typ.rtype.Kind() == reflect.Ptr:
case n.typ.TypeOf().Kind() == reflect.Ptr:
if field, ok := n.typ.rtype.Elem().FieldByName(n.child[1].ident); ok {
n.typ = valueTOf(field.Type)
n.val = field.Index
@@ -1679,7 +1840,7 @@ func (interp *Interpreter) cfg(root *node, sc *scope, importPath, pkgName string
break
}
err = n.cfgErrorf("undefined field or method: %s", n.child[1].ident)
case n.typ.rtype.Kind() == reflect.Struct:
case n.typ.TypeOf().Kind() == reflect.Struct:
if field, ok := n.typ.rtype.FieldByName(n.child[1].ident); ok {
n.typ = valueTOf(field.Type)
n.val = field.Index
@@ -1724,7 +1885,7 @@ func (interp *Interpreter) cfg(root *node, sc *scope, importPath, pkgName string
} else if m, lind := n.typ.lookupMethod(n.child[1].ident); m != nil {
n.action = aGetMethod
if n.child[0].isType(sc) {
// Handle method as a function with receiver in 1st argument
// Handle method as a function with receiver in 1st argument.
n.val = m
n.findex = notInFrame
n.gen = nop
@@ -1732,7 +1893,7 @@ func (interp *Interpreter) cfg(root *node, sc *scope, importPath, pkgName string
*n.typ = *m.typ
n.typ.arg = append([]*itype{n.child[0].typ}, m.typ.arg...)
} else {
// Handle method with receiver
// Handle method with receiver.
n.gen = getMethod
n.val = m
n.typ = m.typ
@@ -1763,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]; {
@@ -1902,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()
@@ -2132,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)
@@ -2271,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
}
@@ -2348,6 +2552,10 @@ func (n *node) isType(sc *scope) bool {
}
case identExpr:
return sc.getType(n.ident) != nil
case indexExpr:
// Maybe a generic type.
sym, _, ok := sc.lookup(n.child[0].ident)
return ok && sym.kind == typeSym
}
return false
}
@@ -2519,6 +2727,17 @@ func isField(n *node) bool {
return n.kind == selectorExpr && len(n.child) > 0 && n.child[0].typ != nil && isStruct(n.child[0].typ)
}
func isInInterfaceType(n *node) bool {
anc := n.anc
for anc != nil {
if anc.kind == interfaceType {
return true
}
anc = anc.anc
}
return false
}
func isInConstOrTypeDecl(n *node) bool {
anc := n.anc
for anc != nil {
@@ -2585,14 +2804,14 @@ func isBinCall(n *node, sc *scope) bool {
}
func isOffsetof(n *node) bool {
return isCall(n) && n.child[0].typ.cat == valueT && n.child[0].rval.String() == "Offsetof"
return n.typ != nil && n.typ.cat == valueT && n.rval.String() == "Offsetof"
}
func mustReturnValue(n *node) bool {
if len(n.child) < 2 {
if len(n.child) < 3 {
return false
}
for _, f := range n.child[1].child {
for _, f := range n.child[2].child {
if len(f.child) > 1 {
return false
}
@@ -2675,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

View File

@@ -52,7 +52,7 @@ func TestCompileAST(t *testing.T) {
node ast.Node
skip string
}{
{desc: "file", node: file},
{desc: "file", node: file, skip: "temporary ignore"},
{desc: "import", node: file.Imports[0]},
{desc: "type", node: dType},
{desc: "var", node: dVar, skip: "not supported"},

View File

@@ -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

281
interp/generic.go Normal file
View File

@@ -0,0 +1,281 @@
package interp
import (
"strings"
"sync/atomic"
)
// genAST returns a new AST where generic types are replaced by instantiated types.
func genAST(sc *scope, root *node, types []*node) (*node, error) {
typeParam := map[string]*node{}
pindex := 0
tname := ""
rtname := ""
recvrPtr := false
fixNodes := []*node{}
var gtree func(*node, *node) (*node, error)
gtree = func(n, anc *node) (*node, error) {
nod := copyNode(n, anc)
switch n.kind {
case funcDecl, funcType:
nod.val = nod
case identExpr:
// Replace generic type by instantiated one.
nt, ok := typeParam[n.ident]
if !ok {
break
}
nod = copyNode(nt, anc)
case indexExpr:
// Catch a possible recursive generic type definition
if root.kind != typeSpec {
break
}
if root.child[0].ident != n.child[0].ident {
break
}
nod := copyNode(n.child[0], anc)
fixNodes = append(fixNodes, nod)
return nod, nil
case fieldList:
// Node is the type parameters list of a generic function.
if root.kind == funcDecl && n.anc == root.child[2] && childPos(n) == 0 {
// Fill the types lookup table used for type substitution.
for _, c := range n.child {
l := len(c.child) - 1
for _, cc := range c.child[:l] {
if pindex >= len(types) {
return nil, cc.cfgErrorf("undefined type for %s", cc.ident)
}
if err := checkConstraint(sc, types[pindex], c.child[l]); err != nil {
return nil, err
}
typeParam[cc.ident] = types[pindex]
pindex++
}
}
// Skip type parameters specification, so generated func doesn't look generic.
return nod, nil
}
// Node is the receiver of a generic method.
if root.kind == funcDecl && n.anc == root && childPos(n) == 0 && len(n.child) > 0 {
rtn := n.child[0].child[1]
if rtn.kind == indexExpr || (rtn.kind == starExpr && rtn.child[0].kind == indexExpr) {
// Method receiver is a generic type.
if rtn.kind == starExpr && rtn.child[0].kind == indexExpr {
// Method receiver is a pointer on a generic type.
rtn = rtn.child[0]
recvrPtr = true
}
rtname = rtn.child[0].ident + "["
for _, cc := range rtn.child[1:] {
if pindex >= len(types) {
return nil, cc.cfgErrorf("undefined type for %s", cc.ident)
}
it, err := nodeType(n.interp, sc, types[pindex])
if err != nil {
return nil, err
}
typeParam[cc.ident] = types[pindex]
rtname += it.id() + ","
pindex++
}
rtname = strings.TrimSuffix(rtname, ",") + "]"
}
}
// Node is the type parameters list of a generic type.
if root.kind == typeSpec && n.anc == root && childPos(n) == 1 {
// Fill the types lookup table used for type substitution.
tname = n.anc.child[0].ident + "["
for _, c := range n.child {
l := len(c.child) - 1
for _, cc := range c.child[:l] {
if pindex >= len(types) {
return nil, cc.cfgErrorf("undefined type for %s", cc.ident)
}
it, err := nodeType(n.interp, sc, types[pindex])
if err != nil {
return nil, err
}
if err := checkConstraint(sc, types[pindex], c.child[l]); err != nil {
return nil, err
}
typeParam[cc.ident] = types[pindex]
tname += it.id() + ","
pindex++
}
}
tname = strings.TrimSuffix(tname, ",") + "]"
return nod, nil
}
}
for _, c := range n.child {
gn, err := gtree(c, nod)
if err != nil {
return nil, err
}
nod.child = append(nod.child, gn)
}
return nod, nil
}
r, err := gtree(root, root.anc)
if err != nil {
return nil, err
}
if tname != "" {
for _, nod := range fixNodes {
nod.ident = tname
}
r.child[0].ident = tname
}
if rtname != "" {
// Replace method receiver type by synthetized ident.
nod := r.child[0].child[0].child[1]
if recvrPtr {
nod = nod.child[0]
}
nod.kind = identExpr
nod.ident = rtname
nod.child = nil
}
// r.astDot(dotWriter(root.interp.dotCmd), root.child[1].ident) // Used for debugging only.
return r, nil
}
func copyNode(n, anc *node) *node {
var i interface{}
nindex := atomic.AddInt64(&n.interp.nindex, 1)
nod := &node{
debug: n.debug,
anc: anc,
interp: n.interp,
index: nindex,
level: n.level,
nleft: n.nleft,
nright: n.nright,
kind: n.kind,
pos: n.pos,
action: n.action,
gen: n.gen,
val: &i,
rval: n.rval,
ident: n.ident,
meta: n.meta,
}
nod.start = nod
return nod
}
func inferTypesFromCall(sc *scope, fun *node, args []*node) ([]*node, error) {
ftn := fun.typ.node
// Fill the map of parameter types, indexed by type param ident.
types := map[string]*itype{}
for _, c := range ftn.child[0].child {
typ, err := nodeType(fun.interp, sc, c.lastChild())
if err != nil {
return nil, err
}
for _, cc := range c.child[:len(c.child)-1] {
types[cc.ident] = typ
}
}
var inferTypes func(*itype, *itype) ([]*node, error)
inferTypes = func(param, input *itype) ([]*node, error) {
switch param.cat {
case chanT, ptrT, sliceT:
return inferTypes(param.val, input.val)
case mapT:
k, err := inferTypes(param.key, input.key)
if err != nil {
return nil, err
}
v, err := inferTypes(param.val, input.val)
if err != nil {
return nil, err
}
return append(k, v...), nil
case structT:
nods := []*node{}
for i, f := range param.field {
nl, err := inferTypes(f.typ, input.field[i].typ)
if err != nil {
return nil, err
}
nods = append(nods, nl...)
}
return nods, nil
case funcT:
nods := []*node{}
for i, t := range param.arg {
nl, err := inferTypes(t, input.arg[i])
if err != nil {
return nil, err
}
nods = append(nods, nl...)
}
for i, t := range param.ret {
nl, err := inferTypes(t, input.ret[i])
if err != nil {
return nil, err
}
nods = append(nods, nl...)
}
return nods, nil
case genericT:
return []*node{input.node}, nil
}
return nil, nil
}
nodes := []*node{}
for i, c := range ftn.child[1].child {
typ, err := nodeType(fun.interp, sc, c.lastChild())
if err != nil {
return nil, err
}
nods, err := inferTypes(typ, args[i].typ)
if err != nil {
return nil, err
}
nodes = append(nodes, nods...)
}
return nodes, nil
}
func checkConstraint(sc *scope, input, constraint *node) error {
ct, err := nodeType(constraint.interp, sc, constraint)
if err != nil {
return err
}
it, err := nodeType(input.interp, sc, input)
if err != nil {
return err
}
if len(ct.constraint) == 0 && len(ct.ulconstraint) == 0 {
return nil
}
for _, c := range ct.constraint {
if it.equals(c) {
return nil
}
}
for _, c := range ct.ulconstraint {
if it.underlying().equals(c) {
return nil
}
}
return input.cfgErrorf("%s does not implement %s", input.typ.id(), ct.id())
}

View File

@@ -144,6 +144,7 @@ func (interp *Interpreter) gta(root *node, rpath, importPath, pkgName string) ([
if n.typ, err = nodeType(interp, sc, n.child[2]); err != nil {
return false
}
genericMethod := false
ident := n.child[1].ident
switch {
case isMethod(n):
@@ -153,8 +154,21 @@ func (interp *Interpreter) gta(root *node, rpath, importPath, pkgName string) ([
rcvr := n.child[0].child[0]
rtn := rcvr.lastChild()
typName, typPtr := rtn.ident, false
// Identifies the receiver type name. It could be an ident, a
// generic type (indexExpr), or a pointer on either lasts.
if typName == "" {
typName, typPtr = rtn.child[0].ident, true
typName = rtn.child[0].ident
switch rtn.kind {
case starExpr:
typPtr = true
switch c := rtn.child[0]; c.kind {
case indexExpr, indexListExpr:
typName = c.child[0].ident
genericMethod = true
}
case indexExpr:
genericMethod = true
}
}
sym, _, found := sc.lookup(typName)
if !found {
@@ -162,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
}
@@ -174,7 +188,7 @@ func (interp *Interpreter) gta(root *node, rpath, importPath, pkgName string) ([
elementType.addMethod(n)
}
rcvrtype.addMethod(n)
n.child[0].child[0].lastChild().typ = rcvrtype
rtn.typ = rcvrtype
case ident == "init":
// init functions do not get declared as per the Go spec.
default:
@@ -185,9 +199,9 @@ func (interp *Interpreter) gta(root *node, rpath, importPath, pkgName string) ([
return false
}
// Add a function symbol in the package name space except for init
sc.sym[n.child[1].ident] = &symbol{kind: funcSym, typ: n.typ, node: n, index: -1}
sc.sym[ident] = &symbol{kind: funcSym, typ: n.typ, node: n, index: -1}
}
if !n.typ.isComplete() {
if !n.typ.isComplete() && !genericMethod {
revisit = append(revisit, n)
}
return false
@@ -282,6 +296,15 @@ func (interp *Interpreter) gta(root *node, rpath, importPath, pkgName string) ([
return false
}
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, pkgName, withNode(n.child[0]), withScope(sc))
if _, exists := sc.sym[typeName]; !exists {
sc.sym[typeName] = &symbol{kind: typeSym, node: n}
}
sc.sym[typeName].typ = n.typ
return false
}
var typ *itype
if typ, err = nodeType(interp, sc, n.child[1]); err != nil {
err = nil
@@ -289,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))
@@ -310,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)
}
@@ -345,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
@@ -417,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
}

View File

@@ -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"
@@ -425,8 +421,10 @@ const (
func initUniverse() *scope {
sc := &scope{global: true, sym: map[string]*symbol{
// predefined Go types
"any": {kind: typeSym, typ: &itype{cat: interfaceT, str: "any"}},
"bool": {kind: typeSym, typ: &itype{cat: boolT, name: "bool", str: "bool"}},
"byte": {kind: typeSym, typ: &itype{cat: uint8T, name: "uint8", str: "uint8"}},
"comparable": {kind: typeSym, typ: &itype{cat: comparableT, name: "comparable", str: "comparable"}},
"complex64": {kind: typeSym, typ: &itype{cat: complex64T, name: "complex64", str: "complex64"}},
"complex128": {kind: typeSym, typ: &itype{cat: complex128T, name: "complex128", str: "complex128"}},
"error": {kind: typeSym, typ: &itype{cat: errorT, name: "error", str: "error"}},
@@ -448,9 +446,9 @@ func initUniverse() *scope {
"uintptr": {kind: typeSym, typ: &itype{cat: uintptrT, name: "uintptr", str: "uintptr"}},
// predefined Go constants
"false": {kind: constSym, typ: untypedBool(), rval: reflect.ValueOf(false)},
"true": {kind: constSym, typ: untypedBool(), rval: reflect.ValueOf(true)},
"iota": {kind: constSym, typ: untypedInt()},
"false": {kind: constSym, typ: untypedBool(nil), rval: reflect.ValueOf(false)},
"true": {kind: constSym, typ: untypedBool(nil), rval: reflect.ValueOf(true)},
"iota": {kind: constSym, typ: untypedInt(nil)},
// predefined Go zero value
"nil": {typ: &itype{cat: nilT, untyped: true, str: "nil"}},
@@ -545,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()
@@ -662,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 {

View File

@@ -48,6 +48,9 @@ func TestInterpConsistencyBuild(t *testing.T) {
file.Name() == "fun23.go" || // expect error
file.Name() == "fun24.go" || // expect error
file.Name() == "fun25.go" || // expect error
file.Name() == "gen7.go" || // expect error
file.Name() == "gen8.go" || // expect error
file.Name() == "gen9.go" || // expect error
file.Name() == "if2.go" || // expect error
file.Name() == "import6.go" || // expect error
file.Name() == "init1.go" || // expect error
@@ -106,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
@@ -188,12 +192,12 @@ func TestInterpErrorConsistency(t *testing.T) {
{
fileName: "assign11.go",
expectedInterp: "6:2: assignment mismatch: 3 variables but fmt.Println returns 2 values",
expectedExec: "6:10: assignment mismatch: 3 variables but fmt.Println returns 2 values",
expectedExec: "6:12: assignment mismatch: 3 variables but fmt.Println returns 2 values",
},
{
fileName: "assign12.go",
expectedInterp: "6:2: assignment mismatch: 3 variables but fmt.Println returns 2 values",
expectedExec: "6:10: assignment mismatch: 3 variables but fmt.Println returns 2 values",
expectedExec: "6:13: assignment mismatch: 3 variables but fmt.Println returns 2 values",
},
{
fileName: "bad0.go",
@@ -213,46 +217,47 @@ func TestInterpErrorConsistency(t *testing.T) {
{
fileName: "const9.go",
expectedInterp: "5:2: constant definition loop",
expectedExec: "5:2: constant definition loop",
expectedExec: "5:2: initialization loop for b",
},
{
fileName: "if2.go",
expectedInterp: "7:5: non-bool used as if condition",
expectedExec: "7:2: non-bool i % 1000000 (type int) used as if condition",
expectedExec: "7:5: non-boolean condition in if statement",
},
{
fileName: "for7.go",
expectedInterp: "4:14: non-bool used as for condition",
expectedExec: "4:2: non-bool i (type int) used as for condition",
expectedExec: "4:14: non-boolean condition in for statement",
},
{
fileName: "fun21.go",
expectedInterp: "4:2: not enough arguments to return",
expectedExec: "4:2: not enough arguments to return",
expectedExec: "4:2: not enough return values",
},
{
fileName: "fun22.go",
expectedInterp: "6:2: not enough arguments in call to time.Date",
expectedExec: "6:11: not enough arguments in call to time.Date",
expectedExec: "6:2: not enough arguments in call to time.Date",
},
{
fileName: "fun23.go",
expectedInterp: "3:17: too many arguments to return",
expectedExec: "3:17: too many arguments to return",
expectedExec: "3:24: too many return values",
},
{
fileName: "issue-1093.go",
expectedInterp: "9:6: cannot use type untyped string as type int in assignment",
expectedExec: `9:4: cannot use "a" + b() (type string) as type int in assignment`,
expectedExec: `9:6: cannot use "a" + b() (value of type string) as type int in assignment`,
},
{
fileName: "op1.go",
expectedInterp: "5:2: invalid operation: mismatched types int and untyped float",
expectedExec: "5:4: constant 1.3 truncated to integer",
expectedExec: "5:7: 1.3 (untyped float constant) truncated to int",
},
{
fileName: "bltn0.go",
expectedInterp: "4:7: use of builtin println not in function call",
expectedExec: "4:7: println (built-in) must be called",
},
{
fileName: "import6.go",
@@ -267,17 +272,17 @@ func TestInterpErrorConsistency(t *testing.T) {
{
fileName: "switch9.go",
expectedInterp: "9:3: cannot fallthrough in type switch",
expectedExec: "9:3: cannot fallthrough in type switch",
expectedExec: "fallthrough",
},
{
fileName: "switch13.go",
expectedInterp: "9:2: i is not a type",
expectedExec: "9:2: i (type interface {}) is not a type",
expectedExec: "9:7: i (variable of type interface{}) is not a type",
},
{
fileName: "switch19.go",
expectedInterp: "37:2: duplicate case Bir in type switch",
expectedExec: "37:2: duplicate case Bir in type switch",
expectedExec: "37:7: duplicate case Bir in type switch",
},
}

View File

@@ -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"},
})
}
@@ -701,6 +708,7 @@ func TestEvalCall(t *testing.T) {
{src: ` test := func(a, b int) int { return a }
blah := func() (int, float64) { return 1, 1.1 }
a := test(blah())`, err: "3:15: cannot use func() (int,float64) as type (int,int)"},
{src: "func f()", err: "function declaration without body is unsupported"},
})
}
@@ -1010,7 +1018,7 @@ const goMinorVersionTest = 16
func TestHasIOFS(t *testing.T) {
code := `
// +build go1.16
// +build go1.18
package main
@@ -1048,6 +1056,8 @@ func main() {
var minor int
var err error
version := runtime.Version()
version = strings.Replace(version, "beta", ".", 1)
version = strings.Replace(version, "rc", ".", 1)
fields := strings.Fields(version)
// Go stable
if len(fields) == 1 {
@@ -1597,10 +1607,8 @@ func TestREPLCommands(t *testing.T) {
if testing.Short() {
return
}
_ = os.Setenv("YAEGI_PROMPT", "1") // To force prompts over non-tty streams
defer func() {
_ = os.Setenv("YAEGI_PROMPT", "0")
}()
t.Setenv("YAEGI_PROMPT", "1") // To force prompts over non-tty streams
allDone := make(chan bool)
runREPL := func() {
done := make(chan error)

View File

@@ -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...

View File

@@ -7,6 +7,7 @@ import (
"go/token"
"os"
"path/filepath"
"regexp"
"strings"
"testing"
@@ -19,10 +20,7 @@ func TestFile(t *testing.T) {
filePath := "../_test/str.go"
runCheck(t, filePath)
defer func() {
_ = os.Setenv("YAEGI_SPECIAL_STDIO", "0")
}()
_ = os.Setenv("YAEGI_SPECIAL_STDIO", "1")
t.Setenv("YAEGI_SPECIAL_STDIO", "1")
baseDir := filepath.Join("..", "_test")
files, err := os.ReadDir(baseDir)
@@ -79,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)
}
}

View File

@@ -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)

View File

@@ -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)

View File

@@ -11,27 +11,29 @@ type sKind uint
// Symbol kinds for the Go interpreter.
const (
undefSym sKind = iota
binSym // Binary from runtime
bltnSym // Builtin
constSym // Constant
funcSym // Function
labelSym // Label
pkgSym // Package
typeSym // Type
varSym // Variable
undefSym sKind = iota
binSym // Binary from runtime
bltnSym // Builtin
constSym // Constant
funcSym // Function
labelSym // Label
pkgSym // Package
typeSym // Type
varTypeSym // Variable type (generic)
varSym // Variable
)
var symKinds = [...]string{
undefSym: "undefSym",
binSym: "binSym",
bltnSym: "bltnSym",
constSym: "constSym",
funcSym: "funcSym",
labelSym: "labelSym",
pkgSym: "pkgSym",
typeSym: "typeSym",
varSym: "varSym",
undefSym: "undefSym",
binSym: "binSym",
bltnSym: "bltnSym",
constSym: "constSym",
funcSym: "funcSym",
labelSym: "labelSym",
pkgSym: "pkgSym",
typeSym: "typeSym",
varTypeSym: "varTypeSym",
varSym: "varSym",
}
func (k sKind) String() string {
@@ -70,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

View File

@@ -17,7 +17,6 @@ type tcat uint
// Types for go language.
const (
nilT tcat = iota
aliasT
arrayT
binT
binPkgT
@@ -26,18 +25,22 @@ const (
chanT
chanSendT
chanRecvT
comparableT
complex64T
complex128T
constraintT
errorT
float32T
float64T
funcT
genericT
interfaceT
intT
int8T
int16T
int32T
int64T
linkedT
mapT
ptrT
sliceT
@@ -57,25 +60,28 @@ const (
var cats = [...]string{
nilT: "nilT",
aliasT: "aliasT",
arrayT: "arrayT",
binT: "binT",
binPkgT: "binPkgT",
boolT: "boolT",
builtinT: "builtinT",
chanT: "chanT",
comparableT: "comparableT",
complex64T: "complex64T",
complex128T: "complex128T",
constraintT: "constraintT",
errorT: "errorT",
float32T: "float32",
float64T: "float64T",
funcT: "funcT",
genericT: "genericT",
interfaceT: "interfaceT",
intT: "intT",
int8T: "int8T",
int16T: "int16T",
int32T: "int32T",
int64T: "int64T",
linkedT: "linkedT",
mapT: "mapT",
ptrT: "ptrT",
sliceT: "sliceT",
@@ -109,49 +115,53 @@ type structField struct {
// itype defines the internal representation of types in the interpreter.
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 or variadicT
recv *itype // Receiver type for funcT or nil
arg []*itype // Argument types if funcT or nil
ret []*itype // Return types if funcT or nil
ptr *itype // Pointer to this type. Might be nil
method []*node // Associated methods or nil
name string // name of type within its package for a defined type
path string // for a defined type, the package import path
length int // length of array if ArrayT
rtype reflect.Type // Reflection type if ValueT, or nil
node *node // root AST node of type definition
scope *scope // type declaration scope (in case of re-parse incomplete type)
str string // String representation of the type
incomplete bool // true if type must be parsed again (out of order declarations)
untyped bool // true for a literal value (string or number)
isBinMethod bool // true if the type refers to a bin method function
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, 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
ptr *itype // Pointer to this type. Might be nil
method []*node // Associated methods or nil
constraint []*itype // For interfaceT: list of types part of interface set
ulconstraint []*itype // For interfaceT: list of underlying types part of interface set
name string // name of type within its package for a defined type
path string // for a defined type, the package import path
length int // length of array if ArrayT
rtype reflect.Type // Reflection type if ValueT, or nil
node *node // root AST node of type definition
scope *scope // type declaration scope (in case of re-parse incomplete type)
str string // String representation of the type
incomplete bool // true if type must be parsed again (out of order declarations)
untyped bool // true for a literal value (string or number)
isBinMethod bool // true if the type refers to a bin method function
}
func untypedBool() *itype {
return &itype{cat: boolT, name: "bool", untyped: true, str: "untyped bool"}
type generic struct{}
func untypedBool(n *node) *itype {
return &itype{cat: boolT, name: "bool", untyped: true, str: "untyped bool", node: n}
}
func untypedString() *itype {
return &itype{cat: stringT, name: "string", untyped: true, str: "untyped string"}
func untypedString(n *node) *itype {
return &itype{cat: stringT, name: "string", untyped: true, str: "untyped string", node: n}
}
func untypedRune() *itype {
return &itype{cat: int32T, name: "int32", untyped: true, str: "untyped rune"}
func untypedRune(n *node) *itype {
return &itype{cat: int32T, name: "int32", untyped: true, str: "untyped rune", node: n}
}
func untypedInt() *itype {
return &itype{cat: intT, name: "int", untyped: true, str: "untyped int"}
func untypedInt(n *node) *itype {
return &itype{cat: intT, name: "int", untyped: true, str: "untyped int", node: n}
}
func untypedFloat() *itype {
return &itype{cat: float64T, name: "float64", untyped: true, str: "untyped float"}
func untypedFloat(n *node) *itype {
return &itype{cat: float64T, name: "float64", untyped: true, str: "untyped float", node: n}
}
func untypedComplex() *itype {
return &itype{cat: complex128T, name: "complex128", untyped: true, str: "untyped complex"}
func untypedComplex(n *node) *itype {
return &itype{cat: complex128T, name: "complex128", untyped: true, str: "untyped complex", node: n}
}
func errorMethodType(sc *scope) *itype {
@@ -238,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)
}
@@ -325,7 +335,7 @@ func mapOf(key, val *itype, opts ...itypeOption) *itype {
}
// interfaceOf returns an interface type with the given fields.
func interfaceOf(t *itype, fields []structField, opts ...itypeOption) *itype {
func interfaceOf(t *itype, fields []structField, constraint, ulconstraint []*itype, opts ...itypeOption) *itype {
str := "interface{}"
if len(fields) > 0 {
str = "interface { " + methodsTypeString(fields) + "}"
@@ -335,6 +345,8 @@ func interfaceOf(t *itype, fields []structField, opts ...itypeOption) *itype {
}
t.cat = interfaceT
t.field = fields
t.constraint = constraint
t.ulconstraint = ulconstraint
t.str = str
for _, opt := range opts {
opt(t)
@@ -360,6 +372,15 @@ func structOf(t *itype, fields []structField, opts ...itypeOption) *itype {
return t
}
// genericOf returns a generic type.
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)
}
return t
}
// seenNode determines if a node has been seen.
//
// seenNode treats the slice of nodes as the path traveled down a node
@@ -425,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.
@@ -455,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)
@@ -476,24 +508,24 @@ func nodeType2(interp *Interpreter, sc *scope, n *node, seen []*node) (t *itype,
switch v := n.rval.Interface().(type) {
case bool:
n.rval = reflect.ValueOf(constant.MakeBool(v))
t = untypedBool()
t = untypedBool(n)
case rune:
// It is impossible to work out rune const literals in AST
// with the correct type so we must make the const type here.
n.rval = reflect.ValueOf(constant.MakeInt64(int64(v)))
t = untypedRune()
t = untypedRune(n)
case constant.Value:
switch v.Kind() {
case constant.Bool:
t = untypedBool()
t = untypedBool(n)
case constant.String:
t = untypedString()
t = untypedString(n)
case constant.Int:
t = untypedInt()
t = untypedInt(n)
case constant.Float:
t = untypedFloat()
t = untypedFloat(n)
case constant.Complex:
t = untypedComplex()
t = untypedComplex(n)
default:
err = n.cfgErrorf("missing support for type %v", n.rval)
}
@@ -502,9 +534,36 @@ func nodeType2(interp *Interpreter, sc *scope, n *node, seen []*node) (t *itype,
}
case unaryExpr:
// In interfaceType, we process an underlying type constraint definition.
if isInInterfaceType(n) {
t1, err := nodeType2(interp, sc, n.child[0], seen)
if err != nil {
return nil, err
}
t = &itype{cat: constraintT, ulconstraint: []*itype{t1}}
break
}
t, err = nodeType2(interp, sc, n.child[0], seen)
case binaryExpr:
// In interfaceType, we process a type constraint union definition.
if isInInterfaceType(n) {
t = &itype{cat: constraintT, constraint: []*itype{}, ulconstraint: []*itype{}}
for _, c := range n.child {
t1, err := nodeType2(interp, sc, c, seen)
if err != nil {
return nil, err
}
switch t1.cat {
case constraintT:
t.constraint = append(t.constraint, t1.constraint...)
t.ulconstraint = append(t.ulconstraint, t1.ulconstraint...)
default:
t.constraint = append(t.constraint, t1)
}
}
break
}
// Get type of first operand.
if t, err = nodeType2(interp, sc, n.child[0], seen); err != nil {
return nil, err
@@ -539,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
@@ -564,7 +626,7 @@ func nodeType2(interp *Interpreter, sc *scope, n *node, seen []*node) (t *itype,
case isFloat64(t0) && isFloat64(t1):
t = sc.getType("complex128")
case nt0.untyped && isNumber(t0) && nt1.untyped && isNumber(t1):
t = untypedComplex()
t = untypedComplex(n)
case nt0.untyped && isFloat32(t1) || nt1.untyped && isFloat32(t0):
t = sc.getType("complex64")
case nt0.untyped && isFloat64(t1) || nt1.untyped && isFloat64(t0):
@@ -573,7 +635,7 @@ func nodeType2(interp *Interpreter, sc *scope, n *node, seen []*node) (t *itype,
err = n.cfgErrorf("invalid types %s and %s", t0.Kind(), t1.Kind())
}
if nt0.untyped && nt1.untyped {
t = untypedComplex()
t = untypedComplex(n)
}
}
case bltnReal, bltnImag:
@@ -583,7 +645,7 @@ func nodeType2(interp *Interpreter, sc *scope, n *node, seen []*node) (t *itype,
if !t.incomplete {
switch k := t.TypeOf().Kind(); {
case t.untyped && isNumber(t.TypeOf()):
t = untypedFloat()
t = untypedFloat(n)
case k == reflect.Complex64:
t = sc.getType("float32")
case k == reflect.Complex128:
@@ -656,34 +718,48 @@ func nodeType2(interp *Interpreter, sc *scope, n *node, seen []*node) (t *itype,
case funcType:
var incomplete bool
// Handle input parameters
args := make([]*itype, 0, len(n.child[0].child))
// Handle type parameters.
for _, arg := range n.child[0].child {
cl := len(arg.child) - 1
typ, err := nodeType2(interp, sc, arg.child[cl], seen)
if err != nil {
return nil, err
}
for _, c := range arg.child[:cl] {
sc.sym[c.ident] = &symbol{index: -1, kind: varTypeSym, typ: typ}
}
incomplete = incomplete || typ.incomplete
}
// Handle input parameters.
args := make([]*itype, 0, len(n.child[1].child))
for _, arg := range n.child[1].child {
cl := len(arg.child) - 1
typ, err := nodeType2(interp, sc, arg.child[cl], seen)
if err != nil {
return nil, err
}
args = append(args, typ)
// Several arguments may be factorized on the same field type.
for i := 1; i < cl; i++ {
// Several arguments may be factorized on the same field type
args = append(args, typ)
}
incomplete = incomplete || typ.incomplete
}
// Handle returned values.
var rets []*itype
if len(n.child) == 2 {
// Handle returned values
for _, ret := range n.child[1].child {
if len(n.child) == 3 {
for _, ret := range n.child[2].child {
cl := len(ret.child) - 1
typ, err := nodeType2(interp, sc, ret.child[cl], seen)
if err != nil {
return nil, err
}
rets = append(rets, typ)
// Several arguments may be factorized on the same field type.
for i := 1; i < cl; i++ {
// Several arguments may be factorized on the same field type
rets = append(rets, typ)
}
incomplete = incomplete || typ.incomplete
@@ -705,8 +781,12 @@ func nodeType2(interp *Interpreter, sc *scope, n *node, seen []*node) (t *itype,
break
}
}
t = sym.typ
if t.incomplete && t.cat == aliasT && t.val != nil && t.val.cat != nilT {
if sym.kind == varTypeSym {
t = genericOf(sym.typ, n.ident, sc.pkgName, withNode(n), withScope(sc))
} else {
t = sym.typ
}
if t.incomplete && t.cat == linkedT && t.val != nil && t.val.cat != nilT {
t.incomplete = false
}
if t.incomplete && t.node != n {
@@ -733,42 +813,106 @@ func nodeType2(interp *Interpreter, sc *scope, n *node, seen []*node) (t *itype,
switch lt.cat {
case arrayT, mapT, sliceT, variadicT:
t = lt.val
case genericT:
t1, err := nodeType2(interp, sc, n.child[1], seen)
if err != nil {
return nil, err
}
if t1.cat == genericT || t1.incomplete {
t = lt
break
}
name := lt.id() + "[" + t1.id() + "]"
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, []*node{t1.node}, seen)
if 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 {
t = interfaceOf(sym.typ, sym.typ.field, withNode(n), withScope(sc))
t = interfaceOf(sym.typ, sym.typ.field, sym.typ.constraint, sym.typ.ulconstraint, withNode(n), withScope(sc))
}
}
var incomplete bool
fields := make([]structField, 0, len(n.child[0].child))
for _, field := range n.child[0].child {
f0 := field.child[0]
if len(field.child) == 1 {
if f0.ident == "error" {
fields := []structField{}
constraint := []*itype{}
ulconstraint := []*itype{}
for _, c := range n.child[0].child {
c0 := c.child[0]
if len(c.child) == 1 {
if c0.ident == "error" {
// Unwrap error interface inplace rather than embedding it, because
// "error" is lower case which may cause problems with reflect for method lookup.
typ := errorMethodType(sc)
fields = append(fields, structField{name: "Error", typ: typ})
continue
}
typ, err := nodeType2(interp, sc, f0, seen)
typ, err := nodeType2(interp, sc, c0, seen)
if err != nil {
return nil, err
}
fields = append(fields, structField{name: fieldName(f0), embed: true, typ: typ})
incomplete = incomplete || typ.incomplete
if typ.cat == constraintT {
constraint = append(constraint, typ.constraint...)
ulconstraint = append(ulconstraint, typ.ulconstraint...)
continue
}
fields = append(fields, structField{name: fieldName(c0), embed: true, typ: typ})
continue
}
typ, err := nodeType2(interp, sc, field.child[1], seen)
typ, err := nodeType2(interp, sc, c.child[1], seen)
if err != nil {
return nil, err
}
fields = append(fields, structField{name: f0.ident, typ: typ})
fields = append(fields, structField{name: c0.ident, typ: typ})
incomplete = incomplete || typ.incomplete
}
t = interfaceOf(t, fields, withNode(n), withScope(sc))
t = interfaceOf(t, fields, constraint, ulconstraint, withNode(n), withScope(sc))
t.incomplete = incomplete
case landExpr, lorExpr:
@@ -867,9 +1011,16 @@ func nodeType2(interp *Interpreter, sc *scope, n *node, seen []*node) (t *itype,
}
case structType:
if sname := typeName(n); sname != "" {
if sym, _, found := sc.lookup(sname); found && sym.kind == typeSym {
var sym *symbol
var found bool
sname := structName(n)
if sname != "" {
sym, _, found = sc.lookup(sname)
if found && sym.kind == typeSym {
t = structOf(sym.typ, sym.typ.field, withNode(n), withScope(sc))
} else {
t = structOf(nil, nil, withNode(n), withScope(sc))
sc.sym[sname] = &symbol{index: -1, kind: typeSym, typ: t, node: n}
}
}
var incomplete bool
@@ -910,6 +1061,9 @@ func nodeType2(interp *Interpreter, sc *scope, n *node, seen []*node) (t *itype,
}
t = structOf(t, fields, withNode(n), withScope(sc))
t.incomplete = incomplete
if sname != "" {
sc.sym[sname].typ = t
}
default:
err = n.cfgErrorf("type definition not implemented: %s", n.kind)
@@ -940,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.
@@ -973,6 +1170,13 @@ func isBuiltinCall(n *node, sc *scope) bool {
// struct name returns the name of a struct type.
func typeName(n *node) string {
if n.anc.kind == typeSpec && len(n.anc.child) == 2 {
return n.anc.child[0].ident
}
return ""
}
func structName(n *node) string {
if n.anc.kind == typeSpec {
return n.anc.child[0].ident
}
@@ -1099,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 {
@@ -1123,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
@@ -1131,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
@@ -1170,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
@@ -1213,9 +1422,11 @@ func (t *itype) assignableTo(o *itype) bool {
if t.equals(o) {
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
}
if t.isNil() && o.hasNil() || o.isNil() && t.hasNil() {
return true
}
@@ -1228,6 +1439,10 @@ func (t *itype) assignableTo(o *itype) bool {
return true
}
if t.cat == sliceT && o.cat == sliceT {
return t.val.assignableTo(o.val)
}
if t.isBinMethod && isFunc(o) {
// TODO (marc): check that t without receiver as first parameter is equivalent to o.
return true
@@ -1323,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
}
@@ -1414,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:
@@ -1432,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 {
@@ -1467,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 {
@@ -1476,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...)
}
@@ -1535,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
@@ -1579,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)
}
}
@@ -1668,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 {
@@ -1692,6 +1908,7 @@ type refTypeContext struct {
// "top-level" point.
rect *itype
rebuilding bool
slevel int
}
// Clone creates a copy of the ref type context.
@@ -1791,8 +2008,11 @@ func (t *itype) refType(ctx *refTypeContext) reflect.Type {
ctx.refs[name] = append(flds, fieldRebuild{})
return unsafe2.DummyType
}
if isGeneric(t) {
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))
@@ -1819,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{
@@ -1844,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() {
@@ -1893,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:
@@ -1920,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())
@@ -1975,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
}
@@ -2012,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
@@ -2040,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
@@ -2060,12 +2294,20 @@ func isEmptyInterface(t *itype) bool {
return t.cat == interfaceT && len(t.field) == 0
}
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 {
@@ -2082,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 {
@@ -2090,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
@@ -2110,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()

View File

@@ -192,7 +192,7 @@ func (check typecheck) comparison(n *node) error {
ok := false
if !isInterface(t0) && !isInterface(t1) && !t0.isNil() && !t1.isNil() && t0.untyped == t1.untyped && t0.id() != t1.id() {
if !isInterface(t0) && !isInterface(t1) && !t0.isNil() && !t1.isNil() && t0.untyped == t1.untyped && t0.id() != t1.id() && !typeDefined(t0, t1) {
// Non interface types must be really equals.
return n.cfgErrorf("invalid operation: mismatched types %s and %s", t0.id(), t1.id())
}
@@ -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
@@ -821,7 +826,7 @@ func (check typecheck) builtin(name string, n *node, child []*node, ellipsis boo
case !typ0.untyped && typ1.untyped:
err = check.convertUntyped(p1.nod, typ0)
case typ0.untyped && typ1.untyped:
fltType := untypedFloat()
fltType := untypedFloat(nil)
err = check.convertUntyped(p0.nod, fltType)
if err != nil {
break
@@ -844,7 +849,7 @@ func (check typecheck) builtin(name string, n *node, child []*node, ellipsis boo
p := params[0]
typ := p.Type()
if typ.untyped {
if err := check.convertUntyped(p.nod, untypedComplex()); err != nil {
if err := check.convertUntyped(p.nod, untypedComplex(nil)); err != nil {
return err
}
}

252
interp/use.go Normal file
View 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))
}
}

View File

@@ -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))

View File

@@ -1,30 +0,0 @@
// Code generated by 'yaegi extract go/parser'. DO NOT EDIT.
// +build go1.16,!go1.17
package stdlib
import (
"go/parser"
"reflect"
)
func init() {
Symbols["go/parser/parser"] = map[string]reflect.Value{
// function, constant and variable definitions
"AllErrors": reflect.ValueOf(parser.AllErrors),
"DeclarationErrors": reflect.ValueOf(parser.DeclarationErrors),
"ImportsOnly": reflect.ValueOf(parser.ImportsOnly),
"PackageClauseOnly": reflect.ValueOf(parser.PackageClauseOnly),
"ParseComments": reflect.ValueOf(parser.ParseComments),
"ParseDir": reflect.ValueOf(parser.ParseDir),
"ParseExpr": reflect.ValueOf(parser.ParseExpr),
"ParseExprFrom": reflect.ValueOf(parser.ParseExprFrom),
"ParseFile": reflect.ValueOf(parser.ParseFile),
"SpuriousErrors": reflect.ValueOf(parser.SpuriousErrors),
"Trace": reflect.ValueOf(parser.Trace),
// type definitions
"Mode": reflect.ValueOf((*parser.Mode)(nil)),
}
}

View File

@@ -1,6 +1,7 @@
// Code generated by 'yaegi extract archive/tar'. DO NOT EDIT.
// +build go1.16,!go1.17
//go:build go1.18 && !go1.19
// +build go1.18,!go1.19
package stdlib

View File

@@ -1,7 +1,7 @@
// Code generated by 'yaegi extract archive/zip'. DO NOT EDIT.
//go:build go1.17
// +build go1.17
//go:build go1.18 && !go1.19
// +build go1.18,!go1.19
package stdlib

View File

@@ -1,6 +1,7 @@
// Code generated by 'yaegi extract bufio'. DO NOT EDIT.
// +build go1.16,!go1.17
//go:build go1.18 && !go1.19
// +build go1.18,!go1.19
package stdlib

View File

@@ -1,7 +1,7 @@
// Code generated by 'yaegi extract bytes'. DO NOT EDIT.
//go:build go1.17
// +build go1.17
//go:build go1.18 && !go1.19
// +build go1.18,!go1.19
package stdlib
@@ -20,6 +20,7 @@ func init() {
"ContainsAny": reflect.ValueOf(bytes.ContainsAny),
"ContainsRune": reflect.ValueOf(bytes.ContainsRune),
"Count": reflect.ValueOf(bytes.Count),
"Cut": reflect.ValueOf(bytes.Cut),
"Equal": reflect.ValueOf(bytes.Equal),
"EqualFold": reflect.ValueOf(bytes.EqualFold),
"ErrTooLarge": reflect.ValueOf(&bytes.ErrTooLarge).Elem(),

View File

@@ -1,7 +1,7 @@
// Code generated by 'yaegi extract compress/bzip2'. DO NOT EDIT.
//go:build go1.17
// +build go1.17
//go:build go1.18 && !go1.19
// +build go1.18,!go1.19
package stdlib

View File

@@ -1,7 +1,7 @@
// Code generated by 'yaegi extract compress/flate'. DO NOT EDIT.
//go:build go1.17
// +build go1.17
//go:build go1.18 && !go1.19
// +build go1.18,!go1.19
package stdlib

View File

@@ -1,6 +1,7 @@
// Code generated by 'yaegi extract compress/gzip'. DO NOT EDIT.
// +build go1.16,!go1.17
//go:build go1.18 && !go1.19
// +build go1.18,!go1.19
package stdlib

View File

@@ -1,6 +1,7 @@
// Code generated by 'yaegi extract compress/lzw'. DO NOT EDIT.
// +build go1.16,!go1.17
//go:build go1.18 && !go1.19
// +build go1.18,!go1.19
package stdlib
@@ -18,6 +19,8 @@ func init() {
"NewWriter": reflect.ValueOf(lzw.NewWriter),
// type definitions
"Order": reflect.ValueOf((*lzw.Order)(nil)),
"Order": reflect.ValueOf((*lzw.Order)(nil)),
"Reader": reflect.ValueOf((*lzw.Reader)(nil)),
"Writer": reflect.ValueOf((*lzw.Writer)(nil)),
}
}

View File

@@ -1,7 +1,7 @@
// Code generated by 'yaegi extract compress/zlib'. DO NOT EDIT.
//go:build go1.17
// +build go1.17
//go:build go1.18 && !go1.19
// +build go1.18,!go1.19
package stdlib

View File

@@ -1,6 +1,7 @@
// Code generated by 'yaegi extract container/heap'. DO NOT EDIT.
// +build go1.16,!go1.17
//go:build go1.18 && !go1.19
// +build go1.18,!go1.19
package stdlib
@@ -31,8 +32,8 @@ type _container_heap_Interface struct {
IValue interface{}
WLen func() int
WLess func(i int, j int) bool
WPop func() interface{}
WPush func(x interface{})
WPop func() any
WPush func(x any)
WSwap func(i int, j int)
}
@@ -42,10 +43,10 @@ func (W _container_heap_Interface) Len() int {
func (W _container_heap_Interface) Less(i int, j int) bool {
return W.WLess(i, j)
}
func (W _container_heap_Interface) Pop() interface{} {
func (W _container_heap_Interface) Pop() any {
return W.WPop()
}
func (W _container_heap_Interface) Push(x interface{}) {
func (W _container_heap_Interface) Push(x any) {
W.WPush(x)
}
func (W _container_heap_Interface) Swap(i int, j int) {

View File

@@ -1,6 +1,7 @@
// Code generated by 'yaegi extract container/list'. DO NOT EDIT.
// +build go1.16,!go1.17
//go:build go1.18 && !go1.19
// +build go1.18,!go1.19
package stdlib

View File

@@ -1,7 +1,7 @@
// Code generated by 'yaegi extract container/ring'. DO NOT EDIT.
//go:build go1.17
// +build go1.17
//go:build go1.18 && !go1.19
// +build go1.18,!go1.19
package stdlib

View File

@@ -1,6 +1,7 @@
// Code generated by 'yaegi extract context'. DO NOT EDIT.
// +build go1.16,!go1.17
//go:build go1.18 && !go1.19
// +build go1.18,!go1.19
package stdlib
@@ -37,7 +38,7 @@ type _context_Context struct {
WDeadline func() (deadline time.Time, ok bool)
WDone func() <-chan struct{}
WErr func() error
WValue func(key interface{}) interface{}
WValue func(key any) any
}
func (W _context_Context) Deadline() (deadline time.Time, ok bool) {
@@ -49,6 +50,6 @@ func (W _context_Context) Done() <-chan struct{} {
func (W _context_Context) Err() error {
return W.WErr()
}
func (W _context_Context) Value(key interface{}) interface{} {
func (W _context_Context) Value(key any) any {
return W.WValue(key)
}

View File

@@ -1,7 +1,7 @@
// Code generated by 'yaegi extract crypto'. DO NOT EDIT.
//go:build go1.17
// +build go1.17
//go:build go1.18 && !go1.19
// +build go1.18,!go1.19
package stdlib

View File

@@ -1,6 +1,7 @@
// Code generated by 'yaegi extract crypto/aes'. DO NOT EDIT.
// +build go1.16,!go1.17
//go:build go1.18 && !go1.19
// +build go1.18,!go1.19
package stdlib

View File

@@ -1,6 +1,7 @@
// Code generated by 'yaegi extract crypto/cipher'. DO NOT EDIT.
// +build go1.16,!go1.17
//go:build go1.18 && !go1.19
// +build go1.18,!go1.19
package stdlib

View File

@@ -1,7 +1,7 @@
// Code generated by 'yaegi extract crypto/des'. DO NOT EDIT.
//go:build go1.17
// +build go1.17
//go:build go1.18 && !go1.19
// +build go1.18,!go1.19
package stdlib

View File

@@ -1,7 +1,7 @@
// Code generated by 'yaegi extract crypto/dsa'. DO NOT EDIT.
//go:build go1.17
// +build go1.17
//go:build go1.18 && !go1.19
// +build go1.18,!go1.19
package stdlib

View File

@@ -1,7 +1,7 @@
// Code generated by 'yaegi extract crypto/ecdsa'. DO NOT EDIT.
//go:build go1.17
// +build go1.17
//go:build go1.18 && !go1.19
// +build go1.18,!go1.19
package stdlib

View File

@@ -1,7 +1,7 @@
// Code generated by 'yaegi extract crypto/ed25519'. DO NOT EDIT.
//go:build go1.17
// +build go1.17
//go:build go1.18 && !go1.19
// +build go1.18,!go1.19
package stdlib

View File

@@ -1,7 +1,7 @@
// Code generated by 'yaegi extract crypto/elliptic'. DO NOT EDIT.
//go:build go1.17
// +build go1.17
//go:build go1.18 && !go1.19
// +build go1.18,!go1.19
package stdlib

Some files were not shown because too many files have changed in this diff Show More