google-protobuf 3.25.3 → 4.29.2

Sign up to get free protection for your applications and to get access to all the features.
Files changed (60) hide show
  1. checksums.yaml +4 -4
  2. data/ext/google/protobuf_c/convert.c +46 -18
  3. data/ext/google/protobuf_c/defs.c +368 -30
  4. data/ext/google/protobuf_c/extconf.rb +1 -1
  5. data/ext/google/protobuf_c/glue.c +16 -0
  6. data/ext/google/protobuf_c/map.c +74 -29
  7. data/ext/google/protobuf_c/map.h +7 -3
  8. data/ext/google/protobuf_c/message.c +120 -118
  9. data/ext/google/protobuf_c/message.h +2 -6
  10. data/ext/google/protobuf_c/protobuf.c +30 -17
  11. data/ext/google/protobuf_c/protobuf.h +3 -7
  12. data/ext/google/protobuf_c/repeated_field.c +58 -23
  13. data/ext/google/protobuf_c/repeated_field.h +6 -2
  14. data/ext/google/protobuf_c/ruby-upb.c +13774 -11526
  15. data/ext/google/protobuf_c/ruby-upb.h +11198 -9048
  16. data/ext/google/protobuf_c/shared_convert.c +10 -5
  17. data/ext/google/protobuf_c/shared_convert.h +2 -2
  18. data/ext/google/protobuf_c/shared_message.c +3 -31
  19. data/ext/google/protobuf_c/shared_message.h +0 -4
  20. data/ext/google/protobuf_c/third_party/utf8_range/utf8_range.c +467 -0
  21. data/ext/google/protobuf_c/third_party/utf8_range/utf8_range.h +9 -8
  22. data/lib/google/protobuf/any_pb.rb +1 -22
  23. data/lib/google/protobuf/api_pb.rb +1 -24
  24. data/lib/google/protobuf/descriptor_pb.rb +3 -23
  25. data/lib/google/protobuf/duration_pb.rb +1 -22
  26. data/lib/google/protobuf/empty_pb.rb +1 -22
  27. data/lib/google/protobuf/ffi/descriptor.rb +4 -4
  28. data/lib/google/protobuf/ffi/descriptor_pool.rb +3 -1
  29. data/lib/google/protobuf/ffi/enum_descriptor.rb +3 -1
  30. data/lib/google/protobuf/ffi/ffi.rb +8 -6
  31. data/lib/google/protobuf/ffi/field_descriptor.rb +13 -2
  32. data/lib/google/protobuf/ffi/file_descriptor.rb +3 -13
  33. data/lib/google/protobuf/ffi/internal/arena.rb +0 -6
  34. data/lib/google/protobuf/ffi/internal/convert.rb +21 -30
  35. data/lib/google/protobuf/ffi/map.rb +50 -24
  36. data/lib/google/protobuf/ffi/message.rb +189 -66
  37. data/lib/google/protobuf/ffi/method_descriptor.rb +114 -0
  38. data/lib/google/protobuf/ffi/object_cache.rb +3 -3
  39. data/lib/google/protobuf/ffi/oneof_descriptor.rb +3 -1
  40. data/lib/google/protobuf/ffi/repeated_field.rb +47 -19
  41. data/lib/google/protobuf/ffi/service_descriptor.rb +107 -0
  42. data/lib/google/protobuf/field_mask_pb.rb +1 -22
  43. data/lib/google/protobuf/internal/object_cache.rb +99 -0
  44. data/lib/google/protobuf/plugin_pb.rb +2 -24
  45. data/lib/google/protobuf/repeated_field.rb +4 -5
  46. data/lib/google/protobuf/source_context_pb.rb +1 -22
  47. data/lib/google/protobuf/struct_pb.rb +1 -22
  48. data/lib/google/protobuf/timestamp_pb.rb +1 -22
  49. data/lib/google/protobuf/type_pb.rb +1 -24
  50. data/lib/google/protobuf/wrappers_pb.rb +1 -22
  51. data/lib/google/protobuf.rb +1 -1
  52. data/lib/google/protobuf_ffi.rb +3 -2
  53. data/lib/google/protobuf_native.rb +0 -1
  54. data/lib/google/tasks/ffi.rake +1 -3
  55. metadata +25 -12
  56. data/ext/google/protobuf_c/third_party/utf8_range/naive.c +0 -92
  57. data/ext/google/protobuf_c/third_party/utf8_range/range2-neon.c +0 -157
  58. data/ext/google/protobuf_c/third_party/utf8_range/range2-sse.c +0 -170
  59. data/lib/google/protobuf/descriptor_dsl.rb +0 -465
  60. data/lib/google/protobuf/object_cache.rb +0 -97
@@ -17,12 +17,16 @@ module Google
17
17
  attach_function :get_message_has, :upb_Message_HasFieldByDef, [:Message, FieldDescriptor], :bool
