示例#1
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);
        }
示例#2
0
        // Returns the type of property identified by the expression string
        // (As in, "IntProperty", "ObjectProperty", etc)
        protected string GetPropertyType(string uprop_expr_str, DkmVisualizedExpression context_expr)
        {
            // We get this from the value of the Name member of the UProperty's UClass.
            var    uprop_em = ExpressionManipulator.FromExpression(uprop_expr_str);
            string prop_class_name_expr_str = uprop_em.PtrCast(Typ.UObjectBase).PtrMember(Memb.ObjClass).PtrCast(Typ.UObjectBase).PtrMember(Memb.ObjName).Expression;

            return(UE4Utility.GetFNameAsString(prop_class_name_expr_str, context_expr));
        }
示例#3
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));
        }
示例#4
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());
        }
示例#5
0
        public static BoolEvaluation TestUClassFlags(string uclass_expr_str, string flags, DkmVisualizedExpression context_expr)
        {
            ExpressionManipulator em = ExpressionManipulator.FromExpression(uclass_expr_str);

            em = em.PtrCast(Typ.UClass).PtrMember(Memb.ClassFlags);
            var class_flags_expr_str = em.Expression;

            // @TODO: Could do the flag test on this side by hard coding in the value of RF_Native, but for now
            // doing it the more robust way, and evaluating the expression in the context of the debugee.
            return(TestExpressionFlags(class_flags_expr_str, flags, context_expr));
        }
示例#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);
        }
示例#7
0
        // Takes in an address string which identifies the location of the property value in memory,
        // along with property type information.
        // Outputs a string expression which will evaluate to the precise location with the correct C++ type.
        protected string AdjustPropertyExpressionStringForType(string address_str, string prop_type, string uprop_expr_str, DkmVisualizedExpression context_expr, CppTypeInfo cpp_type_info)
        {
            // Special cases first
            switch (prop_type)
            {
            case Prop.Bool:
            {
                // Needs special treatment since can be a single bit field as well as just a regular bool
                // Get a UBoolProperty context
                var uboolprop_em = ExpressionManipulator.FromExpression(uprop_expr_str).PtrCast(CppProp.Bool);
                // Read the byte offset and field mask properties
                string byte_offset_str = GetBoolPropertyByteOffset(uboolprop_em.Expression, context_expr);
                string field_mask_str  = GetBoolPropertyFieldMask(uboolprop_em.Expression, context_expr);
                // Format an expression which adds the byte offset onto the address, derefs
                // and then bitwise ANDs with the field mask.
                return(String.Format("(*({0} + {1}) & {2}) != 0",
                                     address_str,
                                     byte_offset_str,
                                     field_mask_str
                                     ));
            }

            case Prop.Byte:
            {
                // Enum properties are a bit awkward, since due to the different ways the enums can be declared,
                // we can't reliably access them by a cast and dereference.
                // eg. A regular enum type will be considered 64 bits.
                // So, we need to use uint8 to get at the memory, and then do the cast, or rather conversion,
                // *after* dereferencing in order to get the correct type.
                return(String.Format("({0})*({1}*){2}{3}",
                                     cpp_type_info.Type,
                                     Typ.Byte,
                                     address_str,
                                     cpp_type_info.Format
                                     ));
            }

            default:
                break;
            }

            // If we got here, we just need to get the corresponding C++ type, then cast the address
            // to it and dereference.
            // [*(type*)address,fmt]
            return(String.Format("*({0}*){1}{2}", cpp_type_info.Type, address_str, cpp_type_info.Format));
        }
        // NOTE: 'expr' must resolve to either <UObject-type>* or <UObject-type>.
        // Furthermore, it must have already been passed to a UObjectVisualizer, which has
        // performed the initial evalution.
        public UPropertyAccessContext(DkmVisualizedExpression expr)
        {
            context_expr_ = expr;

            string base_expression_str = Utility.GetExpressionFullName(context_expr_);

            base_expression_str = Utility.StripExpressionFormatting(base_expression_str);

            obj_em_ = ExpressionManipulator.FromExpression(base_expression_str);

            // Determine if our base expression is <UObject-type>* or <UObject-type>
            var uobj_data = context_expr_.GetDataItem <UObjectDataItem>();

            Debug.Assert(uobj_data != null);
            if (!uobj_data.IsPointer)
            {
                obj_em_ = obj_em_.AddressOf();
            }
        }
