|  | @@ -255,10 +255,54 @@ typedef struct {
 | 
	
		
			
				|  |  |  // value into the map.
 | 
	
		
			
				|  |  |  typedef struct {
 | 
	
		
			
				|  |  |    VALUE map;
 | 
	
		
			
				|  |  | +  const map_handlerdata_t* handlerdata;
 | 
	
		
			
				|  |  |    char key_storage[NATIVE_SLOT_MAX_SIZE];
 | 
	
		
			
				|  |  |    char value_storage[NATIVE_SLOT_MAX_SIZE];
 | 
	
		
			
				|  |  |  } map_parse_frame_t;
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  | +static void MapParseFrame_mark(void* _self) {
 | 
	
		
			
				|  |  | +  map_parse_frame_t* frame = _self;
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +  // This shouldn't strictly be necessary since this should be rooted by the
 | 
	
		
			
				|  |  | +  // message itself, but it can't hurt.
 | 
	
		
			
				|  |  | +  rb_gc_mark(frame->map);
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +  native_slot_mark(frame->handlerdata->key_field_type, &frame->key_storage);
 | 
	
		
			
				|  |  | +  native_slot_mark(frame->handlerdata->value_field_type, &frame->value_storage);
 | 
	
		
			
				|  |  | +}
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +void MapParseFrame_free(void* self) {
 | 
	
		
			
				|  |  | +  xfree(self);
 | 
	
		
			
				|  |  | +}
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +rb_data_type_t MapParseFrame_type = {
 | 
	
		
			
				|  |  | +  "MapParseFrame",
 | 
	
		
			
				|  |  | +  { MapParseFrame_mark, MapParseFrame_free, NULL },
 | 
	
		
			
				|  |  | +};
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +// Array of Ruby objects wrapping map_parse_frame_t.
 | 
	
		
			
				|  |  | +// We don't allow multiple concurrent decodes, so we assume that this global
 | 
	
		
			
				|  |  | +// variable is specific to the "current" decode.
 | 
	
		
			
				|  |  | +VALUE map_parse_frames;
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +static map_parse_frame_t* map_push_frame(VALUE map,
 | 
	
		
			
				|  |  | +                                         const map_handlerdata_t* handlerdata) {
 | 
	
		
			
				|  |  | +  map_parse_frame_t* frame = ALLOC(map_parse_frame_t);
 | 
	
		
			
				|  |  | +  frame->handlerdata = handlerdata;
 | 
	
		
			
				|  |  | +  frame->map = map;
 | 
	
		
			
				|  |  | +  native_slot_init(handlerdata->key_field_type, &frame->key_storage);
 | 
	
		
			
				|  |  | +  native_slot_init(handlerdata->value_field_type, &frame->value_storage);
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +  rb_ary_push(map_parse_frames,
 | 
	
		
			
				|  |  | +              TypedData_Wrap_Struct(rb_cObject, &MapParseFrame_type, frame));
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +  return frame;
 | 
	
		
			
				|  |  | +}
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +static void map_pop_frame() {
 | 
	
		
			
				|  |  | +  rb_ary_pop(map_parse_frames);
 | 
	
		
			
				|  |  | +}
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  |  // Handler to begin a map entry: allocates a temporary frame. This is the
 | 
	
		
			
				|  |  |  // 'startsubmsg' handler on the msgdef that contains the map field.
 | 
	
		
			
				|  |  |  static void *startmapentry_handler(void *closure, const void *hd) {
 | 
	
	
		
			
				|  | @@ -266,13 +310,7 @@ static void *startmapentry_handler(void *closure, const void *hd) {
 | 
	
		
			
				|  |  |    const map_handlerdata_t* mapdata = hd;
 | 
	
		
			
				|  |  |    VALUE map_rb = DEREF(msg, mapdata->ofs, VALUE);
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  | -  map_parse_frame_t* frame = ALLOC(map_parse_frame_t);
 | 
	
		
			
				|  |  | -  frame->map = map_rb;
 | 
	
		
			
				|  |  | -
 | 
	
		
			
				|  |  | -  native_slot_init(mapdata->key_field_type, &frame->key_storage);
 | 
	
		
			
				|  |  | -  native_slot_init(mapdata->value_field_type, &frame->value_storage);
 | 
	
		
			
				|  |  | -
 | 
	
		
			
				|  |  | -  return frame;
 | 
	
		
			
				|  |  | +  return map_push_frame(map_rb, mapdata);
 | 
	
		
			
				|  |  |  }
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  |  // Handler to end a map entry: inserts the value defined during the message into
 | 
	
	
		
			
				|  | @@ -298,7 +336,7 @@ static bool endmap_handler(void *closure, const void *hd, upb_status* s) {
 | 
	
		
			
				|  |  |        &frame->value_storage);
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  |    Map_index_set(frame->map, key, value);
 | 
	
		
			
				|  |  | -  xfree(frame);
 | 
	
		
			
				|  |  | +  map_pop_frame();
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  |    return true;
 | 
	
		
			
				|  |  |  }
 | 
	
	
		
			
				|  | @@ -737,6 +775,10 @@ VALUE Message_decode(VALUE klass, VALUE data) {
 | 
	
		
			
				|  |  |    msg_rb = rb_class_new_instance(0, NULL, msgklass);
 | 
	
		
			
				|  |  |    TypedData_Get_Struct(msg_rb, MessageHeader, &Message_type, msg);
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  | +  // We generally expect this to be clear already, but clear it in case parsing
 | 
	
		
			
				|  |  | +  // previously got interrupted somehow.
 | 
	
		
			
				|  |  | +  rb_ary_clear(map_parse_frames);
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  |    {
 | 
	
		
			
				|  |  |      const upb_pbdecodermethod* method = msgdef_decodermethod(desc);
 | 
	
		
			
				|  |  |      const upb_handlers* h = upb_pbdecodermethod_desthandlers(method);
 | 
	
	
		
			
				|  | @@ -781,6 +823,10 @@ VALUE Message_decode_json(VALUE klass, VALUE data) {
 | 
	
		
			
				|  |  |    msg_rb = rb_class_new_instance(0, NULL, msgklass);
 | 
	
		
			
				|  |  |    TypedData_Get_Struct(msg_rb, MessageHeader, &Message_type, msg);
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  | +  // We generally expect this to be clear already, but clear it in case parsing
 | 
	
		
			
				|  |  | +  // previously got interrupted somehow.
 | 
	
		
			
				|  |  | +  rb_ary_clear(map_parse_frames);
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  |    {
 | 
	
		
			
				|  |  |      const upb_json_parsermethod* method = msgdef_jsonparsermethod(desc);
 | 
	
		
			
				|  |  |      stackenv se;
 |