18
18
  attach_function :set_message_field, :upb_Message_SetFieldByDef, [:Message, FieldDescriptor, MessageValue.by_value, Internal::Arena], :bool
19
19
  attach_function :encode_message, :upb_Encode, [:Message, MiniTable.by_ref, :size_t, Internal::Arena, :pointer, :pointer], EncodeStatus
20
- attach_function :json_decode_message, :upb_JsonDecode, [:binary_string, :size_t, :Message, Descriptor, :DefPool, :int, Internal::Arena, Status.by_ref], :bool
20
+ attach_function :json_decode_message_detecting_nonconformance, :upb_JsonDecodeDetectingNonconformance, [:binary_string, :size_t, :Message, Descriptor, :DefPool, :int, Internal::Arena, Status.by_ref], :int
21
21
  attach_function :json_encode_message, :upb_JsonEncode, [:Message, Descriptor, :DefPool, :int, :binary_string, :size_t, Status.by_ref], :size_t
22
22
  attach_function :decode_message, :upb_Decode, [:binary_string, :size_t, :Message, MiniTable.by_ref, :ExtensionRegistry, :int, Internal::Arena], DecodeStatus
23
23
  attach_function :get_mutable_message, :upb_Message_Mutable, [:Message, FieldDescriptor, Internal::Arena], MutableMessageValue.by_value
24
- attach_function :get_message_which_oneof, :upb_Message_WhichOneof, [:Message, OneofDescriptor], FieldDescriptor
24
+ attach_function :get_message_which_oneof, :upb_Message_WhichOneofByDef, [:Message, OneofDescriptor], FieldDescriptor
25
25
  attach_function :message_discard_unknown, :upb_Message_DiscardUnknown, [:Message, Descriptor, :int], :bool
26
+ attach_function :message_next, :upb_Message_Next, [:Message, Descriptor, :DefPool, :FieldDefPointer, MessageValue.by_ref, :pointer], :bool
27
+ attach_function :message_freeze, :upb_Message_Freeze, [:Message, MiniTable.by_ref], :void
28
+ attach_function :message_frozen?, :upb_Message_IsFrozen, [:Message], :bool
29
+
26
30
  # MessageValue
27
31
  attach_function :message_value_equal, :shared_Msgval_IsEqual, [MessageValue.by_value, MessageValue.by_value, CType, Descriptor], :bool
28
32
  attach_function :message_value_hash, :shared_Msgval_GetHash, [MessageValue.by_value, CType, Descriptor, :uint64_t], :uint64_t
@@ -57,11 +61,35 @@ module Google
57
61
  instance
58
62
  end
59
63
 
64
+ ##
65
+ # Is this object frozen?
66
+ # Returns true if either this Ruby wrapper or the underlying
67
+ # representation are frozen. Freezes the wrapper if the underlying
68
+ # representation is already frozen but this wrapper isn't.
69
+ def frozen?
70
+ unless Google::Protobuf::FFI.message_frozen? @msg
71
+ raise RuntimeError.new "Ruby frozen Message with mutable representation" if super
72
+ return false
73
+ end
74
+ method(:freeze).super_method.call unless super
75
+ true
76
+ end
77
+
78
+ ##
79
+ # Freezes the map object. We have to intercept this so we can freeze the
80
+ # underlying representation, not just the Ruby wrapper. Returns self.
60
81
  def freeze
82
+ if method(:frozen?).super_method.call
83
+ unless Google::Protobuf::FFI.message_frozen? @msg
84
+ raise RuntimeError.new "Underlying representation of message still mutable despite frozen wrapper"
85
+ end
86
+ return self
87
+ end
88
+ unless Google::Protobuf::FFI.message_frozen? @msg
89
+ Google::Protobuf::FFI.message_freeze(@msg, Google::Protobuf::FFI.get_mini_table(self.class.descriptor))
90
+ end
61
91
  super
62
- @arena.pin self
63
- self
64
- end
92
+ end
65
93
 
66
94
  def dup
67
95
  duplicate = self.class.private_constructor(@arena)
@@ -242,7 +270,11 @@ module Google
242
270
  message = new
243
271
  pool_def = message.class.descriptor.instance_variable_get(:@descriptor_pool).descriptor_pool
244
272
  status = Google::Protobuf::FFI::Status.new
245
- unless Google::Protobuf::FFI.json_decode_message(data, data.bytesize, message.instance_variable_get(:@msg), message.class.descriptor, pool_def, decoding_options, message.instance_variable_get(:@arena), status)
273
+ result = Google::Protobuf::FFI.json_decode_message_detecting_nonconformance(data, data.bytesize, message.instance_variable_get(:@msg), message.class.descriptor, pool_def, decoding_options, message.instance_variable_get(:@arena), status)
274
+ case result
275
+ when Google::Protobuf::FFI::Upb_JsonDecodeResult_OkWithEmptyStringNumerics
276
+ warn Google::Protobuf::FFI.error_message(status)
277
+ when Google::Protobuf::FFI::Upb_JsonDecodeResult_Error
246
278
  raise ParseError.new "Error occurred during parsing: #{Google::Protobuf::FFI.error_message(status)}"
