Skip to content

Struct fields implementing starlark.Value are not passed through verbatim as starlark.Value #12

@jazzy-crane

Description

@jazzy-crane

I'm not sure how best to explain this, but I'll give it a go. Please ping back if you need more details.

I'm looking at using starlight against some Go structs generated from proto definitions by protoc.

As an example:

enum BarEnum {
    BAR_UNKNOWN = 0
    BAR_ONE = 1
    BAR_TWO = 2
}

message Foo {
    string Msg = 1
    BarEnum Bar = 2;
}

I would then pass in the values of BarEnum in my

testFoo := Foo { Msg: "Hello World", Bar: BarEnum_BAR_ONE }
var env map[string]interface{}
env["testFoo"] = testFoo
env["BAR_UNKNOWN"] = BarEnum_BAR_UNKNOWN
env["BAR_ONE"] = BarEnum_BAR_ONE
env["BAR_TWO"] = BarEnum_BAR_TWO

starScipt = `
func(e):
    return e.Bar == BAR_ONE
return func(testFoo)
`
starlight.Eval([]byte(starScript), env, nil)
...
get result
...

Unfortunately the function is returning false. Digging deeper it seems that what protoc generates for BarEnum looks something like this:

type BarEnum int32

const (
	BarEnum_BAR_UNKNOWN BarEnum = 0
	BarEnum_BAR_ONE BarEnum = 1
	BarEnum_BAR_TWO BarEnum = 2
)

var BarEnum_name = map[int32]string{
	0: "BAR_UNKNOWN",
	1: "BAR_ONE",
	2: "BAR_TWO",
}

var BarEnum_value = map[string]int32{
	"BAR_UNKNOWN":  0,
	"BAR_ONE": 1,
	"BAR_TWO": 2,
}

func (x BarEnum) String() string {
	return proto.EnumName(BarEnum_name, int32(x))
}

func (BarEnum) EnumDescriptor() ([]byte, []int) {
	return fileDescriptor_14343df069b9efbf, []int{0}
}

while the maps are useful to iterate to export the enum values, the fact that BarEnum is a basic type with functions on it causes it to be exported as a GoInterface, and so not comparable as the base type.

This is fine. I noticed that if the type implements starlark.Value it can be passed through verbatim. I did this (adding Type, Freeze, Truth, Hash and CompareSameType implementations to BarEnum) and found that the enum values I was exporting were now comparable, but they still didn't compare properly with the Bar field of the Foo struct.

Digging a little deeper, it seems for member fields of structs you call toValue() for the field. This skips the 'does it implement starlark.Value, then pass it through verbatim' check. My case seems to work if I change the line:

        if field.Kind() != reflect.Invalid {
-               return toValue(field)
+               return ToValue(field.Interface())
        }
        return nil, nil

Is this fine to do or does it break other cases?

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions