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 }
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); }
/// <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); }
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); } } }
/// <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); } }
/// <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); } }
private void ZeroMemory() { FMemory.Memzero(ref this); }
/// <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); }
/// <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); } } }
internal void ZeroMemory() { FMemory.Memzero(ref this); }