gitlab.com/Raven-IO/raven-delve@v1.22.4/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 "gitlab.com/Raven-IO/raven-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(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 }