247
279
  end
248
280
  message
@@ -301,53 +333,136 @@ module Google
301
333
 
302
334
  include Google::Protobuf::Internal::Convert
303
335
 
304
- def internal_deep_freeze
305
- freeze
306
- self.class.descriptor.each do |field_descriptor|
307
- next if field_descriptor.has_presence? && !Google::Protobuf::FFI.get_message_has(@msg, field_descriptor)
308
- if field_descriptor.map? or field_descriptor.repeated? or field_descriptor.sub_message?
309
- get_field(field_descriptor).send :internal_deep_freeze
310
- end
336
+ ##
337
+ # Checks ObjectCache for a sentinel empty frozen Map of the key and
338
+ # value types matching the field descriptor's MessageDef and returns
339
+ # the cache entry. If an entry is not found, one is created and added
340
+ # to the cache keyed by the MessageDef pointer first.
341
+ # @param field_descriptor [FieldDescriptor] Field to retrieve.
342
+ def empty_frozen_map(field_descriptor)
343
+ sub_message_def = Google::Protobuf::FFI.get_subtype_as_message field_descriptor
344
+ frozen_map = OBJECT_CACHE.get sub_message_def
345
+ if frozen_map.nil?
346
+ frozen_map = Google::Protobuf::Map.send(:construct_for_field, field_descriptor)
347
+ OBJECT_CACHE.try_add(sub_message_def, frozen_map.freeze)
311
348
  end
312
- self
349
+ raise "Empty Frozen Map is not frozen" unless frozen_map.frozen?
350
+ frozen_map
351
+ end
352
+
353
+ ##
354
+ # Returns a frozen Map instance for the given field. If the message
355
+ # already has a value for that field, it is used. If not, a sentinel
356
+ # (per FieldDescriptor) empty frozen Map is returned instead.
357
+ # @param field_descriptor [FieldDescriptor] Field to retrieve.
358
+ def frozen_map_from_field_descriptor(field_descriptor)
359
+ message_value = Google::Protobuf::FFI.get_message_value @msg, field_descriptor
360
+ return empty_frozen_map field_descriptor if message_value[:map_val].null?
361
+ get_map_field(message_value[:map_val], field_descriptor).freeze
362
+ end
363
+
364
+ ##
365
+ # Returns a Map instance for the given field. If the message is frozen
366
+ # the return value is also frozen. If not, a mutable instance is
367
+ # returned instead.
368
+ # @param field_descriptor [FieldDescriptor] Field to retrieve.
369
+ def map_from_field_descriptor(field_descriptor)
370
+ return frozen_map_from_field_descriptor field_descriptor if frozen?
371
+ mutable_message_value = Google::Protobuf::FFI.get_mutable_message @msg, field_descriptor, @arena
372
+ get_map_field(mutable_message_value[:map], field_descriptor)
373
+ end
374
+
375
+ ##
376
+ # Checks ObjectCache for a sentinel empty frozen RepeatedField of the
377
+ # value type matching the field descriptor's MessageDef and returns
378
+ # the cache entry. If an entry is not found, one is created and added
379
+ # to the cache keyed by the MessageDef pointer first.
380
+ # @param field_descriptor [FieldDescriptor] Field to retrieve.
381
+ def empty_frozen_repeated_field(field_descriptor)
382
+ sub_message_def = Google::Protobuf::FFI.get_subtype_as_message field_descriptor
383
+ frozen_repeated_field = OBJECT_CACHE.get sub_message_def
384
+ if frozen_repeated_field.nil?
385
+ frozen_repeated_field = Google::Protobuf::RepeatedField.send(:construct_for_field, field_descriptor)
386
+ OBJECT_CACHE.try_add(sub_message_def, frozen_repeated_field.freeze)
387
+ end
388
+ raise "Empty frozen RepeatedField is not frozen" unless frozen_repeated_field.frozen?
389
+ frozen_repeated_field
390
+ end
391
+
392
+ ##
393
+ # Returns a frozen RepeatedField instance for the given field. If the
394
+ # message already has a value for that field, it is used. If not, a
395
+ # sentinel (per FieldDescriptor) empty frozen RepeatedField is
396
+ # returned instead.
397
+ # @param field_descriptor [FieldDescriptor] Field to retrieve.
398
+ def frozen_repeated_field_from_field_descriptor(field_descriptor)
399
+ message_value = Google::Protobuf::FFI.get_message_value @msg, field_descriptor
400
+ return empty_frozen_repeated_field field_descriptor if message_value[:array_val].null?
401
+ get_repeated_field(message_value[:array_val], field_descriptor).freeze
313
402
  end
314
403
 
