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> /// Internal function to call into the property system to destruct elements. /// </summary> private void DestructItems(int index, int count) { if (count <= 0) { return; } bool destroyElements = !Native_UProperty.HasAnyPropertyFlags(elementProp, EPropertyFlags.IsPlainOldData | EPropertyFlags.NoDestructor); if (destroyElements) { int stride = setLayout.Size; IntPtr elementPtr = GetElementPtrWithoutCheck(index); for (; count != 0; ++index) { if (IsValidIndex(index)) { Native_UProperty.DestroyValue_InContainer(elementProp, elementPtr); --count; } elementPtr += stride; } } }
/// <summary> /// Internal function to call into the property system to destruct elements. /// </summary> private void DestructItems(int index, int count) { if (count <= 0) { return; } bool destroyKeys = !Native_UProperty.HasAnyPropertyFlags(keyProp, EPropertyFlags.IsPlainOldData | EPropertyFlags.NoDestructor); bool destroyValues = !Native_UProperty.HasAnyPropertyFlags(valueProp, EPropertyFlags.IsPlainOldData | EPropertyFlags.NoDestructor); if (destroyKeys || destroyValues) { int stride = mapLayout.SetLayout.Size; IntPtr pairPtr = GetPairPtr(index); if (destroyKeys) { if (destroyValues) { for (; count != 0; ++index) { if (IsValidIndex(index)) { Native_UProperty.DestroyValue_InContainer(keyProp, pairPtr); Native_UProperty.DestroyValue_InContainer(valueProp, pairPtr); --count; } pairPtr += stride; } } else { for (; count != 0; ++index) { if (IsValidIndex(index)) { Native_UProperty.DestroyValue_InContainer(keyProp, pairPtr); --count; } pairPtr += stride; } } } else { for (; count != 0; ++index) { if (IsValidIndex(index)) { Native_UProperty.DestroyValue_InContainer(valueProp, pairPtr); --count; } pairPtr += stride; } } } }
/// <summary> /// Internal function to call into the property system to destruct elements. /// </summary> /// <param name="index">first item to .</param> /// <param name="count">number of items to .</param> private void DestructItems(int index, int count) { if (!Native_UProperty.HasAnyPropertyFlags(innerProperty, EPropertyFlags.IsPlainOldData | EPropertyFlags.NoDestructor)) { IntPtr dest = GetRawPtr(index); for (int i = 0; i < count; i++, dest += elementSize) { Native_UProperty.DestroyValue(innerProperty, dest); } } }
private unsafe void HandleInvokeFunctionFromNative(IntPtr obj, FFrame *stack, IntPtr result, UFunction.FuncInvokerManaged managedFunctionInvoker) { IntPtr function = stack->CurrentNativeFunction; IntPtr paramsBuffer = stack->Locals; // 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)) { if (Native_UProperty.HasAnyPropertyFlags(paramProp, EPropertyFlags.OutParm)) { 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. }
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); } }
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> /// 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 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> /// Used to safely check whether any of the passed in flags are set. This is required /// as PropertyFlags currently is a 64 bit data type and bool is a 32 bit data type so /// simply using PropertyFlags&CPF_MyFlagBiggerThanMaxInt won't work correctly when /// assigned directly to an bool. /// </summary> /// <param name="flagsToCheck">Object flags to check for.</param> /// <returns>true if any of the passed in flags are set, false otherwise (including no flags passed in).</returns> public bool HasAnyPropertyFlags(EPropertyFlags flagsToCheck) { return(Native_UProperty.HasAnyPropertyFlags(Address, flagsToCheck)); }
public bool Update(IntPtr address) { if (Address != address) { Address = address; Size = 0; ArrayDim = 0; IsEditConst = false; IsBlueprintReadOnly = false; GenericArg1Address = IntPtr.Zero; GenericArg1Size = 0; GenericArg1ArrayDim = 0; GenericArg2Address = IntPtr.Zero; GenericArg2Size = 0; GenericArg2ArrayDim = 0; PropertyType = EPropertyType.Unknown; if (address == IntPtr.Zero) { return(true); } EPropertyType propertyType = NativeReflection.GetPropertyType(address); if (propertyType != EPropertyType.Unknown) { PropertyType = propertyType; Size = Native_UProperty.Get_ElementSize(address); ArrayDim = Native_UProperty.Get_ArrayDim(address); IsEditConst = Native_UProperty.HasAnyPropertyFlags(address, EPropertyFlags.EditConst); IsBlueprintReadOnly = Native_UProperty.HasAnyPropertyFlags(address, EPropertyFlags.BlueprintReadOnly); switch (propertyType) { case EPropertyType.Array: GenericArg1Address = Native_UArrayProperty.Get_Inner(address); if (GenericArg1Address != IntPtr.Zero) { GenericArg1Size = Native_UProperty.Get_ElementSize(GenericArg1Address); GenericArg1ArrayDim = Native_UProperty.Get_ArrayDim(GenericArg1Address); } break; case EPropertyType.Map: GenericArg1Address = Native_UMapProperty.Get_KeyProp(address); if (GenericArg1Address != IntPtr.Zero) { GenericArg1Size = Native_UProperty.Get_ElementSize(GenericArg1Address); GenericArg1ArrayDim = Native_UProperty.Get_ArrayDim(GenericArg1Address); } GenericArg2Address = Native_UMapProperty.Get_ValueProp(address); if (GenericArg2Address != IntPtr.Zero) { GenericArg2Size = Native_UProperty.Get_ElementSize(GenericArg2Address); GenericArg2ArrayDim = Native_UProperty.Get_ArrayDim(GenericArg2Address); } break; case EPropertyType.Set: GenericArg1Address = Native_USetProperty.Get_ElementProp(address); if (GenericArg1Address != IntPtr.Zero) { GenericArg1Size = Native_UProperty.Get_ElementSize(GenericArg1Address); GenericArg1ArrayDim = Native_UProperty.Get_ArrayDim(GenericArg1Address); } break; } } return(true); } return(false); }