private IValuePresentation GetValuePresentation(IObjectValueRole <TValue> serializedPropertyRole, SerializedPropertyKind propertyType, IPresentationOptions options, out string extraDetail) { extraDetail = null; var valueProperty = GetValueFieldName(propertyType); var valueReference = valueProperty == null ? null : serializedPropertyRole.GetInstancePropertyReference(valueProperty); if (propertyType == SerializedPropertyKind.Enum) { extraDetail = SerializedPropertyHelper.GetEnumValueIndexAsEnumName(serializedPropertyRole, valueReference, options); } else if (propertyType == SerializedPropertyKind.Character) { extraDetail = SerializedPropertyHelper.GetIntValueAsPrintableChar(valueReference, options); } else if (propertyType == SerializedPropertyKind.Integer) { var type = serializedPropertyRole.GetInstancePropertyReference("type")?.AsStringSafe(options) ?.GetString(); if (type == "char") { extraDetail = SerializedPropertyHelper.GetIntValueAsPrintableChar(valueReference, options); } } return(valueReference?.ToValue(ValueServices)?.GetValuePresentation(options)); }
private IValueEntity GetGameObjectScenePath(IObjectValueRole <TValue> componentRole, IPresentationOptions options) { var gameObjectRole = Logger.CatchEvaluatorException <TValue, IObjectValueRole <TValue> >( () => componentRole.GetInstancePropertyReference("gameObject", true) ?.AsObjectSafe(options), exception => Logger.LogThrownUnityException(exception, componentRole.ValueReference.OriginatingFrame, ValueServices, options)); return(ScenePathValueHelper.GetScenePathValue(gameObjectRole, options, ValueServices, Logger)); }
private IValueEntity GetGameObjectScenePath(IObjectValueRole <TValue> componentRole, IPresentationOptions options) { var gameObjectRole = componentRole.GetInstancePropertyReference("gameObject", true)?.AsObjectSafe(options); if (gameObjectRole == null) { return(null); } return(ScenePathValueHelper.GetScenePathValue(gameObjectRole, options, ValueServices, myLogger)); }
protected override IEnumerable <IValueEntity> GetChildren(IObjectValueRole <TValue> valueRole, IMetadataTypeLite instanceType, IPresentationOptions options, IUserDataHolder dataHolder, CancellationToken token) { yield return(new GameObjectComponentsGroup(valueRole, ValueServices, myLogger)); // The children of the current GameObject (as seen in Unity's Hierarchy view) are actually the children of // GameObject.transform. This should never be null var transformProperty = valueRole.GetInstancePropertyReference("transform"); if (transformProperty != null) { yield return(new GameObjectChildrenGroup(transformProperty, ValueServices)); } }
public static string GetEnumValueIndexAsEnumName <TValue>(IObjectValueRole <TValue> serializedProperty, IValueReference <TValue> enumValueIndexReference, IPresentationOptions options) where TValue : class { var primitiveValue = enumValueIndexReference.AsPrimitiveSafe(options)?.GetPrimitive(); if (primitiveValue is int enumValueIndex && serializedProperty.GetInstancePropertyReference("enumNames")?.GetPrimaryRole(options) is IArrayValueRole <TValue> enumNamesArray) { return(enumNamesArray.GetElementReference(enumValueIndex).AsStringSafe(options)?.GetString()); } return(null); }
public static bool TryEvaluatePrimitiveProperty <TValue, TPrimitive>(IObjectValueRole <TValue> valueRole, string property, IValueFetchOptions options, out TPrimitive value) where TValue : class where TPrimitive : struct { value = default; var primitiveValueRole = valueRole.GetInstancePropertyReference(property)?.AsPrimitiveSafe(options); if (!(primitiveValueRole?.GetPrimitive() is TPrimitive primitive)) { return(false); } value = primitive; return(true); }
private IEnumerable <IValueEntity> GetChildrenImpl(IPresentationOptions options, CancellationToken token) { // The children of a GameObject (as seen in Unity's Hierarchy view) are actually the children of // gameObject.transform. This will never be null. var transformRole = myGameObjectRole.GetInstancePropertyReference("transform")?.AsObjectSafe(options); if (transformRole == null) { myLogger.Warn("Unable to retrieve GameObject.transform"); yield break; } var childCount = transformRole.GetInstancePropertyReference("childCount", true) ?.AsPrimitiveSafe(options)?.GetPrimitiveSafe <int>() ?? 0; if (childCount == 0) { myLogger.Trace("No child transform, or unable to fetch childCount"); yield break; } var transformType = transformRole.ReifiedType.MetadataType.FindTypeThroughHierarchy("UnityEngine.Transform"); myGetChildMethod = transformType?.GetMethods().FirstOrDefault(ourGetChildSelector); if (myGetChildMethod == null) { myLogger.Warn("Unable to find Transform.GetChild method"); yield break; } if (options.ClusterArrays) { foreach (var valueEntity in GetChunkedChildren(transformRole, 0, childCount, options, token)) { yield return(valueEntity); } } else { for (var i = 0; i < childCount; i++) { yield return(GetElementValueAt(transformRole, i, options)); } } }
public static IValueEntity GetScenePathValue <TValue>([CanBeNull] IObjectValueRole <TValue> gameObjectRole, IPresentationOptions options, IValueServicesFacade <TValue> valueServices, ILogger logger) where TValue : class { if (gameObjectRole == null) { return(null); } return(logger.CatchEvaluatorException <TValue, IValueEntity>(() => { // Only available in the editor. Not available for players, where we'll display nothing. // TODO: Hand roll this for players. Simply follow transform.parent // However, this will obviously be more expensive to calculate var frame = gameObjectRole.ValueReference.OriginatingFrame; var animationUtilityType = valueServices.GetReifiedType(frame, "UnityEditor.AnimationUtility, UnityEditor") ?? valueServices.GetReifiedType(frame, "UnityEditor.AnimationUtility, UnityEditor.CoreModule"); var method = animationUtilityType?.MetadataType.GetMethods() .FirstOrDefault(ourCalculateTransformPathSelector); if (method == null) { logger.Trace( "Unable to get metadata for AnimationUtility.CalculateTransformPath method. Is this a player?"); return null; } var targetTransformReference = gameObjectRole.GetInstancePropertyReference("transform"); var targetTransformRole = targetTransformReference?.AsObjectSafe(options); // Search in bases - transform might be a RectTransform or a Transform, and root is defined on Transform var rootTransformReference = targetTransformRole?.GetInstancePropertyReference("root", true); var rootTransformRole = rootTransformReference?.AsObjectSafe(options); if (targetTransformRole == null || rootTransformRole == null) { logger.Warn("Unable to evaluate gameObject.transform and/or gameObject.transform.root or values are null."); return null; } var rootTransformName = rootTransformRole.GetInstancePropertyReference("name", true) ?.AsStringSafe(options)?.GetString() ?? ""; var pathValue = animationUtilityType.CallStaticMethod(frame, options, method, targetTransformReference.GetValue(options), rootTransformReference.GetValue(options)); var path = new SimpleValueReference <TValue>(pathValue, frame, valueServices.RoleFactory) .AsStringSafe(options)?.GetString(); if (path == null) { // We expect empty string at least logger.Warn("Unexpected null returned from AnimationUtility.CalculateTransformPath"); return null; } var fullPath = path.IsNullOrEmpty() ? rootTransformName : rootTransformName + "/" + path; var fullPathValue = valueServices.ValueFactory.CreateString(frame, options, fullPath); // Don't show type presentation. This is informational, rather than an actual property var simpleReference = new SimpleValueReference <TValue>(fullPathValue, null, "Scene path", ValueOriginKind.Property, ValueFlags.None | ValueFlags.IsReadOnly | ValueFlags.IsDefaultTypePresentation, frame, valueServices.RoleFactory); // Wrap the simple reference - the default StringValuePresenter will display the simple reference as a // string property, with syntax colouring, quotes and type name. Our TextValuePresenter will handle the // TextValueReference and use the flags we've set return new TextValueReference <TValue>(simpleReference, valueServices.RoleFactory).ToValue( valueServices); }, exception => logger.LogThrownUnityException(exception, gameObjectRole.ValueReference.OriginatingFrame, valueServices, options))); }
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))); }
public override IValuePresentation PresentValue(IObjectValueRole <TValue> valueRole, IMetadataTypeLite instanceType, IPresentationOptions options, IUserDataHolder dataHolder, CancellationToken token) { var showName = (valueRole.ValueReference as CalculatedValueReferenceDecorator <TValue>)?.AllowNameInValue ?? true; var showTypeName = (valueRole.ValueReference as CalculatedValueReferenceDecorator <TValue>) ?.AllowDefaultTypePresentation ?? true; var nameValuePresentation = valueRole.GetInstancePropertyReference("name")?.ToValue(ValueServices) ?.GetValuePresentation(options); var propertyTypeReference = valueRole.GetInstancePropertyReference("propertyType"); var propertyTypeValuePresentation = propertyTypeReference?.ToValue(ValueServices)?.GetValuePresentation(options); var propertyTypeEnumValueObject = propertyTypeReference?.AsObjectSafe(options)?.GetEnumValue(options); var propertyType = (SerializedPropertyKind)Enum.ToObject(typeof(SerializedPropertyKind), propertyTypeEnumValueObject ?? SerializedPropertyKind.Generic); int? arraySize = null; string arrayElementType = null; string genericType = null; if (propertyType == SerializedPropertyKind.Generic) { if (Util.TryEvaluatePrimitiveProperty(valueRole, "isArray", options, out bool isArray) && isArray) { arraySize = valueRole.GetInstancePropertyReference("arraySize")?.AsPrimitiveSafe(options) ?.GetPrimitive <int>(); arrayElementType = valueRole.GetInstancePropertyReference("arrayElementType")?.AsStringSafe(options)?.GetString(); } else if (Util.TryEvaluatePrimitiveProperty(valueRole, "isFixedBuffer", options, out bool isFixedBuffer) && isFixedBuffer) { arraySize = valueRole.GetInstancePropertyReference("fixedBufferSize")?.AsPrimitiveSafe(options) ?.GetPrimitive <int>(); arrayElementType = valueRole.GetInstancePropertyReference("arrayElementType")?.AsStringSafe(options)?.GetString(); } else { genericType = valueRole.GetInstancePropertyReference("type")?.AsStringSafe(options)?.GetString(); } } var valuePresentation = GetValuePresentation(valueRole, propertyType, options, out var extraDetail); var parts = PresentationBuilder.New(); parts.OpenBrace(); if (showName && nameValuePresentation != null) { parts.Comment("name: ").Add(nameValuePresentation.Value.ToArray()) .Add(ValuePresentationPart.Space); } parts.Comment("propertyType: "); if (propertyType == SerializedPropertyKind.Generic && arraySize != null && arrayElementType != null) { parts.Default($"{arrayElementType}[{arraySize}]"); } else if (genericType != null) { parts.Default(genericType); } else if (propertyTypeValuePresentation != null) { parts.Add(propertyTypeValuePresentation.Value.ToArray()); } else { parts.Comment("(Unknown)"); } if (valuePresentation != null) { parts.Add(ValuePresentationPart.Space) .Comment("value: ").Add(valuePresentation.Value.ToArray()); if (!string.IsNullOrEmpty(extraDetail)) { parts.Add(ValuePresentationPart.Space).SpecialSymbol("(").Default(extraDetail).SpecialSymbol(")"); } } parts.ClosedBrace(); // Hide the default type presentation if we've been asked to var flags = !showTypeName ? ValueFlags.IsDefaultTypePresentation : 0; return(SimplePresentation.Create(parts.Result(), ValueResultKind.Success, ValueFlags.None | flags, instanceType)); }