404
+ ##
405
+ # Returns a RepeatedField instance for the given field. If the message
406
+ # is frozen the return value is also frozen. If not, a mutable
407
+ # instance is returned instead.
408
+ # @param field_descriptor [FieldDescriptor] Field to retrieve.
409
+ def repeated_field_from_field_descriptor(field_descriptor)
410
+ return frozen_repeated_field_from_field_descriptor field_descriptor if frozen?
411
+ mutable_message_value = Google::Protobuf::FFI.get_mutable_message @msg, field_descriptor, @arena
412
+ get_repeated_field(mutable_message_value[:array], field_descriptor)
413
+ end
414
+
415
+ ##
416
+ # Returns a Message instance for the given field. If the message
417
+ # is frozen nil is always returned. Otherwise, a mutable instance is
418
+ # returned instead.
419
+ # @param field_descriptor [FieldDescriptor] Field to retrieve.
420
+ def message_from_field_descriptor(field_descriptor)
421
+ return nil if frozen?
422
+ return nil unless Google::Protobuf::FFI.get_message_has @msg, field_descriptor
423
+ mutable_message = Google::Protobuf::FFI.get_mutable_message @msg, field_descriptor, @arena
424
+ sub_message = mutable_message[:msg]
425
+ sub_message_def = Google::Protobuf::FFI.get_subtype_as_message field_descriptor
426
+ Descriptor.send(:get_message, sub_message, sub_message_def, @arena)
427
+ end
428
+
429
+ ##
430
+ # Returns a scalar value for the given field. If the message
431
+ # is frozen the return value is also frozen.
432
+ # @param field_descriptor [FieldDescriptor] Field to retrieve.
433
+ def scalar_from_field_descriptor(field_descriptor)
434
+ c_type = field_descriptor.send(:c_type)
435
+ message_value = Google::Protobuf::FFI.get_message_value @msg, field_descriptor
436
+ msg_or_enum_def = c_type == :enum ? Google::Protobuf::FFI.get_subtype_as_enum(field_descriptor) : nil
437
+ return_value = convert_upb_to_ruby message_value, c_type, msg_or_enum_def
438
+ frozen? ? return_value.freeze : return_value
439
+ end
440
+
441
+ ##
442
+ # Dynamically define accessors methods for every field of @descriptor.
443
+ # Methods with names that conflict with existing methods are skipped.
315
444
  def self.setup_accessors!
316
445
  @descriptor.each do |field_descriptor|
317
446
  field_name = field_descriptor.name
318
447
  unless instance_methods(true).include?(field_name.to_sym)
319
- #TODO - at a high level, dispatching to either
320
- # index_internal or get_field would be logically correct, but slightly slower.
448
+ # Dispatching to either index_internal or get_field is logically
449
+ # correct, but slightly slower due to having to perform extra
450
+ # lookups on each invocation rather than doing it once here.
321
451
  if field_descriptor.map?
322
452
  define_method(field_name) do
323
- mutable_message_value = Google::Protobuf::FFI.get_mutable_message @msg, field_descriptor, @arena
324
- get_map_field(mutable_message_value[:map], field_descriptor)
453
+ map_from_field_descriptor field_descriptor
325
454
  end
326
455
  elsif field_descriptor.repeated?
327
456
  define_method(field_name) do
328
- mutable_message_value = Google::Protobuf::FFI.get_mutable_message @msg, field_descriptor, @arena
329
- get_repeated_field(mutable_message_value[:array], field_descriptor)
457
+ repeated_field_from_field_descriptor field_descriptor
330
458
  end
331
459
  elsif field_descriptor.sub_message?
332
460
  define_method(field_name) do
333
- return nil unless Google::Protobuf::FFI.get_message_has @msg, field_descriptor
334
- mutable_message = Google::Protobuf::FFI.get_mutable_message @msg, field_descriptor, @arena
335
- sub_message = mutable_message[:msg]
336
- sub_message_def = Google::Protobuf::FFI.get_subtype_as_message(field_descriptor)
337
- Descriptor.send(:get_message, sub_message, sub_message_def, @arena)
461
+ message_from_field_descriptor field_descriptor
338
462
  end
339
463
  else
340
- c_type = field_descriptor.send(:c_type)
341
- if c_type == :enum
342
- define_method(field_name) do
343
- message_value = Google::Protobuf::FFI.get_message_value @msg, field_descriptor
344
- convert_upb_to_ruby message_value, c_type, Google::Protobuf::FFI.get_subtype_as_enum(field_descriptor)
345
- end
346
- else
347
- define_method(field_name) do
348
- message_value = Google::Protobuf::FFI.get_message_value @msg, field_descriptor
349
- convert_upb_to_ruby message_value, c_type
350
- end
464
+ define_method(field_name) do
465
+ scalar_from_field_descriptor field_descriptor
351
466
  end
352
467
  end
353
468
  define_method("#{field_name}=") do |value|
