Browse Source

Merge pull request #3342 from FrancisTheCat/master

Json: Improved unmarshalling of `using _: T` fields
gingerBill 1 year ago
parent
commit
58d0635f48
1 changed files with 41 additions and 9 deletions
  1. 41 9
      core/encoding/json/unmarshal.odin

+ 41 - 9
core/encoding/json/unmarshal.odin

@@ -370,13 +370,13 @@ unmarshal_object :: proc(p: ^Parser, v: any, end_token: Token_Kind) -> (err: Unm
 			
 			fields := reflect.struct_fields_zipped(ti.id)
 
-			field_test :: #force_inline proc "contextless" (field_used: [^]byte, index: int) -> bool {
-				prev_set := field_used[index/8] & byte(index&7) != 0
-				field_used[index/8] |= byte(index&7)
+			field_test :: #force_inline proc "contextless" (field_used: [^]byte, offset: uintptr) -> bool {
+				prev_set := field_used[offset/8] & byte(offset&7) != 0
+				field_used[offset/8] |= byte(offset&7)
 				return prev_set
 			}
 
-			field_used_bytes := (len(fields)+7)/8
+			field_used_bytes := (reflect.size_of_typeid(ti.id)+7)/8
 			field_used := intrinsics.alloca(field_used_bytes, 1)
 			intrinsics.mem_zero(field_used, field_used_bytes)
 
@@ -399,13 +399,45 @@ unmarshal_object :: proc(p: ^Parser, v: any, end_token: Token_Kind) -> (err: Unm
 				}
 			}
 			
-			if use_field_idx >= 0 {
-				if field_test(field_used, use_field_idx) {
+			check_children_using_fields :: proc(key: string, parent: typeid) -> (
+				offset: uintptr,
+				type: ^reflect.Type_Info,
+				found: bool,
+			) {
+				for field in reflect.struct_fields_zipped(parent) {
+					if field.is_using && field.name == "_" {
+						offset, type, found = check_children_using_fields(key, field.type.id)
+						if found {
+							offset += field.offset
+							return
+						}
+					}
+
+					if field.name == key {
+						offset = field.offset
+						type = field.type
+						found = true
+						return
+					}
+				}
+				return
+			}
+
+			offset: uintptr
+			type: ^reflect.Type_Info
+			field_found: bool = use_field_idx >= 0
+
+			if field_found {
+				offset = fields[use_field_idx].offset
+				type = fields[use_field_idx].type
+			} else {
+				offset, type, field_found = check_children_using_fields(key, ti.id)
+			}
+
+			if field_found {
+				if field_test(field_used, offset) {
 					return .Multiple_Use_Field
 				}
-				offset := fields[use_field_idx].offset
-				type := fields[use_field_idx].type
-				name := fields[use_field_idx].name
 				
 				field_ptr := rawptr(uintptr(v.data) + offset)
 				field := any{field_ptr, type.id}