google-protobuf 3.15.6 → 3.17.3

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: e6f2d2b024b64eb8c142ff7eaea74a58431a8a12f936ab7152ab5c8721efd121
4
- data.tar.gz: 4ed37ac80d30d80f12a18d1b9608b4793d1d25304937b80fdb031cae748a8da2
3
+ metadata.gz: b1ded185284a923b15342a8f5600e74debdd40f99faf40c35c1b8675636406ff
4
+ data.tar.gz: '018d94176b65ef8ddcb7b27b785dfaac029ada709e999e58bcc194bff018f984'
5
5
  SHA512:
6
- metadata.gz: a42d5b9110675cb943a6add8acb5981d81ebbed4827adea17d7d5a819b42dd3e1ae9d0a5cf26288ab4f90f6a6ca28d3325ac431043e50510703aa5fafbef9cac
7
- data.tar.gz: 1774c58e9133f64859d84745a39b099b43a0006b331b1926cab3e42ccc8e352c03e1dce990d050253f9c537fb07ed230af10c85558379c68864320a016e1a0e3
6
+ metadata.gz: f0c47bf01f20c9327b089e60d2bb36d1eee98483eb862e7434b9409a5b18dc24ab38808660b997fbbb66ed21fa012417aaeb1164019ea715c7547d5928ca1d37
7
+ data.tar.gz: 01e37346bed78463df8a19d1b60315494065094b13aa4f76bbffb872be51f163297542d8a6db2a8d7902c2987312e06f23d3b55e2ed6833f988733c90f11e97c
@@ -868,6 +868,20 @@ static VALUE FieldDescriptor_default(VALUE _self) {
868
868
  return Convert_UpbToRuby(default_val, TypeInfo_get(self->fielddef), Qnil);
869
869
  }
870
870
 
871
+
872
+ /*
873
+ * call-seq:
874
+ * FieldDescriptor.json_name => json_name
875
+ *
876
+ * Returns this field's json_name, as a Ruby string, or nil if not yet set.
877
+ */
878
+ static VALUE FieldDescriptor_json_name(VALUE _self) {
879
+ FieldDescriptor* self = ruby_to_FieldDescriptor(_self);
880
+ const upb_fielddef *f = self->fielddef;
881
+ const char *json_name = upb_fielddef_jsonname(f);
882
+ return rb_str_new2(json_name);
883
+ }
884
+
871
885
  /*
872
886
  * call-seq:
873
887
  * FieldDescriptor.label => label
@@ -1043,6 +1057,7 @@ static void FieldDescriptor_register(VALUE module) {
1043
1057
  rb_define_method(klass, "name", FieldDescriptor_name, 0);
1044
1058
  rb_define_method(klass, "type", FieldDescriptor__type, 0);
1045
1059
  rb_define_method(klass, "default", FieldDescriptor_default, 0);
1060
+ rb_define_method(klass, "json_name", FieldDescriptor_json_name, 0);
1046
1061
  rb_define_method(klass, "label", FieldDescriptor_label, 0);
1047
1062
  rb_define_method(klass, "number", FieldDescriptor_number, 0);
1048
1063
  rb_define_method(klass, "submsg_name", FieldDescriptor_submsg_name, 0);
@@ -1750,6 +1765,16 @@ static void msgdef_add_field(VALUE msgbuilder_rb, upb_label_t label, VALUE name,
1750
1765
  field_proto,
1751
1766
  FileBuilderContext_strdup(self->file_builder, default_value));
1752
1767
  }
1768
+
1769
+ if (rb_funcall(options, rb_intern("key?"), 1,
1770
+ ID2SYM(rb_intern("json_name"))) == Qtrue) {
1771
+ VALUE json_name =
1772
+ rb_hash_lookup(options, ID2SYM(rb_intern("json_name")));
1773
+
1774
+ google_protobuf_FieldDescriptorProto_set_json_name(
1775
+ field_proto,
1776
+ FileBuilderContext_strdup(self->file_builder, json_name));
1777
+ }
1753
1778
  }
1754
1779
 
1755
1780
  if (oneof_index >= 0) {
@@ -1899,18 +1924,20 @@ static VALUE MessageBuilderContext_required(int argc, VALUE* argv,
1899
1924
  */
