Пример #1
0
        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));
        }
Пример #2
0
        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));
        }
Пример #3
0
        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));
        }
Пример #4
0
        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));
            }
        }
Пример #5
0
        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);
        }
Пример #6
0
        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));
                    }
                }
            }
Пример #8
0
        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)));
        }
Пример #10
0
        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));
        }