示例#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());
        }
示例#10
0
        // Flags should be in a form that can be inserted into a bit test expression in the
        // debuggee context (eg. a raw integer, or a combination of RF_*** flags)
        public static bool TestUObjectFlags(string uobj_expr_str, string flags, DkmVisualizedExpression context_expr)
        {
            ExpressionManipulator em = ExpressionManipulator.FromExpression(uobj_expr_str);

            em = em.PtrCast(Typ.UObjectBase).PtrMember(Memb.ObjFlags);
            var obj_flags_expr_str = em.Expression;

            // @NOTE: Value string is formatted as 'RF_... | RF_... (<integer value>)'
            // So for now, rather than extracting what we want, just inject the full expression for
            // the flags member into the below flag test expression.
            //string obj_flags_str = ((DkmSuccessEvaluationResult)obj_flags_expr.EvaluationResult).Value;

            // @TODO: Could do the flag test on this side by hard coding in the value of RF_Native, but for now
            // doing it the more robust way, and evaluating the expression in the context of the debugee.
            string flag_test_expr_str = String.Format(
                "({0} & {1}) != 0",
                obj_flags_expr_str,
                flags
                );

            return(EvaluateBooleanExpression(flag_test_expr_str, context_expr));
        }
示例#11
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
                       ));
        }
