google-protobuf 4.27.5-arm64-darwin → 4.28.0-arm64-darwin

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.

Potentially problematic release.


This version of google-protobuf might be problematic. Click here for more details.

checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 7acd4ee3b08b60fdd120c64dfe843e6fbef8f1298e8f0680ac681e623de6ede6
4
- data.tar.gz: b824030c2175944372d990853118aea46bb125b3a39c0d8e9b496c89b7b398e9
3
+ metadata.gz: 1063401cc02d9b397e7fb45ac63ad76ad9d68fcee1c0faf150ed5b38df713f61
4
+ data.tar.gz: f77beee0f039a5a96fbc4dbdb68e535b4cae664e6885c5d2ae5ba7ac4b86e161
5
5
  SHA512:
6
- metadata.gz: faac98ccff59dc25e7c26565d2857c9e11d7a8185d66d0e2b825eb87aff68533273945cecb733ed7085405899808e229c8a21f2788c7b8a4898d3378f7c8dca2
7
- data.tar.gz: d58cf29d29fadae552952a1b7a810cfa79395afee7befdd17e97b760c46be2533a7b98c8ce48cde8a763068d489d2d4561172e4227b654648d67b818ba183c76
6
+ metadata.gz: 49add2971fc1a9060219f884dc3bb48ac02766cf843680c42fe6cc71141665363f9ebce62a3e171370235bece4afaaf2afef01f34a4f1052b5b94a0348deb472
7
+ data.tar.gz: fef8d4892bd5ccafd203d98d3588c8a10b315d3f026668821a4a35ee539fcfd3f8076d14c0f1275967a94b7a6aec7bb3b1e6c8965d5ddcf1a0bad54e24dc8924
@@ -104,6 +104,41 @@ unknownval:
104
104
  rb_raise(rb_eRangeError, "Unknown symbol value for enum field '%s'.", name);
105
105
  }
106
106
 
107
+ VALUE Convert_CheckStringUtf8(VALUE str) {
108
+ VALUE utf8 = rb_enc_from_encoding(rb_utf8_encoding());
109
+
110
+ if (rb_obj_encoding(str) == utf8) {
111
+ // Note: Just because a string is marked as having UTF-8 encoding does
112
+ // not mean that it is *valid* UTF-8. We have to check separately
113
+ // whether it is valid.
114
+ if (rb_enc_str_coderange(str) == ENC_CODERANGE_BROKEN) {
115
+ // TODO: For now
116
+ // we only warn for this case. We will remove the warning and throw an
117
+ // exception below in the 30.x release
118
+
119
+ rb_warn(
120
+ "String is invalid UTF-8. This will be an error in a future "
121
+ "version.");
122
+ // VALUE exc = rb_const_get_at(
123
+ // rb_cEncoding, rb_intern("InvalidByteSequenceError"));
124
+ // rb_raise(exc, "String is invalid UTF-8");
125
+ }
126
+ } else {
127
+ // Note: this will not duplicate underlying string data unless
128
+ // necessary.
129
+ //
130
+ // This will throw an exception if the conversion cannot be performed:
131
+ // - Encoding::UndefinedConversionError if certain characters cannot be
132
+ // converted to UTF-8.
133
+ // - Encoding::InvalidByteSequenceError if certain characters were invalid
134
+ // in the source encoding.
135
+ str = rb_str_encode(str, utf8, 0, Qnil);
136
+ PBRUBY_ASSERT(rb_enc_str_coderange(str) != ENC_CODERANGE_BROKEN);
137
+ }
138
+
139
+ return str;
140
+ }
141
+
107
142
  upb_MessageValue Convert_RubyToUpb(VALUE value, const char* name,
108
143
  TypeInfo type_info, upb_Arena* arena) {
109
144
  upb_MessageValue ret;
@@ -137,8 +172,7 @@ upb_MessageValue Convert_RubyToUpb(VALUE value, const char* name,
137
172
  }
138
173
  break;
139
174
  }
