go.starlark.net@v0.0.0-20231101134539-556fd59b42f6/lib/proto/proto.go (about) 1 // Copyright 2020 The Bazel Authors. All rights reserved. 2 // Use of this source code is governed by a BSD-style 3 // license that can be found in the LICENSE file. 4 5 // Package proto defines a module of utilities for constructing and 6 // accessing protocol messages within Starlark programs. 7 // 8 // THIS PACKAGE IS EXPERIMENTAL AND ITS INTERFACE MAY CHANGE. 9 // 10 // This package defines several types of Starlark value: 11 // 12 // Message -- a protocol message 13 // RepeatedField -- a repeated field of a message, like a list 14 // 15 // FileDescriptor -- information about a .proto file 16 // FieldDescriptor -- information about a message field (or extension field) 17 // MessageDescriptor -- information about the type of a message 18 // EnumDescriptor -- information about an enumerated type 19 // EnumValueDescriptor -- a value of an enumerated type 20 // 21 // A Message value is a wrapper around a protocol message instance. 22 // Starlark programs may access and update Messages using dot notation: 23 // 24 // x = msg.field 25 // msg.field = x + 1 26 // msg.field += 1 27 // 28 // Assignments to message fields perform dynamic checks on the type and 29 // range of the value to ensure that the message is at all times valid. 30 // 31 // The value of a repeated field of a message is represented by the 32 // list-like data type, RepeatedField. Its elements may be accessed, 33 // iterated, and updated in the usual ways. As with assignments to 34 // message fields, an assignment to an element of a RepeatedField 35 // performs a dynamic check to ensure that the RepeatedField holds 36 // only elements of the correct type. 37 // 38 // type(msg.uint32s) # "proto.repeated<uint32>" 39 // msg.uint32s[0] = 1 40 // msg.uint32s[0] = -1 # error: invalid uint32: -1 41 // 42 // Any iterable may be assigned to a repeated field of a message. If 43 // the iterable is itself a value of type RepeatedField, the message 44 // field holds a reference to it. 45 // 46 // msg2.uint32s = msg.uint32s # both messages share one RepeatedField 47 // msg.uint32s[0] = 123 48 // print(msg2.uint32s[0]) # "123" 49 // 50 // The RepeatedFields' element types must match. 51 // It is not enough for the values to be merely valid: 52 // 53 // msg.uint32s = [1, 2, 3] # makes a copy 54 // msg.uint64s = msg.uint32s # error: repeated field has wrong type 55 // msg.uint64s = list(msg.uint32s) # ok; makes a copy 56 // 57 // For all other iterables, a new RepeatedField is constructed from the 58 // elements of the iterable. 59 // 60 // msg.uints32s = [1, 2, 3] 61 // print(type(msg.uints32s)) # "proto.repeated<uint32>" 62 // 63 // 64 // To construct a Message from encoded binary or text data, call 65 // Unmarshal or UnmarshalText. These two functions are exposed to 66 // Starlark programs as proto.unmarshal{,_text}. 67 // 68 // To construct a Message from an existing Go proto.Message instance, 69 // you must first encode the Go message to binary, then decode it using 70 // Unmarshal. This ensures that messages visible to Starlark are 71 // encapsulated and cannot be mutated once their Starlark wrapper values 72 // are frozen. 73 // 74 // TODO(adonovan): document descriptors, enums, message instantiation. 75 // 76 // See proto_test.go for an example of how to use the 'proto' 77 // module in an application that embeds Starlark. 78 // 79 package proto 80 81 // TODO(adonovan): Go and Starlark API improvements: 82 // - Make Message and RepeatedField comparable. 83 // (NOTE: proto.Equal works only with generated message types.) 84 // - Support maps, oneof, any. But not messageset if we can avoid it. 85 // - Support "well-known types". 86 // - Defend against cycles in object graph. 87 // - Test missing required fields in marshalling. 88 89 import ( 90 "bytes" 91 "fmt" 92 "sort" 93 "strings" 94 "unsafe" 95 _ "unsafe" // for linkname hack 96 97 "google.golang.org/protobuf/encoding/prototext" 98 "google.golang.org/protobuf/proto" 99 "google.golang.org/protobuf/reflect/protoreflect" 100 "google.golang.org/protobuf/reflect/protoregistry" 101 "google.golang.org/protobuf/types/dynamicpb" 102 103 "go.starlark.net/starlark" 104 "go.starlark.net/starlarkstruct" 105 "go.starlark.net/syntax" 106 ) 107 108 // SetPool associates with the specified Starlark thread the 109 // descriptor pool used to find descriptors for .proto files and to 110 // instantiate messages from descriptors. Clients must call SetPool 111 // for a Starlark thread to use this package. 112 // 113 // For example: 114 // SetPool(thread, protoregistry.GlobalFiles) 115 // 116 func SetPool(thread *starlark.Thread, pool DescriptorPool) { 117 thread.SetLocal(contextKey, pool) 118 } 119 120 // Pool returns the descriptor pool previously associated with this thread. 121 func Pool(thread *starlark.Thread) DescriptorPool { 122 pool, _ := thread.Local(contextKey).(DescriptorPool) 123 return pool 124 } 125 126 const contextKey = "proto.DescriptorPool" 127 128 // A DescriptorPool loads FileDescriptors by path name or package name, 129 // possibly on demand. 130 // 131 // It is a superinterface of protodesc.Resolver, so any Resolver 132 // implementation is a valid pool. For example. 133 // protoregistry.GlobalFiles, which loads FileDescriptors from the 134 // compressed binary information in all the *.pb.go files linked into 135 // the process; and protodesc.NewFiles, which holds a set of 136 // FileDescriptorSet messages. See star2proto for example usage. 137 type DescriptorPool interface { 138 FindFileByPath(string) (protoreflect.FileDescriptor, error) 139 } 140 141 var Module = &starlarkstruct.Module{ 142 Name: "proto", 143 Members: starlark.StringDict{ 144 "file": starlark.NewBuiltin("proto.file", file), 145 "has": starlark.NewBuiltin("proto.has", has), 146 "marshal": starlark.NewBuiltin("proto.marshal", marshal), 147 "marshal_text": starlark.NewBuiltin("proto.marshal_text", marshal), 148 "set_field": starlark.NewBuiltin("proto.set_field", setFieldStarlark), 149 "get_field": starlark.NewBuiltin("proto.get_field", getFieldStarlark), 150 "unmarshal": starlark.NewBuiltin("proto.unmarshal", unmarshal), 151 "unmarshal_text": starlark.NewBuiltin("proto.unmarshal_text", unmarshal_text), 152 153 // TODO(adonovan): 154 // - merge(msg, msg) -> msg 155 // - equals(msg, msg) -> bool 156 // - diff(msg, msg) -> string 157 // - clone(msg) -> msg 158 }, 159 } 160 161 // file(filename) loads the FileDescriptor of the given name, or the 162 // first if the pool contains more than one. 163 // 164 // It's unfortunate that renaming a .proto file in effect breaks the 165 // interface it presents to Starlark. Ideally one would import 166 // descriptors by package name, but there may be many FileDescriptors 167 // for the same package name, and there is no "package descriptor". 168 // (Technically a pool may also have many FileDescriptors with the same 169 // file name, but this can't happen with a single consistent snapshot.) 170 func file(thread *starlark.Thread, fn *starlark.Builtin, args starlark.Tuple, kwargs []starlark.Tuple) (starlark.Value, error) { 171 var filename string 172 if err := starlark.UnpackPositionalArgs(fn.Name(), args, kwargs, 1, &filename); err != nil { 173 return nil, err 174 } 175 176 pool := Pool(thread) 177 if pool == nil { 178 return nil, fmt.Errorf("internal error: SetPool was not called") 179 } 180 181 desc, err := pool.FindFileByPath(filename) 182 if err != nil { 183 return nil, err 184 } 185 186 return FileDescriptor{Desc: desc}, nil 187 } 188 189 // has(msg, field) reports whether the specified field of the message is present. 190 // A field may be specified by name (string) or FieldDescriptor. 191 // has reports an error if the message type has no such field. 192 func has(thread *starlark.Thread, fn *starlark.Builtin, args starlark.Tuple, kwargs []starlark.Tuple) (starlark.Value, error) { 193 var x, field starlark.Value 194 if err := starlark.UnpackPositionalArgs(fn.Name(), args, kwargs, 2, &x, &field); err != nil { 195 return nil, err 196 } 197 msg, ok := x.(*Message) 198 if !ok { 199 return nil, fmt.Errorf("%s: got %s, want proto.Message", fn.Name(), x.Type()) 200 } 201 202 var fdesc protoreflect.FieldDescriptor 203 switch field := field.(type) { 204 case starlark.String: 205 var err error 206 fdesc, err = fieldDesc(msg.desc(), string(field)) 207 if err != nil { 208 return nil, err 209 } 210 211 case FieldDescriptor: 212 if field.Desc.ContainingMessage() != msg.desc() { 213 return nil, fmt.Errorf("%s: %v does not have field %v", fn.Name(), msg.desc().FullName(), field) 214 } 215 fdesc = field.Desc 216 217 default: 218 return nil, fmt.Errorf("%s: for field argument, got %s, want string or proto.FieldDescriptor", fn.Name(), field.Type()) 219 } 220 221 return starlark.Bool(msg.msg.Has(fdesc)), nil 222 } 223 224 // marshal{,_text}(msg) encodes a Message value to binary or text form. 225 func marshal(_ *starlark.Thread, fn *starlark.Builtin, args starlark.Tuple, kwargs []starlark.Tuple) (starlark.Value, error) { 226 var m *Message 227 if err := starlark.UnpackPositionalArgs(fn.Name(), args, kwargs, 1, &m); err != nil { 228 return nil, err 229 } 230 if fn.Name() == "proto.marshal" { 231 data, err := proto.Marshal(m.Message()) 232 if err != nil { 233 return nil, fmt.Errorf("%s: %v", fn.Name(), err) 234 } 235 return starlark.Bytes(data), nil 236 } else { 237 text, err := prototext.MarshalOptions{Indent: " "}.Marshal(m.Message()) 238 if err != nil { 239 return nil, fmt.Errorf("%s: %v", fn.Name(), err) 240 } 241 return starlark.String(text), nil 242 } 243 } 244 245 // unmarshal(msg) decodes a binary protocol message to a Message. 246 func unmarshal(thread *starlark.Thread, fn *starlark.Builtin, args starlark.Tuple, kwargs []starlark.Tuple) (starlark.Value, error) { 247 var desc MessageDescriptor 248 var data starlark.Bytes 249 if err := starlark.UnpackPositionalArgs(fn.Name(), args, kwargs, 2, &desc, &data); err != nil { 250 return nil, err 251 } 252 return unmarshalData(desc.Desc, []byte(data), true) 253 } 254 255 // unmarshal_text(msg) decodes a text protocol message to a Message. 256 func unmarshal_text(thread *starlark.Thread, fn *starlark.Builtin, args starlark.Tuple, kwargs []starlark.Tuple) (starlark.Value, error) { 257 var desc MessageDescriptor 258 var data string 259 if err := starlark.UnpackPositionalArgs(fn.Name(), args, kwargs, 2, &desc, &data); err != nil { 260 return nil, err 261 } 262 return unmarshalData(desc.Desc, []byte(data), false) 263 } 264 265 // set_field(msg, field, value) updates the value of a field. 266 // It is typically used for extensions, which cannot be updated using msg.field = v notation. 267 func setFieldStarlark(thread *starlark.Thread, fn *starlark.Builtin, args starlark.Tuple, kwargs []starlark.Tuple) (starlark.Value, error) { 268 // TODO(adonovan): allow field to be specified by name (for non-extension fields), like has? 269 var m *Message 270 var field FieldDescriptor 271 var v starlark.Value 272 if err := starlark.UnpackPositionalArgs(fn.Name(), args, kwargs, 3, &m, &field, &v); err != nil { 273 return nil, err 274 } 275 276 if *m.frozen { 277 return nil, fmt.Errorf("%s: cannot set %v field of frozen %v message", fn.Name(), field, m.desc().FullName()) 278 } 279 280 if field.Desc.ContainingMessage() != m.desc() { 281 return nil, fmt.Errorf("%s: %v does not have field %v", fn.Name(), m.desc().FullName(), field) 282 } 283 284 return starlark.None, setField(m.msg, field.Desc, v) 285 } 286 287 // get_field(msg, field) retrieves the value of a field. 288 // It is typically used for extension fields, which cannot be accessed using msg.field notation. 289 func getFieldStarlark(thread *starlark.Thread, fn *starlark.Builtin, args starlark.Tuple, kwargs []starlark.Tuple) (starlark.Value, error) { 290 // TODO(adonovan): allow field to be specified by name (for non-extension fields), like has? 291 var msg *Message 292 var field FieldDescriptor 293 if err := starlark.UnpackPositionalArgs(fn.Name(), args, kwargs, 2, &msg, &field); err != nil { 294 return nil, err 295 } 296 297 if field.Desc.ContainingMessage() != msg.desc() { 298 return nil, fmt.Errorf("%s: %v does not have field %v", fn.Name(), msg.desc().FullName(), field) 299 } 300 301 return msg.getField(field.Desc), nil 302 } 303 304 // The Call method implements the starlark.Callable interface. 305 // When a message descriptor is called, it returns a new instance of the 306 // protocol message it describes. 307 // 308 // Message(msg) -- return a shallow copy of an existing message 309 // Message(k=v, ...) -- return a new message with the specified fields 310 // Message(dict(...)) -- return a new message with the specified fields 311 // 312 func (d MessageDescriptor) CallInternal(thread *starlark.Thread, args starlark.Tuple, kwargs []starlark.Tuple) (starlark.Value, error) { 313 dest := &Message{ 314 msg: newMessage(d.Desc), 315 frozen: new(bool), 316 } 317 318 // Single positional argument? 319 if len(args) > 0 { 320 if len(kwargs) > 0 { 321 return nil, fmt.Errorf("%s: got both positional and named arguments", d.Desc.Name()) 322 } 323 if len(args) > 1 { 324 return nil, fmt.Errorf("%s: got %d positional arguments, want at most 1", d.Desc.Name(), len(args)) 325 } 326 327 // Keep consistent with MessageKind case of toProto. 328 // (support the same argument types). 329 switch src := args[0].(type) { 330 case *Message: 331 if dest.desc() != src.desc() { 332 return nil, fmt.Errorf("%s: got message of type %s, want type %s", d.Desc.Name(), src.desc().FullName(), dest.desc().FullName()) 333 } 334 335 // Make shallow copy of message. 336 // TODO(adonovan): How does frozen work if we have shallow copy? 337 src.msg.Range(func(fdesc protoreflect.FieldDescriptor, v protoreflect.Value) bool { 338 dest.msg.Set(fdesc, v) 339 return true 340 }) 341 return dest, nil 342 343 case *starlark.Dict: 344 kwargs = src.Items() 345 // fall through 346 347 default: 348 return nil, fmt.Errorf("%s: got %s, want dict or message", d.Desc.Name(), src.Type()) 349 } 350 } 351 352 // Convert named arguments to field values. 353 err := setFields(dest.msg, kwargs) 354 return dest, err 355 } 356 357 // setFields updates msg as if by msg.name=value for each (name, value) in items. 358 func setFields(msg protoreflect.Message, items []starlark.Tuple) error { 359 for _, item := range items { 360 name, ok := starlark.AsString(item[0]) 361 if !ok { 362 return fmt.Errorf("got %s, want string", item[0].Type()) 363 } 364 fdesc, err := fieldDesc(msg.Descriptor(), name) 365 if err != nil { 366 return err 367 } 368 if err := setField(msg, fdesc, item[1]); err != nil { 369 return err 370 } 371 } 372 return nil 373 } 374 375 // setField validates a Starlark field value, converts it to canonical form, 376 // and assigns to the field of msg. If value is None, the field is unset. 377 func setField(msg protoreflect.Message, fdesc protoreflect.FieldDescriptor, value starlark.Value) error { 378 // None unsets a field. 379 if value == starlark.None { 380 msg.Clear(fdesc) 381 return nil 382 } 383 384 // Assigning to a repeated field must make a copy, 385 // because the fields.Set doesn't specify whether 386 // it aliases the list or not, so we cannot assume. 387 // 388 // This is potentially surprising as 389 // x = []; msg.x = x; y = msg.x 390 // causes x and y not to alias. 391 if fdesc.IsList() { 392 iter := starlark.Iterate(value) 393 if iter == nil { 394 return fmt.Errorf("got %s for .%s field, want iterable", value.Type(), fdesc.Name()) 395 } 396 defer iter.Done() 397 398 list := msg.Mutable(fdesc).List() 399 list.Truncate(0) 400 var x starlark.Value 401 for i := 0; iter.Next(&x); i++ { 402 v, err := toProto(fdesc, x) 403 if err != nil { 404 return fmt.Errorf("index %d: %v", i, err) 405 } 406 list.Append(v) 407 } 408 return nil 409 } 410 411 if fdesc.IsMap() { 412 mapping, ok := value.(starlark.IterableMapping) 413 if !ok { 414 return fmt.Errorf("in map field %s: expected mappable type, but got %s", fdesc.Name(), value.Type()) 415 } 416 417 iter := mapping.Iterate() 418 defer iter.Done() 419 420 // Each value is converted using toProto as usual, passing the key/value 421 // field descriptors to check their types. 422 mutMap := msg.Mutable(fdesc).Map() 423 var k starlark.Value 424 for iter.Next(&k) { 425 kproto, err := toProto(fdesc.MapKey(), k) 426 if err != nil { 427 return fmt.Errorf("in key of map field %s: %w", fdesc.Name(), err) 428 } 429 430 // `found` is discarded, as the presence of the key in the 431 // iterator guarantees the presence of some value (even if it is 432 // starlark.None). Mismatching values will be caught in toProto 433 // below. 434 v, _, err := mapping.Get(k) 435 if err != nil { 436 return fmt.Errorf("in map field %s, at key %s: %w", fdesc.Name(), k.String(), err) 437 } 438 439 vproto, err := toProto(fdesc.MapValue(), v) 440 if err != nil { 441 return fmt.Errorf("in map field %s, at key %s: %w", fdesc.Name(), k.String(), err) 442 } 443 444 mutMap.Set(kproto.MapKey(), vproto) 445 } 446 447 return nil 448 } 449 450 v, err := toProto(fdesc, value) 451 if err != nil { 452 return fmt.Errorf("in field %s: %v", fdesc.Name(), err) 453 } 454 455 if fdesc.IsExtension() { 456 // The protoreflect.Message.NewField method must be able 457 // to return a new instance of the field type. Without 458 // having the Go type information available for extensions, 459 // the implementation of NewField won't know what to do. 460 // 461 // Thus we must augment the FieldDescriptor to one that 462 // additional holds Go representation type information 463 // (based in this case on dynamicpb). 464 fdesc = dynamicpb.NewExtensionType(fdesc).TypeDescriptor() 465 _ = fdesc.(protoreflect.ExtensionTypeDescriptor) 466 } 467 468 msg.Set(fdesc, v) 469 return nil 470 } 471 472 // toProto converts a Starlark value for a message field into protoreflect form. 473 func toProto(fdesc protoreflect.FieldDescriptor, v starlark.Value) (protoreflect.Value, error) { 474 switch fdesc.Kind() { 475 case protoreflect.BoolKind: 476 // To avoid mistakes, we require v be exactly a bool. 477 if v, ok := v.(starlark.Bool); ok { 478 return protoreflect.ValueOfBool(bool(v)), nil 479 } 480 481 case protoreflect.Fixed32Kind, 482 protoreflect.Uint32Kind: 483 // uint32 484 if i, ok := v.(starlark.Int); ok { 485 if u, ok := i.Uint64(); ok && uint64(uint32(u)) == u { 486 return protoreflect.ValueOfUint32(uint32(u)), nil 487 } 488 return noValue, fmt.Errorf("invalid %s: %v", typeString(fdesc), i) 489 } 490 491 case protoreflect.Int32Kind, 492 protoreflect.Sfixed32Kind, 493 protoreflect.Sint32Kind: 494 // int32 495 if i, ok := v.(starlark.Int); ok { 496 if i, ok := i.Int64(); ok && int64(int32(i)) == i { 497 return protoreflect.ValueOfInt32(int32(i)), nil 498 } 499 return noValue, fmt.Errorf("invalid %s: %v", typeString(fdesc), i) 500 } 501 502 case protoreflect.Uint64Kind, 503 protoreflect.Fixed64Kind: 504 // uint64 505 if i, ok := v.(starlark.Int); ok { 506 if u, ok := i.Uint64(); ok { 507 return protoreflect.ValueOfUint64(u), nil 508 } 509 return noValue, fmt.Errorf("invalid %s: %v", typeString(fdesc), i) 510 } 511 512 case protoreflect.Int64Kind, 513 protoreflect.Sfixed64Kind, 514 protoreflect.Sint64Kind: 515 // int64 516 if i, ok := v.(starlark.Int); ok { 517 if i, ok := i.Int64(); ok { 518 return protoreflect.ValueOfInt64(i), nil 519 } 520 return noValue, fmt.Errorf("invalid %s: %v", typeString(fdesc), i) 521 } 522 523 case protoreflect.StringKind: 524 if s, ok := starlark.AsString(v); ok { 525 return protoreflect.ValueOfString(s), nil 526 } else if b, ok := v.(starlark.Bytes); ok { 527 // TODO(adonovan): allow bytes for string? Not friendly to a Java port. 528 return protoreflect.ValueOfBytes([]byte(b)), nil 529 } 530 531 case protoreflect.BytesKind: 532 if s, ok := starlark.AsString(v); ok { 533 // TODO(adonovan): don't allow string for bytes: it's hostile to a Java port. 534 // Instead provide b"..." literals in the core 535 // and a bytes(str) conversion. 536 return protoreflect.ValueOfBytes([]byte(s)), nil 537 } else if b, ok := v.(starlark.Bytes); ok { 538 return protoreflect.ValueOfBytes([]byte(b)), nil 539 } 540 541 case protoreflect.DoubleKind: 542 switch v := v.(type) { 543 case starlark.Float: 544 return protoreflect.ValueOfFloat64(float64(v)), nil 545 case starlark.Int: 546 return protoreflect.ValueOfFloat64(float64(v.Float())), nil 547 } 548 549 case protoreflect.FloatKind: 550 switch v := v.(type) { 551 case starlark.Float: 552 return protoreflect.ValueOfFloat32(float32(v)), nil 553 case starlark.Int: 554 return protoreflect.ValueOfFloat32(float32(v.Float())), nil 555 } 556 557 case protoreflect.GroupKind, 558 protoreflect.MessageKind: 559 // Keep consistent with MessageDescriptor.CallInternal! 560 desc := fdesc.Message() 561 switch v := v.(type) { 562 case *Message: 563 if desc != v.desc() { 564 return noValue, fmt.Errorf("got %s, want %s", v.desc().FullName(), desc.FullName()) 565 } 566 return protoreflect.ValueOfMessage(v.msg), nil // alias it directly 567 568 case *starlark.Dict: 569 dest := newMessage(desc) 570 err := setFields(dest, v.Items()) 571 return protoreflect.ValueOfMessage(dest), err 572 } 573 574 case protoreflect.EnumKind: 575 enumval, err := enumValueOf(fdesc.Enum(), v) 576 if err != nil { 577 return noValue, err 578 } 579 return protoreflect.ValueOfEnum(enumval.Number()), nil 580 } 581 582 return noValue, fmt.Errorf("got %s, want %s", v.Type(), typeString(fdesc)) 583 } 584 585 var noValue protoreflect.Value 586 587 // toStarlark returns a Starlark value for the value x of a message field. 588 // If the result is a repeated field or message, 589 // the result aliases the original and has the specified "frozenness" flag. 590 // 591 // fdesc is only used for the type, not other properties of the field. 592 func toStarlark(typ protoreflect.FieldDescriptor, x protoreflect.Value, frozen *bool) starlark.Value { 593 if list, ok := x.Interface().(protoreflect.List); ok { 594 return &RepeatedField{ 595 typ: typ, 596 list: list, 597 frozen: frozen, 598 } 599 } 600 return toStarlark1(typ, x, frozen) 601 } 602 603 // toStarlark1, for scalar (non-repeated) values only. 604 func toStarlark1(typ protoreflect.FieldDescriptor, x protoreflect.Value, frozen *bool) starlark.Value { 605 606 switch typ.Kind() { 607 case protoreflect.BoolKind: 608 return starlark.Bool(x.Bool()) 609 610 case protoreflect.Fixed32Kind, 611 protoreflect.Uint32Kind, 612 protoreflect.Uint64Kind, 613 protoreflect.Fixed64Kind: 614 return starlark.MakeUint64(x.Uint()) 615 616 case protoreflect.Int32Kind, 617 protoreflect.Sfixed32Kind, 618 protoreflect.Sint32Kind, 619 protoreflect.Int64Kind, 620 protoreflect.Sfixed64Kind, 621 protoreflect.Sint64Kind: 622 return starlark.MakeInt64(x.Int()) 623 624 case protoreflect.StringKind: 625 return starlark.String(x.String()) 626 627 case protoreflect.BytesKind: 628 return starlark.Bytes(x.Bytes()) 629 630 case protoreflect.DoubleKind, protoreflect.FloatKind: 631 return starlark.Float(x.Float()) 632 633 case protoreflect.GroupKind, protoreflect.MessageKind: 634 return &Message{ 635 msg: x.Message(), 636 frozen: frozen, 637 } 638 639 case protoreflect.EnumKind: 640 // Invariant: only EnumValueDescriptor may appear here. 641 enumval := typ.Enum().Values().ByNumber(x.Enum()) 642 return EnumValueDescriptor{Desc: enumval} 643 } 644 645 panic(fmt.Sprintf("got %T, want %s", x, typeString(typ))) 646 } 647 648 // A Message is a Starlark value that wraps a protocol message. 649 // 650 // Two Messages are equivalent if and only if they are identical. 651 // 652 // When a Message value becomes frozen, a Starlark program may 653 // not modify the underlying protocol message, nor any Message 654 // or RepeatedField wrapper values derived from it. 655 type Message struct { 656 msg protoreflect.Message // any concrete type is allowed 657 frozen *bool // shared by a group of related Message/RepeatedField wrappers 658 } 659 660 // Message returns the wrapped message. 661 func (m *Message) Message() protoreflect.ProtoMessage { return m.msg.Interface() } 662 663 func (m *Message) desc() protoreflect.MessageDescriptor { return m.msg.Descriptor() } 664 665 var _ starlark.HasSetField = (*Message)(nil) 666 667 // Unmarshal parses the data as a binary protocol message of the specified type, 668 // and returns it as a new Starlark message value. 669 func Unmarshal(desc protoreflect.MessageDescriptor, data []byte) (*Message, error) { 670 return unmarshalData(desc, data, true) 671 } 672 673 // UnmarshalText parses the data as a text protocol message of the specified type, 674 // and returns it as a new Starlark message value. 675 func UnmarshalText(desc protoreflect.MessageDescriptor, data []byte) (*Message, error) { 676 return unmarshalData(desc, data, false) 677 } 678 679 // unmarshalData constructs a Starlark proto.Message by decoding binary or text data. 680 func unmarshalData(desc protoreflect.MessageDescriptor, data []byte, binary bool) (*Message, error) { 681 m := &Message{ 682 msg: newMessage(desc), 683 frozen: new(bool), 684 } 685 var err error 686 if binary { 687 err = proto.Unmarshal(data, m.Message()) 688 } else { 689 err = prototext.Unmarshal(data, m.Message()) 690 } 691 if err != nil { 692 return nil, fmt.Errorf("unmarshalling %s failed: %v", desc.FullName(), err) 693 } 694 return m, nil 695 } 696 697 func (m *Message) String() string { 698 buf := new(bytes.Buffer) 699 buf.WriteString(string(m.desc().FullName())) 700 buf.WriteByte('(') 701 702 // Sort fields (including extensions) by number. 703 var fields []protoreflect.FieldDescriptor 704 m.msg.Range(func(fdesc protoreflect.FieldDescriptor, v protoreflect.Value) bool { 705 // TODO(adonovan): opt: save v in table too. 706 fields = append(fields, fdesc) 707 return true 708 }) 709 sort.Slice(fields, func(i, j int) bool { 710 return fields[i].Number() < fields[j].Number() 711 }) 712 713 for i, fdesc := range fields { 714 if i > 0 { 715 buf.WriteString(", ") 716 } 717 if fdesc.IsExtension() { 718 // extension field: "[pkg.Msg.field]" 719 buf.WriteString(string(fdesc.FullName())) 720 } else if fdesc.Kind() != protoreflect.GroupKind { 721 // ordinary field: "field" 722 buf.WriteString(string(fdesc.Name())) 723 } else { 724 // group field: "MyGroup" 725 // 726 // The name of a group is the mangled version, 727 // while the true name of a group is the message itself. 728 // For example, for a group called "MyGroup", 729 // the inlined message will be called "MyGroup", 730 // but the field will be named "mygroup". 731 // This rule complicates name logic everywhere. 732 buf.WriteString(string(fdesc.Message().Name())) 733 } 734 buf.WriteString("=") 735 writeString(buf, fdesc, m.msg.Get(fdesc)) 736 } 737 buf.WriteByte(')') 738 return buf.String() 739 } 740 741 func (m *Message) Type() string { return "proto.Message" } 742 func (m *Message) Truth() starlark.Bool { return true } 743 func (m *Message) Freeze() { *m.frozen = true } 744 func (m *Message) Hash() (h uint32, err error) { return uint32(uintptr(unsafe.Pointer(m))), nil } // identity hash 745 746 // Attr returns the value of this message's field of the specified name. 747 // Extension fields are not accessible this way as their names are not unique. 748 func (m *Message) Attr(name string) (starlark.Value, error) { 749 // The name 'descriptor' is already effectively reserved 750 // by the Go API for generated message types. 751 if name == "descriptor" { 752 return MessageDescriptor{Desc: m.desc()}, nil 753 } 754 755 fdesc, err := fieldDesc(m.desc(), name) 756 if err != nil { 757 return nil, err 758 } 759 return m.getField(fdesc), nil 760 } 761 762 func (m *Message) getField(fdesc protoreflect.FieldDescriptor) starlark.Value { 763 if fdesc.IsExtension() { 764 // See explanation in setField. 765 fdesc = dynamicpb.NewExtensionType(fdesc).TypeDescriptor() 766 } 767 768 if m.msg.Has(fdesc) { 769 return toStarlark(fdesc, m.msg.Get(fdesc), m.frozen) 770 } 771 return defaultValue(fdesc) 772 } 773 774 //go:linkname detrandDisable google.golang.org/protobuf/internal/detrand.Disable 775 func detrandDisable() 776 777 func init() { 778 // Nasty hack to disable the randomization of output that occurs in textproto. 779 // TODO(adonovan): once go/proto-proposals/canonical-serialization 780 // is resolved the need for the hack should go away. See also go/go-proto-stability. 781 // If the proposal is rejected, we will need our own text-mode formatter. 782 detrandDisable() 783 } 784 785 // defaultValue returns the (frozen) default Starlark value for a given message field. 786 func defaultValue(fdesc protoreflect.FieldDescriptor) starlark.Value { 787 frozen := true 788 789 // The default value of a repeated field is an empty list. 790 if fdesc.IsList() { 791 return &RepeatedField{typ: fdesc, list: emptyList{}, frozen: &frozen} 792 } 793 794 // The zero value for a message type is an empty instance of that message. 795 if desc := fdesc.Message(); desc != nil { 796 return &Message{msg: newMessage(desc), frozen: &frozen} 797 } 798 799 // Convert the default value, which is not necessarily zero, to Starlark. 800 // The frozenness isn't used as the remaining types are all immutable. 801 return toStarlark1(fdesc, fdesc.Default(), &frozen) 802 } 803 804 // A frozen empty implementation of protoreflect.List. 805 type emptyList struct{ protoreflect.List } 806 807 func (emptyList) Len() int { return 0 } 808 809 // newMessage returns a new empty instance of the message type described by desc. 810 func newMessage(desc protoreflect.MessageDescriptor) protoreflect.Message { 811 // If desc refers to a built-in message, 812 // use the more efficient generated type descriptor (a Go struct). 813 mt, err := protoregistry.GlobalTypes.FindMessageByName(desc.FullName()) 814 if err == nil && mt.Descriptor() == desc { 815 return mt.New() 816 } 817 818 // For all others, use the generic dynamicpb representation. 819 return dynamicpb.NewMessage(desc).ProtoReflect() 820 } 821 822 // fieldDesc returns the descriptor for the named non-extension field. 823 func fieldDesc(desc protoreflect.MessageDescriptor, name string) (protoreflect.FieldDescriptor, error) { 824 if fdesc := desc.Fields().ByName(protoreflect.Name(name)); fdesc != nil { 825 return fdesc, nil 826 } 827 return nil, starlark.NoSuchAttrError(fmt.Sprintf("%s has no .%s field", desc.FullName(), name)) 828 } 829 830 // SetField updates a non-extension field of this message. 831 // It implements the HasSetField interface. 832 func (m *Message) SetField(name string, v starlark.Value) error { 833 fdesc, err := fieldDesc(m.desc(), name) 834 if err != nil { 835 return err 836 } 837 if *m.frozen { 838 return fmt.Errorf("cannot set .%s field of frozen %s message", 839 name, m.desc().FullName()) 840 } 841 return setField(m.msg, fdesc, v) 842 } 843 844 // AttrNames returns the set of field names defined for this message. 845 // It satisfies the starlark.HasAttrs interface. 846 func (m *Message) AttrNames() []string { 847 seen := make(map[string]bool) 848 849 // standard fields 850 seen["descriptor"] = true 851 852 // non-extension fields 853 fields := m.desc().Fields() 854 for i := 0; i < fields.Len(); i++ { 855 fdesc := fields.Get(i) 856 if !fdesc.IsExtension() { 857 seen[string(fdesc.Name())] = true 858 } 859 } 860 861 names := make([]string, 0, len(seen)) 862 for name := range seen { 863 names = append(names, name) 864 } 865 sort.Strings(names) 866 return names 867 } 868 869 // typeString returns a user-friendly description of the type of a 870 // protocol message field (or element of a repeated field). 871 func typeString(fdesc protoreflect.FieldDescriptor) string { 872 switch fdesc.Kind() { 873 case protoreflect.GroupKind, 874 protoreflect.MessageKind: 875 return string(fdesc.Message().FullName()) 876 877 case protoreflect.EnumKind: 878 return string(fdesc.Enum().FullName()) 879 880 default: 881 return strings.ToLower(strings.TrimPrefix(fdesc.Kind().String(), "TYPE_")) 882 } 883 } 884 885 // A RepeatedField is a Starlark value that wraps a repeated field of a protocol message. 886 // 887 // An assignment to an element of a repeated field incurs a dynamic 888 // check that the new value has (or can be converted to) the correct 889 // type using conversions similar to those done when calling a 890 // MessageDescriptor to construct a message. 891 // 892 // TODO(adonovan): make RepeatedField implement starlark.Comparable. 893 // Should the comparison include type, or be defined on the elements alone? 894 type RepeatedField struct { 895 typ protoreflect.FieldDescriptor // only for type information, not field name 896 list protoreflect.List 897 frozen *bool 898 itercount int 899 } 900 901 var _ starlark.HasSetIndex = (*RepeatedField)(nil) 902 903 func (rf *RepeatedField) Type() string { 904 return fmt.Sprintf("proto.repeated<%s>", typeString(rf.typ)) 905 } 906 907 func (rf *RepeatedField) SetIndex(i int, v starlark.Value) error { 908 if *rf.frozen { 909 return fmt.Errorf("cannot insert value in frozen repeated field") 910 } 911 if rf.itercount > 0 { 912 return fmt.Errorf("cannot insert value in repeated field with active iterators") 913 } 914 x, err := toProto(rf.typ, v) 915 if err != nil { 916 // The repeated field value cannot know which field it 917 // belongs to---it might be shared by several of the 918 // same type---so the error message is suboptimal. 919 return fmt.Errorf("setting element of repeated field: %v", err) 920 } 921 rf.list.Set(i, x) 922 return nil 923 } 924 925 func (rf *RepeatedField) Freeze() { *rf.frozen = true } 926 func (rf *RepeatedField) Hash() (uint32, error) { return 0, fmt.Errorf("unhashable: %s", rf.Type()) } 927 func (rf *RepeatedField) Index(i int) starlark.Value { 928 return toStarlark1(rf.typ, rf.list.Get(i), rf.frozen) 929 } 930 func (rf *RepeatedField) Iterate() starlark.Iterator { 931 if !*rf.frozen { 932 rf.itercount++ 933 } 934 return &repeatedFieldIterator{rf, 0} 935 } 936 func (rf *RepeatedField) Len() int { return rf.list.Len() } 937 func (rf *RepeatedField) String() string { 938 // We use list [...] notation even though it not exactly a list. 939 buf := new(bytes.Buffer) 940 buf.WriteByte('[') 941 for i := 0; i < rf.list.Len(); i++ { 942 if i > 0 { 943 buf.WriteString(", ") 944 } 945 writeString(buf, rf.typ, rf.list.Get(i)) 946 } 947 buf.WriteByte(']') 948 return buf.String() 949 } 950 func (rf *RepeatedField) Truth() starlark.Bool { return rf.list.Len() > 0 } 951 952 type repeatedFieldIterator struct { 953 rf *RepeatedField 954 i int 955 } 956 957 func (it *repeatedFieldIterator) Next(p *starlark.Value) bool { 958 if it.i < it.rf.Len() { 959 *p = it.rf.Index(it.i) 960 it.i++ 961 return true 962 } 963 return false 964 } 965 966 func (it *repeatedFieldIterator) Done() { 967 if !*it.rf.frozen { 968 it.rf.itercount-- 969 } 970 } 971 972 func writeString(buf *bytes.Buffer, fdesc protoreflect.FieldDescriptor, v protoreflect.Value) { 973 // TODO(adonovan): opt: don't materialize the Starlark value. 974 // TODO(adonovan): skip message type when printing submessages? {...}? 975 var frozen bool // ignored 976 x := toStarlark(fdesc, v, &frozen) 977 buf.WriteString(x.String()) 978 } 979 980 // -------- descriptor values -------- 981 982 // A FileDescriptor is an immutable Starlark value that describes a 983 // .proto file. It is a reference to a protoreflect.FileDescriptor. 984 // Two FileDescriptor values compare equal if and only if they refer to 985 // the same protoreflect.FileDescriptor. 986 // 987 // Its fields are the names of the message types (MessageDescriptor) and enum 988 // types (EnumDescriptor). 989 type FileDescriptor struct { 990 Desc protoreflect.FileDescriptor // TODO(adonovan): hide field, expose method? 991 } 992 993 var _ starlark.HasAttrs = FileDescriptor{} 994 995 func (f FileDescriptor) String() string { return string(f.Desc.Path()) } 996 func (f FileDescriptor) Type() string { return "proto.FileDescriptor" } 997 func (f FileDescriptor) Truth() starlark.Bool { return true } 998 func (f FileDescriptor) Freeze() {} // immutable 999 func (f FileDescriptor) Hash() (h uint32, err error) { return starlark.String(f.Desc.Path()).Hash() } 1000 func (f FileDescriptor) Attr(name string) (starlark.Value, error) { 1001 if desc := f.Desc.Messages().ByName(protoreflect.Name(name)); desc != nil { 1002 return MessageDescriptor{Desc: desc}, nil 1003 } 1004 if desc := f.Desc.Extensions().ByName(protoreflect.Name(name)); desc != nil { 1005 return FieldDescriptor{desc}, nil 1006 } 1007 if enum := f.Desc.Enums().ByName(protoreflect.Name(name)); enum != nil { 1008 return EnumDescriptor{Desc: enum}, nil 1009 } 1010 return nil, nil 1011 } 1012 func (f FileDescriptor) AttrNames() []string { 1013 var names []string 1014 messages := f.Desc.Messages() 1015 for i, n := 0, messages.Len(); i < n; i++ { 1016 names = append(names, string(messages.Get(i).Name())) 1017 } 1018 extensions := f.Desc.Extensions() 1019 for i, n := 0, extensions.Len(); i < n; i++ { 1020 names = append(names, string(extensions.Get(i).Name())) 1021 } 1022 enums := f.Desc.Enums() 1023 for i, n := 0, enums.Len(); i < n; i++ { 1024 names = append(names, string(enums.Get(i).Name())) 1025 } 1026 sort.Strings(names) 1027 return names 1028 } 1029 1030 // A MessageDescriptor is an immutable Starlark value that describes a protocol 1031 // message type. 1032 // 1033 // A MessageDescriptor value contains a reference to a protoreflect.MessageDescriptor. 1034 // Two MessageDescriptor values compare equal if and only if they refer to the 1035 // same protoreflect.MessageDescriptor. 1036 // 1037 // The fields of a MessageDescriptor value are the names of any message types 1038 // (MessageDescriptor), fields or extension fields (FieldDescriptor), 1039 // and enum types (EnumDescriptor) nested within the declaration of this message type. 1040 type MessageDescriptor struct { 1041 Desc protoreflect.MessageDescriptor 1042 } 1043 1044 var ( 1045 _ starlark.Callable = MessageDescriptor{} 1046 _ starlark.HasAttrs = MessageDescriptor{} 1047 ) 1048 1049 func (d MessageDescriptor) String() string { return string(d.Desc.FullName()) } 1050 func (d MessageDescriptor) Type() string { return "proto.MessageDescriptor" } 1051 func (d MessageDescriptor) Truth() starlark.Bool { return true } 1052 func (d MessageDescriptor) Freeze() {} // immutable 1053 func (d MessageDescriptor) Hash() (h uint32, err error) { 1054 return starlark.String(d.Desc.FullName()).Hash() 1055 } 1056 func (d MessageDescriptor) Attr(name string) (starlark.Value, error) { 1057 if desc := d.Desc.Messages().ByName(protoreflect.Name(name)); desc != nil { 1058 return MessageDescriptor{desc}, nil 1059 } 1060 if desc := d.Desc.Extensions().ByName(protoreflect.Name(name)); desc != nil { 1061 return FieldDescriptor{desc}, nil 1062 } 1063 if desc := d.Desc.Fields().ByName(protoreflect.Name(name)); desc != nil { 1064 return FieldDescriptor{desc}, nil 1065 } 1066 if desc := d.Desc.Enums().ByName(protoreflect.Name(name)); desc != nil { 1067 return EnumDescriptor{desc}, nil 1068 } 1069 return nil, nil 1070 } 1071 func (d MessageDescriptor) AttrNames() []string { 1072 var names []string 1073 messages := d.Desc.Messages() 1074 for i, n := 0, messages.Len(); i < n; i++ { 1075 names = append(names, string(messages.Get(i).Name())) 1076 } 1077 enums := d.Desc.Enums() 1078 for i, n := 0, enums.Len(); i < n; i++ { 1079 names = append(names, string(enums.Get(i).Name())) 1080 } 1081 sort.Strings(names) 1082 return names 1083 } 1084 func (d MessageDescriptor) Name() string { return string(d.Desc.Name()) } // for Callable 1085 1086 // A FieldDescriptor is an immutable Starlark value that describes 1087 // a field (possibly an extension field) of protocol message. 1088 // 1089 // A FieldDescriptor value contains a reference to a protoreflect.FieldDescriptor. 1090 // Two FieldDescriptor values compare equal if and only if they refer to the 1091 // same protoreflect.FieldDescriptor. 1092 // 1093 // The primary use for FieldDescriptors is to access extension fields of a message. 1094 // 1095 // A FieldDescriptor value has not attributes. 1096 // TODO(adonovan): expose metadata fields (e.g. name, type). 1097 type FieldDescriptor struct { 1098 Desc protoreflect.FieldDescriptor 1099 } 1100 1101 var ( 1102 _ starlark.HasAttrs = FieldDescriptor{} 1103 ) 1104 1105 func (d FieldDescriptor) String() string { return string(d.Desc.FullName()) } 1106 func (d FieldDescriptor) Type() string { return "proto.FieldDescriptor" } 1107 func (d FieldDescriptor) Truth() starlark.Bool { return true } 1108 func (d FieldDescriptor) Freeze() {} // immutable 1109 func (d FieldDescriptor) Hash() (h uint32, err error) { 1110 return starlark.String(d.Desc.FullName()).Hash() 1111 } 1112 func (d FieldDescriptor) Attr(name string) (starlark.Value, error) { 1113 // TODO(adonovan): expose metadata fields of Desc? 1114 return nil, nil 1115 } 1116 func (d FieldDescriptor) AttrNames() []string { 1117 var names []string 1118 // TODO(adonovan): expose metadata fields of Desc? 1119 sort.Strings(names) 1120 return names 1121 } 1122 1123 // An EnumDescriptor is an immutable Starlark value that describes an 1124 // protocol enum type. 1125 // 1126 // An EnumDescriptor contains a reference to a protoreflect.EnumDescriptor. 1127 // Two EnumDescriptor values compare equal if and only if they 1128 // refer to the same protoreflect.EnumDescriptor. 1129 // 1130 // An EnumDescriptor may be called like a function. It converts its 1131 // sole argument, which must be an int, string, or EnumValueDescriptor, 1132 // to an EnumValueDescriptor. 1133 // 1134 // The fields of an EnumDescriptor value are the values of the 1135 // enumeration, each of type EnumValueDescriptor. 1136 type EnumDescriptor struct { 1137 Desc protoreflect.EnumDescriptor 1138 } 1139 1140 var ( 1141 _ starlark.HasAttrs = EnumDescriptor{} 1142 _ starlark.Callable = EnumDescriptor{} 1143 ) 1144 1145 func (e EnumDescriptor) String() string { return string(e.Desc.FullName()) } 1146 func (e EnumDescriptor) Type() string { return "proto.EnumDescriptor" } 1147 func (e EnumDescriptor) Truth() starlark.Bool { return true } 1148 func (e EnumDescriptor) Freeze() {} // immutable 1149 func (e EnumDescriptor) Hash() (h uint32, err error) { return 0, nil } // TODO(adonovan): number? 1150 func (e EnumDescriptor) Attr(name string) (starlark.Value, error) { 1151 if v := e.Desc.Values().ByName(protoreflect.Name(name)); v != nil { 1152 return EnumValueDescriptor{v}, nil 1153 } 1154 return nil, nil 1155 } 1156 func (e EnumDescriptor) AttrNames() []string { 1157 var names []string 1158 values := e.Desc.Values() 1159 for i, n := 0, values.Len(); i < n; i++ { 1160 names = append(names, string(values.Get(i).Name())) 1161 } 1162 sort.Strings(names) 1163 return names 1164 } 1165 func (e EnumDescriptor) Name() string { return string(e.Desc.Name()) } // for Callable 1166 1167 // The Call method implements the starlark.Callable interface. 1168 // A call to an enum descriptor converts its argument to a value of that enum type. 1169 func (e EnumDescriptor) CallInternal(_ *starlark.Thread, args starlark.Tuple, kwargs []starlark.Tuple) (starlark.Value, error) { 1170 var x starlark.Value 1171 if err := starlark.UnpackPositionalArgs(string(e.Desc.Name()), args, kwargs, 1, &x); err != nil { 1172 return nil, err 1173 } 1174 v, err := enumValueOf(e.Desc, x) 1175 if err != nil { 1176 return nil, fmt.Errorf("%s: %v", e.Desc.Name(), err) 1177 } 1178 return EnumValueDescriptor{Desc: v}, nil 1179 } 1180 1181 // enumValueOf converts an int, string, or enum value to a value of the specified enum type. 1182 func enumValueOf(enum protoreflect.EnumDescriptor, x starlark.Value) (protoreflect.EnumValueDescriptor, error) { 1183 switch x := x.(type) { 1184 case starlark.Int: 1185 i, err := starlark.AsInt32(x) 1186 if err != nil { 1187 return nil, fmt.Errorf("invalid number %s for %s enum", x, enum.Name()) 1188 } 1189 desc := enum.Values().ByNumber(protoreflect.EnumNumber(i)) 1190 if desc == nil { 1191 return nil, fmt.Errorf("invalid number %d for %s enum", i, enum.Name()) 1192 } 1193 return desc, nil 1194 1195 case starlark.String: 1196 name := protoreflect.Name(x) 1197 desc := enum.Values().ByName(name) 1198 if desc == nil { 1199 return nil, fmt.Errorf("invalid name %q for %s enum", name, enum.Name()) 1200 } 1201 return desc, nil 1202 1203 case EnumValueDescriptor: 1204 if parent := x.Desc.Parent(); parent != enum { 1205 return nil, fmt.Errorf("invalid value %s.%s for %s enum", 1206 parent.Name(), x.Desc.Name(), enum.Name()) 1207 } 1208 return x.Desc, nil 1209 } 1210 1211 return nil, fmt.Errorf("cannot convert %s to %s enum", x.Type(), enum.Name()) 1212 } 1213 1214 // An EnumValueDescriptor is an immutable Starlark value that represents one value of an enumeration. 1215 // 1216 // An EnumValueDescriptor contains a reference to a protoreflect.EnumValueDescriptor. 1217 // Two EnumValueDescriptor values compare equal if and only if they 1218 // refer to the same protoreflect.EnumValueDescriptor. 1219 // 1220 // An EnumValueDescriptor has the following fields: 1221 // 1222 // index -- int, index of this value within the enum sequence 1223 // name -- string, name of this enum value 1224 // number -- int, numeric value of this enum value 1225 // type -- EnumDescriptor, the enum type to which this value belongs 1226 // 1227 type EnumValueDescriptor struct { 1228 Desc protoreflect.EnumValueDescriptor 1229 } 1230 1231 var ( 1232 _ starlark.HasAttrs = EnumValueDescriptor{} 1233 _ starlark.Comparable = EnumValueDescriptor{} 1234 ) 1235 1236 func (e EnumValueDescriptor) String() string { 1237 enum := e.Desc.Parent() 1238 return string(enum.Name() + "." + e.Desc.Name()) // "Enum.EnumValue" 1239 } 1240 func (e EnumValueDescriptor) Type() string { return "proto.EnumValueDescriptor" } 1241 func (e EnumValueDescriptor) Truth() starlark.Bool { return true } 1242 func (e EnumValueDescriptor) Freeze() {} // immutable 1243 func (e EnumValueDescriptor) Hash() (h uint32, err error) { return uint32(e.Desc.Number()), nil } 1244 func (e EnumValueDescriptor) AttrNames() []string { 1245 return []string{"index", "name", "number", "type"} 1246 } 1247 func (e EnumValueDescriptor) Attr(name string) (starlark.Value, error) { 1248 switch name { 1249 case "index": 1250 return starlark.MakeInt(e.Desc.Index()), nil 1251 case "name": 1252 return starlark.String(e.Desc.Name()), nil 1253 case "number": 1254 return starlark.MakeInt(int(e.Desc.Number())), nil 1255 case "type": 1256 enum := e.Desc.Parent() 1257 return EnumDescriptor{Desc: enum.(protoreflect.EnumDescriptor)}, nil 1258 } 1259 return nil, nil 1260 } 1261 func (x EnumValueDescriptor) CompareSameType(op syntax.Token, y_ starlark.Value, depth int) (bool, error) { 1262 y := y_.(EnumValueDescriptor) 1263 switch op { 1264 case syntax.EQL: 1265 return x.Desc == y.Desc, nil 1266 case syntax.NEQ: 1267 return x.Desc != y.Desc, nil 1268 default: 1269 return false, fmt.Errorf("%s %s %s not implemented", x.Type(), op, y_.Type()) 1270 } 1271 }