示例#12
0
        // Takes in a UE4 property type string (eg. IntPropery, ObjectProperty, etc) along with a
        // expression string which evaluates to a UProperty*, and maps to the corresponding C++ type.
        protected CppTypeInfo[] GetCppTypeForPropertyType(string prop_type, string uprop_expr_str, DkmVisualizedExpression context_expr)
        {
            switch (prop_type)
            {
            case Prop.Bool:
                return(new CppTypeInfo[] {
                    new CppTypeInfo("bool")
                });

            case Prop.Struct:
            {
                // This is gonna be effort.
                // Get UStructProperty
                var ustructprop_em = ExpressionManipulator.FromExpression(uprop_expr_str).PtrCast(CppProp.Struct);
                // Need to access its UScriptStruct* member 'Struct', and get its object name.
                var struct_name_expr_str = ustructprop_em.PtrMember(Memb.CppStruct).PtrCast(Typ.UObjectBase).PtrMember(Memb.ObjName).Expression;
                // Get that name string
                string struct_name = UE4Utility.GetFNameAsString(struct_name_expr_str, context_expr);
                // Add the 'F' prefix
                return(new CppTypeInfo[] {
                        new CppTypeInfo("F" + struct_name)
                    });
            }

            case Prop.Byte:
            {
                // Could be plain uint8, or a UEnum.
                // Get UByteProperty
                var ubyteprop_em = ExpressionManipulator.FromExpression(uprop_expr_str).PtrCast(CppProp.Byte);
                // Need to access its UEnum* member 'Enum'.
                var uenum_em = ubyteprop_em.PtrMember(Memb.EnumType);
                // Evaluate this to see if it is null or not
                bool is_enum_valid = !UE4Utility.IsPointerNull(uenum_em.Expression, context_expr);
                if (is_enum_valid)
                {
                    // This property is an enum, so the type we want is the fully qualified C++ enum type.

                    // @NOTE: Seems that the CppType member should be exactly what we need, but appears to actually be unused.
                    //string cpp_enum_name_expr_str = uenum_em.PtrMember("CppType").Expression;

                    string uenum_fname_expr_str = uenum_em.PtrCast(Typ.UObjectBase).PtrMember(Memb.ObjName).Expression;
                    string uenum_name           = UE4Utility.GetFNameAsString(uenum_fname_expr_str, context_expr);

                    // We need to know if it's a namespaced enum or not
                    string is_namespaced_enum_expr_str = String.Format(
                        "{0}==UEnum::ECppForm::Namespaced",
                        uenum_em.PtrMember(Memb.EnumCppForm).Expression
                        );
                    var is_namespaced_res = UE4Utility.EvaluateBooleanExpression(is_namespaced_enum_expr_str, context_expr);
                    // @TODO: on evaluation failure??
                    CppTypeInfo primary;
                    if (is_namespaced_res.IsValid && is_namespaced_res.Value)
                    {
                        // A namespaced enum type should (hopefully) always be <UEnum::Name>::Type
                        primary = new CppTypeInfo(String.Format("{0}::Type", uenum_name), uenum_name);
                    }
                    else
                    {
                        // For regular or enum class enums, the full type name should be just the name of the UEnum object.
                        primary = new CppTypeInfo(uenum_name);
                    }

                    return(new CppTypeInfo[] {
                            primary,
                            // Fallback onto regular byte display, in case enum name symbol not available
                            new CppTypeInfo(Typ.Byte, uenum_name + "?")
                        });
                }
                else
                {
                    // Must just be a regular uint8
                    return(new CppTypeInfo[] {
                            new CppTypeInfo(Typ.Byte)
                        });
                }
            }

            case Prop.Array:
            {
                // Okay, cast to UArrayProperty and get the inner property type
                var array_prop_em = ExpressionManipulator.FromExpression(uprop_expr_str).PtrCast(CppProp.Array);
                var inner_prop_em = array_prop_em.PtrMember(Memb.ArrayInner);

                // @TODO: Need to look into how TArray< bool > is handled.
                string inner_prop_type     = GetPropertyType(inner_prop_em.Expression, context_expr);
                var    inner_cpp_type_info = GetCppTypeForPropertyType(inner_prop_type, inner_prop_em.Expression, context_expr);

                var result = new CppTypeInfo[inner_cpp_type_info.Length];
                for (int i = 0; i < inner_cpp_type_info.Length; ++i)
                {
                    // Type needed is TArray< %inner_cpp_type%, FDefaultAllocator >
                    string type = String.Format("{0}<{1},{2}>", Typ.Array, inner_cpp_type_info[i].Type, Typ.DefaultAlloc);
                    // Omit allocator from display string, since for UPROPERTY arrays it can't be anything else
                    string display = String.Format("{0}<{1}>", Typ.Array, inner_cpp_type_info[i].Display);
                    result[i] = new CppTypeInfo(type, display);
                }
                return(result);
            }

            case Prop.Object:
            case Prop.Asset:
            {
                if (!Config.ShowExactUObjectTypes)
                {
                    break;
                }

                string obj_cpp_type_name = Typ.UObject;

                // Need to find out the subtype of the property, which is specified by UObjectPropertyBase::PropertyClass
                var    objprop_em        = ExpressionManipulator.FromExpression(uprop_expr_str).PtrCast(CppProp.ObjectBase);
                var    subtype_uclass_em = objprop_em.PtrMember(Memb.ObjectSubtype);
                var    uclass_fname_em   = subtype_uclass_em.PtrCast(Typ.UObjectBase).PtrMember(Memb.ObjName);
                string uclass_fname      = UE4Utility.GetFNameAsString(uclass_fname_em.Expression, context_expr);

                // Is the property class native?
                var is_native_res = UE4Utility.IsNativeUClassOrUInterface(subtype_uclass_em.Expression, context_expr);
                // @TODO: currently not really handling failed eval
                bool   is_native = is_native_res.IsValid ? is_native_res.Value : true;
                string native_uclass_fname;
                if (is_native)
                {
                    // Yes
                    native_uclass_fname = uclass_fname;
                }
                else
                {
                    // No, we need to retrieve the name of its native base
                    native_uclass_fname = UE4Utility.GetBlueprintClassNativeBaseName(subtype_uclass_em.Expression, context_expr);
                }

                Debug.Assert(native_uclass_fname != null);

                // Now we have to convert the unprefixed name, to a prefixed C++ type name
                obj_cpp_type_name = UE4Utility.DetermineNativeUClassCppTypeName(native_uclass_fname, context_expr);

                string uclass_display_name = UE4Utility.GetBlueprintClassDisplayName(uclass_fname);
                switch (prop_type)
                {
                case Prop.Object:
                {
                    // if not native, add a suffix to the display type showing the blueprint class of the property
                    // @NOTE: this is nothing to do with what object the value points to and what its type may be. property meta data only.
                    string suffix          = is_native ? String.Empty : String.Format(" [{0}]", uclass_display_name);
                    string primary_type    = String.Format("{0} *", obj_cpp_type_name);
                    string primary_display = String.Format("{0} *{1}", obj_cpp_type_name, suffix);
                    // fallback, no symbols available for the native base type, so use 'UObject' instead
                    string fallback_type    = String.Format("{0} *", Typ.UObject);
                    string fallback_display = String.Format("{0}? *{1}", obj_cpp_type_name, suffix);

                    return(new CppTypeInfo[]
                            {
                                new CppTypeInfo(primary_type, primary_display),
                                new CppTypeInfo(fallback_type, fallback_display)
                            });
                }

                case Prop.Asset:
                {
                    // @NOTE: Don't really see anything to gain by casting to TAssetPtr< xxx >, since it's just another level of encapsulation that isn't
                    // needed for visualization purposes.
                    string suffix          = String.Format(" [{0}]", is_native ? obj_cpp_type_name : uclass_display_name);
                    string primary_type    = Typ.AssetPtr;                                                  //String.Format("TAssetPtr<{0}>", obj_cpp_type_name);
                    string primary_display = String.Format("{0}{1}", Typ.AssetPtr, suffix);

                    // If just using FAssetPtr, no need for a fallback since we don't need to evaluate the specialized template parameter type
                    return(new CppTypeInfo[]
                            {
                                new CppTypeInfo(primary_type, primary_display)
                            });
                }

                default:
                    Debug.Assert(false);
                    return(null);
                }
            }

/*				@TODO: Not so important. What's below is wrong, but essentially if we implement this, it's just to differentiate between UClass, UBlueprintGeneratedClass, etc
 *                              case "ClassProperty":
 *                              case "AssetClassProperty":
 *                                      {
 *                                              if (!Config.ShowExactUObjectTypes)
 *                                              {
 *                                                      break;
 *                                              }
 *
 *                                              // Need to find out the subtype of the property, which is specified by UClassProperty::MetaClass/UAssetClassProperty::MetaClass
 *                                              // Cast to whichever property type we are (either UClassProperty or UAssetClassProperty)
 *                                              string propclass_name = String.Format("U{0}", prop_type);
 *                                              var classprop_em = ExpressionManipulator.FromExpression(uprop_expr_str).PtrCast(propclass_name);
 *                                              // Either way, we have a member called 'MetaClass' which specified the base UClass stored in this property
 *                                              var subtype_uclass_em = classprop_em.PtrMember("MetaClass");
 *                                              var subtype_fname_em = subtype_uclass_em.PtrCast("UObjectBase").PtrMember("Name");
 *                                              string subtype_fname = UE4Utility.GetFNameAsString(subtype_fname_em.Expression, context_expr);
 *                                              return String.Format("U{0}*", subtype_fname);
 *                                      }
 */         }

            // Standard cases, just use cpp type stored in map.
            // If not found, null string will be returned.
            string cpp_type = null;

            if (ue4_proptype_map_.TryGetValue(prop_type, out cpp_type))
            {
                return(new CppTypeInfo[] { new CppTypeInfo(cpp_type) });
            }
            else
            {
                return(null);
            }
        }
示例#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);
            }
        }
示例#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;
        }