Exemplo n.º 1
0
        // @NOTE: Currently ignoring FName::Number, and assuming valid.
        public static string GetFNameAsString(string expr_str, DkmVisualizedExpression context_expr)
        {
            /*			var em = ExpressionManipulator.FromExpression(expr_str);
             *                      em = em.DirectMember(Memb.CompIndex);
             *                      DkmSuccessEvaluationResult comp_idx_eval = DefaultEE.DefaultEval(em.Expression, context_expr, true) as DkmSuccessEvaluationResult;
             *
             *                      // @TODO: For now, to avoid requiring more lookups, we'll allow a failure and return null,
             *                      // and let the caller decide how to interpret it.
             *                      //Debug.Assert(comp_idx_eval != null);
             *                      if(comp_idx_eval == null)
             *                      {
             *                              return null;
             *                      }
             *
             *                      string comp_idx_str = comp_idx_eval.Value;
             * //			"((FNameEntry&)GNameBlocksDebug[Value >> FNameDebugVisualizer::OffsetBits][FNameDebugVisualizer::EntryStride * (Value & FNameDebugVisualizer::OffsetMask)])"
             * //			// @TODO: Condition = "Header.Len > FNameDebugVisualizer::MaxLength" -> Illegal
             * //			"Header.bIsWide" -> WideName,[Header.Len]su
             * //			AnsiName,[Header.Len]s
             *
             *                      string ansi_expr_str = String.Format("((FNameEntry*)(((FNameEntry***)GFNameTableForDebuggerVisualizers_MT)[{0} / 16384][{0} % 16384]))->AnsiName", comp_idx_str);
             *                      var ansi_eval = DefaultEE.DefaultEval(ansi_expr_str, context_expr, true);
             *                      return ansi_eval.GetUnderlyingString();
             */

            var default_eval = DefaultEE.DefaultEval(expr_str, context_expr, false /*true*/) as DkmSuccessEvaluationResult;

            if (default_eval == null)
            {
                return(null);
            }
            // Remove quotes
            return(default_eval.Value.Substring(1, default_eval.Value.Length - 2));
        }
Exemplo n.º 2
0
        // Given an expression string resolving to a UClass* (which is assumed to point to a **Blueprint** class), this determines
        // the UE4 (non-prefixed) name of the native base class.
        public static string GetBlueprintClassNativeBaseName(string uclass_expr_str, DkmVisualizedExpression context_expr)
        {
            var uclass_em = ExpressionManipulator.FromExpression(uclass_expr_str);

            do
            {
                var super_em      = uclass_em.PtrCast(Typ.UStruct).PtrMember(Memb.SuperStruct).PtrCast(Typ.UClass);
                var is_native_res = IsNativeUClassOrUInterface(super_em.Expression, context_expr);
                if (!is_native_res.IsValid)
                {
                    return(null);
                }

                if (is_native_res.Value)
                {
                    var name_em = super_em.PtrCast(Typ.UObjectBase).PtrMember(Memb.ObjName);
                    return(GetFNameAsString(name_em.Expression, context_expr));
                }


                uclass_em = super_em;
            }while (DefaultEE.DefaultEval(uclass_em.PtrCast(Cpp.Void).Expression, context_expr, true).TagValue == DkmEvaluationResult.Tag.SuccessResult);

            // This shouldn't really happen
            return(null);
        }
Exemplo n.º 3
0
        public override void PrepareExpansion(out DkmEvaluationResultEnumContext enumContext)
        {
            // First, default expansion

            // @TODO: Calling this both in EvaluateExpressionResult, and from UseDefaultEvaluationBehavior
            // in the component. Be good if we can somehow test if this is actually redoing the evaluation,
            // or just returning the existing one.
            var eval = DefaultEE.DefaultEval(expression_, true);

            DkmEvaluationResult[]          children;
            DkmEvaluationResultEnumContext default_enum_ctx;

            expression_.GetChildrenCallback(eval, 0, expression_.InspectionContext, out children, out default_enum_ctx);

            // Now any custom additions to the expansion
            int custom_children = 0;

            // Need to determine if we should display the property list child
            DeterminePropertyListVisibility();
            if (IsPropertyListShown)
            {
                custom_children++;
            }

            // Create an aggregate enumeration context, with the length of the default enum, plus what we are adding on.
            // Store the default enumeration context inside it as a data item.
            enumContext = DkmEvaluationResultEnumContext.Create(
                default_enum_ctx.Count + custom_children,
                expression_.StackFrame,
                expression_.InspectionContext,
                new DefaultEnumContextDataItem(default_enum_ctx)
                );
        }