@@ -357,14 +472,16 @@ module Google
357
472
  clear_internal(field_descriptor)
358
473
  end
359
474
  if field_descriptor.type == :enum
360
- define_method("#{field_name}_const") do
361
- if field_descriptor.repeated?
475
+ if field_descriptor.repeated?
476
+ define_method("#{field_name}_const") do
362
477
  return_value = []
363
- get_field(field_descriptor).send(:each_msg_val) do |msg_val|
478
+ repeated_field_from_field_descriptor(field_descriptor).send(:each_msg_val) do |msg_val|
364
479
  return_value << msg_val[:int32_val]
365
480
  end
366
481
  return_value
367
- else
482
+ end
483
+ else
484
+ define_method("#{field_name}_const") do
368
485
  message_value = Google::Protobuf::FFI.get_message_value @msg, field_descriptor
369
486
  message_value[:int32_val]
370
487
  end
@@ -391,12 +508,21 @@ module Google
391
508
  end
392
509
  end
393
510
 
511
+ ##
512
+ # Dynamically define accessors methods for every OneOf field of
513
+ # @descriptor.
394
514
  def self.setup_oneof_accessors!
395
515
  @oneof_field_names = []
396
516
  @descriptor.each_oneof do |oneof_descriptor|
397
517
  self.add_oneof_accessors_for! oneof_descriptor
398
518
  end
399
519
  end
520
+
521
+ ##
522
+ # Dynamically define accessors methods for the given OneOf field.
523
+ # Methods with names that conflict with existing methods are skipped.
524
+ # @param oneof_descriptor [OneofDescriptor] Field to create accessors
525
+ # for.
400
526
  def self.add_oneof_accessors_for!(oneof_descriptor)
401
527
  field_name = oneof_descriptor.name.to_sym
402
528
  @oneof_field_names << field_name
@@ -527,6 +653,10 @@ module Google
527
653
  Google::Protobuf::FFI.clear_message_field(@msg, field_def)
528
654
  end
529
655
 
656
+ # Accessor for field by name. Does not delegate to methods setup by
657
+ # self.setup_accessors! in order to avoid conflicts with bad field
658
+ # names e.g. `dup` or `class` which are perfectly valid for proto
659
+ # fields.
530
660
  def index_internal(name)
531
661
  field_descriptor = self.class.descriptor.lookup(name)
532
662
  get_field field_descriptor unless field_descriptor.nil?
@@ -554,7 +684,7 @@ module Google
554
684
  # one if omitted or nil.
555
685
  def initialize(initial_value = nil, arena = nil, msg = nil)
556
686
  @arena = arena || Google::Protobuf::FFI.create_arena
557
- @msg = msg || Google::Protobuf::FFI.new_message_from_def(self.class.descriptor, @arena)
687
+ @msg = msg || Google::Protobuf::FFI.new_message_from_def(Google::Protobuf::FFI.get_mini_table(self.class.descriptor), @arena)
558
688
 
559
689
  unless initial_value.nil?
560
690
  raise ArgumentError.new "Expected hash arguments or message, not #{initial_value.class}" unless initial_value.respond_to? :each
@@ -575,9 +705,9 @@ module Google
575
705
 
576
706
  next if value.nil?
577
707
  if field_descriptor.map?
578
- index_assign_internal(Google::Protobuf::Map.send(:construct_for_field, field_descriptor, @arena, value: value), name: key.to_s)
708
+ index_assign_internal(Google::Protobuf::Map.send(:construct_for_field, field_descriptor, arena: @arena, value: value), name: key.to_s)
579
709
  elsif field_descriptor.repeated?
580
- index_assign_internal(RepeatedField.send(:construct_for_field, field_descriptor, @arena, values: value), name: key.to_s)
710
+ index_assign_internal(RepeatedField.send(:construct_for_field, field_descriptor, arena: @arena, values: value), name: key.to_s)
581
711
  else
582
712
  index_assign_internal(value, name: key.to_s)
583
713
  end
@@ -590,21 +720,20 @@ module Google
590
720
  end
591
721
 
592
722
  ##
593
- # Gets a field of this message identified by the argument definition.
723
+ # Gets a field of this message identified by FieldDescriptor.
594
724
  #
595
- # @param field [FieldDescriptor] Descriptor of the field to get
725
+ # @param field [FieldDescriptor] Field to retrieve.
726
+ # @param unwrap [Boolean](false) If true, unwraps wrappers.
596
727
  def get_field(field, unwrap: false)
597
728
  if field.map?
598
- mutable_message_value = Google::Protobuf::FFI.get_mutable_message @msg, field, @arena
599
- get_map_field(mutable_message_value[:map], field)
729
+ map_from_field_descriptor field
600
730
  elsif field.repeated?
601
- mutable_message_value = Google::Protobuf::FFI.get_mutable_message @msg, field, @arena
602
- get_repeated_field(mutable_message_value[:array], field)
731
+ repeated_field_from_field_descriptor field
603
732
  elsif field.sub_message?
