Terminate pointer-only descriptor traversal
This commit is contained in:
@@ -334,8 +334,21 @@ func prependIndex(index int, nested []int) []int {
|
||||
|
||||
type schemaPath struct {
|
||||
descriptor *typeDescriptor
|
||||
field *fieldDescriptor
|
||||
dynamic bool
|
||||
}
|
||||
|
||||
// terminalDescriptor follows a pointer-only descriptor chain without entering
|
||||
// any value. Recursive pointer aliases can refer back to an earlier descriptor,
|
||||
// so callers must handle a repeated descriptor as an unaddressable chain.
|
||||
func terminalDescriptor(descriptor *typeDescriptor) (*typeDescriptor, bool) {
|
||||
visited := make(map[*typeDescriptor]struct{})
|
||||
for descriptor != nil && descriptor.kind == descriptorPointer {
|
||||
if _, exists := visited[descriptor]; exists {
|
||||
return nil, false
|
||||
}
|
||||
visited[descriptor] = struct{}{}
|
||||
descriptor = descriptor.element
|
||||
}
|
||||
return descriptor, descriptor != nil
|
||||
}
|
||||
|
||||
func resolveSchemaPath(schema *schemaDescriptor, path Path) (schemaPath, bool) {
|
||||
@@ -343,10 +356,11 @@ func resolveSchemaPath(schema *schemaDescriptor, path Path) (schemaPath, bool) {
|
||||
return schemaPath{}, false
|
||||
}
|
||||
current := schema.root
|
||||
var field *fieldDescriptor
|
||||
for _, segment := range path.segments {
|
||||
for current.kind == descriptorPointer {
|
||||
current = current.element
|
||||
var ok bool
|
||||
current, ok = terminalDescriptor(current)
|
||||
if !ok {
|
||||
return schemaPath{}, false
|
||||
}
|
||||
switch current.kind {
|
||||
case descriptorStruct:
|
||||
@@ -355,12 +369,10 @@ func resolveSchemaPath(schema *schemaDescriptor, path Path) (schemaPath, bool) {
|
||||
}
|
||||
position, ok := current.fieldByName[segment.key]
|
||||
if ok {
|
||||
field = ¤t.fields[position]
|
||||
current = field.descriptor
|
||||
current = current.fields[position].descriptor
|
||||
continue
|
||||
}
|
||||
if current.inlineMap != nil {
|
||||
field = current.inlineMap
|
||||
current = current.inlineMap.descriptor.element
|
||||
continue
|
||||
}
|
||||
@@ -369,26 +381,20 @@ func resolveSchemaPath(schema *schemaDescriptor, path Path) (schemaPath, bool) {
|
||||
if segment.kind == pathSegmentIndex {
|
||||
return schemaPath{}, false
|
||||
}
|
||||
field = nil
|
||||
current = current.element
|
||||
case descriptorSlice, descriptorArray:
|
||||
index, ok := schemaIndex(segment)
|
||||
if !ok || (current.kind == descriptorArray && index >= current.length) {
|
||||
return schemaPath{}, false
|
||||
}
|
||||
field = nil
|
||||
current = current.element
|
||||
case descriptorDynamic:
|
||||
return schemaPath{descriptor: current, field: field, dynamic: true}, true
|
||||
return schemaPath{descriptor: current}, true
|
||||
default:
|
||||
return schemaPath{}, false
|
||||
}
|
||||
}
|
||||
dynamic := current
|
||||
for dynamic.kind == descriptorPointer {
|
||||
dynamic = dynamic.element
|
||||
}
|
||||
return schemaPath{descriptor: current, field: field, dynamic: dynamic.kind == descriptorDynamic}, true
|
||||
return schemaPath{descriptor: current}, true
|
||||
}
|
||||
|
||||
func schemaIndex(segment pathSegment) (int, bool) {
|
||||
|
||||
Reference in New Issue
Block a user