Exemplo n.º 4
0
        protected static string GetBoolPropertyFieldMask(string uboolprop_expr_str, DkmVisualizedExpression context_expr)
        {
            var boolprop_em = ExpressionManipulator.FromExpression(uboolprop_expr_str).PtrMember(Memb.FieldMask);
            // uint8 property
            var eval    = DefaultEE.DefaultEval(boolprop_em.Expression, context_expr, true) as DkmSuccessEvaluationResult;
            var val_str = eval.Value;

            return(Utility.GetNumberFromUcharValueString(val_str));
        }
Exemplo n.º 5
0
        // @TODO: Unsafe, assumes valid and non-empty.
        public static string GetFStringAsString(string expr_str, DkmVisualizedExpression context_expr)
        {
            var em = ExpressionManipulator.FromExpression(expr_str);

            em = em.DirectMember("Data").DirectMember("AllocatorInstance").DirectMember("Data").PtrCast("wchar_t");
            var data_eval = DefaultEE.DefaultEval(em.Expression, context_expr, true);

            Debug.Assert(data_eval != null);
            return(data_eval.GetUnderlyingString());
        }
Exemplo n.º 6
0
        // Given an expression which resolves to a pointer of any kind, returns the pointer value as an integer.
        public static ulong EvaluatePointerAddress(string pointer_expr_str, DkmVisualizedExpression context_expr)
        {
            ExpressionManipulator em = ExpressionManipulator.FromExpression(pointer_expr_str);
            // Cast to void* to avoid unnecessary visualization processing.
            string address_expr_str = em.PtrCast(Cpp.Void).Expression;
            var    address_eval     = (DkmEvaluationResult)DefaultEE.DefaultEval(address_expr_str, context_expr, true);

            if (address_eval.TagValue == DkmEvaluationResult.Tag.SuccessResult)
            {
                var eval = address_eval as DkmSuccessEvaluationResult;
                return(eval.Address != null ? eval.Address.Value : 0);
            }
            return(0);
        }
Exemplo n.º 7
0
        void IDkmCustomVisualizer.UseDefaultEvaluationBehavior(DkmVisualizedExpression expression, out bool useDefaultEvaluationBehavior, out DkmEvaluationResult defaultEvaluationResult)
        {
            Debug.Print("UE4PV: UseDefaultEvaluationBehavior('{0}'/'{1}', [{2}, {3}])",
                        Utility.GetExpressionFullName(expression),
                        Utility.GetExpressionName(expression),
                        expression.TagValue,
                        expression.VisualizerId
                        );

            if (KUE4VS.ExtContext.Instance.ExtensionOptions.EnablePropVis)
            {
                var data_item = expression.GetDataItem <ExpressionDataItem>();
                if (data_item != null)
                {
                    Debug.Assert(data_item.Visualizer != null);

                    if (data_item.Visualizer.WantsCustomExpansion)
                    {
                        useDefaultEvaluationBehavior = false;
                        defaultEvaluationResult      = null;
                        return;
                    }
                }
            }

            // Don't need any special expansion, just delegate back to the default EE
            useDefaultEvaluationBehavior = true;

            /* @NOTE:
             * Not sure where exactly the problem is, but UObject properties don't expand in VS 2013.
             * When we try, there is an initial call to this method with the property's child expr,
             * so we come here, and do a default eval, which, if the prop is a UObject, will invoke
             * EvaluateVisualizedExpression above with a new root expr. In 2015, that is followed by
             * another call to this method for the root expr, which has an attached visualizer and we
             * do the custom expansion. In 2013, it seems the second call into this method does not
             * occur for some reason.
             */
            defaultEvaluationResult = DefaultEE.DefaultEval(expression, false);

/*
 *      Doing this will crash VS!
 *      DkmSuccessEvaluationResult.Create(null, null, "", "", DkmEvaluationResultFlags.None,
 *                              "", "", "", DkmEvaluationResultCategory.Other, DkmEvaluationResultAccessType.None,
 *                              DkmEvaluationResultStorageType.None, DkmEvaluationResultTypeModifierFlags.None,
 *                              null, null, null, null);
 */     }