604
733
  return nil unless Google::Protobuf::FFI.get_message_has @msg, field
605
- sub_message_def = Google::Protobuf::FFI.get_subtype_as_message(field)
606
734
  if unwrap
607
735
  if field.has?(self)
736
+ sub_message_def = Google::Protobuf::FFI.get_subtype_as_message field
608
737
  wrapper_message_value = Google::Protobuf::FFI.get_message_value @msg, field
609
738
  fields = Google::Protobuf::FFI.field_count(sub_message_def)
610
739
  raise "Sub message has #{fields} fields! Expected exactly 1." unless fields == 1
@@ -615,43 +744,37 @@ module Google
615
744
  nil
616
745
  end
617
746
  else
618
- mutable_message = Google::Protobuf::FFI.get_mutable_message @msg, field, @arena
619
- sub_message = mutable_message[:msg]
620
- Descriptor.send(:get_message, sub_message, sub_message_def, @arena)
747
+ message_from_field_descriptor field
621
748
  end
622
749
  else
623
- c_type = field.send(:c_type)
624
- message_value = Google::Protobuf::FFI.get_message_value @msg, field
625
- if c_type == :enum
626
- convert_upb_to_ruby message_value, c_type, Google::Protobuf::FFI.get_subtype_as_enum(field)
627
- else
628
- convert_upb_to_ruby message_value, c_type
629
- end
750
+ scalar_from_field_descriptor field
630
751
  end
631
752
  end
632
753
 
633
754
  ##
634
- # @param array [::FFI::Pointer] Pointer to the Array
755
+ # Gets a RepeatedField from the ObjectCache or creates a new one.
756
+ # @param array [::FFI::Pointer] Pointer to the upb_Array
635
757
  # @param field [Google::Protobuf::FieldDescriptor] Type of the repeated field
636
758
  def get_repeated_field(array, field)
637
759
  return nil if array.nil? or array.null?
638
760
  repeated_field = OBJECT_CACHE.get(array.address)
639
761
  if repeated_field.nil?
640
- repeated_field = RepeatedField.send(:construct_for_field, field, @arena, array: array)
641
- repeated_field.send :internal_deep_freeze if frozen?
762
+ repeated_field = RepeatedField.send(:construct_for_field, field, arena: @arena, array: array)
763
+ repeated_field.freeze if frozen?
642
764
  end
643
765
  repeated_field
644
766
  end
645
767
 
646
768
  ##
647
- # @param map [::FFI::Pointer] Pointer to the Map
769
+ # Gets a Map from the ObjectCache or creates a new one.
770
+ # @param map [::FFI::Pointer] Pointer to the upb_Map
648
771
  # @param field [Google::Protobuf::FieldDescriptor] Type of the map field
649
772
  def get_map_field(map, field)
650
773
  return nil if map.nil? or map.null?
651
774
  map_field = OBJECT_CACHE.get(map.address)
652
775
  if map_field.nil?
653
- map_field = Google::Protobuf::Map.send(:construct_for_field, field, @arena, map: map)
654
- map_field.send :internal_deep_freeze if frozen?
776
+ map_field = Google::Protobuf::Map.send(:construct_for_field, field, arena: @arena, map: map)
777
+ map_field.freeze if frozen?
655
778
  end
656
779
  map_field
657
780
  end
