google-protobuf 3.2.0 → 3.9.1
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 +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
|
}
|