Exemplo n.º 8
0
 // @TODO: Differentiate between false result and evaluation failure
 public static bool EvaluateBooleanExpression(string bool_expr_str, DkmVisualizedExpression context_expr)
 {
     try
     {
         var eval = DefaultEE.DefaultEval(bool_expr_str, context_expr, true);
         if (eval.TagValue == DkmEvaluationResult.Tag.SuccessResult)
         {
             var result = eval as DkmSuccessEvaluationResult;
             return(bool.Parse(result.Value));
         }
         else
         {
             return(false);
         }
     }
     catch (Exception e)
     {
         return(false);
     }
 }
Exemplo n.º 9
0
        // @NOTE: Currently ignoring FName::Number, and assuming valid.
        public static string GetFNameAsString(string expr_str, DkmVisualizedExpression context_expr)
        {
            var em = ExpressionManipulator.FromExpression(expr_str);

            em = em.DirectMember(Memb.CompIndex);
            DkmSuccessEvaluationResult comp_idx_eval = DefaultEE.DefaultEval(em.Expression, context_expr, true) as DkmSuccessEvaluationResult;

            // @TODO: For now, to avoid requiring more lookups, we'll allow a failure and return null,
            // and let the caller decide how to interpret it.
            //Debug.Assert(comp_idx_eval != null);
            if (comp_idx_eval == null)
            {
                return(null);
            }

            string comp_idx_str  = comp_idx_eval.Value;
            string ansi_expr_str = String.Format("((FNameEntry*)(((FNameEntry***)GFNameTableForDebuggerVisualizers_MT)[{0} / 16384][{0} % 16384]))->AnsiName", comp_idx_str);
            var    ansi_eval     = DefaultEE.DefaultEval(ansi_expr_str, context_expr, true);

            return(ansi_eval.GetUnderlyingString());
        }
Exemplo n.º 10
0
        // Determines the corresponding C++ name for the passed in UClass unprefixed name.
        public static string DetermineNativeUClassCppTypeName(string uclass_fname, DkmVisualizedExpression context_expr)
        {
            // Now we need to prefix the name, with either 'A' for actor-derived classes, or 'U' for all others.
            string cpp_prefix = "U";

            // This is kind of awkward. Cleanest approach would be to walk up the super-class chain, but that could be
            // a LOT of expression evaluations.
            // The following approach is hacky but should be quicker. Relying on the assumption that UE4 wouldn't allow
            // both a UObject class and an AActor class with the same name (@TODO: is this true??), we just assume the 'U'
            // prefix, form an arbitrary expression using it, and see if it can be successfully evaluated by the debugger.
            // If not, we assume the prefix is 'A' instead.

            // @NOTE: This will fail if someone is stupid enough to create a reflected class 'AMyClass' as well
            // as a non-reflected class 'UMyClass'.

            // First do quick checks for common types, to avoid an unnecessary expression evaluation.
            if (uclass_fname == "Actor")
            {
                cpp_prefix = "A";
            }
            else if (uclass_fname != "Object")
            {
                // Assume 'U'.
                // Also, after casting to our assumed type, cast again to void*. This shouldn't affect the success/failure result
                // of the evaluation, but will avoid unnecessary visualization callbacks when the evaluation succeeds.
                string assumed_cpp_type = cpp_prefix + uclass_fname;
                string cast_expr_str    = String.Format("(void*)({0}*)nullptr", assumed_cpp_type);
                var    cast_eval        = DefaultEE.DefaultEval(cast_expr_str, context_expr, true);
                if (cast_eval.TagValue != DkmEvaluationResult.Tag.SuccessResult)
                {
                    // Cast failed, assume therefore must be an actor type
                    cpp_prefix = "A";
                }
            }

            return(cpp_prefix + uclass_fname);
        }
