in debugger/debugger-worker/src/Values/Render/ChildrenRenderers/SerialisedPropertyChildrenRenderer.cs [71:160]
private IEnumerable<IValueEntity> GetChildrenImpl(IObjectValueRole<TValue> valueRole,
IMetadataTypeLite instanceType,
IPresentationOptions options,
IUserDataHolder dataHolder,
CancellationToken token)
{
var enumValueObject = valueRole.GetInstancePropertyReference("propertyType")
?.AsObjectSafe(options)?.GetEnumValue(options);
var propertyType = (SerializedPropertyKind) Enum.ToObject(typeof(SerializedPropertyKind),
enumValueObject ?? SerializedPropertyKind.Invalid);
// Fall back to showing everything if we don't have any special handling for the property type. This should
// protect us if Unity introduces a new serialised property type.
if (!myHandledPropertyTypes.Contains(propertyType))
{
foreach (var valueEntity in base.GetChildren(valueRole, instanceType, options, dataHolder, token))
yield return valueEntity;
yield break;
}
var propertySpecificFields = myPerTypeFieldNames[propertyType];
var isArray = false;
var isFixedBuffer = false;
// Generic means a custom serializable struct, an array or a fixed buffer (public unsafe fixed int buf[10])
// Strings are also arrays, and have properties for each character. We'll show them too.
if (propertyType == SerializedPropertyKind.Generic || propertyType == SerializedPropertyKind.String)
{
if (!Util.TryEvaluatePrimitiveProperty(valueRole, "isArray", options, out isArray) || !isArray)
Util.TryEvaluatePrimitiveProperty(valueRole, "isFixedBuffer", options, out isFixedBuffer);
}
// We filter all non-public members, so make sure we don't show the group
var effectiveOptions = options.WithOverridden(o => o.GroupPrivateMembers = false);
var references = EnumerateChildren(valueRole, effectiveOptions, token);
references = FilterChildren(references, propertySpecificFields, isArray, isFixedBuffer);
references = DecorateChildren(valueRole, references, propertyType, options);
references = SortChildren(references);
foreach (var valueEntity in RenderChildren(valueRole, references, effectiveOptions, token))
yield return valueEntity;
if (!Util.TryEvaluatePrimitiveProperty(valueRole, "hasChildren", options, out bool hasChildren))
Logger.Warn("Cannot evaluate hasChildren for serializedProperty");
else if (hasChildren)
{
// Arrays, fixed buffer arrays and strings (which are arrays) all say they have children. They don't.
// They have a special sibling (i.e. same depth) that can only be enumerated with Next(true), and is
// skipped with Next(false).
// We don't show these special siblings, because they're not children, and the data is already displayed
// as part of the array/fixed buffer element group.
// TODO: Should we show this?
// It might be confusing if these special properties are never shown, especially if someone is using
// the debugger to try to figure out the shape of the serialised stream. The question is, how do we show
// something that is enumerated as a child, but stored as a sibling? The best solution is a dump of the
// whole serialised stream, which is not what we're doing as part of the debugger. One solution would be
// to add an "Array" node that is shown instead of "Children" - but that would still look like a child
// node, and would only include the "Size" node over the existing array/fixed buffer element group.
// For now, just ignore these special properties and leave it to the array/fixed buffer element group.
if (!isArray && !isFixedBuffer && propertyType != SerializedPropertyKind.String)
yield return new ChildrenGroup(valueRole, ValueServices, Logger);
}
if (isArray)
{
if (!Util.TryEvaluatePrimitiveProperty(valueRole, "arraySize", options, out int arraySize))
Logger.Warn("Cannot evaluate arraySize for serializedProperty");
else if (arraySize > 0)
{
yield return new ArrayElementsGroup(valueRole, arraySize,
MethodSelectors.SerializedProperty_GetArrayElementAtIndex, ValueServices, Logger);
}
}
else if (isFixedBuffer)
{
if (!Util.TryEvaluatePrimitiveProperty(valueRole, "fixedBufferSize", options, out int fixedBufferSize))
Logger.Warn("Cannot evaluate fixedBufferSize for serializedProperty");
else if (fixedBufferSize > 0)
{
yield return new ArrayElementsGroup(valueRole, fixedBufferSize,
MethodSelectors.SerializedProperty_GetFixedBufferElementAtIndex, ValueServices, Logger);
}
}
// Disable debugger type proxy options to avoid recursion. See IsApplicable.
var rawViewOptions = options.WithOverridden(o => o.EvaluateDebuggerTypeProxy = false);
yield return new SimpleEntityGroup(PresentationOptions.RawViewGroupName,
valueRole.ValueReference.ToValue(ValueServices).GetChildren(rawViewOptions, token));
}