aboutsummaryrefslogtreecommitdiff
path: root/ruby/ext/google/protobuf_c/encode_decode.c
diff options
context:
space:
mode:
Diffstat (limited to 'ruby/ext/google/protobuf_c/encode_decode.c')
-rw-r--r--ruby/ext/google/protobuf_c/encode_decode.c13
1 files changed, 8 insertions, 5 deletions
diff --git a/ruby/ext/google/protobuf_c/encode_decode.c b/ruby/ext/google/protobuf_c/encode_decode.c
index 3a441be3..b9ecd456 100644
--- a/ruby/ext/google/protobuf_c/encode_decode.c
+++ b/ruby/ext/google/protobuf_c/encode_decode.c
@@ -349,8 +349,6 @@ static void *oneofsubmsg_handler(void *closure,
MessageHeader* msg = closure;
const oneof_handlerdata_t *oneofdata = hd;
uint32_t oldcase = DEREF(msg, oneofdata->case_ofs, uint32_t);
- DEREF(msg, oneofdata->case_ofs, uint32_t) =
- oneofdata->oneof_case_num;
VALUE subdesc =
get_def_obj((void*)oneofdata->md);
@@ -361,6 +359,11 @@ static void *oneofsubmsg_handler(void *closure,
DEREF(msg, oneofdata->ofs, VALUE) =
rb_class_new_instance(0, NULL, subklass);
}
+ // Set the oneof case *after* allocating the new class instance -- see comment
+ // in layout_set() as to why. There are subtle interactions with possible GC
+ // points and oneof field type transitions.
+ DEREF(msg, oneofdata->case_ofs, uint32_t) =
+ oneofdata->oneof_case_num;
VALUE submsg_rb = DEREF(msg, oneofdata->ofs, VALUE);
MessageHeader* submsg;
@@ -965,11 +968,11 @@ static void putmsg(VALUE msg_rb, const Descriptor* desc,
uint32_t offset =
desc->layout->fields[upb_fielddef_index(f)].offset +
sizeof(MessageHeader);
- uint32_t oneof_case_offset =
- desc->layout->fields[upb_fielddef_index(f)].case_offset +
- sizeof(MessageHeader);
if (upb_fielddef_containingoneof(f)) {
+ uint32_t oneof_case_offset =
+ desc->layout->fields[upb_fielddef_index(f)].case_offset +
+ sizeof(MessageHeader);
// For a oneof, check that this field is actually present -- skip all the
// below if not.
if (DEREF(msg, oneof_case_offset, uint32_t) !=