Exemplo n.º 11
0
        void IDkmCustomVisualizer.UseDefaultEvaluationBehavior(DkmVisualizedExpression expression, out bool useDefaultEvaluationBehavior, out DkmEvaluationResult defaultEvaluationResult)
        {
            Debug.Print("UE4PV: UseDefaultEvaluationBehavior('{0}'/'{1}', [{2}, {3}])",
                        Utility.GetExpressionFullName(expression),
                        Utility.GetExpressionName(expression),
                        expression.TagValue,
                        expression.VisualizerId
                        );

            var data_item = expression.GetDataItem <ExpressionDataItem>();

            if (data_item != null)
            {
                Debug.Assert(data_item.Visualizer != null);

                if (data_item.Visualizer.WantsCustomExpansion)
                {
                    useDefaultEvaluationBehavior = false;
                    defaultEvaluationResult      = null;
                    return;
                }
            }

            // Don't need any special expansion, just delegate back to the default EE
            useDefaultEvaluationBehavior = true;

            /* @NOTE:
             * Not sure where exactly the problem is, but UObject properties don't expand in VS 2013.
             * When we try, there is an initial call to this method with the property's child expr,
             * so we come here, and do a default eval, which, if the prop is a UObject, will invoke
             * EvaluateVisualizedExpression above with a new root expr. In 2014, that is followed by
             * another call to this method for the root expr, which has an attached visualizer and we
             * do the custom expansion. In 2013, it seems the second call into this method does not
             * occur for some reason.
             */
            defaultEvaluationResult = DefaultEE.DefaultEval(expression, false);
        }
Exemplo n.º 12
0
        protected DkmEvaluationResult GeneratePropertyValueEval(string container_expr_str, string uprop_expr_str, uint index, DkmVisualizedExpression context_expr)
        {
            Debug.Print("UE4PV: Trying to generate property value for property #{0}", index + 1);

            var uprop_em = ExpressionManipulator.FromExpression(uprop_expr_str);

            // Get name of property
            string prop_name_expr_str = uprop_em.PtrCast(Typ.UObjectBase).PtrMember(Memb.ObjName).Expression;
            string prop_name          = UE4Utility.GetFNameAsString(prop_name_expr_str, context_expr);

            // And the property type
            string prop_type = GetPropertyType(uprop_em.Expression, context_expr);

            // Now, determine address of the actual property value
            // First requires container address
            // Cast to void* first, so that expression evaluation is simplified and faster
            container_expr_str = ExpressionManipulator.FromExpression(container_expr_str).PtrCast(Cpp.Void).Expression;
            var container_eval = DefaultEE.DefaultEval(container_expr_str, context_expr, true) as DkmSuccessEvaluationResult;

            Debug.Assert(container_eval != null && container_eval.Address != null);
            string address_str = container_eval.Address.Value.ToString();

            // Next need offset in container (which is an int32 property of the UProperty class)
            var    offset_expr_str = uprop_em.PtrMember(Memb.PropOffset).Expression;
            var    offset_eval     = DefaultEE.DefaultEval(offset_expr_str, context_expr, true) as DkmSuccessEvaluationResult;
            string offset_str      = offset_eval.Value;

            // Then need to create an expression which adds on the offset
            address_str = String.Format("((uint8*){0} + {1})", address_str, offset_str);

            // Next, we need to cast this expression depending on the type of property we have.
            // Retrieve a list of possible cast expressions.
            var cpp_type_info_list = GetCppTypeForPropertyType(prop_type, uprop_em.Expression, context_expr);
            // Accept the first one that is successfully evaluated
            DkmSuccessEvaluationResult success_eval = null;
            string display_type = null;

            foreach (var cpp_type_info in cpp_type_info_list)
            {
                string prop_value_expr_str = AdjustPropertyExpressionStringForType(address_str, prop_type, uprop_em.Expression, context_expr, cpp_type_info);

                Debug.Print("UE4PV: Attempting exp eval as: '{0}'", prop_value_expr_str);

                // Attempt to evaluate the expression
                DkmEvaluationResult raw_eval = DefaultEE.DefaultEval(prop_value_expr_str, context_expr, false);
                if (raw_eval.TagValue == DkmEvaluationResult.Tag.SuccessResult)
                {
                    // Success
                    success_eval = raw_eval as DkmSuccessEvaluationResult;
                    display_type = cpp_type_info.Display;
                    break;
                }
            }

            if (success_eval == null)
            {
                // Was not able to find an evaluatable expression.
                return(null);
            }

            return(DkmSuccessEvaluationResult.Create(
                       expression_.InspectionContext,
                       expression_.StackFrame,
                       prop_name,
                       success_eval.FullName,         //prop_value_expr_str,
                       success_eval.Flags,
                       success_eval.Value,
                       // @TODO: Perhaps need to disable for some stuff, like bitfield bool?
                       success_eval.EditableValue,
                       display_type,         //success_eval.Type,
                       success_eval.Category,
                       success_eval.Access,
                       success_eval.StorageType,
                       success_eval.TypeModifierFlags,
                       success_eval.Address,
                       success_eval.CustomUIVisualizers,
                       success_eval.ExternalModules,
                       null
                       ));
        }
