public void AddPair(IntPtr keyPtr, IntPtr valuePtr) { IntPtr localKeyPropForCapture = keyProp; IntPtr localValuePropForCapture = valueProp; HashDelegates.GetKeyHash keyHash = delegate(IntPtr elementKey) { return(Native_UProperty.GetValueTypeHash(localKeyPropForCapture, elementKey)); }; HashDelegates.Equality keyEquality = delegate(IntPtr a, IntPtr b) { return(Native_UProperty.Identical(localKeyPropForCapture, a, b, 0)); }; HashDelegates.ConstructAndAssign keyConstructAndAssign = delegate(IntPtr newElementKey) { if (Native_UProperty.HasAnyPropertyFlags(localKeyPropForCapture, EPropertyFlags.ZeroConstructor)) { FMemory.Memzero(newElementKey, Native_UProperty.GetSize(localKeyPropForCapture)); } else { Native_UProperty.InitializeValue(localKeyPropForCapture, newElementKey); } Native_UProperty.CopySingleValue(localKeyPropForCapture, newElementKey, keyPtr); }; HashDelegates.ConstructAndAssign valueConstructAndAssign = delegate(IntPtr newElementValue) { if (Native_UProperty.HasAnyPropertyFlags(localValuePropForCapture, EPropertyFlags.ZeroConstructor)) { FMemory.Memzero(newElementValue, Native_UProperty.GetSize(localValuePropForCapture)); } else { Native_UProperty.InitializeValue(localValuePropForCapture, newElementValue); } Native_UProperty.CopySingleValue(localValuePropForCapture, newElementValue, valuePtr); }; HashDelegates.Assign valueAssign = delegate(IntPtr existingElementValue) { Native_UProperty.CopySingleValue(localValuePropForCapture, existingElementValue, valuePtr); }; HashDelegates.Destruct keyDestruct = delegate(IntPtr elementKey) { if (!Native_UProperty.HasAnyPropertyFlags(localKeyPropForCapture, EPropertyFlags.IsPlainOldData | EPropertyFlags.NoDestructor)) { Native_UProperty.DestroyValue(localKeyPropForCapture, elementKey); } }; HashDelegates.Destruct valueDestruct = delegate(IntPtr elementValue) { if (!Native_UProperty.HasAnyPropertyFlags(localValuePropForCapture, EPropertyFlags.IsPlainOldData | EPropertyFlags.NoDestructor)) { Native_UProperty.DestroyValue(localValuePropForCapture, elementValue); } }; map->Add(keyPtr, valuePtr, ref mapLayout, keyHash, keyEquality, keyConstructAndAssign, valueConstructAndAssign, valueAssign, keyDestruct, valueDestruct); }
private FText() { ownsNativeAddress = true; nativeAddress = FMemory.Malloc(FTextNative.StructSize); FMemory.Memzero(nativeAddress, FTextNative.StructSize); OwnsReference = true;// This assumes FText is initialized with a reference after this ctor }
/// <summary> /// Adds zeroed values to the end of the array (calls FMemory.Memzero on the memory range). /// </summary> /// <param name="count">The number of items to insert.</param> /// <returns>the indnex of the first newly added item</returns> public int AddZeroedValues(int count) { int oldNum = array->Add(elementSize, count); IntPtr dest = GetRawPtr(); FMemory.Memzero(dest, count * elementSize); return(oldNum); }
public static unsafe void HackVTable(UObject obj) { // This will swap out the vtable entry and store the old one in our managed UClass if (!Native_UObjectBaseUtility.IsA(obj.Address, Runtime.Classes.UClass)) { UClass unrealClass = obj.GetClass(); if (unrealClass.VTableOriginalFunctions == null) { IntPtr *vtable = *(IntPtr **)obj.Address; unrealClass.VTableOriginalFunctions = new Dictionary <int, UClass.VTableOriginalFunc>(); foreach (FunctionRedirect redirect in vtableRedirects) { if (!Native_UObjectBaseUtility.IsA(obj.Address, redirect.Class)) { continue; } IntPtr originalFunctionAddress = vtable[redirect.VTableIndex]; if (originalFunctionAddress != redirect.NativeCallback) { IntPtr originalOwnerClassAddress = FindOriginalVTableOwner( redirect.Class, unrealClass.Address, originalFunctionAddress, redirect.VTableIndex); if (originalOwnerClassAddress != unrealClass.Address) { UClass originalOwnerClass = GCHelper.Find <UClass>(originalOwnerClassAddress); if (originalOwnerClass.VTableOriginalFunctions == null) { HackVTable(originalOwnerClass.GetDefaultObject()); } } IntPtr pageAlignedPtr = FMemory.PageAlignPointer((IntPtr)(&vtable[redirect.VTableIndex])); FMemory.PageProtect(pageAlignedPtr, (IntPtr)IntPtr.Size, true, true); *(&vtable[redirect.VTableIndex]) = redirect.NativeCallback; } else { // The VTable has already been swapped out. Find the original function address. UClass superClass = unrealClass; while ((superClass = superClass.GetSuperClass()) != null && superClass.VTableOriginalFunctions == null) { } Debug.Assert(superClass != null && superClass.VTableOriginalFunctions != null && superClass.VTableOriginalFunctions.ContainsKey(redirect.VTableIndex)); originalFunctionAddress = superClass.VTableOriginalFunctions[redirect.VTableIndex].FuncAddress; } unrealClass.VTableOriginalFunctions.Add(redirect.VTableIndex, new UClass.VTableOriginalFunc(originalFunctionAddress)); } } } }
private unsafe void HandleInvokeFunctionFromNative(IntPtr obj, FFrame stack, IntPtr result, UFunction.FuncInvokerManaged managedFunctionInvoker) { IntPtr function = stack.CurrentNativeFunction; IntPtr paramsBuffer = stack.Locals; if (managedFunctionInvoker != null) { // 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 back from the locals buffer if (Native_UFunction.HasAnyFunctionFlags(function, EFunctionFlags.HasOutParms)) { // This assumes that UProperty will be itterated in the exact same order as the caller created stack->OutParms // (we could iterate stack->OutParms until nullptr but it isn't null terminated on release builds) FOutParmRec *outParms = stack.OutParmsPtr; foreach (IntPtr paramProp in new NativeReflection.NativeFieldIterator(Runtime.Classes.UProperty, function, false)) { EPropertyFlags paramFlags = Native_UProperty.GetPropertyFlags(paramProp); if ((paramFlags & EPropertyFlags.OutParm) == EPropertyFlags.OutParm && (paramFlags & EPropertyFlags.ConstParm) != EPropertyFlags.ConstParm) { Debug.Assert(outParms->Property == paramProp); // - REMOVING the DestroyValue call. The issue with this DestroyValue call is that PropAddr // and paramsBuffer can (will?) be refering to the same data which we should have set in // managedFunctionInvoker. So a call to DestroyValue would be destroying the data we want! // Though if PropAddr is for some reason still holding onto unknown memory this will leak // memory and we will need to investigate further. // - The "refering to the same data" is because of the params Memcpy in UObject::ProcessEvent. // They wont have the same address but their inital data will be the same and as such // things like FString data pointer would be pointing to the same address. // - We may still need to call DestroyValue in the BP/VM code path above. // Destroy the existing memory (this assumed the existing memory is valid or at least memzerod) //Native_UProperty.DestroyValue(paramProp, outParms->PropAddr); // A raw memcpy should be OK since the managed invoker should have set this memory appropriately. FMemory.Memcpy(outParms->PropAddr, paramsBuffer + Native_UProperty.GetOffset_ForUFunction(paramProp), Native_UProperty.Get_ElementSize(paramProp));// Should be ArrayDim*ElementSize but ArrayDim should always be 1 for params outParms = outParms->NextOutParamPtr; } } } // We assume that the caller will clean up the memory held by stack->Locals so we don't iterate over the // DestructorLink as we do in HandleInvokeFunctionFromBP. HandleInvokeFunctionFromBP needs to as it creates // copies of data when calling stack->Step() which don't occur here as we use the existing stack->Locals buffer. }
internal void Initialize() { EnsureNotInitialized(); if (Address == IntPtr.Zero) { Address = FMemory.Malloc(NativeReflection.GetStructSize(structAddress)); Native_UStruct.InitializeStruct(structAddress, Address, 1); } initialized = true; }
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; } }
private void ClearItems(int index, int count) { IntPtr dest = GetRawPtr(index); if (Native_UProperty.HasAnyPropertyFlags(innerProperty, EPropertyFlags.ZeroConstructor | EPropertyFlags.NoDestructor)) { FMemory.Memzero(dest, count * elementSize); } else { for (int i = 0; i < count; i++, dest += elementSize) { Native_UProperty.ClearValue(innerProperty, dest); } } }
public void Dispose(bool disposing) { if (!disposed) { if (OwnsReference) { nativeInstance->TextData.ReleaseSharedReference(espMode); } if (ownsNativeAddress && nativeAddress != IntPtr.Zero) { FMemory.Free(nativeAddress); nativeAddress = IntPtr.Zero; ownsNativeAddress = false; } disposed = true; } }
/// <summary> /// Internal function to call into the property system to construct / initialize elements. /// </summary> /// <param name="index"First item to construct.></param> private void ConstructItem(int index) { bool zeroElement = Native_UProperty.HasAnyPropertyFlags(elementProp, EPropertyFlags.ZeroConstructor); IntPtr dest = GetElementPtrWithoutCheck(index); if (zeroElement) { // If any nested property needs zeroing, just pre-zero the whole space FMemory.Memzero(dest, setLayout.Size); } if (!zeroElement) { Native_UProperty.InitializeValue_InContainer(elementProp, dest); } }
public static unsafe void Unload() { foreach (FunctionRedirect redirect in vtableRedirects) { using (FStringUnsafe dummyNameUnsafe = new FStringUnsafe(redirect.DummyName)) { Native_VTableHacks.Set_VTableCallback(ref dummyNameUnsafe.Array, IntPtr.Zero); } } // Restore the old vtable entry on hotreload. This is important as otherwise we would lose the original function address // which is stored in the managed UClass (which gets destroyed on hotreload) foreach (IntPtr objAddress in new NativeReflection.NativeObjectIterator(Runtime.Classes.UObject, EObjectFlags.NoFlags)) { foreach (FunctionRedirect redirect in vtableRedirects) { if (!Native_UObjectBaseUtility.IsA(objAddress, redirect.Class)) { continue; } IntPtr *vtable = *(IntPtr **)objAddress; if (vtable[redirect.VTableIndex] == redirect.NativeCallback) { UObject obj = GCHelper.Find(objAddress); Debug.Assert(obj != null); UClass unrealClass = obj.GetClass(); Debug.Assert(unrealClass != null); UClass.VTableOriginalFunc originalFunc; if (unrealClass.VTableOriginalFunctions != null && unrealClass.VTableOriginalFunctions.TryGetValue(redirect.VTableIndex, out originalFunc)) { IntPtr pageAlignedPtr = FMemory.PageAlignPointer((IntPtr)(&vtable[redirect.VTableIndex])); FMemory.PageProtect(pageAlignedPtr, (IntPtr)IntPtr.Size, true, true); *(&vtable[redirect.VTableIndex]) = originalFunc.FuncAddress; } } } } }
/// <summary> /// Adds the element to the set, returning true if the element was added, or false if the element was already present /// </summary> public void AddElement(IntPtr elementToAdd) { IntPtr localElementPropForCapture = elementProp; HashDelegates.GetKeyHash elementHash = delegate(IntPtr elementKey) { return(Native_UProperty.GetValueTypeHash(localElementPropForCapture, elementKey)); }; HashDelegates.Equality elementEquality = delegate(IntPtr a, IntPtr b) { return(Native_UProperty.Identical(localElementPropForCapture, a, b, 0)); }; HashDelegates.Construct elementConstruct = delegate(IntPtr newElement) { if (Native_UProperty.HasAnyPropertyFlags(localElementPropForCapture, EPropertyFlags.ZeroConstructor)) { FMemory.Memzero(newElement, Native_UProperty.GetSize(localElementPropForCapture)); } else { Native_UProperty.InitializeValue(localElementPropForCapture, newElement); } Native_UProperty.CopySingleValue(localElementPropForCapture, newElement, elementToAdd); }; HashDelegates.Destruct elementDestruct = delegate(IntPtr element) { if (!Native_UProperty.HasAnyPropertyFlags(localElementPropForCapture, EPropertyFlags.IsPlainOldData | EPropertyFlags.NoDestructor)) { Native_UProperty.DestroyValue(localElementPropForCapture, element); } }; set->Add( elementToAdd, ref setLayout, elementHash, elementEquality, elementConstruct, elementDestruct); }
/// <summary> /// Internal function to call into the property system to construct / initialize elements. /// </summary> /// <param name="index">First item to construct.</param> private void ConstructItem(int index) { bool zeroKey = Native_UProperty.HasAnyPropertyFlags(keyProp, EPropertyFlags.ZeroConstructor); bool zeroValue = Native_UProperty.HasAnyPropertyFlags(valueProp, EPropertyFlags.ZeroConstructor); IntPtr dest = GetPairPtrWithoutCheck(index); if (zeroKey || zeroValue) { // If any nested property needs zeroing, just pre-zero the whole space FMemory.Memzero(dest, mapLayout.SetLayout.Size); } if (!zeroKey) { Native_UProperty.InitializeValue_InContainer(keyProp, dest); } if (!zeroValue) { Native_UProperty.InitializeValue_InContainer(valueProp, dest); } }
/// <summary> /// Dynamically invokes the function /// </summary> /// <param name="obj"></param> /// <param name="parameters"></param> /// <returns></returns> public object DynamicInvoke(UObject obj, params object[] parameters) { if (parameters == null) { parameters = new object[0]; } bool validParams = true; Dictionary <UProperty, Delegate> fromNativeParams = new Dictionary <UProperty, Delegate>(); Dictionary <UProperty, Delegate> toNativeParams = new Dictionary <UProperty, Delegate>(); UProperty returnValueProp = null; List <UProperty> paramProps = new List <UProperty>(); foreach (UProperty prop in GetProperties <UProperty>()) { if (prop.HasAnyPropertyFlags(EPropertyFlags.Parm)) { if (prop.HasAnyPropertyFlags(EPropertyFlags.ReturnParm)) { returnValueProp = prop; } else { paramProps.Add(prop); } Type paramType = UProperty.GetTypeFromProperty(prop); if (paramType == null) { validParams = false; break; } Delegate fromNative = MarshalingDelegateResolverSlow.GetFromNative(paramType); Delegate toNative = MarshalingDelegateResolverSlow.GetToNative(paramType); if (fromNative == null || toNative == null) { validParams = false; break; } fromNativeParams.Add(prop, fromNative); toNativeParams.Add(prop, toNative); } } if (parameters.Length != paramProps.Count) { validParams = false; } if (!validParams) { return(null); } // Sort the parameters by offset, this is assumingly the correct thing to do? // - Otherwise we need to take the param names into this function. Or just not sort at all? //paramProps.Sort((x, y) => x.GetOffset_ForUFunction().CompareTo(y.GetOffset_ForUFunction())); object result = null; unsafe { int paramsSize = ParmsSize; byte * paramsBufferAllocation = stackalloc byte[ParmsSize]; IntPtr paramsBuffer = new IntPtr(paramsBufferAllocation); FMemory.Memzero(paramsBuffer, paramsSize); // Initialize default values for all parameters foreach (UProperty prop in GetProperties <UProperty>()) { if (prop.HasAnyPropertyFlags(EPropertyFlags.Parm)) { Native.Native_UProperty.InitializeValue_InContainer(prop.Address, paramsBuffer); } } // Copy the managed parameters to the buffer for (int i = 0; i < parameters.Length; i++) { UProperty paramProp = paramProps[i]; object paramValue = parameters[i]; if (paramValue != null && (!paramProp.HasAnyPropertyFlags(EPropertyFlags.OutParm) || paramProp.HasAnyPropertyFlags(EPropertyFlags.ReferenceParm))) { toNativeParams[paramProp].DynamicInvoke( paramsBuffer + paramProp.GetOffset_ForUFunction(), (int)0, paramProp.Address, paramValue); } } // Invoke the function NativeReflection.InvokeFunction(obj.Address, Address, paramsBuffer, paramsSize); // Copy parameters / return value from the buffer for (int i = 0; i < parameters.Length; i++) { UProperty paramProp = paramProps[i]; if (paramProp.HasAnyPropertyFlags(EPropertyFlags.OutParm)) { parameters[i] = fromNativeParams[paramProp].DynamicInvoke( paramsBuffer + paramProp.GetOffset_ForUFunction(), (int)0, paramProp.Address); } } if (returnValueProp != null) { result = fromNativeParams[returnValueProp].DynamicInvoke( paramsBuffer + returnValueProp.GetOffset_ForUFunction(), (int)0, returnValueProp.Address); } // Destroy the memory for all of the parameters foreach (UProperty prop in GetProperties <UProperty>()) { if (prop.HasAnyPropertyFlags(EPropertyFlags.Parm)) { Native.Native_UProperty.DestroyValue_InContainer(prop.Address, paramsBuffer); } } } return(result); }
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); } } }
/// <summary> /// Checks if the given UScriptStruct address is POD and has a zero constructor (initializes to zero). /// /// NOTE: A struct could be PlainOldData but unsafe to zero initialize due to having a non-zero constructor / EForceInit constructor. /// </summary> internal static bool IsPODZeroInit(IntPtr unrealStruct) { if (unrealStruct != IntPtr.Zero && Native_UObjectBaseUtility.IsA(unrealStruct, Classes.UScriptStruct)) { EStructFlags structFlags = Native_UScriptStruct.Get_StructFlags(unrealStruct); IntPtr cppStructOps = Native_UScriptStruct.GetCppStructOps(unrealStruct); if (cppStructOps != IntPtr.Zero) { bool isPlainOldData = Native_ICppStructOps.IsPlainOldData(cppStructOps); bool hasZeroConstructor = Native_ICppStructOps.HasZeroConstructor(cppStructOps); bool hasNoopConstructor = Native_ICppStructOps.HasNoopConstructor(cppStructOps); if (!hasZeroConstructor && structFlags.HasFlag(EStructFlags.ZeroConstructor)) { // This struct flag could have been set in the zero constructor check in UScriptStruct::PrepareCppStructOps hasZeroConstructor = true; } if (isPlainOldData && hasZeroConstructor && !hasNoopConstructor) { return(true); } if (isPlainOldData && !hasZeroConstructor) { if (hasNoopConstructor) { // The struct has a no-op constructor and takes EForceInit to init return(false); } // This is a copy of a check made in UScriptStruct::PrepareCppStructOps to check if the struct constructs to zero // if (CppStructOps->IsPlainOldData() && !CppStructOps->HasZeroConstructor()) { ... } int size = Native_ICppStructOps.GetSize(cppStructOps); IntPtr buffer = FMemory.Malloc(size); FMemory.Memzero(buffer, size); Native_ICppStructOps.Construct(cppStructOps, buffer); Native_ICppStructOps.Construct(cppStructOps, buffer);// slightly more like to catch "internal counters" if we do this twice bool isZeroConstruct = true; unsafe { byte *bufferPtr = (byte *)buffer; for (int i = 0; i < size; i++) { if (bufferPtr[i] != 0) { isZeroConstruct = false; break; } } } FMemory.Free(buffer); if (isZeroConstruct) { // "Native struct %s has DISCOVERED zero construction. Size = %d" System.Diagnostics.Debugger.Break(); return(true); } } return(false); } else { // Only treat it as blittable if it is POD and has a zero constructor return(structFlags.HasFlag(EStructFlags.IsPlainOldData | EStructFlags.ZeroConstructor)); } } return(false); }
private void ZeroMemory() { FMemory.Memzero(ref this); }
internal void ZeroMemory() { FMemory.Memzero(ref this); }