140
- case kUpb_CType_String: {
141
- VALUE utf8 = rb_enc_from_encoding(rb_utf8_encoding());
175
+ case kUpb_CType_String:
142
176
  if (rb_obj_class(value) == rb_cSymbol) {
143
177
  value = rb_funcall(value, rb_intern("to_s"), 0);
144
178
  } else if (!rb_obj_is_kind_of(value, rb_cString)) {
@@ -147,19 +181,9 @@ upb_MessageValue Convert_RubyToUpb(VALUE value, const char* name,
147
181
  rb_class2name(CLASS_OF(value)));
148
182
  }
149
183
 
150
- if (rb_obj_encoding(value) != utf8) {
151
- // Note: this will not duplicate underlying string data unless
152
- // necessary.
153
- value = rb_str_encode(value, utf8, 0, Qnil);
154
-
155
- if (rb_enc_str_coderange(value) == ENC_CODERANGE_BROKEN) {
156
- rb_raise(rb_eEncodingError, "String is invalid UTF-8");
157
- }
158
- }
159
-
184
+ value = Convert_CheckStringUtf8(value);
160
185
  ret.str_val = Convert_StringData(value, arena);
161
186
  break;
162
- }
163
187
  case kUpb_CType_Bytes: {
164
188
  VALUE bytes = rb_enc_from_encoding(rb_ascii8bit_encoding());
165
189
  if (rb_obj_class(value) != rb_cString) {
@@ -63,9 +63,10 @@ static VALUE Map_alloc(VALUE klass) {
63
63
  return TypedData_Wrap_Struct(klass, &Map_type, self);
64
64
  }
65
65
 
66
- VALUE Map_GetRubyWrapper(upb_Map* map, upb_CType key_type, TypeInfo value_type,
67
- VALUE arena) {
66
+ VALUE Map_GetRubyWrapper(const upb_Map* map, upb_CType key_type,
67
+ TypeInfo value_type, VALUE arena) {
68
68
  PBRUBY_ASSERT(map);
69
+ PBRUBY_ASSERT(arena != Qnil);
69
70
 
70
71
  VALUE val = ObjectCache_Get(map);
71
72
 
@@ -83,7 +84,6 @@ VALUE Map_GetRubyWrapper(upb_Map* map, upb_CType key_type, TypeInfo value_type,
83
84
  }
84
85
  return ObjectCache_TryAdd(map, val);
85
86
  }
86
-
87
87
  return val;
88
88
  }
89
89
 
@@ -105,8 +105,9 @@ static TypeInfo Map_keyinfo(Map* self) {
105
105
  }
106
106
 
107
107
  static upb_Map* Map_GetMutable(VALUE _self) {
108
- rb_check_frozen(_self);
109
- return (upb_Map*)ruby_to_Map(_self)->map;
108
+ const upb_Map* map = ruby_to_Map(_self)->map;
109
+ Protobuf_CheckNotFrozen(_self, upb_Map_IsFrozen(map));
110
+ return (upb_Map*)map;
110
111
  }
111
112
 
112
113
  VALUE Map_CreateHash(const upb_Map* map, upb_CType key_type,
@@ -439,14 +440,14 @@ static VALUE Map_has_key(VALUE _self, VALUE key) {
439
440
  * nil if none was present. Throws an exception if the key is of the wrong type.
440
441
  */
441
442
  static VALUE Map_delete(VALUE _self, VALUE key) {
443
+ upb_Map* map = Map_GetMutable(_self);
442
444
  Map* self = ruby_to_Map(_self);
443
- rb_check_frozen(_self);
444
445
 
445
446
  upb_MessageValue key_upb =
446
447
  Convert_RubyToUpb(key, "", Map_keyinfo(self), NULL);
447
448
  upb_MessageValue val_upb;
448
449
 
449
- if (upb_Map_Delete(Map_GetMutable(_self), key_upb, &val_upb)) {
450
+ if (upb_Map_Delete(map, key_upb, &val_upb)) {
450
451
  return Convert_UpbToRuby(val_upb, self->value_type_info, self->arena);
451
452
  } else {
452
453
  return Qnil;
@@ -560,29 +561,79 @@ VALUE Map_eq(VALUE _self, VALUE _other) {
560
561
 
561
562
  /*
562
563
  * call-seq:
563
- * Message.freeze => self
564
+ * Map.frozen? => bool
565
+ *
566
+ * Returns true if the map is frozen in either Ruby or the underlying
567
+ * representation. Freezes the Ruby map object if it is not already frozen in
568
+ * Ruby but it is frozen in the underlying representation.
569
+ */
570
+ VALUE Map_frozen(VALUE _self) {
571
+ Map* self = ruby_to_Map(_self);
572
+ if (!upb_Map_IsFrozen(self->map)) {
573
+ PBRUBY_ASSERT(!RB_OBJ_FROZEN(_self));
574
+ return Qfalse;
575
+ }
576
+
577
+ // Lazily freeze the Ruby wrapper.
578
+ if (!RB_OBJ_FROZEN(_self)) RB_OBJ_FREEZE(_self);
579
+ return Qtrue;
580
+ }
581
+
582
+ /*
583
+ * call-seq:
584
+ * Map.freeze => self
564
585
  *
565
- * Freezes the message object. We have to intercept this so we can pin the
566
- * Ruby object into memory so we don't forget it's frozen.
586
+ * Freezes the map object. We have to intercept this so we can freeze the
587
+ * underlying representation, not just the Ruby wrapper.
567
588
  */
568
589
  VALUE Map_freeze(VALUE _self) {
569
590
  Map* self = ruby_to_Map(_self);
591
+ if (RB_OBJ_FROZEN(_self)) {
592
+ PBRUBY_ASSERT(upb_Map_IsFrozen(self->map));
593
+ return _self;
594
+ }
595
+
596
+ if (!upb_Map_IsFrozen(self->map)) {
597
+ if (self->value_type_info.type == kUpb_CType_Message) {
598
+ upb_Map_Freeze(
599
+ Map_GetMutable(_self),
600
+ upb_MessageDef_MiniTable(self->value_type_info.def.msgdef));
601
+ } else {
602
+ upb_Map_Freeze(Map_GetMutable(_self), NULL);
603
+ }
604
+ }
570
605
 
571
- if (RB_OBJ_FROZEN(_self)) return _self;
572
- Arena_Pin(self->arena, _self);
573
606
  RB_OBJ_FREEZE(_self);
574
607
 
575
- if (self->value_type_info.type == kUpb_CType_Message) {
576
- size_t iter = kUpb_Map_Begin;
577
- upb_MessageValue key, val;
608
+ return _self;
609
+ }
610
+
611
+ VALUE Map_EmptyFrozen(const upb_FieldDef* f) {
612
+ PBRUBY_ASSERT(upb_FieldDef_IsMap(f));
613
+ VALUE val = ObjectCache_Get(f);
578
614
 
579
- while (upb_Map_Next(self->map, &key, &val, &iter)) {
580
- VALUE val_val =
581
- Convert_UpbToRuby(val, self->value_type_info, self->arena);
582
- Message_freeze(val_val);
615
+ if (val == Qnil) {
616
+ const upb_FieldDef* key_f = map_field_key(f);
617
+ const upb_FieldDef* val_f = map_field_value(f);
618
+ upb_CType key_type = upb_FieldDef_CType(key_f);
619
+ TypeInfo value_type_info = TypeInfo_get(val_f);
620
+ val = Map_alloc(cMap);
621
+ Map* self;
622
+ TypedData_Get_Struct(val, Map, &Map_type, self);
623
+ self->arena = Arena_new();
624
+ self->map =
625
+ upb_Map_New(Arena_get(self->arena), key_type, value_type_info.type);
626
+ self->key_type = key_type;
627
+ self->value_type_info = value_type_info;
628
+ if (self->value_type_info.type == kUpb_CType_Message) {
629
+ const upb_MessageDef* val_m = value_type_info.def.msgdef;
630
+ self->value_type_class = Descriptor_DefToClass(val_m);
583
631
  }
632
+ return ObjectCache_TryAdd(f, Map_freeze(val));
584
633
  }
585
- return _self;
634
+ PBRUBY_ASSERT(RB_OBJ_FROZEN(val));
635
+ PBRUBY_ASSERT(upb_Map_IsFrozen(ruby_to_Map(val)->map));
636
+ return val;
586
637
  }
587
638
 
588
639
  /*
@@ -671,6 +722,7 @@ void Map_register(VALUE module) {
671
722
  rb_define_method(klass, "clone", Map_dup, 0);
672
723
  rb_define_method(klass, "==", Map_eq, 1);
673
724
  rb_define_method(klass, "freeze", Map_freeze, 0);
725
+ rb_define_method(klass, "frozen?", Map_frozen, 0);
674
726
  rb_define_method(klass, "hash", Map_hash, 0);
675
727
  rb_define_method(klass, "to_h", Map_to_h, 0);
676
728
  rb_define_method(klass, "inspect", Map_inspect, 0);
@@ -11,10 +11,14 @@
11
11
  #include "protobuf.h"
12
12
  #include "ruby-upb.h"
13
13
 
14
+ // Returns a frozen sentinel Ruby wrapper object for an empty upb_Map with the
15
+ // key and value types specified by the field. Creates one if it doesn't exist.
16
+ VALUE Map_EmptyFrozen(const upb_FieldDef* f);
17
+
14
18
  // Returns a Ruby wrapper object for the given map, which will be created if
15
19
  // one does not exist already.
16
- VALUE Map_GetRubyWrapper(upb_Map *map, upb_CType key_type, TypeInfo value_type,
17
- VALUE arena);
20
+ VALUE Map_GetRubyWrapper(const upb_Map *map, upb_CType key_type,
21
+ TypeInfo value_type, VALUE arena);
18
22
 
19
23
  // Gets the underlying upb_Map for this Ruby map object, which must have
20
24
  // key/value type that match |field|. If this is not a map or the type doesn't
@@ -80,11 +80,13 @@ const upb_Message* Message_Get(VALUE msg_rb, const upb_MessageDef** m) {
80
80
  }
81
81
 
82
82
  upb_Message* Message_GetMutable(VALUE msg_rb, const upb_MessageDef** m) {
83
- rb_check_frozen(msg_rb);
84
- return (upb_Message*)Message_Get(msg_rb, m);
83
+ const upb_Message* upb_msg = Message_Get(msg_rb, m);
84
+ Protobuf_CheckNotFrozen(msg_rb, upb_Message_IsFrozen(upb_msg));
85
+ return (upb_Message*)upb_msg;
85
86
  }
86
87
 
87
- void Message_InitPtr(VALUE self_, upb_Message* msg, VALUE arena) {
88
+ void Message_InitPtr(VALUE self_, const upb_Message* msg, VALUE arena) {
89
+ PBRUBY_ASSERT(arena != Qnil);
88
90
  Message* self = ruby_to_Message(self_);
89
91
  self->msg = msg;
90
92
  RB_OBJ_WRITE(self_, &self->arena, arena);
@@ -105,7 +107,7 @@ void Message_CheckClass(VALUE klass) {
105
107
  }
106
108
  }
107
109
 
108
- VALUE Message_GetRubyWrapper(upb_Message* msg, const upb_MessageDef* m,
110
+ VALUE Message_GetRubyWrapper(const upb_Message* msg, const upb_MessageDef* m,
109
111
  VALUE arena) {
110
112
  if (msg == NULL) return Qnil;
111
113
 
@@ -116,7 +118,6 @@ VALUE Message_GetRubyWrapper(upb_Message* msg, const upb_MessageDef* m,
116
118
  val = Message_alloc(klass);
117
119
  Message_InitPtr(val, msg, arena);
118
120
  }
119
-
120
121
  return val;
121
122
  }
122
123
 
@@ -248,7 +249,7 @@ static int extract_method_call(VALUE method_name, Message* self,
248
249
  static VALUE Message_oneof_accessor(VALUE _self, const upb_OneofDef* o,
249
250
  int accessor_type) {
250
251
  Message* self = ruby_to_Message(_self);
251
- const upb_FieldDef* oneof_field = upb_Message_WhichOneof(self->msg, o);
252
+ const upb_FieldDef* oneof_field = upb_Message_WhichOneofByDef(self->msg, o);
252
253
 
253
254
  switch (accessor_type) {
254
255
  case METHOD_PRESENCE:
@@ -288,13 +289,42 @@ static void Message_setfield(upb_Message* msg, const upb_FieldDef* f, VALUE val,
288
289
  upb_Message_SetFieldByDef(msg, f, msgval, arena);
289
290
  }
290
291
 
292
+ VALUE Message_getfield_frozen(const upb_Message* msg, const upb_FieldDef* f,
293
+ VALUE arena) {
294
+ upb_MessageValue msgval = upb_Message_GetFieldByDef(msg, f);
295
+ if (upb_FieldDef_IsMap(f)) {
296
+ if (msgval.map_val == NULL) {
297
+ return Map_EmptyFrozen(f);
298
+ }
299
+ const upb_FieldDef* key_f = map_field_key(f);
300
+ const upb_FieldDef* val_f = map_field_value(f);
301
+ upb_CType key_type = upb_FieldDef_CType(key_f);
302
+ TypeInfo value_type_info = TypeInfo_get(val_f);
303
+ return Map_GetRubyWrapper(msgval.map_val, key_type, value_type_info, arena);
304
+ }
305
+ if (upb_FieldDef_IsRepeated(f)) {
306
+ if (msgval.array_val == NULL) {
307
+ return RepeatedField_EmptyFrozen(f);
308
+ }
309
+ return RepeatedField_GetRubyWrapper(msgval.array_val, TypeInfo_get(f),
310
+ arena);
311
+ }
312
+ VALUE ret;
313
+ if (upb_FieldDef_IsSubMessage(f)) {
314
+ const upb_MessageDef* m = upb_FieldDef_MessageSubDef(f);
315
+ ret = Message_GetRubyWrapper(msgval.msg_val, m, arena);
316
+ } else {
317
+ ret = Convert_UpbToRuby(msgval, TypeInfo_get(f), Qnil);
318
+ }
319
+ return ret;
320
+ }
321
+
291
322
  VALUE Message_getfield(VALUE _self, const upb_FieldDef* f) {
292
323
  Message* self = ruby_to_Message(_self);
293
- // This is a special-case: upb_Message_Mutable() for map & array are logically
294
- // const (they will not change what is serialized) but physically
295
- // non-const, as they do allocate a repeated field or map. The logical
296
- // constness means it's ok to do even if the message is frozen.
297
- upb_Message* msg = (upb_Message*)self->msg;
324
+ if (upb_Message_IsFrozen(self->msg)) {
325
+ return Message_getfield_frozen(self->msg, f, self->arena);
326
+ }
327
+ upb_Message* msg = Message_GetMutable(_self, NULL);
298
328
  upb_Arena* arena = Arena_get(self->arena);
299
329
  if (upb_FieldDef_IsMap(f)) {
300
330
  upb_Map* map = upb_Message_Mutable(msg, f, arena).map;
@@ -307,12 +337,12 @@ VALUE Message_getfield(VALUE _self, const upb_FieldDef* f) {
307
337
  upb_Array* arr = upb_Message_Mutable(msg, f, arena).array;
308
338
  return RepeatedField_GetRubyWrapper(arr, TypeInfo_get(f), self->arena);
309
339
  } else if (upb_FieldDef_IsSubMessage(f)) {
310
- if (!upb_Message_HasFieldByDef(self->msg, f)) return Qnil;
340
+ if (!upb_Message_HasFieldByDef(msg, f)) return Qnil;
311
341
  upb_Message* submsg = upb_Message_Mutable(msg, f, arena).msg;
312
342
  const upb_MessageDef* m = upb_FieldDef_MessageSubDef(f);
313
343
  return Message_GetRubyWrapper(submsg, m, self->arena);
314
344
  } else {
315
- upb_MessageValue msgval = upb_Message_GetFieldByDef(self->msg, f);
345
+ upb_MessageValue msgval = upb_Message_GetFieldByDef(msg, f);
316
346
  return Convert_UpbToRuby(msgval, TypeInfo_get(f), self->arena);
317
347
  }
318
348
  }
@@ -436,7 +466,6 @@ static VALUE Message_method_missing(int argc, VALUE* argv, VALUE _self) {
436
466
  if (argc != 2) {
437
467
  rb_raise(rb_eArgError, "Expected 2 arguments, received %d", argc);
438
468
  }
439
- rb_check_frozen(_self);
440
469
  break;
441
470
  default:
442
471
  if (argc != 1) {
@@ -812,33 +841,42 @@ static VALUE Message_to_h(VALUE _self) {
812
841
 
813
842
  /*
814
843
  * call-seq:
815
- * Message.freeze => self
844
+ * Message.frozen? => bool
816
845
  *
817
- * Freezes the message object. We have to intercept this so we can pin the
818
- * Ruby object into memory so we don't forget it's frozen.
846
+ * Returns true if the message is frozen in either Ruby or the underlying
847
+ * representation. Freezes the Ruby message object if it is not already frozen
848
+ * in Ruby but it is frozen in the underlying representation.
819
849
  */
820
- VALUE Message_freeze(VALUE _self) {
850
+ VALUE Message_frozen(VALUE _self) {
821
851
  Message* self = ruby_to_Message(_self);
852
+ if (!upb_Message_IsFrozen(self->msg)) {
853
+ PBRUBY_ASSERT(!RB_OBJ_FROZEN(_self));
854
+ return Qfalse;
855
+ }
822
856
 
823
- if (RB_OBJ_FROZEN(_self)) return _self;
824
- Arena_Pin(self->arena, _self);
825
- RB_OBJ_FREEZE(_self);
857
+ // Lazily freeze the Ruby wrapper.
858
+ if (!RB_OBJ_FROZEN(_self)) RB_OBJ_FREEZE(_self);
859
+ return Qtrue;
860
+ }
826
861
 
827
- int n = upb_MessageDef_FieldCount(self->msgdef);
828
- for (int i = 0; i < n; i++) {
829
- const upb_FieldDef* f = upb_MessageDef_Field(self->msgdef, i);
830
- VALUE field = Message_getfield(_self, f);
831
-
832
- if (field != Qnil) {
833
- if (upb_FieldDef_IsMap(f)) {
834
- Map_freeze(field);
835
- } else if (upb_FieldDef_IsRepeated(f)) {
836
- RepeatedField_freeze(field);
837
- } else if (upb_FieldDef_IsSubMessage(f)) {
838
- Message_freeze(field);
839
- }
840
- }
862
+ /*
863
+ * call-seq:
864
+ * Message.freeze => self
865
+ *
866
+ * Freezes the message object. We have to intercept this so we can freeze the
867
+ * underlying representation, not just the Ruby wrapper.
868
+ */
869
+ VALUE Message_freeze(VALUE _self) {
870
+ Message* self = ruby_to_Message(_self);
871
+ if (RB_OBJ_FROZEN(_self)) {
872
+ PBRUBY_ASSERT(upb_Message_IsFrozen(self->msg));
873
+ return _self;
841
874
  }
875
+ if (!upb_Message_IsFrozen(self->msg)) {
876
+ upb_Message_Freeze(Message_GetMutable(_self, NULL),
877
+ upb_MessageDef_MiniTable(self->msgdef));
878
+ }
879
+ RB_OBJ_FREEZE(_self);
842
880
  return _self;
843
881
  }
844
882
 
@@ -1352,6 +1390,7 @@ static void Message_define_class(VALUE klass) {
1352
1390
  rb_define_method(klass, "==", Message_eq, 1);
1353
1391
  rb_define_method(klass, "eql?", Message_eq, 1);
1354
1392
  rb_define_method(klass, "freeze", Message_freeze, 0);
1393
+ rb_define_method(klass, "frozen?", Message_frozen, 0);
1355
1394
  rb_define_method(klass, "hash", Message_hash, 0);
1356
1395
  rb_define_method(klass, "to_h", Message_to_h, 0);
1357
1396
  rb_define_method(klass, "inspect", Message_inspect, 0);
@@ -36,7 +36,7 @@ const upb_Message* Message_GetUpbMessage(VALUE value, const upb_MessageDef* m,
36
36
 
37
37
  // Gets or constructs a Ruby wrapper object for the given message. The wrapper
38
38
  // object will reference |arena| and ensure that it outlives this object.
39
- VALUE Message_GetRubyWrapper(upb_Message* msg, const upb_MessageDef* m,
39
+ VALUE Message_GetRubyWrapper(const upb_Message* msg, const upb_MessageDef* m,
40
40
  VALUE arena);
41
41
 
42
42
  // Gets the given field from this message.
@@ -221,15 +221,6 @@ void Arena_fuse(VALUE _arena, upb_Arena *other) {
221
221
 
222
222
  VALUE Arena_new() { return Arena_alloc(cArena); }
223
223
 
224
- void Arena_Pin(VALUE _arena, VALUE obj) {
225
- Arena *arena;
226
- TypedData_Get_Struct(_arena, Arena, &Arena_type, arena);
227
- if (arena->pinned_objs == Qnil) {
228
- RB_OBJ_WRITE(_arena, &arena->pinned_objs, rb_ary_new());
229
- }
230
- rb_ary_push(arena->pinned_objs, obj);
231
- }
232
-
233
224
  void Arena_register(VALUE module) {
234
225
  VALUE internal = rb_define_module_under(module, "Internal");
235
226
  VALUE klass = rb_define_class_under(internal, "Arena", rb_cObject);
@@ -354,3 +345,14 @@ __attribute__((visibility("default"))) void Init_protobuf_c() {
354
345
  rb_define_singleton_method(protobuf, "deep_copy", Google_Protobuf_deep_copy,
355
346
  1);
356
347
  }
348
+
349
+ // -----------------------------------------------------------------------------
350
+ // Utilities
351
+ // -----------------------------------------------------------------------------
352
+
353
+ // Raises a Ruby error if val is frozen in Ruby or UPB.
354
+ void Protobuf_CheckNotFrozen(VALUE val, bool upb_frozen) {
355
+ if (RB_UNLIKELY(rb_obj_frozen_p(val)||upb_frozen)) {
356
+ rb_error_frozen_object(val);
357
+ }
358
+ }
@@ -50,13 +50,6 @@ upb_Arena* Arena_get(VALUE arena);
50
50
  // possible.
51
51
  void Arena_fuse(VALUE arena, upb_Arena* other);
52
52
 
53
- // Pins this Ruby object to the lifetime of this arena, so that as long as the
54
- // arena is alive this object will not be collected.
55
- //
56
- // We use this to guarantee that the "frozen" bit on the object will be
57
- // remembered, even if the user drops their reference to this precise object.
58
- void Arena_Pin(VALUE arena, VALUE obj);
59
-
60
53
  // -----------------------------------------------------------------------------
61
54
  // ObjectCache
62
55
  // -----------------------------------------------------------------------------
@@ -105,6 +98,9 @@ extern VALUE cTypeError;
105
98
  rb_bug("Assertion failed at %s:%d, expr: %s", __FILE__, __LINE__, #expr)
106
99
  #endif
107
100
 
101
+ // Raises a Ruby error if val is frozen in Ruby or upb_frozen is true.
102
+ void Protobuf_CheckNotFrozen(VALUE val, bool upb_frozen);
103
+
108
104
  #define PBRUBY_MAX(x, y) (((x) > (y)) ? (x) : (y))
109
105
 
110
106
  #define UPB_UNUSED(var) (void)var
@@ -44,8 +44,9 @@ static RepeatedField* ruby_to_RepeatedField(VALUE _self) {
44
44
  }
45
45
 
46
46
  static upb_Array* RepeatedField_GetMutable(VALUE _self) {
47
- rb_check_frozen(_self);
48
- return (upb_Array*)ruby_to_RepeatedField(_self)->array;
47
+ const upb_Array* array = ruby_to_RepeatedField(_self)->array;
48
+ Protobuf_CheckNotFrozen(_self, upb_Array_IsFrozen(array));
49
+ return (upb_Array*)array;
49
50
  }
50
51
 
51
52
  VALUE RepeatedField_alloc(VALUE klass) {
@@ -56,9 +57,32 @@ VALUE RepeatedField_alloc(VALUE klass) {
56
57
  return TypedData_Wrap_Struct(klass, &RepeatedField_type, self);
57
58
  }
58
59
 
59
- VALUE RepeatedField_GetRubyWrapper(upb_Array* array, TypeInfo type_info,
60
+ VALUE RepeatedField_EmptyFrozen(const upb_FieldDef* f) {
61
+ PBRUBY_ASSERT(upb_FieldDef_IsRepeated(f));
62
+ VALUE val = ObjectCache_Get(f);
63
+
64
+ if (val == Qnil) {
65
+ val = RepeatedField_alloc(cRepeatedField);
66
+ RepeatedField* self;
67
+ TypedData_Get_Struct(val, RepeatedField, &RepeatedField_type, self);
68
+ self->arena = Arena_new();
69
+ TypeInfo type_info = TypeInfo_get(f);
70
+ self->array = upb_Array_New(Arena_get(self->arena), type_info.type);
71
+ self->type_info = type_info;
72
+ if (self->type_info.type == kUpb_CType_Message) {
73
+ self->type_class = Descriptor_DefToClass(type_info.def.msgdef);
74
+ }
75
+ val = ObjectCache_TryAdd(f, RepeatedField_freeze(val));
76
+ }
77
+ PBRUBY_ASSERT(RB_OBJ_FROZEN(val));
78
+ PBRUBY_ASSERT(upb_Array_IsFrozen(ruby_to_RepeatedField(val)->array));
79
+ return val;
80
+ }
81
+
82
+ VALUE RepeatedField_GetRubyWrapper(const upb_Array* array, TypeInfo type_info,
60
83
  VALUE arena) {
61
84
  PBRUBY_ASSERT(array);
85
+ PBRUBY_ASSERT(arena != Qnil);
62
86
  VALUE val = ObjectCache_Get(array);
63
87
 
64
88
  if (val == Qnil) {
@@ -78,7 +102,6 @@ VALUE RepeatedField_GetRubyWrapper(upb_Array* array, TypeInfo type_info,
78
102
  PBRUBY_ASSERT(ruby_to_RepeatedField(val)->type_info.def.msgdef ==
79
103
  type_info.def.msgdef);
80
104
  PBRUBY_ASSERT(ruby_to_RepeatedField(val)->array == array);
81
-
82
105
  return val;
83
106
  }
84
107
 
@@ -471,29 +494,49 @@ VALUE RepeatedField_eq(VALUE _self, VALUE _other) {
471
494
  return Qtrue;
472
495
  }
473
496
 
497
+ /*
498
+ * call-seq:
499
+ * RepeatedField.frozen? => bool
500
+ *
501
+ * Returns true if the repeated field is frozen in either Ruby or the underlying
502
+ * representation. Freezes the Ruby repeated field object if it is not already
503
+ * frozen in Ruby but it is frozen in the underlying representation.
504
+ */
505
+ VALUE RepeatedField_frozen(VALUE _self) {
506
+ RepeatedField* self = ruby_to_RepeatedField(_self);
507
+ if (!upb_Array_IsFrozen(self->array)) {
508
+ PBRUBY_ASSERT(!RB_OBJ_FROZEN(_self));
509
+ return Qfalse;
510
+ }
511
+
512
+ // Lazily freeze the Ruby wrapper.
513
+ if (!RB_OBJ_FROZEN(_self)) RB_OBJ_FREEZE(_self);
514
+ return Qtrue;
515
+ }
516
+
474
517
  /*
475
518
  * call-seq:
476
519
  * RepeatedField.freeze => self
477
520
  *
478
- * Freezes the repeated field. We have to intercept this so we can pin the Ruby
479
- * object into memory so we don't forget it's frozen.
521
+ * Freezes the repeated field object. We have to intercept this so we can freeze
522
+ * the underlying representation, not just the Ruby wrapper.
480
523
  */
481
524
  VALUE RepeatedField_freeze(VALUE _self) {
482
525
  RepeatedField* self = ruby_to_RepeatedField(_self);
526
+ if (RB_OBJ_FROZEN(_self)) {
527
+ PBRUBY_ASSERT(upb_Array_IsFrozen(self->array));
528
+ return _self;
529
+ }
483
530
 
484
- if (RB_OBJ_FROZEN(_self)) return _self;
485
- Arena_Pin(self->arena, _self);
486
- RB_OBJ_FREEZE(_self);
487
-
488
- if (self->type_info.type == kUpb_CType_Message) {
489
- int size = upb_Array_Size(self->array);
490
- int i;
491
- for (i = 0; i < size; i++) {
492
- upb_MessageValue msgval = upb_Array_Get(self->array, i);
493
- VALUE val = Convert_UpbToRuby(msgval, self->type_info, self->arena);
494
- Message_freeze(val);
531
+ if (!upb_Array_IsFrozen(self->array)) {
532
+ if (self->type_info.type == kUpb_CType_Message) {
533
+ upb_Array_Freeze(RepeatedField_GetMutable(_self),
534
+ upb_MessageDef_MiniTable(self->type_info.def.msgdef));
535
+ } else {
536
+ upb_Array_Freeze(RepeatedField_GetMutable(_self), NULL);
495
537
  }
496
538
  }
539
+ RB_OBJ_FREEZE(_self);
497
540
  return _self;
498
541
  }
499
542
 
@@ -640,6 +683,7 @@ void RepeatedField_register(VALUE module) {
640
683
  rb_define_method(klass, "==", RepeatedField_eq, 1);
641
684
  rb_define_method(klass, "to_ary", RepeatedField_to_ary, 0);
642
685
  rb_define_method(klass, "freeze", RepeatedField_freeze, 0);
686
+ rb_define_method(klass, "frozen?", RepeatedField_frozen, 0);
643
687
  rb_define_method(klass, "hash", RepeatedField_hash, 0);
644
688
  rb_define_method(klass, "+", RepeatedField_plus, 1);
645
689
  rb_define_method(klass, "concat", RepeatedField_concat, 1);
@@ -11,9 +11,13 @@
11
11
  #include "protobuf.h"
12
12
  #include "ruby-upb.h"
13
13
 
14
+ // Returns a frozen sentinel Ruby wrapper object for an empty upb_Array of the
15
+ // type specified by the field. Creates one if it doesn't exist.
16
+ VALUE RepeatedField_EmptyFrozen(const upb_FieldDef* f);
17
+
14
18
  // Returns a Ruby wrapper object for the given upb_Array, which will be created
15
19
  // if one does not exist already.
16
- VALUE RepeatedField_GetRubyWrapper(upb_Array* msg, TypeInfo type_info,
20
+ VALUE RepeatedField_GetRubyWrapper(const upb_Array* msg, TypeInfo type_info,
17
21
  VALUE arena);
18
22
 
19
23
  // Gets the underlying upb_Array for this Ruby RepeatedField object, which must