Exemplo n.º 13
0
        public void EvaluateProperties()
        {
            List <DkmEvaluationResult> evals = new List <DkmEvaluationResult>();

            // Assume we've been constructed with the fabricated property list expression
            DkmChildVisualizedExpression proplist_expr = (DkmChildVisualizedExpression)expression_;

            Debug.Assert(proplist_expr != null);

            // start could be an expression with the type of any UObject-derived class
            DkmVisualizedExpression start_expr = proplist_expr.Parent;

            string base_expression_str = Utility.GetExpressionFullName(start_expr);

            base_expression_str = Utility.StripExpressionFormatting(base_expression_str);

            ExpressionManipulator obj_em = null;

            // @TODO: Deal with non-pointer start expression
            obj_em = ExpressionManipulator.FromExpression(base_expression_str);

            // Determine if our base expression is <UObject-type>* or <UObject-type>
            bool is_pointer = start_expr.GetDataItem <UObjectDataItem>().IsPointer;

            if (!is_pointer)
            {
                obj_em = obj_em.AddressOf();
            }

            var uclass_em = obj_em.PtrCast(Typ.UObjectBase).PtrMember(Memb.ObjClass);

            if (Config.PropertyDisplayPolicy == Config.PropDisplayPolicyType.BlueprintOnly)
            {
                // See if the actual class of the object instance whose properties we want to enumerate
                // is native or not.
                var is_native_res = UE4Utility.TestUClassFlags(
                    uclass_em.Expression,
                    ClassFlags.Native,
                    start_expr
                    );

                // If the instance class is native, then it can't possibly have any non-native properties,
                // so bail out now.
                // @TODO: Even if the class is not native, we should still be able to avoid doing all the work
                // for enumerating every native property in order to find the non-native ones...
                // @TODO: How best to deal with failed is_native evaluation?
                if (is_native_res.IsValid && is_native_res.Value)
                {
                    return;
                }
            }

            // Get the UStruct part of the UClass, in order to begin iterating properties
            var ustruct_em = uclass_em.PtrCast(Typ.UStruct);
            // Now access PropertyLink member, which is the start of the linked list of properties
            var prop_em = ustruct_em.PtrMember(Memb.FirstProperty);

            uint idx = 0;

            while (true)
            {
                Debug.Print("UE4PV: Invoking raw eval on UProperty* expression");

                var prop_eval = DefaultEE.DefaultEval(prop_em.Expression, start_expr, true) as DkmSuccessEvaluationResult;
                Debug.Assert(prop_eval != null);

                if (prop_eval.Address.Value == 0)
                {
                    // nullptr, end of property list
                    break;
                }

                bool should_skip = false;

                if (!should_skip && Config.PropertyDisplayPolicy == Config.PropDisplayPolicyType.BlueprintOnly)
                {
                    // Check to see if this property is native or blueprint
                    // We can test this by getting the UProperty's Outer, and checking its object flags for RF_Native.
                    var prop_outer_em = prop_em.PtrCast(Typ.UObjectBase).PtrMember(Memb.ObjOuter);

                    // @NOTE: RF_Native has gone, and checking for RF_MarkAsNative never seems to return true...
                    //var is_native_res = UE4Utility.TestUObjectFlags(prop_outer_em.Expression, ObjFlags.Native, start_expr);

                    // So, access class flags instead.
                    // Note that we make the assumption here that the property's outer is a UClass, which should be safe since
                    // we're starting out with a uobject, so all properties, including inherited ones, should be outered to a uclass.
                    var prop_outer_uclass_em = prop_outer_em.PtrCast(Typ.UClass);
                    var is_native_res        = UE4Utility.TestUClassFlags(prop_outer_uclass_em.Expression, ClassFlags.Native, start_expr);

                    // According to UE4 UStruct API docs, property linked list is ordered from most-derived
                    // to base. If so, we should be able to bail out here knowing that having hit a native property,
                    // all following properties must be native too.
                    if (is_native_res.IsValid && is_native_res.Value)
                    {
                        return;
                    }
                }

                if (!should_skip)
                {
                    // @TODO: Here we use the starting expression for the container.
                    // May not work if the root expression was not of pointer type!!
                    var prop_val_eval = GeneratePropertyValueEval(
                        obj_em.Expression,
                        prop_em.Expression,
                        idx,
                        start_expr
                        );
                    if (prop_val_eval != null && !Config.IsPropertyHidden(prop_val_eval.Name))
                    {
                        prop_evals_[prop_val_eval.Name] = prop_val_eval;
                        ++idx;
                    }
                }

                // Advance to next link
                prop_em = prop_em.PtrMember(Memb.NextProperty);
            }
        }
