public FunctionRedirect(IntPtr unrealClass, string dummyName, Delegate callback) { Debug.Assert(!Native_UStruct.IsChildOf(unrealClass, Runtime.Classes.UClass)); Class = unrealClass; DummyName = dummyName; Callback = callback; }
public bool IsChildOf(UStruct someBase) { if (someBase == null) { return(false); } return(Native_UStruct.IsChildOf(Address, someBase.Address)); }
/// <summary> /// Try and find boolean metadata with the given key. If not found on this class, work up hierarchy looking for it. /// </summary> /// <param name="key"></param> /// <returns></returns> public bool GetBoolMetaDataHierarchical(FName key) { #if WITH_EDITOR return(Native_UStruct.GetBoolMetaDataHierarchical(Address, ref key)); #else return(false); #endif }
/// <summary> /// Returns the struct/ class prefix used for the C++ declaration of this struct/ class. /// </summary> /// <returns>Prefix character used for C++ declaration of this struct/ class.</returns> public string GetPrefixCPP() { using (FStringUnsafe resultUnsafe = new FStringUnsafe()) { Native_UStruct.GetPrefixCPP(Address, ref resultUnsafe.Array); return(resultUnsafe.Value); } }
/// <summary> /// Try and find boolean metadata with the given key. If not found on this class, work up hierarchy looking for it. /// </summary> /// <param name="key"></param> /// <returns></returns> public bool GetBoolMetaDataHierarchical(FName key) { // WITH_EDITOR if (Native_UStruct.GetBoolMetaDataHierarchical == null) { return(false); } return(Native_UStruct.GetBoolMetaDataHierarchical(Address, ref key)); }
internal void Initialize() { EnsureNotInitialized(); if (Address == IntPtr.Zero) { Address = FMemory.Malloc(NativeReflection.GetStructSize(structAddress)); Native_UStruct.InitializeStruct(structAddress, Address, 1); } initialized = true; }
/// <summary> /// Try and find string metadata with the given key. If not found on this class, work up hierarchy looking for it. /// </summary> /// <param name="key"></param> /// <param name="outValue"></param> /// <returns></returns> public bool GetStringMetaDataHierarchical(FName key, out string outValue) { #if WITH_EDITOR using (FStringUnsafe outValueUnsafe = new FStringUnsafe()) { bool result = Native_UStruct.GetStringMetaDataHierarchical(Address, ref key, ref outValueUnsafe.Array); outValue = outValueUnsafe.Value; return(result); } #else outValue = null; return(false); #endif }
internal void Destroy() { if (Address != IntPtr.Zero) { Native_UStruct.DestroyStruct(structAddress, Address, 1); if (selfAllocated) { FMemory.Free(Address); } Address = IntPtr.Zero; initialized = false; selfAllocated = false; } }
/// <summary> /// Try and find string metadata with the given key. If not found on this class, work up hierarchy looking for it. /// </summary> /// <param name="key"></param> /// <param name="outValue"></param> /// <returns></returns> public bool GetStringMetaDataHierarchical(FName key, ref string outValue) { // WITH_EDITOR if (Native_UStruct.GetStringMetaDataHierarchical == null) { return(false); } using (FStringUnsafe outValueUnsafe = new FStringUnsafe(outValue)) { bool result = Native_UStruct.GetStringMetaDataHierarchical(Address, ref key, ref outValueUnsafe.Array); outValue = outValueUnsafe.Value; return(result); } }
/// <summary> /// Gets the first known managed type for a given UClass /// </summary> /// <param name="unrealClass">The UClass to get the managed type from</param> /// <param name="includeAbstract">Include abstract types (if true a wrapper cannot be instantiated from this type)</param> /// <returns>The managed type</returns> public static Type GetFirstKnownType(UClass unrealClass, bool includeAbstract = true) { Type type = null; IntPtr unrealClassAddress = unrealClass.Address; while (type == null && unrealClassAddress != IntPtr.Zero) { classesByAddress.TryGetValue(unrealClassAddress, out type); if (type != null && !includeAbstract && type.IsAbstract) { type = null; } unrealClassAddress = Native_UStruct.GetSuperStruct(unrealClassAddress); } return(type); }
/// <summary> /// Create properties / functions and link the class /// </summary> public void Initialize() { Native_UStruct.Set_Children(StaticClass, IntPtr.Zero); BindingFlags propertyBindingFlags = BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Instance | BindingFlags.DeclaredOnly; foreach (PropertyInfo property in Type.GetProperties(propertyBindingFlags)) { if (property.GetCustomAttribute <USharpPathAttribute>() != null) { CreateProperty(StaticClass, property); } } Native_UField.Bind(StaticClass); Native_UStruct.StaticLink(StaticClass, true); firstRun = false; }
private IntPtr FindProperty(string propertyName) { FName fname = new FName(propertyName); IntPtr property = Native_UStruct.FindPropertyByName(nativeClass, ref fname); if (!(FBuild.BuildShipping || FBuild.BuildTest)) { if (property == IntPtr.Zero) { FMessage.Log(FMessage.LogNet, ELogVerbosity.Fatal, $"Attempt to replicate property '{propertyName}' which does not exist."); } else if (!Native_UProperty.HasAnyPropertyFlags(property, EPropertyFlags.Net)) { FMessage.Log(FMessage.LogNet, ELogVerbosity.Fatal, $"Attempt to replicate property '{propertyName}' that was not tagged to replicate! Please use 'Replicated' or 'ReplicatedUsing' keyword in the UProperty() declaration."); } } return(property); }
/// <summary> /// Validates that the given property exists and matches the given UProperty class (e.g. UBoolProperty::StaticClass()) /// </summary> /// <param name="unrealStruct">The address of the structure which owns the property</param> /// <param name="propertyName">The name of the property</param> /// <param name="propertyClass">The expected UProperty class of the property</param> /// <returns></returns> public static bool ValidatePropertyClass(IntPtr unrealStruct, string propertyName, IntPtr propertyClass) { IntPtr field = FindField(unrealStruct, propertyName); if (field == IntPtr.Zero || !Native_UObjectBaseUtility.IsA(field, Classes.UProperty)) { return(false); } IntPtr actualClass = Native_UObjectBase.GetClass(field); if (actualClass == propertyClass) { return(true); } if (actualClass != IntPtr.Zero && propertyClass == Classes.UEnumProperty && Native_UStruct.IsChildOf(actualClass, Classes.UNumericProperty)) { return(Native_UNumericProperty.IsEnum(field)); } return(false); }
public UProperty FindPropertyByName(FName name) { return(GCHelper.Find <UProperty>(Native_UStruct.FindPropertyByName(Address, ref name))); }
/// <summary> /// Gets the first known type from a given UObject address (classes may be defined at runtime which we don't have a type mapping for. /// Look up the inheritance chain until we have a known type) /// </summary> /// <param name="objectAddress">The UObject address</param> /// <param name="isKnownType">True if the result is the exact type of the given UObject address</param> /// <param name="includeAbstract">Include abstract types (if true a wrapper cannot be instantiated from this type)</param> /// <returns>The first known managed type</returns> public static Type GetFirstKnownType(IntPtr objectAddress, out bool isKnownType, bool includeAbstract = true) { isKnownType = true; if (objectAddress == IntPtr.Zero) { return(null); } Type type = GetType(objectAddress); if (type != null && (!type.IsAbstract || includeAbstract)) { return(type); } else { type = null; } IntPtr parentClassAddress = IntPtr.Zero; IntPtr unrealClassAddress = Native_UObjectBase.GetClass(objectAddress); if (unrealClassAddress == Classes.UClass) { // The objectAddress is already a class unrealClassAddress = objectAddress; } if (unrealClassAddress != IntPtr.Zero) { parentClassAddress = Native_UStruct.GetSuperStruct(unrealClassAddress); } while (type == null && parentClassAddress != IntPtr.Zero) { isKnownType = false; classesByAddress.TryGetValue(parentClassAddress, out type); if (type != null && type.IsAbstract && !includeAbstract) { type = null; } parentClassAddress = Native_UStruct.GetSuperStruct(parentClassAddress); } if (type == null && unrealClassAddress != IntPtr.Zero) { // Some fallbacks if (Native_UStruct.IsChildOf(unrealClassAddress, Classes.UStruct)) { if (Native_UStruct.IsChildOf(unrealClassAddress, Classes.UClass)) { if (Native_UStruct.IsChildOf(unrealClassAddress, Classes.UBlueprintGeneratedClass)) { return(typeof(UBlueprintGeneratedClass)); } return(typeof(UClass)); } if (Native_UStruct.IsChildOf(unrealClassAddress, Classes.UScriptStruct)) { if (Native_UStruct.IsChildOf(unrealClassAddress, Classes.UUserDefinedStruct)) { return(typeof(UUserDefinedStruct)); } return(typeof(UScriptStruct)); } if (Native_UStruct.IsChildOf(unrealClassAddress, Classes.UEnum)) { if (Native_UStruct.IsChildOf(unrealClassAddress, Classes.UUserDefinedEnum)) { return(typeof(UUserDefinedEnum)); } return(typeof(UEnum)); } if (Native_UStruct.IsChildOf(unrealClassAddress, Classes.UFunction)) { return(typeof(UFunction)); } return(typeof(UStruct)); } // UObject fallback (would expect this to always be true) if (Native_UStruct.IsChildOf(unrealClassAddress, Classes.UObject)) { return(typeof(UObject)); } } return(type); }
/// <summary> /// Returns the size of the structure (use GetStructSize for getting the size of an actual struct) /// </summary> /// <returns></returns> public int GetStructureSize() { return(Native_UStruct.GetStructureSize(Address)); }
/// <summary> /// Destroy a struct in memory. This may be done by calling the native destructor and then the constructor or individually reinitializing properties /// </summary> /// <param name="dest">Pointer to memory to destory</param> /// <param name="arrayDim">Number of elements in the array</param> public void DestroyStruct(IntPtr dest, int arrayDim) { Native_UStruct.DestroyStruct(Address, dest, arrayDim); }
/// <summary> /// Initialize a struct over uninitialized memory. This may be done by calling the native constructor or individually initializing properties /// </summary> /// <param name="dest">Pointer to memory to initialize</param> /// <param name="arrayDim">Number of elements in the array</param> public void InitializeStruct(IntPtr dest, int arrayDim) { Native_UStruct.InitializeStruct(Address, dest, arrayDim); }
public void TagSubobjects(EObjectFlags newFlags) { Native_UStruct.TagSubobjects(Address, newFlags); }
public void StaticLink(bool relinkExistingProperties) { Native_UStruct.StaticLink(Address, relinkExistingProperties); }
private unsafe void HandleInvokeFunctionFromBP(IntPtr obj, FFrame *stack, IntPtr result, UFunction.FuncInvokerManaged managedFunctionInvoker) { // NOTE: ScriptCore.cpp uses PropertiesSize instead of ParamsSize. Is it ever any different? (it says alignment // may make them different) If it is different we should probably use PropertiesSize (including in generated code / // IL) as ScriptCore.cpp uses a memcpy of our memory. Debug.Assert(Native_UStruct.Get_PropertiesSize(stack->CurrentNativeFunction) == Native_UFunction.Get_ParmsSize(stack->CurrentNativeFunction)); IntPtr function = stack->CurrentNativeFunction; int paramsSize = Native_UFunction.Get_ParmsSize(function); int numParams = Native_UFunction.Get_NumParms(function); bool hasOutParams = Native_UFunction.HasAnyFunctionFlags(function, EFunctionFlags.HasOutParms); IntPtr *outParamsBufferPtr = stackalloc IntPtr[numParams]; byte * paramsBufferPtr = stackalloc byte[paramsSize]; IntPtr paramsBuffer = (IntPtr)paramsBufferPtr; // We could skip this memzero as stackalloc will (always?) zero memory even though the spec states // "The content of the newly allocated memory is undefined." // https://github.com/dotnet/coreclr/issues/1279 FMemory.Memzero(paramsBuffer, paramsSize); if (hasOutParams) { int paramIndex = 0; foreach (IntPtr param in new NativeReflection.NativeFieldIterator(Runtime.Classes.UProperty, function, false)) { // Not required but using for Debug.Assert() when getting the value stack->MostRecentPropertyAddress = IntPtr.Zero; stack->Step(stack->Object, paramsBuffer + Native_UProperty.GetOffset_ForUFunction(param)); outParamsBufferPtr[paramIndex] = stack->MostRecentPropertyAddress; if (Native_UProperty.HasAnyPropertyFlags(param, EPropertyFlags.ReturnParm)) { // This should be UObject::execEndFunctionParms which will just do "stack->Code--;" for allowing // the caller to use PFINISH aftwards outParamsBufferPtr[paramIndex] = result; } paramIndex++; } } else { foreach (IntPtr param in new NativeReflection.NativeFieldIterator(Runtime.Classes.UProperty, function, false)) { stack->Step(stack->Object, paramsBuffer + Native_UProperty.GetOffset_ForUFunction(param)); } } stack->PFinish();// Skip EX_EndFunctionParms // Call the managed function invoker which will marshal the params from the native params buffer and then call the // target managed function managedFunctionInvoker(paramsBuffer, obj); // Copy out params from the temp buffer if (hasOutParams) { int paramIndex = 0; foreach (IntPtr paramProp in new NativeReflection.NativeFieldIterator(Runtime.Classes.UProperty, function, false)) { if (Native_UProperty.HasAnyPropertyFlags(paramProp, EPropertyFlags.OutParm)) { Debug.Assert(outParamsBufferPtr[paramIndex] != IntPtr.Zero); // - See "REMOVING the DestroyValue call" below. // Destroy the existing memory (this assumed the existing memory is valid or at least memzerod) //Native_UProperty.DestroyValue(paramProp, outParamsBufferPtr[paramIndex]); // A raw memcpy should be OK since the managed invoker should have set this memory appropriately. FMemory.Memcpy(outParamsBufferPtr[paramIndex], paramsBuffer + Native_UProperty.GetOffset_ForUFunction(paramProp), Native_UProperty.Get_ElementSize(paramProp));// Should be ArrayDim*ElementSize but ArrayDim should always be 1 for params } paramIndex++; } } // Parameters are copied when calling stack->Step(). We are responsible for destroying non-blittable types // which were copied (FString, TArray, etc). For C++ this works out well due to the copy constructors etc. // // Example where an FString is constructed from stack->Step(): // UObject::execStringConst(...) { *(FString*)RESULT_PARAM = (ANSICHAR*)Stack.Code; } // // For C# it might be better if we reimplemented all of the IMPLEMENT_VM_FUNCTION functions to reduce the amount // of copying as we currently need ANOTHER copy to get it from the temp buffer into a C# type (which is done // inside the managedFunctionInvoker function) foreach (IntPtr paramProp in new NativeReflection.NativeFieldIterator(Runtime.Classes.UProperty, function, EFieldIteratorType.Destructor, false)) { // When is this ever false? It seems to be checked in UObject::ProcessEvent() // "Destroy local variables except function parameters." - used for BP locals? Debug.Assert(Native_UProperty.IsInContainer(paramProp, paramsSize)); // Out params are copied to the memory maintained by the caller so only destroy "by value" parameters. if (!Native_UProperty.HasAnyPropertyFlags(paramProp, EPropertyFlags.OutParm)) { Native_UProperty.DestroyValue_InContainer(paramProp, paramsBuffer); } } }
public UStruct GetInheritanceSuper() { return(GCHelper.Find <UStruct>(Native_UStruct.GetInheritanceSuper(Address))); }
public static ManagedUnrealClass CreateClass(Type type) { ManagedUnrealClass existingClass = FindClass(type); if (existingClass != null) { if (!FBuild.WithHotReload) { // TODO: Add support for hotreloading C# classes when WITH_HOT_RELOAD isn't available // - WITH_HOT_RELOAD will be false on shipping, monolithic and server builds // - Would need to make a copy of FHotReloadClassReinstancer (or just use it directly if // it doesn't depend on WITH_HOT_RELOAD and gets compiled into builds) // - Would likely break blueprint classes which depend on any C# classes reinstanced in this way return(existingClass); } existingClass.Clear(); HotReloadClassCount++; } if (!type.IsSubclassOf(typeof(UObject))) { return(null); } USharpPathAttribute pathAttribute = type.GetCustomAttribute <USharpPathAttribute>(); if (pathAttribute == null || string.IsNullOrEmpty(pathAttribute.Path)) { return(null); } IntPtr parentClass = GetStaticClass(type.BaseType); if (parentClass == IntPtr.Zero) { return(null); } string root, directory, moduleName, className, memberName; FPackageName.GetPathInfo(pathAttribute.Path, out root, out directory, out moduleName, out className, out memberName); string packageName = "/" + root + "/" + directory; if (string.IsNullOrEmpty(moduleName) || string.IsNullOrEmpty(className)) { return(null); } IntPtr package = NativeReflection.FindObject(Native_UPackage.StaticClass(), IntPtr.Zero, packageName, true); if (package == IntPtr.Zero) { package = NativeReflection.CreatePackage(IntPtr.Zero, packageName); Native_UPackage.SetPackageFlags(package, EPackageFlags.CompiledIn); // TODO: Find how to create a proper guid for a package (UHT CodeGenerator.cpp seems to use a crc of generated code) using (System.Security.Cryptography.SHA256 sha256 = System.Security.Cryptography.SHA256.Create()) { byte[] hash = sha256.ComputeHash(Encoding.ASCII.GetBytes(packageName)); // Truncate the hash byte[] buffer = new byte[16]; Buffer.BlockCopy(hash, 0, buffer, 0, buffer.Length); Native_UPackage.SetGuid(package, new Guid(buffer)); } } ManagedUnrealClass managedUnrealClass = null; if (existingClass != null) { managedUnrealClass = existingClass; } else { managedUnrealClass = new ManagedUnrealClass(type, packageName, className, parentClass); } managedUnrealClass.StaticClass = USharpClass.CreateClassPtr( managedUnrealClass.PackageName, managedUnrealClass.ClassName, (uint)Native_UStruct.GetPropertiesSize(managedUnrealClass.ParentClass), EClassFlags.None, EClassCastFlags.None, managedUnrealClass.ConfigName, managedUnrealClass.ParentClass, managedUnrealClass.WithinClass, managedUnrealClass.ClassConstructor, managedUnrealClass.ClassVTableHelperCtorCaller, managedUnrealClass.ClassAddReferencedObjects); Native_UObjectBase.UObjectForceRegistration(managedUnrealClass.StaticClass); if (existingClass == null) { Classes.Add(type, managedUnrealClass); ClassesByAddress.Add(managedUnrealClass.StaticClass, managedUnrealClass); } managedUnrealClass.Initialize(); return(managedUnrealClass); }