1900
1925
  static VALUE MessageBuilderContext_repeated(int argc, VALUE* argv,
1901
1926
  VALUE _self) {
1902
- VALUE name, type, number, type_class;
1927
+ VALUE name, type, number;
1928
+ VALUE type_class, options = Qnil;
1903
1929
 
1904
- if (argc < 3) {
1905
- rb_raise(rb_eArgError, "Expected at least 3 arguments.");
1930
+ rb_scan_args(argc, argv, "32", &name, &type, &number, &type_class, &options);
1931
+
1932
+ // Allow passing (name, type, number, options) or
1933
+ // (name, type, number, type_class, options)
1934
+ if (argc == 4 && RB_TYPE_P(type_class, T_HASH)) {
1935
+ options = type_class;
1936
+ type_class = Qnil;
1906
1937
  }
1907
- name = argv[0];
1908
- type = argv[1];
1909
- number = argv[2];
1910
- type_class = (argc > 3) ? argv[3] : Qnil;
1911
1938
 
1912
1939
  msgdef_add_field(_self, UPB_LABEL_REPEATED, name, type, number, type_class,
1913
- Qnil, -1, false);
1940
+ options, -1, false);
1914
1941
 
1915
1942
  return Qnil;
1916
1943
  }
@@ -17,4 +17,5 @@ end
17
17
  $objs = ["protobuf.o", "convert.o", "defs.o", "message.o",
18
18
  "repeated_field.o", "map.o", "ruby-upb.o", "wrap_memcpy.o"]
19
19
 
20
+ find_header('third_party/wyhash/wyhash.h', '../../../..')
20
21
  create_makefile("google/protobuf_c")
@@ -167,7 +167,8 @@ VALUE Map_deep_copy(VALUE obj) {
167
167
  new_arena_rb);
168
168
  }
169
169
 
