traefik / yaegi

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

Unable to use `go-jose/go-jose` #1502

Open Cyb3r-Jak3 opened 1 year ago

Cyb3r-Jak3 commented 1 year ago

The following program sample.go triggers an unexpected result

package main

import (
    "fmt"

    "github.com/go-jose/go-jose/v3"
)

func main() {
    fmt.Println(jose.A128GCM)
}

Expected result

go run main.go
A128GCM

Got

yaegi ./main.go
go-jose/go-jose/v3/json/encode.go:650:48: cannot use type *json.encodeState as type io.Writer

Yaegi Version

v0.14.3

Additional Notes

No response

jmanero commented 1 year ago

It looks like the interpreter may not be handling embedding in an unexported type. This it the json.encodeState type from go-jose:

// An encodeState encodes JSON into a bytes.Buffer.
type encodeState struct {
    bytes.Buffer // accumulated output
    scratch      [64]byte
}

bytes.Buffer should implement io.Writer

ssbeatty commented 1 year ago

Struct embedding doesn't seem to work very well at the moment In this code

package main

import (
    "bytes"
    "reflect"
)

func main() {
    tt := reflect.StructOf([]reflect.StructField{
        {Name: "Buffer", Type: reflect.TypeOf(bytes.Buffer{}), Anonymous: true},
    })

    if _, present := tt.MethodByName("Write"); present {
        println("tt has Write func")
    } else {
        println("tt has not Write func")
    }

    bt := reflect.StructOf([]reflect.StructField{
        {Name: "Buffer", Type: reflect.TypeOf(&bytes.Buffer{}), Anonymous: true},
    })

    if _, present := bt.MethodByName("Write"); present {
        println("bt has Write func")
    } else {
        println("bt has not Write func")
    }
}

// ouput
// tt has not Write func
// bt has Write func

When the field type passed into reflect.StructOf is pointer/non-pointer, the number of methods that reflect.Type can obtain is different

So in https://github.com/traefik/yaegi/blob/9d658604be3462097e3197c49d97c53efa8424ae/interp/run.go#L542

Is it more reasonable to change to the following code

    if dest.Kind() == reflect.Interface {
        if src.Kind() == reflect.Pointer {
            return src.Elem().Implements(dest)
        } else {
            return src.Implements(dest)
        }
    }

With this modification, the following code will work fine

// An encodeState encodes JSON into a bytes.Buffer.
type encodeState struct {
    *bytes.Buffer // accumulated output
    scratch      [64]byte
}

But if a structure is directly embedded, and the methods of this structure all contain pointers, there is nothing can do

Maybe reflect.PtrTo(rtype).Implements(typ) can implement

taliesins commented 1 year ago

@Cyb3r-Jak3 you may be interested in looking at: https://github.com/taliesins/traefik-plugin-oidc/tree/main/jwt

We hacked it to work with yaegi

Cyb3r-Jak3 commented 1 year ago

@taliesins Thanks! I will take a look