traefik / yaegi

Yaegi is Another Elegant Go Interpreter
https://pkg.go.dev/github.com/traefik/yaegi
Apache License 2.0
7.02k stars 349 forks source link

panic: reflect: reflect.Value.Call using value obtained using unexported field #1540

Open qinya0 opened 1 year ago

qinya0 commented 1 year ago

The following program sample.go triggers an unexpected result

package main
import (
        "fmt"
    "github.com/traefik/yaegi/interp"
    "github.com/traefik/yaegi/stdlib"
    "github.com/traefik/yaegi/stdlib/syscall"
    "github.com/traefik/yaegi/stdlib/unrestricted"
    "github.com/traefik/yaegi/stdlib/unsafe"
    "path"
    "reflect"
    "strings"
)

func main() {
    i := interp.New(interp.Options{
        GoPath: "~/gopath/",
    })

    _ = i.Use(stdlib.Symbols)
    _ = i.Use(unsafe.Symbols)
    _ = i.Use(syscall.Symbols)
    _ = i.Use(unrestricted.Symbols)

    importStr := "yaegi-test"
    if _, err := i.Eval(fmt.Sprintf(`import "%s"`, importStr)); err != nil {
        panic("eval import err:" + err.Error())
    }

    basePkg := strings.ReplaceAll(path.Base(importStr), "-", "_")

    // fnNew, err := i.Eval(basePkg + `.TestByteWriter1`) // successful
    fnNew, err := i.Eval(basePkg + `.TestByteWriter2`) // err
    if err != nil {
        panic("eval test err:" + err.Error())
    }

    _ = fnNew.Call([]reflect.Value{})
}

// ~/gopath/src/yaegi-test/test.go
package test

import (
        xxxx
)

type writer interface {
    io.ByteWriter
}

type Writer struct {
    writer
}

func NewWriter(wr writer) *Writer {
    return &Writer{
        writer: wr,
    }
}

// err
func TestByteWriter2() {
    fmt.Println("start TestByteWriter2")
    defer fmt.Println("end TestByteWriter2")

    b := bytes.NewBuffer([]byte{})
    w := NewWriter(bufio.NewWriter(b))

    _ = w.WriteByte(byte('a'))
}

// successful
func TestByteWriter1() {
    fmt.Println("start TestByteWriter1")
    defer fmt.Println("end TestByteWriter1")

    b := bytes.NewBuffer([]byte{})
    w := bufio.NewWriter(b)

    _ = w.WriteByte(byte('a'))
}

Expected result

// output

Got

panic: reflect: reflect.Value.Call using value obtained using unexported field [recovered]
        panic: reflect: reflect.Value.Call using value obtained using unexported field

goroutine 1 [running]:
github.com/traefik/yaegi/interp.runCfg.func1()
        ~/gopath/src/github.com/traefik/yaegi/interp/run.go:205 +0x388
panic({0x1038736a0, 0x140011b1aa0})
        ~/software/go-1.20.2/src/runtime/panic.go:890 +0x21c
reflect.flag.mustBeExportedSlow(0x2b3)
        ~/software/go-1.20.2/src/reflect/value.go:240 +0x90
reflect.flag.mustBeExported(0x2b3)
        ~/software/go-1.20.2/src/reflect/value.go:231 +0x5c
reflect.Value.Call({0x1039703e0, 0x1400161ff40, 0x2b3}, {0x14000e911b8, 0x1, 0x1})
        ~/software/go-1.20.2/src/reflect/value.go:369 +0x4c
github.com/traefik/yaegi/interp.callBin.func2({0x1039703e0, 0x1400161ff40, 0x2b3}, {0x14000e911b8, 0x1, 0x1})
        ~/gopath/src/github.com/traefik/yaegi/interp/run.go:1453 +0x54
github.com/traefik/yaegi/interp.callBin.func11(0x14001613810)
        ~/gopath/src/github.com/traefik/yaegi/interp/run.go:1641 +0x1b8
github.com/traefik/yaegi/interp.runCfg(0x140020d9540, 0x14001613810, 0x140020d8a00, 0x140020d8a00)
        ~/gopath/src/github.com/traefik/yaegi/interp/run.go:213 +0x25c
github.com/traefik/yaegi/interp.genFunctionWrapper.func1.1({0x103e7b798, 0x0, 0x0})
        ~/gopath/src/github.com/traefik/yaegi/interp/run.go:1024 +0x588
reflect.Value.call({0x10386a020, 0x140016b19b0, 0x13}, {0x103725e9e, 0x4}, {0x140004d1d40, 0x0, 0x0})
        ~/software/go-1.20.2/src/reflect/value.go:586 +0xa04
reflect.Value.Call({0x10386a020, 0x140016b19b0, 0x13}, {0x140004d1d40, 0x0, 0x0})
        ~/software/go-1.20.2/src/reflect/value.go:370 +0x74

Yaegi Version

d124954a7dd963431e0061f492c411796367f4e4

Additional Notes

No response