google-protobuf 3.2.0 → 3.9.1
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 +5 -5
- data/ext/google/protobuf_c/defs.c +575 -81
- data/ext/google/protobuf_c/encode_decode.c +456 -149
- data/ext/google/protobuf_c/extconf.rb +15 -2
- data/ext/google/protobuf_c/map.c +47 -3
- data/ext/google/protobuf_c/message.c +308 -82
- data/ext/google/protobuf_c/protobuf.c +7 -3
- data/ext/google/protobuf_c/protobuf.h +86 -12
- data/ext/google/protobuf_c/repeated_field.c +10 -4
- data/ext/google/protobuf_c/storage.c +286 -117
- data/ext/google/protobuf_c/upb.c +14482 -10935
- data/ext/google/protobuf_c/upb.h +2401 -613
- data/ext/google/protobuf_c/wrap_memcpy.c +51 -0
- data/lib/google/protobuf.rb +5 -4
- data/lib/google/protobuf/any_pb.rb +5 -3
- data/lib/google/protobuf/api_pb.rb +23 -21
- data/lib/google/protobuf/duration_pb.rb +5 -3
- data/lib/google/protobuf/empty_pb.rb +3 -1
- data/lib/google/protobuf/field_mask_pb.rb +4 -2
- data/lib/google/protobuf/message_exts.rb +2 -2
- data/lib/google/protobuf/repeated_field.rb +3 -3
- data/lib/google/protobuf/source_context_pb.rb +4 -2
- data/lib/google/protobuf/struct_pb.rb +19 -17
- data/lib/google/protobuf/timestamp_pb.rb +5 -3
- data/lib/google/protobuf/type_pb.rb +68 -66
- data/lib/google/protobuf/well_known_types.rb +13 -1
- data/lib/google/protobuf/wrappers_pb.rb +28 -26
- data/tests/basic.rb +206 -1023
- data/tests/generated_code_test.rb +6 -2
- metadata +5 -5
@@ -2,9 +2,22 @@
|
|
2
2
|
|
3
3
|
require 'mkmf'
|
4
4
|
|
5
|
-
|
5
|
+
if RUBY_PLATFORM =~ /darwin/ || RUBY_PLATFORM =~ /linux/
|
6
|
+
# XOPEN_SOURCE needed for strptime:
|
7
|
+
# https://stackoverflow.com/questions/35234152/strptime-giving-implicit-declaration-and-undefined-reference
|
8
|
+
$CFLAGS += " -std=c99 -O3 -DNDEBUG -D_XOPEN_SOURCE=700"
|
9
|
+
else
|
10
|
+
$CFLAGS += " -std=c99 -O3 -DNDEBUG"
|
11
|
+
end
|
12
|
+
|
13
|
+
|
14
|
+
if RUBY_PLATFORM =~ /linux/
|
15
|
+
# Instruct the linker to point memcpy calls at our __wrap_memcpy wrapper.
|
16
|
+
$LDFLAGS += " -Wl,-wrap,memcpy"
|
17
|
+
end
|
6
18
|
|
7
19
|
$objs = ["protobuf.o", "defs.o", "storage.o", "message.o",
|
8
|
-
"repeated_field.o", "map.o", "encode_decode.o", "upb.o"
|
20
|
+
"repeated_field.o", "map.o", "encode_decode.o", "upb.o",
|
21
|
+
"wrap_memcpy.o"]
|
9
22
|
|
10
23
|
create_makefile("google/protobuf_c")
|
data/ext/google/protobuf_c/map.c
CHANGED
@@ -82,7 +82,7 @@ static VALUE table_key(Map* self, VALUE key,
|
|
82
82
|
case UPB_TYPE_INT64:
|
83
83
|
case UPB_TYPE_UINT32:
|
84
84
|
case UPB_TYPE_UINT64:
|
85
|
-
native_slot_set(self->key_type, Qnil, buf, key);
|
85
|
+
native_slot_set("", self->key_type, Qnil, buf, key);
|
86
86
|
*out_key = buf;
|
87
87
|
*out_length = native_slot_size(self->key_type);
|
88
88
|
break;
|
@@ -146,6 +146,7 @@ void Map_mark(void* _self) {
|
|
146
146
|
Map* self = _self;
|
147
147
|
|
148
148
|
rb_gc_mark(self->value_type_class);
|
149
|
+
rb_gc_mark(self->parse_frame);
|
149
150
|
|
150
151
|
if (self->value_type == UPB_TYPE_STRING ||
|
151
152
|
self->value_type == UPB_TYPE_BYTES ||
|
@@ -174,6 +175,12 @@ VALUE Map_alloc(VALUE klass) {
|
|
174
175
|
return TypedData_Wrap_Struct(klass, &Map_type, self);
|
175
176
|
}
|
176
177
|
|
178
|
+
VALUE Map_set_frame(VALUE map, VALUE val) {
|
179
|
+
Map* self = ruby_to_Map(map);
|
180
|
+
self->parse_frame = val;
|
181
|
+
return val;
|
182
|
+
}
|
183
|
+
|
177
184
|
static bool needs_typeclass(upb_fieldtype_t type) {
|
178
185
|
switch (type) {
|
179
186
|
case UPB_TYPE_MESSAGE:
|
@@ -227,6 +234,7 @@ VALUE Map_init(int argc, VALUE* argv, VALUE _self) {
|
|
227
234
|
|
228
235
|
self->key_type = ruby_to_fieldtype(argv[0]);
|
229
236
|
self->value_type = ruby_to_fieldtype(argv[1]);
|
237
|
+
self->parse_frame = Qnil;
|
230
238
|
|
231
239
|
// Check that the key type is an allowed type.
|
232
240
|
switch (self->key_type) {
|
@@ -378,6 +386,8 @@ VALUE Map_index(VALUE _self, VALUE key) {
|
|
378
386
|
* was just inserted.
|
379
387
|
*/
|
380
388
|
VALUE Map_index_set(VALUE _self, VALUE key, VALUE value) {
|
389
|
+
rb_check_frozen(_self);
|
390
|
+
|
381
391
|
Map* self = ruby_to_Map(_self);
|
382
392
|
|
383
393
|
char keybuf[TABLE_KEY_BUF_LENGTH];
|
@@ -388,7 +398,7 @@ VALUE Map_index_set(VALUE _self, VALUE key, VALUE value) {
|
|
388
398
|
key = table_key(self, key, keybuf, &keyval, &length);
|
389
399
|
|
390
400
|
mem = value_memory(&v);
|
391
|
-
native_slot_set(self->value_type, self->value_type_class, mem, value);
|
401
|
+
native_slot_set("", self->value_type, self->value_type_class, mem, value);
|
392
402
|
|
393
403
|
// Replace any existing value by issuing a 'remove' operation first.
|
394
404
|
upb_strtable_remove2(&self->table, keyval, length, NULL);
|
@@ -430,6 +440,8 @@ VALUE Map_has_key(VALUE _self, VALUE key) {
|
|
430
440
|
* nil if none was present. Throws an exception if the key is of the wrong type.
|
431
441
|
*/
|
432
442
|
VALUE Map_delete(VALUE _self, VALUE key) {
|
443
|
+
rb_check_frozen(_self);
|
444
|
+
|
433
445
|
Map* self = ruby_to_Map(_self);
|
434
446
|
|
435
447
|
char keybuf[TABLE_KEY_BUF_LENGTH];
|
@@ -453,6 +465,8 @@ VALUE Map_delete(VALUE _self, VALUE key) {
|
|
453
465
|
* Removes all entries from the map.
|
454
466
|
*/
|
455
467
|
VALUE Map_clear(VALUE _self) {
|
468
|
+
rb_check_frozen(_self);
|
469
|
+
|
456
470
|
Map* self = ruby_to_Map(_self);
|
457
471
|
|
458
472
|
// Uninit and reinit the table -- this is faster than iterating and doing a
|
@@ -652,6 +666,35 @@ VALUE Map_hash(VALUE _self) {
|
|
652
666
|
return INT2FIX(h);
|
653
667
|
}
|
654
668
|
|
669
|
+
/*
|
670
|
+
* call-seq:
|
671
|
+
* Map.to_h => {}
|
672
|
+
*
|
673
|
+
* Returns a Ruby Hash object containing all the values within the map
|
674
|
+
*/
|
675
|
+
VALUE Map_to_h(VALUE _self) {
|
676
|
+
Map* self = ruby_to_Map(_self);
|
677
|
+
VALUE hash = rb_hash_new();
|
678
|
+
upb_strtable_iter it;
|
679
|
+
for (upb_strtable_begin(&it, &self->table);
|
680
|
+
!upb_strtable_done(&it);
|
681
|
+
upb_strtable_next(&it)) {
|
682
|
+
VALUE key = table_key_to_ruby(
|
683
|
+
self, upb_strtable_iter_key(&it), upb_strtable_iter_keylength(&it));
|
684
|
+
upb_value v = upb_strtable_iter_value(&it);
|
685
|
+
void* mem = value_memory(&v);
|
686
|
+
VALUE value = native_slot_get(self->value_type,
|
687
|
+
self->value_type_class,
|
688
|
+
mem);
|
689
|
+
|
690
|
+
if (self->value_type == UPB_TYPE_MESSAGE) {
|
691
|
+
value = Message_to_h(value);
|
692
|
+
}
|
693
|
+
rb_hash_aset(hash, key, value);
|
694
|
+
}
|
695
|
+
return hash;
|
696
|
+
}
|
697
|
+
|
655
698
|
/*
|
656
699
|
* call-seq:
|
657
700
|
* Map.inspect => string
|
@@ -788,8 +831,8 @@ VALUE Map_iter_value(Map_iter* iter) {
|
|
788
831
|
void Map_register(VALUE module) {
|
789
832
|
VALUE klass = rb_define_class_under(module, "Map", rb_cObject);
|
790
833
|
rb_define_alloc_func(klass, Map_alloc);
|
791
|
-
cMap = klass;
|
792
834
|
rb_gc_register_address(&cMap);
|
835
|
+
cMap = klass;
|
793
836
|
|
794
837
|
rb_define_method(klass, "initialize", Map_init, -1);
|
795
838
|
rb_define_method(klass, "each", Map_each, 0);
|
@@ -804,6 +847,7 @@ void Map_register(VALUE module) {
|
|
804
847
|
rb_define_method(klass, "dup", Map_dup, 0);
|
805
848
|
rb_define_method(klass, "==", Map_eq, 1);
|
806
849
|
rb_define_method(klass, "hash", Map_hash, 0);
|
850
|
+
rb_define_method(klass, "to_h", Map_to_h, 0);
|
807
851
|
rb_define_method(klass, "inspect", Map_inspect, 0);
|
808
852
|
rb_define_method(klass, "merge", Map_merge, 1);
|
809
853
|
rb_include_module(klass, rb_mEnumerable);
|
@@ -44,6 +44,11 @@ void Message_mark(void* _self) {
|
|
44
44
|
}
|
45
45
|
|
46
46
|
void Message_free(void* self) {
|
47
|
+
stringsink* unknown = ((MessageHeader *)self)->unknown_fields;
|
48
|
+
if (unknown != NULL) {
|
49
|
+
stringsink_uninit(unknown);
|
50
|
+
free(unknown);
|
51
|
+
}
|
47
52
|
xfree(self);
|
48
53
|
}
|
49
54
|
|
@@ -67,12 +72,14 @@ VALUE Message_alloc(VALUE klass) {
|
|
67
72
|
msg->descriptor = desc;
|
68
73
|
rb_ivar_set(ret, descriptor_instancevar_interned, descriptor);
|
69
74
|
|
75
|
+
msg->unknown_fields = NULL;
|
76
|
+
|
70
77
|
layout_init(desc->layout, Message_data(msg));
|
71
78
|
|
72
79
|
return ret;
|
73
80
|
}
|
74
81
|
|
75
|
-
static
|
82
|
+
static const upb_fielddef* which_oneof_field(MessageHeader* self, const upb_oneofdef* o) {
|
76
83
|
upb_oneof_iter it;
|
77
84
|
size_t case_ofs;
|
78
85
|
uint32_t oneof_case;
|
@@ -81,7 +88,7 @@ static VALUE which_oneof_field(MessageHeader* self, const upb_oneofdef* o) {
|
|
81
88
|
|
82
89
|
// If no fields in the oneof, always nil.
|
83
90
|
if (upb_oneofdef_numfields(o) == 0) {
|
84
|
-
return
|
91
|
+
return NULL;
|
85
92
|
}
|
86
93
|
// Grab the first field in the oneof so we can get its layout info to find the
|
87
94
|
// oneof_case field.
|
@@ -96,22 +103,165 @@ static VALUE which_oneof_field(MessageHeader* self, const upb_oneofdef* o) {
|
|
96
103
|
oneof_case = *((uint32_t*)((char*)Message_data(self) + case_ofs));
|
97
104
|
|
98
105
|
if (oneof_case == ONEOF_CASE_NONE) {
|
99
|
-
return
|
106
|
+
return NULL;
|
100
107
|
}
|
101
108
|
|
102
109
|
// oneof_case is a field index, so find that field.
|
103
110
|
f = upb_oneofdef_itof(o, oneof_case);
|
104
111
|
assert(f != NULL);
|
105
112
|
|
106
|
-
return
|
113
|
+
return f;
|
114
|
+
}
|
115
|
+
|
116
|
+
enum {
|
117
|
+
METHOD_UNKNOWN = 0,
|
118
|
+
METHOD_GETTER = 1,
|
119
|
+
METHOD_SETTER = 2,
|
120
|
+
METHOD_CLEAR = 3,
|
121
|
+
METHOD_PRESENCE = 4,
|
122
|
+
METHOD_ENUM_GETTER = 5,
|
123
|
+
METHOD_WRAPPER_GETTER = 6,
|
124
|
+
METHOD_WRAPPER_SETTER = 7
|
125
|
+
};
|
126
|
+
|
127
|
+
// Check if the field is a well known wrapper type
|
128
|
+
static bool is_wrapper_type_field(const upb_fielddef* field) {
|
129
|
+
char* field_type_name = rb_class2name(field_type_class(field));
|
130
|
+
|
131
|
+
return strcmp(field_type_name, "Google::Protobuf::DoubleValue") == 0 ||
|
132
|
+
strcmp(field_type_name, "Google::Protobuf::FloatValue") == 0 ||
|
133
|
+
strcmp(field_type_name, "Google::Protobuf::Int32Value") == 0 ||
|
134
|
+
strcmp(field_type_name, "Google::Protobuf::Int64Value") == 0 ||
|
135
|
+
strcmp(field_type_name, "Google::Protobuf::UInt32Value") == 0 ||
|
136
|
+
strcmp(field_type_name, "Google::Protobuf::UInt64Value") == 0 ||
|
137
|
+
strcmp(field_type_name, "Google::Protobuf::BoolValue") == 0 ||
|
138
|
+
strcmp(field_type_name, "Google::Protobuf::StringValue") == 0 ||
|
139
|
+
strcmp(field_type_name, "Google::Protobuf::BytesValue") == 0;
|
140
|
+
}
|
141
|
+
|
142
|
+
// Get a new Ruby wrapper type and set the initial value
|
143
|
+
static VALUE ruby_wrapper_type(const upb_fielddef* field, const VALUE* value) {
|
144
|
+
if (is_wrapper_type_field(field) && value != Qnil) {
|
145
|
+
VALUE hash = rb_hash_new();
|
146
|
+
rb_hash_aset(hash, rb_str_new2("value"), value);
|
147
|
+
VALUE args[1] = { hash };
|
148
|
+
return rb_class_new_instance(1, args, field_type_class(field));
|
149
|
+
}
|
150
|
+
return Qnil;
|
151
|
+
}
|
152
|
+
|
153
|
+
static int extract_method_call(VALUE method_name, MessageHeader* self,
|
154
|
+
const upb_fielddef **f, const upb_oneofdef **o) {
|
155
|
+
Check_Type(method_name, T_SYMBOL);
|
156
|
+
|
157
|
+
VALUE method_str = rb_id2str(SYM2ID(method_name));
|
158
|
+
char* name = RSTRING_PTR(method_str);
|
159
|
+
size_t name_len = RSTRING_LEN(method_str);
|
160
|
+
int accessor_type;
|
161
|
+
const upb_oneofdef* test_o;
|
162
|
+
const upb_fielddef* test_f;
|
163
|
+
|
164
|
+
if (name[name_len - 1] == '=') {
|
165
|
+
accessor_type = METHOD_SETTER;
|
166
|
+
name_len--;
|
167
|
+
// We want to ensure if the proto has something named clear_foo or has_foo?,
|
168
|
+
// we don't strip the prefix.
|
169
|
+
} else if (strncmp("clear_", name, 6) == 0 &&
|
170
|
+
!upb_msgdef_lookupname(self->descriptor->msgdef, name, name_len,
|
171
|
+
&test_f, &test_o)) {
|
172
|
+
accessor_type = METHOD_CLEAR;
|
173
|
+
name = name + 6;
|
174
|
+
name_len = name_len - 6;
|
175
|
+
} else if (strncmp("has_", name, 4) == 0 && name[name_len - 1] == '?' &&
|
176
|
+
!upb_msgdef_lookupname(self->descriptor->msgdef, name, name_len,
|
177
|
+
&test_f, &test_o)) {
|
178
|
+
accessor_type = METHOD_PRESENCE;
|
179
|
+
name = name + 4;
|
180
|
+
name_len = name_len - 5;
|
181
|
+
} else {
|
182
|
+
accessor_type = METHOD_GETTER;
|
183
|
+
}
|
184
|
+
|
185
|
+
bool has_field = upb_msgdef_lookupname(self->descriptor->msgdef, name, name_len,
|
186
|
+
&test_f, &test_o);
|
187
|
+
|
188
|
+
// Look for wrapper type accessor of the form <field_name>_as_value
|
189
|
+
if (!has_field &&
|
190
|
+
(accessor_type == METHOD_GETTER || accessor_type == METHOD_SETTER) &&
|
191
|
+
name_len > 9 && strncmp(name + name_len - 9, "_as_value", 9) == 0) {
|
192
|
+
// Find the field name
|
193
|
+
char wrapper_field_name[name_len - 8];
|
194
|
+
strncpy(wrapper_field_name, name, name_len - 9);
|
195
|
+
wrapper_field_name[name_len - 7] = '\0';
|
196
|
+
|
197
|
+
// Check if field exists and is a wrapper type
|
198
|
+
const upb_oneofdef* test_o_wrapper;
|
199
|
+
const upb_fielddef* test_f_wrapper;
|
200
|
+
if (upb_msgdef_lookupname(self->descriptor->msgdef, wrapper_field_name, name_len - 9,
|
201
|
+
&test_f_wrapper, &test_o_wrapper) &&
|
202
|
+
upb_fielddef_type(test_f_wrapper) == UPB_TYPE_MESSAGE &&
|
203
|
+
is_wrapper_type_field(test_f_wrapper)) {
|
204
|
+
// It does exist!
|
205
|
+
has_field = true;
|
206
|
+
if (accessor_type == METHOD_SETTER) {
|
207
|
+
accessor_type = METHOD_WRAPPER_SETTER;
|
208
|
+
} else {
|
209
|
+
accessor_type = METHOD_WRAPPER_GETTER;
|
210
|
+
}
|
211
|
+
test_o = test_o_wrapper;
|
212
|
+
test_f = test_f_wrapper;
|
213
|
+
}
|
214
|
+
}
|
215
|
+
|
216
|
+
// Look for enum accessor of the form <enum_name>_const
|
217
|
+
if (!has_field && accessor_type == METHOD_GETTER &&
|
218
|
+
name_len > 6 && strncmp(name + name_len - 6, "_const", 6) == 0) {
|
219
|
+
|
220
|
+
// Find enum field name
|
221
|
+
char enum_name[name_len - 5];
|
222
|
+
strncpy(enum_name, name, name_len - 6);
|
223
|
+
enum_name[name_len - 4] = '\0';
|
224
|
+
|
225
|
+
// Check if enum field exists
|
226
|
+
const upb_oneofdef* test_o_enum;
|
227
|
+
const upb_fielddef* test_f_enum;
|
228
|
+
if (upb_msgdef_lookupname(self->descriptor->msgdef, enum_name, name_len - 6,
|
229
|
+
&test_f_enum, &test_o_enum) &&
|
230
|
+
upb_fielddef_type(test_f_enum) == UPB_TYPE_ENUM) {
|
231
|
+
// It does exist!
|
232
|
+
has_field = true;
|
233
|
+
accessor_type = METHOD_ENUM_GETTER;
|
234
|
+
test_o = test_o_enum;
|
235
|
+
test_f = test_f_enum;
|
236
|
+
}
|
237
|
+
}
|
238
|
+
|
239
|
+
// Verify the name corresponds to a oneof or field in this message.
|
240
|
+
if (!has_field) {
|
241
|
+
return METHOD_UNKNOWN;
|
242
|
+
}
|
243
|
+
|
244
|
+
// Method calls like 'has_foo?' are not allowed if field "foo" does not have
|
245
|
+
// a hasbit (e.g. repeated fields or non-message type fields for proto3
|
246
|
+
// syntax).
|
247
|
+
if (accessor_type == METHOD_PRESENCE && test_f != NULL &&
|
248
|
+
!upb_fielddef_haspresence(test_f)) {
|
249
|
+
return METHOD_UNKNOWN;
|
250
|
+
}
|
251
|
+
|
252
|
+
*o = test_o;
|
253
|
+
*f = test_f;
|
254
|
+
return accessor_type;
|
107
255
|
}
|
108
256
|
|
109
257
|
/*
|
110
258
|
* call-seq:
|
111
259
|
* Message.method_missing(*args)
|
112
260
|
*
|
113
|
-
* Provides accessors and setters
|
114
|
-
*
|
261
|
+
* Provides accessors and setters and methods to clear and check for presence of
|
262
|
+
* message fields according to their field names.
|
263
|
+
*
|
264
|
+
* For any field whose name does not conflict with a built-in method, an
|
115
265
|
* accessor is provided with the same name as the field, and a setter is
|
116
266
|
* provided with the name of the field plus the '=' suffix. Thus, given a
|
117
267
|
* message instance 'msg' with field 'foo', the following code is valid:
|
@@ -122,13 +272,17 @@ static VALUE which_oneof_field(MessageHeader* self, const upb_oneofdef* o) {
|
|
122
272
|
* This method also provides read-only accessors for oneofs. If a oneof exists
|
123
273
|
* with name 'my_oneof', then msg.my_oneof will return a Ruby symbol equal to
|
124
274
|
* the name of the field in that oneof that is currently set, or nil if none.
|
275
|
+
*
|
276
|
+
* It also provides methods of the form 'clear_fieldname' to clear the value
|
277
|
+
* of the field 'fieldname'. For basic data types, this will set the default
|
278
|
+
* value of the field.
|
279
|
+
*
|
280
|
+
* Additionally, it provides methods of the form 'has_fieldname?', which returns
|
281
|
+
* true if the field 'fieldname' is set in the message object, else false. For
|
282
|
+
* 'proto3' syntax, calling this for a basic type field will result in an error.
|
125
283
|
*/
|
126
284
|
VALUE Message_method_missing(int argc, VALUE* argv, VALUE _self) {
|
127
285
|
MessageHeader* self;
|
128
|
-
VALUE method_name, method_str;
|
129
|
-
char* name;
|
130
|
-
size_t name_len;
|
131
|
-
bool setter;
|
132
286
|
const upb_oneofdef* o;
|
133
287
|
const upb_fielddef* f;
|
134
288
|
|
@@ -136,54 +290,84 @@ VALUE Message_method_missing(int argc, VALUE* argv, VALUE _self) {
|
|
136
290
|
if (argc < 1) {
|
137
291
|
rb_raise(rb_eArgError, "Expected method name as first argument.");
|
138
292
|
}
|
139
|
-
method_name = argv[0];
|
140
|
-
if (!SYMBOL_P(method_name)) {
|
141
|
-
rb_raise(rb_eArgError, "Expected symbol as method name.");
|
142
|
-
}
|
143
|
-
method_str = rb_id2str(SYM2ID(method_name));
|
144
|
-
name = RSTRING_PTR(method_str);
|
145
|
-
name_len = RSTRING_LEN(method_str);
|
146
|
-
setter = false;
|
147
293
|
|
148
|
-
|
149
|
-
if (
|
150
|
-
setter = true;
|
151
|
-
name_len--;
|
152
|
-
}
|
153
|
-
|
154
|
-
// See if this name corresponds to either a oneof or field in this message.
|
155
|
-
if (!upb_msgdef_lookupname(self->descriptor->msgdef, name, name_len, &f,
|
156
|
-
&o)) {
|
294
|
+
int accessor_type = extract_method_call(argv[0], self, &f, &o);
|
295
|
+
if (accessor_type == METHOD_UNKNOWN || (o == NULL && f == NULL) ) {
|
157
296
|
return rb_call_super(argc, argv);
|
297
|
+
} else if (accessor_type == METHOD_SETTER || accessor_type == METHOD_WRAPPER_SETTER) {
|
298
|
+
if (argc != 2) {
|
299
|
+
rb_raise(rb_eArgError, "Expected 2 arguments, received %d", argc);
|
300
|
+
}
|
301
|
+
rb_check_frozen(_self);
|
302
|
+
} else if (argc != 1) {
|
303
|
+
rb_raise(rb_eArgError, "Expected 1 argument, received %d", argc);
|
158
304
|
}
|
159
305
|
|
306
|
+
// Return which of the oneof fields are set
|
160
307
|
if (o != NULL) {
|
161
|
-
|
162
|
-
if (setter) {
|
308
|
+
if (accessor_type == METHOD_SETTER) {
|
163
309
|
rb_raise(rb_eRuntimeError, "Oneof accessors are read-only.");
|
164
310
|
}
|
165
|
-
|
166
|
-
|
167
|
-
|
168
|
-
|
169
|
-
if (
|
170
|
-
if (
|
171
|
-
|
311
|
+
|
312
|
+
const upb_fielddef* oneof_field = which_oneof_field(self, o);
|
313
|
+
if (accessor_type == METHOD_PRESENCE) {
|
314
|
+
return oneof_field == NULL ? Qfalse : Qtrue;
|
315
|
+
} else if (accessor_type == METHOD_CLEAR) {
|
316
|
+
if (oneof_field != NULL) {
|
317
|
+
layout_clear(self->descriptor->layout, Message_data(self), oneof_field);
|
172
318
|
}
|
173
|
-
layout_set(self->descriptor->layout, Message_data(self), f, argv[1]);
|
174
319
|
return Qnil;
|
175
320
|
} else {
|
176
|
-
|
321
|
+
// METHOD_ACCESSOR
|
322
|
+
return oneof_field == NULL ? Qnil :
|
323
|
+
ID2SYM(rb_intern(upb_fielddef_name(oneof_field)));
|
324
|
+
}
|
325
|
+
// Otherwise we're operating on a single proto field
|
326
|
+
} else if (accessor_type == METHOD_SETTER) {
|
327
|
+
layout_set(self->descriptor->layout, Message_data(self), f, argv[1]);
|
328
|
+
return Qnil;
|
329
|
+
} else if (accessor_type == METHOD_CLEAR) {
|
330
|
+
layout_clear(self->descriptor->layout, Message_data(self), f);
|
331
|
+
return Qnil;
|
332
|
+
} else if (accessor_type == METHOD_PRESENCE) {
|
333
|
+
return layout_has(self->descriptor->layout, Message_data(self), f);
|
334
|
+
} else if (accessor_type == METHOD_WRAPPER_GETTER) {
|
335
|
+
VALUE value = layout_get(self->descriptor->layout, Message_data(self), f);
|
336
|
+
if (value != Qnil) {
|
337
|
+
value = rb_funcall(value, rb_intern("value"), 0);
|
177
338
|
}
|
339
|
+
return value;
|
340
|
+
} else if (accessor_type == METHOD_WRAPPER_SETTER) {
|
341
|
+
VALUE wrapper = ruby_wrapper_type(f, argv[1]);
|
342
|
+
layout_set(self->descriptor->layout, Message_data(self), f, wrapper);
|
343
|
+
return Qnil;
|
344
|
+
} else if (accessor_type == METHOD_ENUM_GETTER) {
|
345
|
+
VALUE enum_type = field_type_class(f);
|
346
|
+
VALUE method = rb_intern("const_get");
|
347
|
+
VALUE raw_value = layout_get(self->descriptor->layout, Message_data(self), f);
|
348
|
+
|
349
|
+
// Map repeated fields to a new type with ints
|
350
|
+
if (upb_fielddef_label(f) == UPB_LABEL_REPEATED) {
|
351
|
+
int array_size = FIX2INT(rb_funcall(raw_value, rb_intern("length"), 0));
|
352
|
+
VALUE array_args[1] = { ID2SYM(rb_intern("int64")) };
|
353
|
+
VALUE array = rb_class_new_instance(1, array_args, CLASS_OF(raw_value));
|
354
|
+
for (int i = 0; i < array_size; i++) {
|
355
|
+
VALUE entry = rb_funcall(enum_type, method, 1, rb_funcall(raw_value,
|
356
|
+
rb_intern("at"), 1, INT2NUM(i)));
|
357
|
+
rb_funcall(array, rb_intern("push"), 1, entry);
|
358
|
+
}
|
359
|
+
return array;
|
360
|
+
}
|
361
|
+
// Convert the value for singular fields
|
362
|
+
return rb_funcall(enum_type, method, 1, raw_value);
|
363
|
+
} else {
|
364
|
+
return layout_get(self->descriptor->layout, Message_data(self), f);
|
178
365
|
}
|
179
366
|
}
|
180
367
|
|
368
|
+
|
181
369
|
VALUE Message_respond_to_missing(int argc, VALUE* argv, VALUE _self) {
|
182
370
|
MessageHeader* self;
|
183
|
-
VALUE method_name, method_str;
|
184
|
-
char* name;
|
185
|
-
size_t name_len;
|
186
|
-
bool setter;
|
187
371
|
const upb_oneofdef* o;
|
188
372
|
const upb_fielddef* f;
|
189
373
|
|
@@ -191,58 +375,60 @@ VALUE Message_respond_to_missing(int argc, VALUE* argv, VALUE _self) {
|
|
191
375
|
if (argc < 1) {
|
192
376
|
rb_raise(rb_eArgError, "Expected method name as first argument.");
|
193
377
|
}
|
194
|
-
method_name = argv[0];
|
195
|
-
if (!SYMBOL_P(method_name)) {
|
196
|
-
rb_raise(rb_eArgError, "Expected symbol as method name.");
|
197
|
-
}
|
198
|
-
method_str = rb_id2str(SYM2ID(method_name));
|
199
|
-
name = RSTRING_PTR(method_str);
|
200
|
-
name_len = RSTRING_LEN(method_str);
|
201
|
-
setter = false;
|
202
378
|
|
203
|
-
|
204
|
-
if (
|
205
|
-
setter = true;
|
206
|
-
name_len--;
|
207
|
-
}
|
208
|
-
|
209
|
-
// See if this name corresponds to either a oneof or field in this message.
|
210
|
-
if (!upb_msgdef_lookupname(self->descriptor->msgdef, name, name_len, &f,
|
211
|
-
&o)) {
|
379
|
+
int accessor_type = extract_method_call(argv[0], self, &f, &o);
|
380
|
+
if (accessor_type == METHOD_UNKNOWN) {
|
212
381
|
return rb_call_super(argc, argv);
|
382
|
+
} else if (o != NULL) {
|
383
|
+
return accessor_type == METHOD_SETTER ? Qfalse : Qtrue;
|
384
|
+
} else {
|
385
|
+
return Qtrue;
|
213
386
|
}
|
214
|
-
|
215
|
-
|
216
|
-
|
217
|
-
|
387
|
+
}
|
388
|
+
|
389
|
+
VALUE create_submsg_from_hash(const upb_fielddef *f, VALUE hash) {
|
390
|
+
const upb_def *d = upb_fielddef_subdef(f);
|
391
|
+
assert(d != NULL);
|
392
|
+
|
393
|
+
VALUE descriptor = get_def_obj(d);
|
394
|
+
VALUE msgclass = rb_funcall(descriptor, rb_intern("msgclass"), 0, NULL);
|
395
|
+
|
396
|
+
VALUE args[1] = { hash };
|
397
|
+
return rb_class_new_instance(1, args, msgclass);
|
218
398
|
}
|
219
399
|
|
220
400
|
int Message_initialize_kwarg(VALUE key, VALUE val, VALUE _self) {
|
221
401
|
MessageHeader* self;
|
222
|
-
|
223
|
-
char* name;
|
402
|
+
char *name;
|
224
403
|
const upb_fielddef* f;
|
225
404
|
TypedData_Get_Struct(_self, MessageHeader, &Message_type, self);
|
226
405
|
|
227
|
-
if (
|
406
|
+
if (TYPE(key) == T_STRING) {
|
407
|
+
name = RSTRING_PTR(key);
|
408
|
+
} else if (TYPE(key) == T_SYMBOL) {
|
409
|
+
name = RSTRING_PTR(rb_id2str(SYM2ID(key)));
|
410
|
+
} else {
|
228
411
|
rb_raise(rb_eArgError,
|
229
|
-
"Expected symbols as hash keys
|
412
|
+
"Expected string or symbols as hash keys when initializing proto from hash.");
|
230
413
|
}
|
231
414
|
|
232
|
-
method_str = rb_id2str(SYM2ID(key));
|
233
|
-
name = RSTRING_PTR(method_str);
|
234
415
|
f = upb_msgdef_ntofz(self->descriptor->msgdef, name);
|
235
416
|
if (f == NULL) {
|
236
417
|
rb_raise(rb_eArgError,
|
237
418
|
"Unknown field name '%s' in initialization map entry.", name);
|
238
419
|
}
|
239
420
|
|
421
|
+
if (TYPE(val) == T_NIL) {
|
422
|
+
return 0;
|
423
|
+
}
|
424
|
+
|
240
425
|
if (is_map_field(f)) {
|
241
426
|
VALUE map;
|
242
427
|
|
243
428
|
if (TYPE(val) != T_HASH) {
|
244
429
|
rb_raise(rb_eArgError,
|
245
|
-
"Expected Hash object as initializer value for map field '%s'.",
|
430
|
+
"Expected Hash object as initializer value for map field '%s' (given %s).",
|
431
|
+
name, rb_class2name(CLASS_OF(val)));
|
246
432
|
}
|
247
433
|
map = layout_get(self->descriptor->layout, Message_data(self), f);
|
248
434
|
Map_merge_into_self(map, val);
|
@@ -251,13 +437,23 @@ int Message_initialize_kwarg(VALUE key, VALUE val, VALUE _self) {
|
|
251
437
|
|
252
438
|
if (TYPE(val) != T_ARRAY) {
|
253
439
|
rb_raise(rb_eArgError,
|
254
|
-
"Expected array as initializer value for repeated field '%s'.",
|
440
|
+
"Expected array as initializer value for repeated field '%s' (given %s).",
|
441
|
+
name, rb_class2name(CLASS_OF(val)));
|
255
442
|
}
|
256
443
|
ary = layout_get(self->descriptor->layout, Message_data(self), f);
|
257
444
|
for (int i = 0; i < RARRAY_LEN(val); i++) {
|
258
|
-
|
445
|
+
VALUE entry = rb_ary_entry(val, i);
|
446
|
+
if (TYPE(entry) == T_HASH && upb_fielddef_issubmsg(f)) {
|
447
|
+
entry = create_submsg_from_hash(f, entry);
|
448
|
+
}
|
449
|
+
|
450
|
+
RepeatedField_push(ary, entry);
|
259
451
|
}
|
260
452
|
} else {
|
453
|
+
if (TYPE(val) == T_HASH && upb_fielddef_issubmsg(f)) {
|
454
|
+
val = create_submsg_from_hash(f, val);
|
455
|
+
}
|
456
|
+
|
261
457
|
layout_set(self->descriptor->layout, Message_data(self), f, val);
|
262
458
|
}
|
263
459
|
return 0;
|
@@ -394,7 +590,12 @@ VALUE Message_inspect(VALUE _self) {
|
|
394
590
|
return str;
|
395
591
|
}
|
396
592
|
|
397
|
-
|
593
|
+
/*
|
594
|
+
* call-seq:
|
595
|
+
* Message.to_h => {}
|
596
|
+
*
|
597
|
+
* Returns the message as a Ruby Hash object, with keys as symbols.
|
598
|
+
*/
|
398
599
|
VALUE Message_to_h(VALUE _self) {
|
399
600
|
MessageHeader* self;
|
400
601
|
VALUE hash;
|
@@ -407,11 +608,36 @@ VALUE Message_to_h(VALUE _self) {
|
|
407
608
|
!upb_msg_field_done(&it);
|
408
609
|
upb_msg_field_next(&it)) {
|
409
610
|
const upb_fielddef* field = upb_msg_iter_field(&it);
|
611
|
+
|
612
|
+
// For proto2, do not include fields which are not set.
|
613
|
+
if (upb_msgdef_syntax(self->descriptor->msgdef) == UPB_SYNTAX_PROTO2 &&
|
614
|
+
field_contains_hasbit(self->descriptor->layout, field) &&
|
615
|
+
!layout_has(self->descriptor->layout, Message_data(self), field)) {
|
616
|
+
continue;
|
617
|
+
}
|
618
|
+
|
410
619
|
VALUE msg_value = layout_get(self->descriptor->layout, Message_data(self),
|
411
620
|
field);
|
412
621
|
VALUE msg_key = ID2SYM(rb_intern(upb_fielddef_name(field)));
|
413
|
-
if (
|
622
|
+
if (is_map_field(field)) {
|
623
|
+
msg_value = Map_to_h(msg_value);
|
624
|
+
} else if (upb_fielddef_label(field) == UPB_LABEL_REPEATED) {
|
414
625
|
msg_value = RepeatedField_to_ary(msg_value);
|
626
|
+
if (upb_msgdef_syntax(self->descriptor->msgdef) == UPB_SYNTAX_PROTO2 &&
|
627
|
+
RARRAY_LEN(msg_value) == 0) {
|
628
|
+
continue;
|
629
|
+
}
|
630
|
+
|
631
|
+
if (upb_fielddef_type(field) == UPB_TYPE_MESSAGE) {
|
632
|
+
for (int i = 0; i < RARRAY_LEN(msg_value); i++) {
|
633
|
+
VALUE elem = rb_ary_entry(msg_value, i);
|
634
|
+
rb_ary_store(msg_value, i, Message_to_h(elem));
|
635
|
+
}
|
636
|
+
}
|
637
|
+
|
638
|
+
} else if (msg_value != Qnil &&
|
639
|
+
upb_fielddef_type(field) == UPB_TYPE_MESSAGE) {
|
640
|
+
msg_value = Message_to_h(msg_value);
|
415
641
|
}
|
416
642
|
rb_hash_aset(hash, msg_key, msg_value);
|
417
643
|
}
|
@@ -495,9 +721,9 @@ VALUE build_class_from_descriptor(Descriptor* desc) {
|
|
495
721
|
get_def_obj(desc->msgdef));
|
496
722
|
rb_define_alloc_func(klass, Message_alloc);
|
497
723
|
rb_require("google/protobuf/message_exts");
|
498
|
-
rb_include_module(klass, rb_eval_string("Google::Protobuf::MessageExts"));
|
724
|
+
rb_include_module(klass, rb_eval_string("::Google::Protobuf::MessageExts"));
|
499
725
|
rb_extend_object(
|
500
|
-
klass, rb_eval_string("Google::Protobuf::MessageExts::ClassMethods"));
|
726
|
+
klass, rb_eval_string("::Google::Protobuf::MessageExts::ClassMethods"));
|
501
727
|
|
502
728
|
rb_define_method(klass, "method_missing",
|
503
729
|
Message_method_missing, -1);
|
@@ -508,15 +734,16 @@ VALUE build_class_from_descriptor(Descriptor* desc) {
|
|
508
734
|
// Also define #clone so that we don't inherit Object#clone.
|
509
735
|
rb_define_method(klass, "clone", Message_dup, 0);
|
510
736
|
rb_define_method(klass, "==", Message_eq, 1);
|
737
|
+
rb_define_method(klass, "eql?", Message_eq, 1);
|
511
738
|
rb_define_method(klass, "hash", Message_hash, 0);
|
512
739
|
rb_define_method(klass, "to_h", Message_to_h, 0);
|
513
|
-
rb_define_method(klass, "to_hash", Message_to_h, 0);
|
514
740
|
rb_define_method(klass, "inspect", Message_inspect, 0);
|
741
|
+
rb_define_method(klass, "to_s", Message_inspect, 0);
|
515
742
|
rb_define_method(klass, "[]", Message_index, 1);
|
516
743
|
rb_define_method(klass, "[]=", Message_index_set, 2);
|
517
744
|
rb_define_singleton_method(klass, "decode", Message_decode, 1);
|
518
745
|
rb_define_singleton_method(klass, "encode", Message_encode, 1);
|
519
|
-
rb_define_singleton_method(klass, "decode_json", Message_decode_json, 1);
|
746
|
+
rb_define_singleton_method(klass, "decode_json", Message_decode_json, -1);
|
520
747
|
rb_define_singleton_method(klass, "encode_json", Message_encode_json, -1);
|
521
748
|
rb_define_singleton_method(klass, "descriptor", Message_descriptor, 0);
|
522
749
|
|
@@ -586,10 +813,9 @@ VALUE build_module_from_enumdesc(EnumDescriptor* enumdesc) {
|
|
586
813
|
const char* name = upb_enum_iter_name(&it);
|
587
814
|
int32_t value = upb_enum_iter_number(&it);
|
588
815
|
if (name[0] < 'A' || name[0] > 'Z') {
|
589
|
-
|
590
|
-
|
591
|
-
|
592
|
-
name);
|
816
|
+
rb_warn("Enum value '%s' does not start with an uppercase letter "
|
817
|
+
"as is required for Ruby constants.",
|
818
|
+
name);
|
593
819
|
}
|
594
820
|
rb_define_const(mod, name, INT2NUM(value));
|
595
821
|
}
|