google-protobuf 4.27.3 → 4.28.0.rc.2

Sign up to get free protection for your applications and to get access to all the features.

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: 634150e62701d649c61a06216e0134020a54022be183cbe07f82def99f1e8e58
4
- data.tar.gz: 7405081dedf538d964fd91af689d1eddf7ba78e7d6a5ded51f45f32f8472712d
3
+ metadata.gz: 6ab25b7cfe908c97bad06b0d6bb7ede952624f17ebc71d5a14d21e4615141142
4
+ data.tar.gz: 1d47940541856464f9ada535d1a19ebc9a526083587df00bd75d2d37e16468d2
5
5
  SHA512:
6
- metadata.gz: bf5c2b83695785285dc3139b48442f3306cda0ee0d61ca150a5cb82844d4171d03bfbfa7366bc28ba1ab3f6b947fc42c537e94b173fba5434d5cbc506d6b4e78
7
- data.tar.gz: e277db5f9bbf4449f79ab59ae077b09da7e63ca8798c3e8ec844bd5286c6c98e0156d94c40323d83b46d15b516eda65a67b66a69205c6fb253d2f1379b3ec530
6
+ metadata.gz: dc3a01ff11fecad3c868afe73f98018920df81ef8614172fa036f64082421cc15b21d2d3af3a24b225e1f5a36fd2f94f68aa5a37532438c76d127a01a5e4c2f8
7
+ data.tar.gz: 745454c9835f82ec37eb5e4a50d3c0a00ee1d953386e5e137b497dcc8513bc5bb1fd4bca4a827031355e8824cc007c5c597814352fb61ac0c64f4b687ce884aa
@@ -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