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  }