@@ -0,0 +1,114 @@
1
+ # Protocol Buffers - Google's data interchange format
2
+ # Copyright 2024 Google Inc. All rights reserved.
3
+ #
4
+ # Use of this source code is governed by a BSD-style
5
+ # license that can be found in the LICENSE file or at
6
+ # https://developers.google.com/open-source/licenses/bsd
7
+
8
+ module Google
9
+ module Protobuf
10
+ class MethodDescriptor
11
+ attr :method_def, :descriptor_pool
12
+
13
+ include Google::Protobuf::Internal::Convert
14
+
15
+ # FFI Interface methods and setup
16
+ extend ::FFI::DataConverter
17
+ native_type ::FFI::Type::POINTER
18
+
19
+ class << self
20
+ prepend Google::Protobuf::Internal::TypeSafety
21
+ include Google::Protobuf::Internal::PointerHelper
22
+
23
+ # @param value [MethodDescriptor] MethodDescriptor to convert to an FFI native type
24
+ # @param _ [Object] Unused
25
+ def to_native(value, _)
26
+ method_def_ptr = value.nil? ? nil : value.instance_variable_get(:@method_def)
27
+ return ::FFI::Pointer::NULL if method_def_ptr.nil?
28
+ raise "Underlying method_def was null!" if method_def_ptr.null?
29
+ method_def_ptr
30
+ end
31
+
32
+ ##
33
+ # @param service_def [::FFI::Pointer] MethodDef pointer to be wrapped
34
+ # @param _ [Object] Unused
35
+ def from_native(method_def, _ = nil)
36
+ return nil if method_def.nil? or method_def.null?
37
+ service_def = Google::Protobuf::FFI.raw_service_def_by_raw_method_def(method_def)
38
+ file_def = Google::Protobuf::FFI.file_def_by_raw_service_def(service_def)
39
+ descriptor_from_file_def(file_def, method_def)
40
+ end
41
+ end
42
+
43
+ def self.new(*arguments, &block)
44
+ raise "Descriptor objects may not be created from Ruby."
45
+ end
46
+
47
+ def to_s
48
+ inspect
49
+ end
50
+
51
+ def inspect
52
+ "#{self.class.name}: #{name}"
53
+ end
54
+
55
+ def name
56
+ @name ||= Google::Protobuf::FFI.get_method_name(self)
57
+ end
58
+
59
+ def options
60
+ @options ||= begin
61
+ size_ptr = ::FFI::MemoryPointer.new(:size_t, 1)
62
+ temporary_arena = Google::Protobuf::FFI.create_arena
63
+ buffer = Google::Protobuf::FFI.method_options(self, size_ptr, temporary_arena)
64
+ Google::Protobuf::MethodOptions.decode(buffer.read_string_length(size_ptr.read(:size_t)).force_encoding("ASCII-8BIT").freeze).freeze
65
+ end
66
+ end
67
+
68
+ def input_type
69
+ @input_type ||= Google::Protobuf::FFI.method_input_type(self)
70
+ end
71
+
72
+ def output_type
73
+ @output_type ||= Google::Protobuf::FFI.method_output_type(self)
74
+ end
75
+
76
+ def client_streaming
77
+ @client_streaming ||= Google::Protobuf::FFI.method_client_streaming(self)
78
+ end
79
+
80
+ def server_streaming
81
+ @server_streaming ||= Google::Protobuf::FFI.method_server_streaming(self)
82
+ end
83
+
84
+ private
85
+
86
+ def initialize(method_def, descriptor_pool)
87
+ @method_def = method_def
88
+ @descriptor_pool = descriptor_pool
89
+ end
90
+
91
+ def self.private_constructor(method_def, descriptor_pool)
92
+ instance = allocate
93
+ instance.send(:initialize, method_def, descriptor_pool)
94
+ instance
95
+ end
96
+
97
+ def c_type
98
+ @c_type ||= Google::Protobuf::FFI.get_c_type(self)
99
+ end
100
+ end
101
+
102
+ class FFI
103
+ # MethodDef
104
+ attach_function :raw_service_def_by_raw_method_def, :upb_MethodDef_Service, [:pointer], :pointer
105
+ attach_function :get_method_name, :upb_MethodDef_Name, [MethodDescriptor], :string
106
+ attach_function :method_options, :MethodDescriptor_serialized_options, [MethodDescriptor, :pointer, Internal::Arena], :pointer
107
+ attach_function :method_input_type, :upb_MethodDef_InputType, [MethodDescriptor], Descriptor
108
+ attach_function :method_output_type, :upb_MethodDef_OutputType, [MethodDescriptor], Descriptor
109
+ attach_function :method_client_streaming, :upb_MethodDef_ClientStreaming, [MethodDescriptor], :bool
110
+ attach_function :method_server_streaming, :upb_MethodDef_ServerStreaming, [MethodDescriptor], :bool
111
+ end
112
+ end
113
+ end
114
+
@@ -18,13 +18,13 @@ module Google
18
18
 
19
19
  def self.cache_implementation
20
20
  if interpreter_supports_non_finalized_keys_in_weak_map? and SIZEOF_LONG >= SIZEOF_VALUE
21
- Google::Protobuf::ObjectCache
21
+ Google::Protobuf::Internal::ObjectCache
22
22
  else
23
- Google::Protobuf::LegacyObjectCache
23
+ Google::Protobuf::Internal::LegacyObjectCache
24
24
  end
25
25
  end
26
26
 
27
27
  public
28
28
  OBJECT_CACHE = cache_implementation.new
29
29
  end
30
- end
30
+ end
@@ -58,7 +58,9 @@ module Google
58
58
  size_ptr = ::FFI::MemoryPointer.new(:size_t, 1)
59
59
  temporary_arena = Google::Protobuf::FFI.create_arena
60
60
  buffer = Google::Protobuf::FFI.oneof_options(self, size_ptr, temporary_arena)
61
- Google::Protobuf::OneofOptions.decode(buffer.read_string_length(size_ptr.read(:size_t)).force_encoding("ASCII-8BIT").freeze).send(:internal_deep_freeze)
61
+ opts = Google::Protobuf::OneofOptions.decode(buffer.read_string_length(size_ptr.read(:size_t)).force_encoding("ASCII-8BIT").freeze)
62
+ opts.clear_features()
63
+ opts.freeze
62
64
  end
