github.com/cnboonhan/delve@v0.0.0-20230908061759-363f2388c2fb/pkg/terminal/starbind/conv.go (about)

     1  package starbind
     2  
     3  import (
     4  	"errors"
     5  	"fmt"
     6  	"math"
     7  	"reflect"
     8  	"strconv"
     9  
    10  	"go.starlark.net/starlark"
    11  
    12  	"github.com/go-delve/delve/service/api"
    13  )
    14  
    15  // autoLoadConfig is the load configuration used to automatically load more from a variable
    16  var autoLoadConfig = api.LoadConfig{MaxVariableRecurse: 1, MaxStringLen: 1024, MaxArrayValues: 64, MaxStructFields: -1}
    17  
    18  // interfaceToStarlarkValue converts an interface{} variable (produced by
    19  // decoding JSON) into a starlark.Value.
    20  func (env *Env) interfaceToStarlarkValue(v interface{}) starlark.Value {
    21  	switch v := v.(type) {
    22  	case bool:
    23  		return starlark.Bool(bool(v))
    24  	case uint8:
    25  		return starlark.MakeUint64(uint64(v))
    26  	case uint16:
    27  		return starlark.MakeUint64(uint64(v))
    28  	case uint32:
    29  		return starlark.MakeUint64(uint64(v))
    30  	case uint64:
    31  		return starlark.MakeUint64(v)
    32  	case uintptr:
    33  		return starlark.MakeUint64(uint64(v))
    34  	case uint:
    35  		return starlark.MakeUint64(uint64(v))
    36  	case int8:
    37  		return starlark.MakeInt64(int64(v))
    38  	case int16:
    39  		return starlark.MakeInt64(int64(v))
    40  	case int32:
    41  		return starlark.MakeInt64(int64(v))
    42  	case int64:
    43  		return starlark.MakeInt64(v)
    44  	case int:
    45  		return starlark.MakeInt64(int64(v))
    46  	case string:
    47  		return starlark.String(v)
    48  	case map[string]uint64:
    49  		// this is the only map type that we use in the api, if we ever want to
    50  		// add more maps to the api a more general approach will be necessary.
    51  		var r starlark.Dict
    52  		for k, v := range v {
    53  			r.SetKey(starlark.String(k), starlark.MakeUint64(v))
    54  		}
    55  		return &r
    56  	case nil:
    57  		return starlark.None
    58  	case error:
    59  		return starlark.String(v.Error())
    60  	default:
    61  		vval := reflect.ValueOf(v)
    62  		switch vval.Type().Kind() {
    63  		case reflect.Ptr:
    64  			if vval.IsNil() {
    65  				return starlark.None
    66  			}
    67  			vval = vval.Elem()
    68  			if vval.Type().Kind() == reflect.Struct {
    69  				return structAsStarlarkValue{vval, env}
    70  			}
    71  		case reflect.Struct:
    72  			return structAsStarlarkValue{vval, env}
    73  		case reflect.Slice:
    74  			return sliceAsStarlarkValue{vval, env}
    75  		}
    76  		return starlark.String(fmt.Sprintf("%v", v))
    77  	}
    78  }
    79  
    80  // sliceAsStarlarkValue converts a reflect.Value containing a slice
    81  // into a starlark value.
    82  // The public methods of sliceAsStarlarkValue implement the Indexable and
    83  // Sequence starlark interfaces.
    84  type sliceAsStarlarkValue struct {
    85  	v   reflect.Value
    86  	env *Env
    87  }
    88  
    89  var _ starlark.Indexable = sliceAsStarlarkValue{}
    90  var _ starlark.Sequence = sliceAsStarlarkValue{}
    91  
    92  func (v sliceAsStarlarkValue) Freeze() {
    93  }
    94  
    95  func (v sliceAsStarlarkValue) Hash() (uint32, error) {
    96  	return 0, fmt.Errorf("not hashable")
    97  }
    98  
    99  func (v sliceAsStarlarkValue) String() string {
   100  	return fmt.Sprintf("%#v", v.v)
   101  }
   102  
   103  func (v sliceAsStarlarkValue) Truth() starlark.Bool {
   104  	return v.v.Len() != 0
   105  }
   106  
   107  func (v sliceAsStarlarkValue) Type() string {
   108  	return v.v.Type().String()
   109  }
   110  
   111  func (v sliceAsStarlarkValue) Index(i int) starlark.Value {
   112  	if i >= v.v.Len() {
   113  		return nil
   114  	}
   115  	return v.env.interfaceToStarlarkValue(v.v.Index(i).Interface())
   116  }
   117  
   118  func (v sliceAsStarlarkValue) Len() int {
   119  	return v.v.Len()
   120  }
   121  
   122  func (v sliceAsStarlarkValue) Iterate() starlark.Iterator {
   123  	return &sliceAsStarlarkValueIterator{0, v.v, v.env}
   124  }
   125  
   126  type sliceAsStarlarkValueIterator struct {
   127  	cur int
   128  	v   reflect.Value
   129  	env *Env
   130  }
   131  
   132  func (it *sliceAsStarlarkValueIterator) Done() {
   133  }
   134  
   135  func (it *sliceAsStarlarkValueIterator) Next(p *starlark.Value) bool {
   136  	if it.cur >= it.v.Len() {
   137  		return false
   138  	}
   139  	*p = it.env.interfaceToStarlarkValue(it.v.Index(it.cur).Interface())
   140  	it.cur++
   141  	return true
   142  }
   143  
   144  // structAsStarlarkValue converts any Go struct into a starlark.Value.
   145  // The public methods of structAsStarlarkValue implement the
   146  // starlark.HasAttrs interface.
   147  type structAsStarlarkValue struct {
   148  	v   reflect.Value
   149  	env *Env
   150  }
   151  
   152  var _ starlark.HasAttrs = structAsStarlarkValue{}
   153  
   154  func (v structAsStarlarkValue) Freeze() {
   155  }
   156  
   157  func (v structAsStarlarkValue) Hash() (uint32, error) {
   158  	return 0, fmt.Errorf("not hashable")
   159  }
   160  
   161  func (v structAsStarlarkValue) String() string {
   162  	if vv, ok := v.v.Interface().(api.Variable); ok {
   163  		return fmt.Sprintf("Variable<%s>", vv.SinglelineString())
   164  	}
   165  	return fmt.Sprintf("%#v", v.v)
   166  }
   167  
   168  func (v structAsStarlarkValue) Truth() starlark.Bool {
   169  	return true
   170  }
   171  
   172  func (v structAsStarlarkValue) Type() string {
   173  	if vv, ok := v.v.Interface().(api.Variable); ok {
   174  		return fmt.Sprintf("Variable<%s>", vv.Type)
   175  	}
   176  	return v.v.Type().String()
   177  }
   178  
   179  func (v structAsStarlarkValue) Attr(name string) (starlark.Value, error) {
   180  	if r, err := v.valueAttr(name); err != nil || r != nil {
   181  		return r, err
   182  	}
   183  	r := v.v.FieldByName(name)
   184  	if !r.IsValid() {
   185  		return starlark.None, fmt.Errorf("no field named %q in %T", name, v.v.Interface())
   186  	}
   187  	return v.env.interfaceToStarlarkValue(r.Interface()), nil
   188  }
   189  
   190  func (v structAsStarlarkValue) valueAttr(name string) (starlark.Value, error) {
   191  	if v.v.Type().Name() != "Variable" || (name != "Value" && name != "Expr") {
   192  		return nil, nil
   193  	}
   194  	v2 := v.v.Interface().(api.Variable)
   195  
   196  	if name == "Expr" {
   197  		return starlark.String(varAddrExpr(&v2)), nil
   198  	}
   199  
   200  	return v.env.variableValueToStarlarkValue(&v2, true)
   201  }
   202  
   203  func varAddrExpr(v *api.Variable) string {
   204  	return fmt.Sprintf("(*(*%q)(%#x))", v.Type, v.Addr)
   205  }
   206  
   207  func (env *Env) variableValueToStarlarkValue(v *api.Variable, top bool) (starlark.Value, error) {
   208  	if !top && v.Addr == 0 && v.Value == "" {
   209  		return starlark.None, nil
   210  	}
   211  
   212  	switch v.Kind {
   213  	case reflect.Struct:
   214  		if v.Len != 0 && len(v.Children) == 0 {
   215  			return starlark.None, errors.New("value not loaded")
   216  		}
   217  		return structVariableAsStarlarkValue{v, env}, nil
   218  	case reflect.Slice, reflect.Array:
   219  		if v.Len != 0 && len(v.Children) == 0 {
   220  			return starlark.None, errors.New("value not loaded")
   221  		}
   222  		return sliceVariableAsStarlarkValue{v, env}, nil
   223  	case reflect.Map:
   224  		if v.Len != 0 && len(v.Children) == 0 {
   225  			return starlark.None, errors.New("value not loaded")
   226  		}
   227  		return mapVariableAsStarlarkValue{v, env}, nil
   228  	case reflect.String:
   229  		return starlark.String(v.Value), nil
   230  	case reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64, reflect.Int:
   231  		n, _ := strconv.ParseInt(v.Value, 0, 64)
   232  		return starlark.MakeInt64(n), nil
   233  	case reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64, reflect.Uint, reflect.Uintptr:
   234  		n, _ := strconv.ParseUint(v.Value, 0, 64)
   235  		return starlark.MakeUint64(n), nil
   236  	case reflect.Bool:
   237  		n, _ := strconv.ParseBool(v.Value)
   238  		return starlark.Bool(n), nil
   239  	case reflect.Float32, reflect.Float64:
   240  		switch v.Value {
   241  		case "+Inf":
   242  			return starlark.Float(math.Inf(+1)), nil
   243  		case "-Inf":
   244  			return starlark.Float(math.Inf(-1)), nil
   245  		case "NaN":
   246  			return starlark.Float(math.NaN()), nil
   247  		default:
   248  			n, _ := strconv.ParseFloat(v.Value, 64)
   249  			return starlark.Float(n), nil
   250  		}
   251  	case reflect.Ptr, reflect.Interface:
   252  		if len(v.Children) > 0 {
   253  			v.Children[0] = *env.autoLoad(varAddrExpr(&v.Children[0]))
   254  		}
   255  		return ptrVariableAsStarlarkValue{v, env}, nil
   256  	}
   257  	return nil, nil
   258  }
   259  
   260  func (env *Env) autoLoad(expr string) *api.Variable {
   261  	v, err := env.ctx.Client().EvalVariable(api.EvalScope{GoroutineID: -1}, expr, autoLoadConfig)
   262  	if err != nil {
   263  		return &api.Variable{Unreadable: err.Error()}
   264  	}
   265  	return v
   266  }
   267  
   268  func (v structAsStarlarkValue) AttrNames() []string {
   269  	typ := v.v.Type()
   270  	r := make([]string, 0, typ.NumField()+1)
   271  	for i := 0; i < typ.NumField(); i++ {
   272  		r = append(r, typ.Field(i).Name)
   273  	}
   274  	return r
   275  }
   276  
   277  // structVariableAsStarlarkValue converts an api.Variable representing a
   278  // struct variable (in the target process) into a starlark.Value.
   279  // The public methods of structVariableAsStarlarkValue implement the
   280  // starlark.HasAttrs and starlark.Mapping interfaces.
   281  type structVariableAsStarlarkValue struct {
   282  	v   *api.Variable
   283  	env *Env
   284  }
   285  
   286  var _ starlark.HasAttrs = structVariableAsStarlarkValue{}
   287  var _ starlark.Mapping = structVariableAsStarlarkValue{}
   288  
   289  func (v structVariableAsStarlarkValue) Freeze() {
   290  }
   291  
   292  func (v structVariableAsStarlarkValue) Hash() (uint32, error) {
   293  	return 0, fmt.Errorf("not hashable")
   294  }
   295  
   296  func (v structVariableAsStarlarkValue) String() string {
   297  	return v.v.SinglelineString()
   298  }
   299  
   300  func (v structVariableAsStarlarkValue) Truth() starlark.Bool {
   301  	return true
   302  }
   303  
   304  func (v structVariableAsStarlarkValue) Type() string {
   305  	return v.v.Type
   306  }
   307  
   308  func (v structVariableAsStarlarkValue) Attr(name string) (starlark.Value, error) {
   309  	for i := range v.v.Children {
   310  		if v.v.Children[i].Name == name {
   311  			v2 := v.env.autoLoad(varAddrExpr(&v.v.Children[i]))
   312  			return v.env.variableValueToStarlarkValue(v2, false)
   313  		}
   314  	}
   315  	return nil, nil // no such field or method
   316  }
   317  
   318  func (v structVariableAsStarlarkValue) AttrNames() []string {
   319  	r := make([]string, len(v.v.Children))
   320  	for i := range v.v.Children {
   321  		r[i] = v.v.Children[i].Name
   322  	}
   323  	return r
   324  }
   325  
   326  func (v structVariableAsStarlarkValue) Get(key starlark.Value) (starlark.Value, bool, error) {
   327  	skey, ok := key.(starlark.String)
   328  	if !ok {
   329  		return starlark.None, false, nil
   330  	}
   331  	r, err := v.Attr(string(skey))
   332  	if r == nil && err == nil {
   333  		return starlark.None, false, nil
   334  	}
   335  	if err != nil {
   336  		return starlark.None, false, err
   337  	}
   338  	return r, true, nil
   339  }
   340  
   341  type sliceVariableAsStarlarkValue struct {
   342  	v   *api.Variable
   343  	env *Env
   344  }
   345  
   346  var _ starlark.Indexable = sliceVariableAsStarlarkValue{}
   347  var _ starlark.Sequence = sliceVariableAsStarlarkValue{}
   348  
   349  func (v sliceVariableAsStarlarkValue) Freeze() {
   350  }
   351  
   352  func (v sliceVariableAsStarlarkValue) Hash() (uint32, error) {
   353  	return 0, fmt.Errorf("not hashable")
   354  }
   355  
   356  func (v sliceVariableAsStarlarkValue) String() string {
   357  	return v.v.SinglelineString()
   358  }
   359  
   360  func (v sliceVariableAsStarlarkValue) Truth() starlark.Bool {
   361  	return v.v.Len != 0
   362  }
   363  
   364  func (v sliceVariableAsStarlarkValue) Type() string {
   365  	return v.v.Type
   366  }
   367  
   368  func (v sliceVariableAsStarlarkValue) Index(i int) starlark.Value {
   369  	if i >= v.Len() {
   370  		return nil
   371  	}
   372  	v2 := v.env.autoLoad(fmt.Sprintf("%s[%d]", varAddrExpr(v.v), i))
   373  	r, err := v.env.variableValueToStarlarkValue(v2, false)
   374  	if err != nil {
   375  		return starlark.String(err.Error())
   376  	}
   377  	return r
   378  }
   379  
   380  func (v sliceVariableAsStarlarkValue) Len() int {
   381  	return int(v.v.Len)
   382  }
   383  
   384  func (v sliceVariableAsStarlarkValue) Iterate() starlark.Iterator {
   385  	return &sliceVariableAsStarlarkValueIterator{0, v.v, v.env}
   386  }
   387  
   388  type sliceVariableAsStarlarkValueIterator struct {
   389  	cur int64
   390  	v   *api.Variable
   391  	env *Env
   392  }
   393  
   394  func (it *sliceVariableAsStarlarkValueIterator) Done() {
   395  }
   396  
   397  func (it *sliceVariableAsStarlarkValueIterator) Next(p *starlark.Value) bool {
   398  	if it.cur >= it.v.Len {
   399  		return false
   400  	}
   401  	s := sliceVariableAsStarlarkValue{it.v, it.env}
   402  	*p = s.Index(int(it.cur))
   403  	it.cur++
   404  	return true
   405  }
   406  
   407  type ptrVariableAsStarlarkValue struct {
   408  	v   *api.Variable
   409  	env *Env
   410  }
   411  
   412  var _ starlark.HasAttrs = ptrVariableAsStarlarkValue{}
   413  var _ starlark.Mapping = ptrVariableAsStarlarkValue{}
   414  
   415  func (v ptrVariableAsStarlarkValue) Freeze() {
   416  }
   417  
   418  func (v ptrVariableAsStarlarkValue) Hash() (uint32, error) {
   419  	return 0, fmt.Errorf("not hashable")
   420  }
   421  
   422  func (v ptrVariableAsStarlarkValue) String() string {
   423  	return v.v.SinglelineString()
   424  }
   425  
   426  func (v ptrVariableAsStarlarkValue) Truth() starlark.Bool {
   427  	return true
   428  }
   429  
   430  func (v ptrVariableAsStarlarkValue) Type() string {
   431  	return v.v.Type
   432  }
   433  
   434  func (v ptrVariableAsStarlarkValue) Attr(name string) (starlark.Value, error) {
   435  	if len(v.v.Children) == 0 {
   436  		return nil, nil // no such field or method
   437  	}
   438  	if v.v.Children[0].Kind == reflect.Struct {
   439  		// autodereference pointers to structs
   440  		x := structVariableAsStarlarkValue{&v.v.Children[0], v.env}
   441  		return x.Attr(name)
   442  	} else if v.v.Kind == reflect.Interface && v.v.Children[0].Kind == reflect.Ptr {
   443  		// allow double-autodereference for iface to ptr to struct
   444  		vchild := &v.v.Children[0]
   445  		if len(vchild.Children) > 0 {
   446  			vchild.Children[0] = *v.env.autoLoad(varAddrExpr(&vchild.Children[0]))
   447  		}
   448  		v2 := ptrVariableAsStarlarkValue{vchild, v.env}
   449  		return v2.Attr(name)
   450  	}
   451  
   452  	return nil, nil
   453  }
   454  
   455  func (v ptrVariableAsStarlarkValue) AttrNames() []string {
   456  	if len(v.v.Children) == 0 {
   457  		// The pointer variable was not loaded; we don't know the field names.
   458  		return nil
   459  	}
   460  	if v.v.Children[0].Kind != reflect.Struct {
   461  		return nil
   462  	}
   463  	// autodereference: present the field names of the pointed-to struct as the
   464  	// fields of this pointer variable.
   465  	x := structVariableAsStarlarkValue{&v.v.Children[0], v.env}
   466  	return x.AttrNames()
   467  }
   468  
   469  func (v ptrVariableAsStarlarkValue) Get(key starlark.Value) (starlark.Value, bool, error) {
   470  	if ikey, ok := key.(starlark.Int); ok {
   471  		if len(v.v.Children) == 0 {
   472  			return starlark.None, true, nil
   473  		}
   474  		if idx, _ := ikey.Int64(); idx == 0 {
   475  			r, err := v.env.variableValueToStarlarkValue(&v.v.Children[0], false)
   476  			if err != nil {
   477  				return starlark.String(err.Error()), true, nil
   478  			}
   479  			return r, true, nil
   480  		}
   481  		return starlark.None, false, nil
   482  	}
   483  
   484  	if len(v.v.Children) == 0 || v.v.Children[0].Kind != reflect.Struct {
   485  		return starlark.None, false, nil
   486  	}
   487  	// autodereference
   488  	x := structVariableAsStarlarkValue{&v.v.Children[0], v.env}
   489  	return x.Get(key)
   490  }
   491  
   492  type mapVariableAsStarlarkValue struct {
   493  	v   *api.Variable
   494  	env *Env
   495  }
   496  
   497  var _ starlark.IterableMapping = mapVariableAsStarlarkValue{}
   498  
   499  func (v mapVariableAsStarlarkValue) Freeze() {
   500  }
   501  
   502  func (v mapVariableAsStarlarkValue) Hash() (uint32, error) {
   503  	return 0, fmt.Errorf("not hashable")
   504  }
   505  
   506  func (v mapVariableAsStarlarkValue) String() string {
   507  	return v.v.SinglelineString()
   508  }
   509  
   510  func (v mapVariableAsStarlarkValue) Truth() starlark.Bool {
   511  	return true
   512  }
   513  
   514  func (v mapVariableAsStarlarkValue) Type() string {
   515  	return v.v.Type
   516  }
   517  
   518  func (v mapVariableAsStarlarkValue) Get(key starlark.Value) (starlark.Value, bool, error) {
   519  	var keyExpr string
   520  	switch key := key.(type) {
   521  	case starlark.Int:
   522  		keyExpr = key.String()
   523  	case starlark.Float:
   524  		keyExpr = fmt.Sprintf("%g", float64(key))
   525  	case starlark.String:
   526  		keyExpr = fmt.Sprintf("%q", string(key))
   527  	case starlark.Bool:
   528  		keyExpr = fmt.Sprintf("%v", bool(key))
   529  	case structVariableAsStarlarkValue:
   530  		keyExpr = varAddrExpr(key.v)
   531  	default:
   532  		return starlark.None, false, fmt.Errorf("key type not supported %T", key)
   533  	}
   534  
   535  	v2 := v.env.autoLoad(fmt.Sprintf("%s[%s]", varAddrExpr(v.v), keyExpr))
   536  	r, err := v.env.variableValueToStarlarkValue(v2, false)
   537  	if err != nil {
   538  		if err.Error() == "key not found" {
   539  			return starlark.None, false, nil
   540  		}
   541  		return starlark.None, false, err
   542  	}
   543  	return r, true, nil
   544  }
   545  
   546  func (v mapVariableAsStarlarkValue) Items() []starlark.Tuple {
   547  	r := make([]starlark.Tuple, 0, len(v.v.Children)/2)
   548  	for i := 0; i < len(v.v.Children); i += 2 {
   549  		r = append(r, mapStarlarkTupleAt(v.v, v.env, i))
   550  	}
   551  	return r
   552  }
   553  
   554  func mapStarlarkTupleAt(v *api.Variable, env *Env, i int) starlark.Tuple {
   555  	keyv := env.autoLoad(varAddrExpr(&v.Children[i]))
   556  	key, err := env.variableValueToStarlarkValue(keyv, false)
   557  	if err != nil {
   558  		key = starlark.None
   559  	}
   560  	valv := env.autoLoad(varAddrExpr(&v.Children[i+1]))
   561  	val, err := env.variableValueToStarlarkValue(valv, false)
   562  	if err != nil {
   563  		val = starlark.None
   564  	}
   565  	return starlark.Tuple{key, val}
   566  }
   567  
   568  func (v mapVariableAsStarlarkValue) Iterate() starlark.Iterator {
   569  	return &mapVariableAsStarlarkValueIterator{0, v.v, v.env}
   570  }
   571  
   572  type mapVariableAsStarlarkValueIterator struct {
   573  	cur int
   574  	v   *api.Variable
   575  	env *Env
   576  }
   577  
   578  func (it *mapVariableAsStarlarkValueIterator) Done() {
   579  }
   580  
   581  func (it *mapVariableAsStarlarkValueIterator) Next(p *starlark.Value) bool {
   582  	if it.cur >= 2*int(it.v.Len) {
   583  		return false
   584  	}
   585  	if it.cur >= len(it.v.Children) {
   586  		v2 := it.env.autoLoad(fmt.Sprintf("%s[%d:]", varAddrExpr(it.v), len(it.v.Children)/2))
   587  		it.v.Children = append(it.v.Children, v2.Children...)
   588  	}
   589  	if it.cur >= len(it.v.Children) {
   590  		return false
   591  	}
   592  
   593  	keyv := it.env.autoLoad(varAddrExpr(&it.v.Children[it.cur]))
   594  	key, err := it.env.variableValueToStarlarkValue(keyv, false)
   595  	if err != nil {
   596  		key = starlark.None
   597  	}
   598  	*p = key
   599  
   600  	it.cur += 2
   601  	return true
   602  }
   603  
   604  // unmarshalStarlarkValue unmarshals a starlark.Value 'val' into a Go variable 'dst'.
   605  // This works similarly to encoding/json.Unmarshal and similar functions,
   606  // but instead of getting its input from a byte buffer, it uses a
   607  // starlark.Value.
   608  func unmarshalStarlarkValue(val starlark.Value, dst interface{}, path string) error {
   609  	return unmarshalStarlarkValueIntl(val, reflect.ValueOf(dst), path)
   610  }
   611  
   612  func unmarshalStarlarkValueIntl(val starlark.Value, dst reflect.Value, path string) (err error) {
   613  	defer func() {
   614  		// catches reflect panics
   615  		ierr := recover()
   616  		if ierr != nil {
   617  			err = fmt.Errorf("error setting argument %q to %s: %v", path, val, ierr)
   618  		}
   619  	}()
   620  
   621  	converr := func(args ...string) error {
   622  		if len(args) > 0 {
   623  			return fmt.Errorf("error setting argument %q: can not convert %s to %s: %s", path, val, dst.Type().String(), args[0])
   624  		}
   625  		return fmt.Errorf("error setting argument %q: can not convert %s to %s", path, val, dst.Type().String())
   626  	}
   627  
   628  	if _, isnone := val.(starlark.NoneType); isnone {
   629  		return nil
   630  	}
   631  
   632  	for dst.Kind() == reflect.Ptr {
   633  		if dst.IsNil() {
   634  			dst.Set(reflect.New(dst.Type().Elem()))
   635  		}
   636  		dst = dst.Elem()
   637  	}
   638  
   639  	switch val := val.(type) {
   640  	case starlark.Bool:
   641  		dst.SetBool(bool(val))
   642  	case starlark.Int:
   643  		switch dst.Kind() {
   644  		case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64, reflect.Uintptr:
   645  			n, ok := val.Uint64()
   646  			if !ok {
   647  				return converr()
   648  			}
   649  			dst.SetUint(n)
   650  		case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64:
   651  			n, ok := val.Int64()
   652  			if !ok {
   653  				return converr()
   654  			}
   655  			dst.SetInt(n)
   656  		default:
   657  			return converr()
   658  		}
   659  	case starlark.Float:
   660  		dst.SetFloat(float64(val))
   661  	case starlark.String:
   662  		dst.SetString(string(val))
   663  	case *starlark.List:
   664  		if dst.Kind() != reflect.Slice {
   665  			return converr()
   666  		}
   667  		dst.Set(reflect.MakeSlice(dst.Type(), val.Len(), val.Len()))
   668  		for i := 0; i < val.Len(); i++ {
   669  			cur := dst.Index(i).Addr()
   670  			err := unmarshalStarlarkValueIntl(val.Index(i), cur, path)
   671  			if err != nil {
   672  				return err
   673  			}
   674  		}
   675  	case *starlark.Dict:
   676  		if dst.Kind() != reflect.Struct {
   677  			return converr()
   678  		}
   679  		for _, k := range val.Keys() {
   680  			if _, ok := k.(starlark.String); !ok {
   681  				return converr(fmt.Sprintf("non-string key %q", k.String()))
   682  			}
   683  			fieldName := string(k.(starlark.String))
   684  			dstfield := dst.FieldByName(fieldName)
   685  			if !dstfield.IsValid() {
   686  				return converr(fmt.Sprintf("unknown field %s", fieldName))
   687  			}
   688  			valfield, _, _ := val.Get(starlark.String(fieldName))
   689  			err := unmarshalStarlarkValueIntl(valfield, dstfield, path+"."+fieldName)
   690  			if err != nil {
   691  				return err
   692  			}
   693  		}
   694  	case structAsStarlarkValue:
   695  		rv := val.v
   696  		if rv.Kind() == reflect.Ptr {
   697  			rv = rv.Elem()
   698  		}
   699  		dst.Set(rv)
   700  	default:
   701  		return converr()
   702  	}
   703  	return nil
   704  }