Exemplo n.º 14
0
        protected void EvaluateExpressionResult()
        {
            // We're going to customize the unexpanded display string, as well as the expanded
            // view (if requested later).

            // @TODO: Really don't understand why, but when we invoke the default eval below, and we get
            // reentrant calls for child member UObjects, they are coming back as root expressions
            // with an empty FullName. This subsequently fails to evaluate if we pass it through to
            // default eval again. Perhaps this is somehow related to breaking the potential infinite
            // recursion of visualizing children in order to visualize the parent, but don't follow how it
            // it supposed to be dealt with.
            if (expression_.TagValue == DkmVisualizedExpression.Tag.RootVisualizedExpression)
            {
                var as_root = expression_ as DkmRootVisualizedExpression;
                if (as_root.FullName.Length == 0)
                {
                    string display_str = "{...}";
                    eval_ = DkmSuccessEvaluationResult.Create(
                        expression_.InspectionContext,
                        expression_.StackFrame,
                        as_root.Name,
                        as_root.Name,
                        DkmEvaluationResultFlags.ReadOnly,
                        display_str,
                        "",
#if !VS2013
                        as_root.Type,
#else
                        "Unknown",
#endif
                        DkmEvaluationResultCategory.Other,
                        DkmEvaluationResultAccessType.None,
                        DkmEvaluationResultStorageType.None,
                        DkmEvaluationResultTypeModifierFlags.None,
                        null,
                        null,
                        null,
                        null
                        );
                    state_ = EvaluationState.MinimalEvaluation;
                    return;
                }
            }
            //

            string custom_display_str             = Resources.UE4PropVis.IDS_DISP_CONDENSED;
            DkmSuccessEvaluationResult proto_eval = null;
            bool   is_pointer;
            bool   is_null;
            string address_str = "";

            // @NOTE: Initially here we were executing the full default evaluation of our expression.
            // Problem is that this will call back into us for all UObject children of the expression,
            // because it needs to generate a visualization for them in order to construct its {member vals...} display string.
            // And then, we just ignore that anyway and display our own...

            // The following attempts to avoid that by casting our expression to void* and then evaluating that.
            // If it fails, we assume we are non-pointer. If it succeeds, we can determine from the result whether we are null or not.

            // @WARNING: This may not be so safe, since we can't determine whether evaluation failed because we tried to cast a non-pointer
            // to void*, or because the passed in expression was not valid in the first place. Believe we should be okay, since we should
            // only be receiving expressions that have already been determined to be suitable for our custom visualizer.

            // Ideally though, we'd be able to get a raw expression evaluation, without any visualization taking place.
            // Seems there must be a way to do this, but looks like it requires using a different API...
            const bool UseVoidCastOptimization = true;

            string default_expr_str = Utility.GetExpressionFullName(expression_);
            if (UseVoidCastOptimization)
            {
                default_expr_str = ExpressionManipulator.FromExpression(default_expr_str).PtrCast(Cpp.Void).Expression;
            }

            DkmEvaluationResult eval = DefaultEE.DefaultEval(default_expr_str, expression_, true);

            if (!UseVoidCastOptimization)
            {
                if (eval.TagValue != DkmEvaluationResult.Tag.SuccessResult)
                {
                    // Genuine failure to evaluate the passed in expression
                    eval_  = eval;
                    state_ = EvaluationState.Failed;
                    return;
                }
                else
                {
                    proto_eval         = (DkmSuccessEvaluationResult)eval;
                    custom_display_str = proto_eval.Value;

                    is_pointer = IsPointer(proto_eval);
                    is_null    = is_pointer && IsPointerNull(proto_eval);
                    // @TODO: need to extract address string
                }
            }
            else
            {
                DkmDataAddress address = null;
                if (eval.TagValue != DkmEvaluationResult.Tag.SuccessResult)
                {
                    // Assume the failure just implies the expression was non-pointer (thereby assuming that it was itself valid!)

                    // @TODO: Could actually fall back onto standard evaluation here, in order to determine for sure
                    // that the original expression is valid. Failure wouldn't be common since most of the time we are
                    // dealing with pointer expressions, so any potential optimization should still be gained.

                    is_pointer = false;
                    is_null    = false;
                }
                else
                {
                    var success = (DkmSuccessEvaluationResult)eval;
                    Debug.Assert(IsPointer(success));
                    is_pointer  = true;
                    is_null     = is_pointer && IsPointerNull(success);
                    address     = success.Address;
                    address_str = success.Value;
                }

                proto_eval = DkmSuccessEvaluationResult.Create(
                    expression_.InspectionContext,
                    expression_.StackFrame,
                    "",
                    "",
                    DkmEvaluationResultFlags.ReadOnly | DkmEvaluationResultFlags.Expandable,
                    "",
                    "",
#if !VS2013
                    Utility.GetExpressionType(expression_),
#else
                    is_pointer ? "UObject *" : "UObject",
#endif
                    DkmEvaluationResultCategory.Other,
                    DkmEvaluationResultAccessType.None,
                    DkmEvaluationResultStorageType.None,
                    DkmEvaluationResultTypeModifierFlags.None,
                    address,
                    null,
                    null,
                    null
                    );
            }

            string obj_expression_str = Utility.GetExpressionFullName(expression_);
            var    obj_em             = ExpressionManipulator.FromExpression(obj_expression_str);

            // Store pointer flags on the expression
            expression_.SetDataItem(
                DkmDataCreationDisposition.CreateAlways,
                new UObjectDataItem(is_pointer, is_null)
                );

            if (!is_pointer)
            {
                // All subsequent manipulations of the expression assume it starts as a pointer to a
                // UObject-derived class, so if our expression is to a dereferenced UObject, just
                // prefix an 'address of' to the expression string.
                obj_em = obj_em.AddressOf();
            }

            // Generate the condensed display string.
            if (is_null)
            {
                if (Config.CustomNullObjectPreview)
                {
                    custom_display_str = "<NULL> UObject";
                }
                else
                {
                    var null_eval = DefaultEE.DefaultEval("(void*)0", expression_, true) as DkmSuccessEvaluationResult;
                    custom_display_str = null_eval.Value + " <NULL>";
                }
            }
            else if (Config.DisplayUObjectPreview)
            {
                // Prefix the address, if this is a pointer expression
                string address_prefix_str = "";
                if (is_pointer)
                {
                    address_prefix_str = address_str + " ";
                }

                // Specialized display for UClass?
                bool uclass_specialized = false;
                if (Config.DisplaySpecializedUClassPreview)
                {
                    var    uclass_em = obj_em.PtrCast(Typ.UObjectBase).PtrMember(Memb.ObjClass);
                    var    _uclass_fname_expr_str = uclass_em.PtrCast(Typ.UObjectBase).PtrMember(Memb.ObjName).Expression;
                    string _obj_uclass_name_str   = UE4Utility.GetFNameAsString(_uclass_fname_expr_str, expression_);
                    // @TODO: To simplify and for performance reasons, just hard coding known UClass variants
                    if (_obj_uclass_name_str == "Class" ||
                        _obj_uclass_name_str == "BlueprintGeneratedClass" ||
                        _obj_uclass_name_str == "WidgetBlueprintGeneratedClass")
                    {
                        var    fname_expr_str = obj_em.PtrCast(Typ.UObjectBase).PtrMember(Memb.ObjName).Expression;
                        string obj_name_str   = UE4Utility.GetFNameAsString(fname_expr_str, expression_);

                        var parent_uclass_fname_expr_str = obj_em.PtrCast(Typ.UStruct).PtrMember(Memb.SuperStruct).PtrCast(Typ.UObjectBase).PtrMember(Memb.ObjName).Expression;
                        // This will return null if lookup failed for any reason.
                        // We'll assume this meant no super class exists (ie. we're dealing with UClass itself)
                        string parent_uclass_name_str = UE4Utility.GetFNameAsString(parent_uclass_fname_expr_str, expression_);
                        if (parent_uclass_name_str == null)
                        {
                            parent_uclass_name_str = "None";
                        }

                        custom_display_str = String.Format(
                            "{0}{{ClassName='{1}', Parent='{2}'}}",
                            address_prefix_str,
                            obj_name_str,
                            parent_uclass_name_str
                            );
                        uclass_specialized = true;
                    }
                }

                if (!uclass_specialized)
                {
                    // For standard UObject condensed display string, show the object FName and its UClass.
                    // @TODO: The evaluations required for this may be a performance issue, since they'll be done for all UObject children of any default visualized
                    // aggregate type, even when it is not expanded (the default behaviour is to create a {...} display list of child member visualizations).
                    var    fname_expr_str = obj_em.PtrCast(Typ.UObjectBase).PtrMember(Memb.ObjName).Expression;
                    string obj_name_str   = UE4Utility.GetFNameAsString(fname_expr_str, expression_);

                    var    uclass_fname_expr_str = obj_em.PtrCast(Typ.UObjectBase).PtrMember(Memb.ObjClass).PtrCast(Typ.UObjectBase).PtrMember(Memb.ObjName).Expression;
                    string obj_uclass_name_str   = UE4Utility.GetFNameAsString(uclass_fname_expr_str, expression_);

                    custom_display_str = String.Format(
                        "{0}{{Name='{1}', Class='{2}'}}",
                        address_prefix_str,
                        obj_name_str,
                        obj_uclass_name_str
                        );
                }
            }

            eval_ = DkmSuccessEvaluationResult.Create(
                proto_eval.InspectionContext,
                proto_eval.StackFrame,
                // Override the eval name with the original expression name, since it will
                // have inherited the ",!" suffix.
                Utility.GetExpressionName(expression_),
                Utility.GetExpressionFullName(expression_),
                proto_eval.Flags,
                custom_display_str,                //success_eval.Value,
                proto_eval.EditableValue,
                proto_eval.Type,
                proto_eval.Category,
                proto_eval.Access,
                proto_eval.StorageType,
                proto_eval.TypeModifierFlags,
                proto_eval.Address,
                proto_eval.CustomUIVisualizers,
                proto_eval.ExternalModules,
                null
                );
            state_ = EvaluationState.Evaluated;
        }