170
- const upb_map* Map_GetUpbMap(VALUE val, const upb_fielddef *field) {
170
+ const upb_map* Map_GetUpbMap(VALUE val, const upb_fielddef* field,
171
+ upb_arena* arena) {
171
172
  const upb_fielddef* key_field = map_field_key(field);
172
173
  const upb_fielddef* value_field = map_field_value(field);
173
174
  TypeInfo value_type_info = TypeInfo_get(value_field);
@@ -189,6 +190,7 @@ const upb_map* Map_GetUpbMap(VALUE val, const upb_fielddef *field) {
189
190
  rb_raise(cTypeError, "Map value type has wrong message/enum class");
190
191
  }
191
192
 
193
+ Arena_fuse(self->arena, arena);
192
194
  return self->map;
193
195
  }
194
196
 
@@ -236,7 +238,7 @@ static VALUE Map_merge_into_self(VALUE _self, VALUE hashmap) {
236
238
  upb_msg *self_msg = Map_GetMutable(_self);
237
239
  size_t iter = UPB_MAP_BEGIN;
238
240
 
239
- upb_arena_fuse(arena, Arena_get(other->arena));
241
+ Arena_fuse(other->arena, arena);
240
242
 
241
243
  if (self->key_type != other->key_type ||
242
244
  self->value_type_info.type != other->value_type_info.type ||
@@ -511,7 +513,7 @@ static VALUE Map_dup(VALUE _self) {
511
513
  upb_arena *arena = Arena_get(new_self->arena);
512
514
  upb_map *new_map = Map_GetMutable(new_map_rb);
513
515
 
514
- upb_arena_fuse(arena, Arena_get(self->arena));
516
+ Arena_fuse(self->arena, arena);
515
517
 
516
518
  while (upb_mapiter_next(self->map, &iter)) {
517
519
  upb_msgval key = upb_mapiter_key(self->map, iter);
@@ -44,7 +44,8 @@ VALUE Map_GetRubyWrapper(upb_map *map, upb_fieldtype_t key_type,
44
44
  // Gets the underlying upb_map for this Ruby map object, which must have
45
45
  // key/value type that match |field|. If this is not a map or the type doesn't
46
46
  // match, raises an exception.
47
- const upb_map *Map_GetUpbMap(VALUE val, const upb_fielddef *field);
47
+ const upb_map *Map_GetUpbMap(VALUE val, const upb_fielddef *field,
48
+ upb_arena *arena);
48
49
 
49
50
  // Implements #inspect for this map by appending its contents to |b|.
50
51
  void Map_Inspect(StringBuilder *b, const upb_map *map, upb_fieldtype_t key_type,
@@ -277,9 +277,9 @@ static void Message_setfield(upb_msg* msg, const upb_fielddef* f, VALUE val,
277
277
  upb_arena* arena) {
278
278
  upb_msgval msgval;
279
279
  if (upb_fielddef_ismap(f)) {
280
- msgval.map_val = Map_GetUpbMap(val, f);
280
+ msgval.map_val = Map_GetUpbMap(val, f, arena);
281
281
  } else if (upb_fielddef_isseq(f)) {
282
- msgval.array_val = RepeatedField_GetUpbArray(val, f);
282
+ msgval.array_val = RepeatedField_GetUpbArray(val, f, arena);
283
283
  } else {
284
284
  if (val == Qnil &&
285
285
  (upb_fielddef_issubmsg(f) || upb_fielddef_realcontainingoneof(f))) {
@@ -660,7 +660,7 @@ static VALUE Message_dup(VALUE _self) {
660
660
  // TODO(copy unknown fields?)
661
661
  // TODO(use official upb msg copy function)
662
662
  memcpy((upb_msg*)new_msg_self->msg, self->msg, size);
663
- upb_arena_fuse(Arena_get(new_msg_self->arena), Arena_get(self->arena));
663
+ Arena_fuse(self->arena, Arena_get(new_msg_self->arena));
664
664
  return new_msg;
665
665
  }
666
666
 
@@ -697,16 +697,13 @@ bool Message_Equal(const upb_msg *m1, const upb_msg *m2, const upb_msgdef *m) {
697
697
  * field is of a primitive type).
698
698
  */
699
699
  static VALUE Message_eq(VALUE _self, VALUE _other) {
700
- if (TYPE(_self) != TYPE(_other)) {
701
- return Qfalse;
702
- }
700
+ if (CLASS_OF(_self) != CLASS_OF(_other)) return Qfalse;
703
701
 
704
702
  Message* self = ruby_to_Message(_self);
705
703
  Message* other = ruby_to_Message(_other);
704
+ assert(self->msgdef == other->msgdef);
706
705
 
707
- return Message_Equal(self->msg, other->msg, self->msgdef)
708
- ? Qtrue
709
- : Qfalse;
706
+ return Message_Equal(self->msg, other->msg, self->msgdef) ? Qtrue : Qfalse;
710
707
  }
711
708
 
712
709
  uint64_t Message_Hash(const upb_msg* msg, const upb_msgdef* m, uint64_t seed) {
@@ -737,7 +734,10 @@ uint64_t Message_Hash(const upb_msg* msg, const upb_msgdef* m, uint64_t seed) {
737
734
  */
738
735
  static VALUE Message_hash(VALUE _self) {
739
736
  Message* self = ruby_to_Message(_self);
740
- return INT2FIX(Message_Hash(self->msg, self->msgdef, 0));
737
+ uint64_t hash_value = Message_Hash(self->msg, self->msgdef, 0);
738
+ // RUBY_FIXNUM_MAX should be one less than a power of 2.
739
+ assert((RUBY_FIXNUM_MAX & (RUBY_FIXNUM_MAX + 1)) == 0);
740
+ return INT2FIX(hash_value & RUBY_FIXNUM_MAX);
741
741
  }
742
742
 
743
743
  /*
@@ -794,6 +794,14 @@ static VALUE Message_CreateHash(const upb_msg *msg, const upb_msgdef *m) {
794
794
  VALUE msg_value;
795
795
  VALUE msg_key;
796
796
 
797
+ if (!is_proto2 && upb_fielddef_issubmsg(field) &&
798
+ !upb_fielddef_isseq(field) && !upb_msg_has(msg, field)) {
799
+ // TODO: Legacy behavior, remove when we fix the is_proto2 differences.
800
+ msg_key = ID2SYM(rb_intern(upb_fielddef_name(field)));
801
+ rb_hash_aset(hash, msg_key, Qnil);
802
+ continue;
803
+ }
804
+
797
805
  // Do not include fields that are not present (oneof or optional fields).
798
806
  if (is_proto2 && upb_fielddef_haspresence(field) &&
799
807
  !upb_msg_has(msg, field)) {
@@ -1306,7 +1314,7 @@ const upb_msg* Message_GetUpbMessage(VALUE value, const upb_msgdef* m,
1306
1314
  }
1307
1315
 
1308
1316
  Message* self = ruby_to_Message(value);
1309
- upb_arena_fuse(arena, Arena_get(self->arena));
1317
+ Arena_fuse(self->arena, arena);
1310
1318
 
1311
1319
  return self->msg;
1312
1320
  }
@@ -37,7 +37,7 @@
37
37
  #include "message.h"
38
38
  #include "repeated_field.h"
39
39
 
40
- VALUE cError;
40
+ VALUE cParseError;
41
41
  VALUE cTypeError;
42
42
 
43
43
  const upb_fielddef* map_field_key(const upb_fielddef* field) {
@@ -180,6 +180,7 @@ static void Arena_mark(void *data) {
180
180
  static void Arena_free(void *data) {
181
181
  Arena *arena = data;
182
182
  upb_arena_free(arena->arena);
183
+ xfree(arena);
183
184
  }
184
185
 
185
186
  static VALUE cArena;
@@ -203,6 +204,16 @@ upb_arena *Arena_get(VALUE _arena) {
203
204
  return arena->arena;
204
205
  }
205
206
 
207
+ void Arena_fuse(VALUE _arena, upb_arena *other) {
208
+ Arena *arena;
209
+ TypedData_Get_Struct(_arena, Arena, &Arena_type, arena);
210
+ if (!upb_arena_fuse(arena->arena, other)) {
211
+ rb_raise(rb_eRuntimeError,
212
+ "Unable to fuse arenas. This should never happen since Ruby does "
213
+ "not use initial blocks");
214
+ }
215
+ }
216
+
206
217
  VALUE Arena_new() {
207
218
  return Arena_alloc(cArena);
208
219
  }
@@ -237,8 +248,16 @@ void Arena_register(VALUE module) {
237
248
  // We use WeakMap for the cache. For Ruby <2.7 we also need a secondary Hash
238
249
  // to store WeakMap keys because Ruby <2.7 WeakMap doesn't allow non-finalizable
239
250
  // keys.
240
-
241
- #if RUBY_API_VERSION_CODE >= 20700
251
+ //
252
+ // We also need the secondary Hash if sizeof(long) < sizeof(VALUE), because this
253
+ // means it may not be possible to fit a pointer into a Fixnum. Keys are
254
+ // pointers, and if they fit into a Fixnum, Ruby doesn't collect them, but if
255
+ // they overflow and require allocating a Bignum, they could get collected
256
+ // prematurely, thus removing the cache entry. This happens on 64-bit Windows,
257
+ // on which pointers are 64 bits but longs are 32 bits. In this case, we enable
258
+ // the secondary Hash to hold the keys and prevent them from being collected.
259
+
260
+ #if RUBY_API_VERSION_CODE >= 20700 && SIZEOF_LONG >= SIZEOF_VALUE
242
261
  #define USE_SECONDARY_MAP 0
243
262
  #else
244
263
  #define USE_SECONDARY_MAP 1
@@ -251,15 +270,81 @@ void Arena_register(VALUE module) {
251
270
  // The object is used only for its identity; it does not contain any data.
252
271
  VALUE secondary_map = Qnil;
253
272
 
273
+ // Mutations to the map are under a mutex, because SeconaryMap_MaybeGC()
274
+ // iterates over the map which cannot happen in parallel with insertions, or
275
+ // Ruby will throw:
276
+ // can't add a new key into hash during iteration (RuntimeError)
277
+ VALUE secondary_map_mutex = Qnil;
278
+
279
+ // Lambda that will GC entries from the secondary map that are no longer present
280
+ // in the primary map.
281
+ VALUE gc_secondary_map_lambda = Qnil;
282
+ ID length;
283
+
284
+ extern VALUE weak_obj_cache;
285
+
254
286
  static void SecondaryMap_Init() {
255
287
  rb_gc_register_address(&secondary_map);
288
+ rb_gc_register_address(&gc_secondary_map_lambda);
289
+ rb_gc_register_address(&secondary_map_mutex);
256
290
  secondary_map = rb_hash_new();
291
+ gc_secondary_map_lambda = rb_eval_string(
292
+ "->(secondary, weak) {\n"
293
+ " secondary.delete_if { |k, v| !weak.key?(v) }\n"
294
+ "}\n");
295
+ secondary_map_mutex = rb_mutex_new();
296
+ length = rb_intern("length");
257
297
  }
258
298
 
259
- static VALUE SecondaryMap_Get(VALUE key) {
299
+ // The secondary map is a regular Hash, and will never shrink on its own.
300
+ // The main object cache is a WeakMap that will automatically remove entries
301
+ // when the target object is no longer reachable, but unless we manually
302
+ // remove the corresponding entries from the secondary map, it will grow
303
+ // without bound.
304
+ //
305
+ // To avoid this unbounded growth we periodically remove entries from the
306
+ // secondary map that are no longer present in the WeakMap. The logic of
307
+ // how often to perform this GC is an artbirary tuning parameter that
308
+ // represents a straightforward CPU/memory tradeoff.
309
+ //
310
+ // Requires: secondary_map_mutex is held.
311
+ static void SecondaryMap_MaybeGC() {
312
+ PBRUBY_ASSERT(rb_mutex_locked_p(secondary_map_mutex) == Qtrue);
313
+ size_t weak_len = NUM2ULL(rb_funcall(weak_obj_cache, length, 0));
314
+ size_t secondary_len = RHASH_SIZE(secondary_map);
315
+ if (secondary_len < weak_len) {
316
+ // Logically this case should not be possible: a valid entry cannot exist in
317
+ // the weak table unless there is a corresponding entry in the secondary
318
+ // table. It should *always* be the case that secondary_len >= weak_len.
319
+ //
320
+ // However ObjectSpace::WeakMap#length (and therefore weak_len) is
321
+ // unreliable: it overreports its true length by including non-live objects.
322
+ // However these non-live objects are not yielded in iteration, so we may
323
+ // have previously deleted them from the secondary map in a previous
324
+ // invocation of SecondaryMap_MaybeGC().
325
+ //
326
+ // In this case, we can't measure any waste, so we just return.
327
+ return;
328
+ }
329
+ size_t waste = secondary_len - weak_len;
330
+ // GC if we could remove at least 2000 entries or 20% of the table size
331
+ // (whichever is greater). Since the cost of the GC pass is O(N), we
332
+ // want to make sure that we condition this on overall table size, to
333
+ // avoid O(N^2) CPU costs.
334
+ size_t threshold = PBRUBY_MAX(secondary_len * 0.2, 2000);
335
+ if (waste > threshold) {
336
+ rb_funcall(gc_secondary_map_lambda, rb_intern("call"), 2,
337
+ secondary_map, weak_obj_cache);
338
+ }
339
+ }
340
+
341
+ // Requires: secondary_map_mutex is held by this thread iff create == true.
342
+ static VALUE SecondaryMap_Get(VALUE key, bool create) {
343
+ PBRUBY_ASSERT(!create || rb_mutex_locked_p(secondary_map_mutex) == Qtrue);
260
344
  VALUE ret = rb_hash_lookup(secondary_map, key);
261
- if (ret == Qnil) {
262
- ret = rb_eval_string("Object.new");
345
+ if (ret == Qnil && create) {
346
+ SecondaryMap_MaybeGC();
347
+ ret = rb_class_new_instance(0, NULL, rb_cObject);
263
348
  rb_hash_aset(secondary_map, key, ret);
264
349
  }
265
350
  return ret;
@@ -267,14 +352,13 @@ static VALUE SecondaryMap_Get(VALUE key) {
267
352
 
268
353
  #endif
269
354
 
270
- static VALUE ObjectCache_GetKey(const void* key) {
271
- char buf[sizeof(key)];
272
- memcpy(&buf, &key, sizeof(key));
273
- intptr_t key_int = (intptr_t)key;
274
- PBRUBY_ASSERT((key_int & 3) == 0);
275
- VALUE ret = LL2NUM(key_int >> 2);
355
+ // Requires: secondary_map_mutex is held by this thread iff create == true.
356
+ static VALUE ObjectCache_GetKey(const void* key, bool create) {
357
+ VALUE key_val = (VALUE)key;
358
+ PBRUBY_ASSERT((key_val & 3) == 0);
359
+ VALUE ret = LL2NUM(key_val >> 2);
276
360
  #if USE_SECONDARY_MAP
277
- ret = SecondaryMap_Get(ret);
361
+ ret = SecondaryMap_Get(ret, create);
278
362
  #endif
279
363
  return ret;
280
364
  }
@@ -298,14 +382,20 @@ static void ObjectCache_Init() {
298
382
 
299
383
  void ObjectCache_Add(const void* key, VALUE val) {
300
384
  PBRUBY_ASSERT(ObjectCache_Get(key) == Qnil);
301
- VALUE key_rb = ObjectCache_GetKey(key);
385
+ #if USE_SECONDARY_MAP
386
+ rb_mutex_lock(secondary_map_mutex);
387
+ #endif
388
+ VALUE key_rb = ObjectCache_GetKey(key, true);
302
389
  rb_funcall(weak_obj_cache, item_set, 2, key_rb, val);
390
+ #if USE_SECONDARY_MAP
391
+ rb_mutex_unlock(secondary_map_mutex);
392
+ #endif
303
393
  PBRUBY_ASSERT(ObjectCache_Get(key) == val);
304
394
  }
305
395
 
306
396
  // Returns the cached object for this key, if any. Otherwise returns Qnil.
307
397
  VALUE ObjectCache_Get(const void* key) {
308
- VALUE key_rb = ObjectCache_GetKey(key);
398
+ VALUE key_rb = ObjectCache_GetKey(key, false);
309
399
  return rb_funcall(weak_obj_cache, item_get, 1, key_rb);
310
400
  }
311
401
 
@@ -368,8 +458,10 @@ void Init_protobuf_c() {
368
458
  Map_register(protobuf);
369
459
  Message_register(protobuf);
370
460
 
371
- cError = rb_const_get(protobuf, rb_intern("Error"));
461
+ cParseError = rb_const_get(protobuf, rb_intern("ParseError"));
462
+ rb_gc_register_mark_object(cParseError);
372
463
  cTypeError = rb_const_get(protobuf, rb_intern("TypeError"));
464
+ rb_gc_register_mark_object(cTypeError);
373
465
 
374
466
  rb_define_singleton_method(protobuf, "discard_unknown",
375
467
  Google_Protobuf_discard_unknown, 1);
@@ -55,6 +55,10 @@ const upb_fielddef* map_field_value(const upb_fielddef* field);
55
55
  VALUE Arena_new();
56
56
  upb_arena *Arena_get(VALUE arena);
57
57
 
58
+ // Fuses this arena to another, throwing a Ruby exception if this is not
59
+ // possible.
60
+ void Arena_fuse(VALUE arena, upb_arena *other);
61
+
58
62
  // Pins this Ruby object to the lifetime of this arena, so that as long as the
59
63
  // arena is alive this object will not be collected.
60
64
  //
@@ -106,6 +110,8 @@ extern VALUE cTypeError;
106
110
  #define PBRUBY_ASSERT(expr) assert(expr)
107
111
  #endif
108
112
 
113
+ #define PBRUBY_MAX(x, y) (((x) > (y)) ? (x) : (y))
114
+
109
115
  #define UPB_UNUSED(var) (void)var
110
116
 
111
117
  #endif // __GOOGLE_PROTOBUF_RUBY_PROTOBUF_H__
@@ -149,7 +149,8 @@ VALUE RepeatedField_deep_copy(VALUE _self) {
149
149
  return new_rptfield;
150
150
  }
151
151
 
152
- const upb_array* RepeatedField_GetUpbArray(VALUE val, const upb_fielddef *field) {
152
+ const upb_array* RepeatedField_GetUpbArray(VALUE val, const upb_fielddef* field,
153
+ upb_arena* arena) {
153
154
  RepeatedField* self;
154
155
  TypeInfo type_info = TypeInfo_get(field);
155
156
 
@@ -167,6 +168,7 @@ const upb_array* RepeatedField_GetUpbArray(VALUE val, const upb_fielddef *field)
167
168
  rb_raise(cTypeError, "Repeated field array has wrong message/enum class");
168
169
  }
169
170
 
171
+ Arena_fuse(self->arena, arena);
170
172
  return self->array;
171
173
  }
172
174
 
@@ -412,7 +414,7 @@ static VALUE RepeatedField_dup(VALUE _self) {
412
414
  int size = upb_array_size(self->array);
413
415
  int i;
414
416
 
415
- upb_arena_fuse(arena, Arena_get(self->arena));
417
+ Arena_fuse(self->arena, arena);
416
418
 
417
419
  for (i = 0; i < size; i++) {
418
420
  upb_msgval msgval = upb_array_get(self->array, i);
@@ -44,7 +44,8 @@ VALUE RepeatedField_GetRubyWrapper(upb_array* msg, TypeInfo type_info,
44
44
  // Gets the underlying upb_array for this Ruby RepeatedField object, which must
45
45
  // have a type that matches |f|. If this is not a repeated field or the type
46
46
  // doesn't match, raises an exception.
47
- const upb_array* RepeatedField_GetUpbArray(VALUE value, const upb_fielddef* f);
47
+ const upb_array* RepeatedField_GetUpbArray(VALUE value, const upb_fielddef* f,
48
+ upb_arena* arena);
48
49
 
49
50
  // Implements #inspect for this repeated field by appending its contents to |b|.
50
51
  void RepeatedField_Inspect(StringBuilder* b, const upb_array* array,