63
65
  end
64
66
 
@@ -24,12 +24,14 @@ module Google
24
24
  module Protobuf
25
25
  class FFI
26
26
  # Array
27
- attach_function :append_array, :upb_Array_Append, [:Array, MessageValue.by_value, Internal::Arena], :bool
28
- attach_function :get_msgval_at,:upb_Array_Get, [:Array, :size_t], MessageValue.by_value
29
- attach_function :create_array, :upb_Array_New, [Internal::Arena, CType], :Array
30
- attach_function :array_resize, :upb_Array_Resize, [:Array, :size_t, Internal::Arena], :bool
31
- attach_function :array_set, :upb_Array_Set, [:Array, :size_t, MessageValue.by_value], :void
32
- attach_function :array_size, :upb_Array_Size, [:Array], :size_t
27
+ attach_function :append_array, :upb_Array_Append, [:Array, MessageValue.by_value, Internal::Arena], :bool
28
+ attach_function :get_msgval_at, :upb_Array_Get, [:Array, :size_t], MessageValue.by_value
29
+ attach_function :create_array, :upb_Array_New, [Internal::Arena, CType], :Array
30
+ attach_function :array_resize, :upb_Array_Resize, [:Array, :size_t, Internal::Arena], :bool
31
+ attach_function :array_set, :upb_Array_Set, [:Array, :size_t, MessageValue.by_value], :void
32
+ attach_function :array_size, :upb_Array_Size, [:Array], :size_t
33
+ attach_function :array_freeze, :upb_Array_Freeze, [:Array, MiniTable.by_ref], :void
34
+ attach_function :array_frozen?, :upb_Array_IsFrozen, [:Array], :bool
33
35
  end
34
36
 
35
37
  class RepeatedField
@@ -174,6 +176,38 @@ module Google
174
176
  end
175
177
  alias size :length
176
178
 
179
+ ##
180
+ # Is this object frozen?
181
+ # Returns true if either this Ruby wrapper or the underlying
182
+ # representation are frozen. Freezes the wrapper if the underlying
183
+ # representation is already frozen but this wrapper isn't.
184
+ def frozen?
185
+ unless Google::Protobuf::FFI.array_frozen? array
186
+ raise RuntimeError.new "Ruby frozen RepeatedField with mutable representation" if super
187
+ return false
188
+ end
189
+ method(:freeze).super_method.call unless super
190
+ true
191
+ end
192
+
193
+ ##
194
+ # Freezes the RepeatedField object. We have to intercept this so we can
195
+ # freeze the underlying representation, not just the Ruby wrapper. Returns
196
+ # self.
197
+ def freeze
198
+ if method(:frozen?).super_method.call
199
+ unless Google::Protobuf::FFI.array_frozen? array
200
+ raise RuntimeError.new "Underlying representation of repeated field still mutable despite frozen wrapper"
201
+ end
202
+ return self
203
+ end
204
+ unless Google::Protobuf::FFI.array_frozen? array
205
+ mini_table = (type == :message) ? Google::Protobuf::FFI.get_mini_table(@descriptor) : nil
206
+ Google::Protobuf::FFI.array_freeze(array, mini_table)
207
+ end
208
+ super
209
+ end
210
+
177
211
  def dup
178
212
  instance = self.class.allocate
179
213
  instance.send(:initialize, type, descriptor: descriptor, arena: arena)
@@ -252,16 +286,6 @@ module Google
252
286
 
253
287
  attr :name, :arena, :array, :type, :descriptor
254
288
 
255
- def internal_deep_freeze
256
- freeze
257
- if type == :message
258
- each do |element|
259
- element.send :internal_deep_freeze
260
- end
261
- end
262
- self
263
- end
264
-
265
289
  def internal_push(*elements)
266
290
  elements.each do |element|
267
291
  append_msg_val convert_ruby_to_upb(element, arena, type, descriptor)
@@ -346,10 +370,14 @@ module Google
346
370
  OBJECT_CACHE.try_add(@array.address, self)
347
371
  end
348
372
 
349
- # @param field [FieldDescriptor] Descriptor of the field where the RepeatedField will be assigned
350
- # @param values [Enumerable] Initial values; may be nil or empty
373
+ ##
374
+ # Constructor that uses the type information from the given
375
+ # FieldDescriptor to configure the new RepeatedField instance.
376
+ # @param field [FieldDescriptor] Type information for the new RepeatedField
351
377
  # @param arena [Arena] Owning message's arena
352
- def self.construct_for_field(field, arena, values: nil, array: nil)
378
+ # @param values [Enumerable] Initial values
379
+ # @param array [::FFI::Pointer] Existing upb_Array
380
+ def self.construct_for_field(field, arena: nil, values: nil, array: nil)
353
381
  instance = allocate
354
382
  options = {initial_values: values, name: field.name, arena: arena, array: array}
355
383
  if [:enum, :message].include? field.type