Merging //UE4/Dev-Main @ 4114877 to Dev-Core (//UE4/Dev-Core)

#rb none

[CL 4115001 by Robert Manuszewski in Dev-Core branch]
This commit is contained in:
Robert Manuszewski
2018-06-06 00:53:29 -04:00
parent fc7573e41a
commit bf397b2e1f
807 changed files with 12733 additions and 10006 deletions

View File

@@ -6,6 +6,7 @@
#include "PyWrapperObject.h"
#include "PyWrapperStruct.h"
#include "PyWrapperEnum.h"
#include "PyWrapperDelegate.h"
#include "PyWrapperName.h"
#include "PyWrapperText.h"
@@ -57,7 +58,7 @@ FPyConversionResult NativizeStructInstance(PyObject* PyObj, UScriptStruct* Struc
StructType->CopyScriptStruct(StructInstance, PyStruct->StructInstance);
}
PYCONVERSION_RETURN(Result, TEXT("Nativize"), *FString::Printf(TEXT("Cannot nativize '%s' as '%s'"), *PyUtil::GetFriendlyTypename(PyObj), *StructType->GetName()));
PYCONVERSION_RETURN(Result, TEXT("Nativize"), *FString::Printf(TEXT("Cannot nativize '%s' as '%s'"), *PyUtil::GetFriendlyTypename(PyObj), *PyUtil::GetFriendlyTypename(PyStructType)));
}
FPyConversionResult PythonizeStructInstance(UScriptStruct* StructType, const void* StructInstance, PyObject*& OutPyObj, const ESetErrorState SetErrorState)
@@ -627,6 +628,48 @@ PyObject* PythonizeStruct(UScriptStruct* Val, const ESetErrorState SetErrorState
return Obj;
}
FPyConversionResult NativizeEnumEntry(PyObject* PyObj, const UEnum* EnumType, int64& OutVal, const ESetErrorState SetErrorState)
{
FPyConversionResult Result = FPyConversionResult::Failure();
PyTypeObject* PyEnumType = FPyWrapperTypeRegistry::Get().GetWrappedEnumType(EnumType);
FPyWrapperEnumPtr PyEnum = FPyWrapperEnumPtr::StealReference(FPyWrapperEnum::CastPyObject(PyObj, PyEnumType, &Result));
if (PyEnum)
{
OutVal = FPyWrapperEnum::GetEnumEntryValue(PyEnum);
}
PYCONVERSION_RETURN(Result, TEXT("Nativize"), *FString::Printf(TEXT("Cannot nativize '%s' as '%s'"), *PyUtil::GetFriendlyTypename(PyObj), *PyUtil::GetFriendlyTypename(PyEnumType)));
}
FPyConversionResult PythonizeEnumEntry(const int64 Val, const UEnum* EnumType, PyObject*& OutPyObj, const ESetErrorState SetErrorState)
{
PyTypeObject* PyEnumType = FPyWrapperTypeRegistry::Get().GetWrappedEnumType(EnumType);
if (const FPyWrapperEnumMetaData* PyEnumMetaData = FPyWrapperEnumMetaData::GetMetaData(PyEnumType))
{
// Find an enum entry using this value
for (FPyWrapperEnum* PyEnumEntry : PyEnumMetaData->EnumEntries)
{
const int64 EnumEntryVal = FPyWrapperEnum::GetEnumEntryValue(PyEnumEntry);
if (EnumEntryVal == Val)
{
Py_INCREF(PyEnumEntry);
OutPyObj = (PyObject*)PyEnumEntry;
return FPyConversionResult::Success();
}
}
}
PYCONVERSION_RETURN(FPyConversionResult::Failure(), TEXT("Nativize"), *FString::Printf(TEXT("Cannot pythonize '%d' (int64) as '%s'"), Val, *PyUtil::GetFriendlyTypename(PyEnumType)));
}
PyObject* PythonizeEnumEntry(const int64 Val, const UEnum* EnumType, const ESetErrorState SetErrorState)
{
PyObject* Obj = nullptr;
PythonizeEnumEntry(Val, EnumType, Obj, SetErrorState);
return Obj;
}
FPyConversionResult NativizeProperty(PyObject* PyObj, const UProperty* Prop, void* ValueAddr, const FPyWrapperOwnerContext& InChangeOwner, const ESetErrorState SetErrorState)
{
#define PYCONVERSION_PROPERTY_RETURN(RESULT) \
@@ -709,7 +752,6 @@ FPyConversionResult NativizeProperty_Direct(PyObject* PyObj, const UProperty* Pr
NATIVIZE_SETTER_PROPERTY(UBoolProperty);
NATIVIZE_INLINE_PROPERTY(UInt8Property);
NATIVIZE_INLINE_PROPERTY(UByteProperty);
NATIVIZE_INLINE_PROPERTY(UInt16Property);
NATIVIZE_INLINE_PROPERTY(UUInt16Property);
NATIVIZE_INLINE_PROPERTY(UIntProperty);
@@ -722,10 +764,74 @@ FPyConversionResult NativizeProperty_Direct(PyObject* PyObj, const UProperty* Pr
NATIVIZE_INLINE_PROPERTY(UNameProperty);
NATIVIZE_INLINE_PROPERTY(UTextProperty);
if (auto* CastProp = Cast<UByteProperty>(Prop))
{
uint8 NewValue = 0;
FPyConversionResult Result = FPyConversionResult::Failure();
if (CastProp->Enum)
{
int64 EnumVal = 0;
Result = NativizeEnumEntry(PyObj, CastProp->Enum, EnumVal, SetErrorState);
if (Result.GetState() == EPyConversionResultState::SuccessWithCoercion)
{
// Don't allow implicit conversion on enum properties
Result.SetState(EPyConversionResultState::Failure);
}
if (Result)
{
NewValue = (uint8)EnumVal;
}
}
else
{
Result = Nativize(PyObj, NewValue, SetErrorState);
}
if (Result)
{
auto* ValuePtr = static_cast<uint8*>(ValueAddr);
if (*ValuePtr != NewValue)
{
EmitPropertyChangeNotifications(InChangeOwner, [&]()
{
*ValuePtr = NewValue;
});
}
}
PYCONVERSION_PROPERTY_RETURN(Result);
}
if (auto* CastProp = Cast<UEnumProperty>(Prop))
{
FPyConversionResult Result = FPyConversionResult::Failure();
UNumericProperty* EnumInternalProp = CastProp->GetUnderlyingProperty();
PYCONVERSION_PROPERTY_RETURN(EnumInternalProp ? NativizeProperty_Direct(PyObj, EnumInternalProp, ValueAddr, InChangeOwner, SetErrorState) : FPyConversionResult::Failure());
if (EnumInternalProp)
{
int64 NewValue = 0;
Result = NativizeEnumEntry(PyObj, CastProp->GetEnum(), NewValue, SetErrorState);
if (Result.GetState() == EPyConversionResultState::SuccessWithCoercion)
{
// Don't allow implicit conversion on enum properties
Result.SetState(EPyConversionResultState::Failure);
}
if (Result)
{
const int64 OldValue = EnumInternalProp->GetSignedIntPropertyValue(ValueAddr);
if (OldValue != NewValue)
{
EmitPropertyChangeNotifications(InChangeOwner, [&]()
{
EnumInternalProp->SetIntPropertyValue(ValueAddr, NewValue);
});
}
}
}
PYCONVERSION_PROPERTY_RETURN(Result);
}
if (auto* CastProp = Cast<UClassProperty>(Prop))
@@ -912,7 +1018,6 @@ FPyConversionResult PythonizeProperty_Direct(const UProperty* Prop, const void*
PYTHONIZE_GETTER_PROPERTY(UBoolProperty);
PYTHONIZE_GETTER_PROPERTY(UInt8Property);
PYTHONIZE_GETTER_PROPERTY(UByteProperty);
PYTHONIZE_GETTER_PROPERTY(UInt16Property);
PYTHONIZE_GETTER_PROPERTY(UUInt16Property);
PYTHONIZE_GETTER_PROPERTY(UIntProperty);
@@ -925,10 +1030,23 @@ FPyConversionResult PythonizeProperty_Direct(const UProperty* Prop, const void*
PYTHONIZE_GETTER_PROPERTY(UNameProperty);
PYTHONIZE_GETTER_PROPERTY(UTextProperty);
if (auto* CastProp = Cast<UByteProperty>(Prop))
{
const uint8 Value = CastProp->GetPropertyValue(ValueAddr);
if (CastProp->Enum)
{
PYCONVERSION_PROPERTY_RETURN(PythonizeEnumEntry((int64)Value, CastProp->Enum, OutPyObj, SetErrorState));
}
else
{
PYCONVERSION_PROPERTY_RETURN(Pythonize(Value, OutPyObj, SetErrorState));
}
}
if (auto* CastProp = Cast<UEnumProperty>(Prop))
{
UNumericProperty* EnumInternalProp = CastProp->GetUnderlyingProperty();
PYCONVERSION_PROPERTY_RETURN(EnumInternalProp ? PythonizeProperty_Direct(EnumInternalProp, ValueAddr, OutPyObj, ConversionMethod, OwnerPyObj, SetErrorState) : FPyConversionResult::Failure());
PYCONVERSION_PROPERTY_RETURN(PythonizeEnumEntry(EnumInternalProp ? EnumInternalProp->GetSignedIntPropertyValue(ValueAddr) : 0, CastProp->GetEnum(), OutPyObj, SetErrorState));
}
if (auto* CastProp = Cast<UClassProperty>(Prop))

View File

@@ -100,6 +100,11 @@ namespace PyConversion
FPyConversionResult PythonizeStruct(UScriptStruct* Val, PyObject*& OutPyObj, const ESetErrorState SetErrorState = ESetErrorState::Yes);
PyObject* PythonizeStruct(UScriptStruct* Val, const ESetErrorState SetErrorState = ESetErrorState::Yes);
/** Conversion for enum entries */
FPyConversionResult NativizeEnumEntry(PyObject* PyObj, const UEnum* EnumType, int64& OutVal, const ESetErrorState SetErrorState = ESetErrorState::Yes);
FPyConversionResult PythonizeEnumEntry(const int64 Val, const UEnum* EnumType, PyObject*& OutPyObj, const ESetErrorState SetErrorState = ESetErrorState::Yes);
PyObject* PythonizeEnumEntry(const int64 Val, const UEnum* EnumType, const ESetErrorState SetErrorState = ESetErrorState::Yes);
namespace Internal
{
/** Internal version of NativizeStructInstance/PythonizeStructInstance that work on the type-erased data */
@@ -193,16 +198,23 @@ namespace PyConversion
/** Conversion for known enum types */
template <typename T>
FPyConversionResult NativizeEnum(PyObject* PyObj, T& OutVal, const ESetErrorState SetErrorState = ESetErrorState::Yes)
FPyConversionResult NativizeEnumEntry(PyObject* PyObj, const UEnum* EnumType, T& OutVal, const ESetErrorState SetErrorState = ESetErrorState::Yes)
{
return Nativize(PyObj, (__underlying_type(T)&)OutVal, SetErrorState);
int64 OutTmpVal = 0;
FPyConversionResult Result = NativizeEnumEntry(PyObj, EnumType, OutTmpVal, SetErrorState);
if (Result)
{
OutVal = (T)OutTmpVal;
}
return Result;
}
/** Conversion for known enum types */
template <typename T>
FPyConversionResult PythonizeEnum(const T& Val, PyObject*& OutPyObj, const ESetErrorState SetErrorState = ESetErrorState::Yes)
FPyConversionResult PythonizeEnumEntry(const T& Val, const UEnum* EnumType, PyObject*& OutPyObj, const ESetErrorState SetErrorState = ESetErrorState::Yes)
{
return Pythonize((__underlying_type(T))Val, OutPyObj, SetErrorState);
const int64 TmpVal = (int64)Val;
return PythonizeEnumEntry(TmpVal, EnumType, OutPyObj, SetErrorState);
}
/** Conversion for property instances (including fixed arrays) - ValueAddr should point to the property data */

View File

@@ -49,7 +49,7 @@ void FPyFileWriter::Write(const FString& InStr)
void FPyFileWriter::WriteDocString(const TCHAR* InDocString)
{
if (*InDocString)
if (InDocString && *InDocString)
{
WriteDocString(FString(InDocString));
}

View File

@@ -653,24 +653,34 @@ void FGeneratedWrappedEnumType::Finalize_PostReady()
{
FGeneratedWrappedType::Finalize_PostReady();
check(MetaData.IsValid() && MetaData->GetTypeId() == FPyWrapperEnumMetaData::StaticTypeId());
TSharedRef<FPyWrapperEnumMetaData> EnumMetaData = StaticCastSharedRef<FPyWrapperEnumMetaData>(MetaData.ToSharedRef());
// Execute Python code within this block
{
FPyScopedGIL GIL;
for (const FGeneratedWrappedEnumEntry& EnumEntry : EnumEntries)
{
FPyWrapperEnum::SetEnumEntryValue(&PyType, EnumEntry.EntryValue, EnumEntry.EntryName.GetData(), EnumEntry.EntryDoc.GetData());
FPyWrapperEnum* PyEnumEntry = FPyWrapperEnum::AddEnumEntry(&PyType, EnumEntry.EntryValue, EnumEntry.EntryName.GetData(), EnumEntry.EntryDoc.GetData());
if (PyEnumEntry)
{
EnumMetaData->EnumEntries.Add(PyEnumEntry);
}
}
}
EnumMetaData->bFinalized = true;
}
void FGeneratedWrappedEnumType::ExtractEnumEntries(const UEnum* InEnum)
{
for (int32 EnumEntryIndex = 0; EnumEntryIndex < InEnum->NumEnums() - 1; ++EnumEntryIndex)
{
// todo: deprecated enum entries?
if (ShouldExportEnumEntry(InEnum, EnumEntryIndex))
{
FGeneratedWrappedEnumEntry& EnumEntry = EnumEntries.AddDefaulted_GetRef();
EnumEntry.EntryName = TCHARToUTF8Buffer(*PythonizeName(InEnum->GetNameStringByIndex(EnumEntryIndex), EPythonizeNameCase::Upper));
EnumEntry.EntryName = TCHARToUTF8Buffer(*GetEnumEntryPythonName(InEnum, EnumEntryIndex));
EnumEntry.EntryDoc = TCHARToUTF8Buffer(*PythonizeTooltip(GetEnumEntryTooltip(InEnum, EnumEntryIndex)));
EnumEntry.EntryValue = InEnum->GetValueByIndex(EnumEntryIndex);
}
@@ -2326,6 +2336,34 @@ TArray<FString> GetDeprecatedEnumPythonNames(const UEnum* InEnum)
return GetDeprecatedFieldPythonNamesImpl(InEnum, ScriptNameMetaDataKey);
}
FString GetEnumEntryPythonName(const UEnum* InEnum, const int32 InEntryIndex)
{
FString EnumEntryName;
// First see if we have a name override in the meta-data
{
EnumEntryName = InEnum->GetMetaData(TEXT("ScriptName"), InEntryIndex);
// This may be a semi-colon separated list - the first item is the one we want for the current name
if (!EnumEntryName.IsEmpty())
{
int32 SemiColonIndex = INDEX_NONE;
if (EnumEntryName.FindChar(TEXT(';'), SemiColonIndex))
{
EnumEntryName.RemoveAt(SemiColonIndex, EnumEntryName.Len() - SemiColonIndex, /*bAllowShrinking*/false);
}
}
}
// Just use the entry name if we have no meta-data
if (EnumEntryName.IsEmpty())
{
EnumEntryName = InEnum->GetNameStringByIndex(InEntryIndex);
}
return PythonizeName(EnumEntryName, EPythonizeNameCase::Upper);
}
FString GetDelegatePythonName(const UFunction* InDelegateSignature)
{
return InDelegateSignature->GetName().LeftChop(19); // Trim the "__DelegateSignature" suffix from the name

View File

@@ -1003,6 +1003,9 @@ namespace PyGenUtil
/** Get the deprecated Python names of the given enum */
TArray<FString> GetDeprecatedEnumPythonNames(const UEnum* InEnum);
/** Get the Python name of the given enum entry */
FString GetEnumEntryPythonName(const UEnum* InEnum, const int32 InEntryIndex);
/** Get the Python name of the given delegate signature */
FString GetDelegatePythonName(const UFunction* InDelegateSignature);

View File

@@ -8,6 +8,7 @@
#include "PyConversion.h"
#include "PyWrapperTypeRegistry.h"
#include "UObject/Package.h"
#include "Framework/Application/SlateApplication.h"
#if PLATFORM_WINDOWS
@@ -139,8 +140,9 @@ PyObject* ParentExternalWindowToSlate(PyObject* InSelf, PyObject* InArgs)
return nullptr;
}
static const UEnum* ParentWindowSearchMethodEnum = FindObject<UEnum>(ANY_PACKAGE, TEXT("ESlateParentWindowSearchMethod"));
ESlateParentWindowSearchMethod ParentWindowSearchMethod = ESlateParentWindowSearchMethod::ActiveWindow;
if (PyParentWindowSearchMethod && !PyConversion::NativizeEnum(PyParentWindowSearchMethod, ParentWindowSearchMethod))
if (PyParentWindowSearchMethod && !PyConversion::NativizeEnumEntry(PyParentWindowSearchMethod, ParentWindowSearchMethodEnum, ParentWindowSearchMethod))
{
PyUtil::SetPythonError(PyExc_TypeError, TEXT("parent_external_window_to_slate"), *FString::Printf(TEXT("Failed to convert argument '%s' to 'SlateParentWindowSearchMethod'"), *PyUtil::GetFriendlyTypename(PyParentWindowSearchMethod)));
return nullptr;

View File

@@ -637,6 +637,11 @@ int ValidateContainerIndexParam(const Py_ssize_t InIndex, const Py_ssize_t InLen
return 0;
}
Py_ssize_t ResolveContainerIndexParam(const Py_ssize_t InIndex, const Py_ssize_t InLen)
{
return InIndex < 0 ? InIndex + InLen : InIndex;
}
UObject* GetOwnerObject(PyObject* InPyObj)
{
FPyWrapperOwnerContext OwnerContext = FPyWrapperOwnerContext(InPyObj);

View File

@@ -215,6 +215,9 @@ namespace PyUtil
/** Validate that the given index is valid for the container length */
int ValidateContainerIndexParam(const Py_ssize_t InIndex, const Py_ssize_t InLen, const UProperty* InProp, const TCHAR* InErrorCtxt);
/** Resolve a container index (taking into account negative indices) */
Py_ssize_t ResolveContainerIndexParam(const Py_ssize_t InIndex, const Py_ssize_t InLen);
/**
* Given a Python object, try and get the owner Unreal object for the instance.
* For wrapped objects this is the wrapped instance, for wrapped structs it will attempt to walk through the owner chain to find a wrapped object.

View File

@@ -407,16 +407,17 @@ PyObject* FPyWrapperArray::GetItem(FPyWrapperArray* InSelf, Py_ssize_t InIndex)
FScriptArrayHelper SelfScriptArrayHelper(InSelf->ArrayProp, InSelf->ArrayInstance);
const int32 ElementCount = SelfScriptArrayHelper.Num();
const Py_ssize_t ResolvedIndex = PyUtil::ResolveContainerIndexParam(InIndex, ElementCount);
if (PyUtil::ValidateContainerIndexParam(InIndex, ElementCount, InSelf->ArrayProp, *PyUtil::GetErrorContext(InSelf)) != 0)
if (PyUtil::ValidateContainerIndexParam(ResolvedIndex, ElementCount, InSelf->ArrayProp, *PyUtil::GetErrorContext(InSelf)) != 0)
{
return nullptr;
}
PyObject* PyItemObj = nullptr;
if (!PyConversion::PythonizeProperty(InSelf->ArrayProp->Inner, SelfScriptArrayHelper.GetRawPtr(InIndex), PyItemObj))
if (!PyConversion::PythonizeProperty(InSelf->ArrayProp->Inner, SelfScriptArrayHelper.GetRawPtr(ResolvedIndex), PyItemObj))
{
PyUtil::SetPythonError(PyExc_TypeError, InSelf, *FString::Printf(TEXT("Failed to convert element property '%s' (%s) at index %d"), *InSelf->ArrayProp->Inner->GetName(), *InSelf->ArrayProp->Inner->GetClass()->GetName(), InIndex));
PyUtil::SetPythonError(PyExc_TypeError, InSelf, *FString::Printf(TEXT("Failed to convert element property '%s' (%s) at index %d"), *InSelf->ArrayProp->Inner->GetName(), *InSelf->ArrayProp->Inner->GetClass()->GetName(), ResolvedIndex));
return nullptr;
}
return PyItemObj;
@@ -431,16 +432,17 @@ int FPyWrapperArray::SetItem(FPyWrapperArray* InSelf, Py_ssize_t InIndex, PyObje
FScriptArrayHelper SelfScriptArrayHelper(InSelf->ArrayProp, InSelf->ArrayInstance);
const int32 ElementCount = SelfScriptArrayHelper.Num();
const Py_ssize_t ResolvedIndex = PyUtil::ResolveContainerIndexParam(InIndex, ElementCount);
const int ValidateIndexResult = PyUtil::ValidateContainerIndexParam(InIndex, ElementCount, InSelf->ArrayProp, *PyUtil::GetErrorContext(InSelf));
const int ValidateIndexResult = PyUtil::ValidateContainerIndexParam(ResolvedIndex, ElementCount, InSelf->ArrayProp, *PyUtil::GetErrorContext(InSelf));
if (ValidateIndexResult != 0)
{
return ValidateIndexResult;
}
if (!PyConversion::NativizeProperty(InValue, InSelf->ArrayProp->Inner, SelfScriptArrayHelper.GetRawPtr(InIndex)))
if (!PyConversion::NativizeProperty(InValue, InSelf->ArrayProp->Inner, SelfScriptArrayHelper.GetRawPtr(ResolvedIndex)))
{
PyUtil::SetPythonError(PyExc_TypeError, InSelf, *FString::Printf(TEXT("Failed to convert element property '%s' (%s) at index %d"), *InSelf->ArrayProp->Inner->GetName(), *InSelf->ArrayProp->Inner->GetClass()->GetName(), InIndex));
PyUtil::SetPythonError(PyExc_TypeError, InSelf, *FString::Printf(TEXT("Failed to convert element property '%s' (%s) at index %d"), *InSelf->ArrayProp->Inner->GetName(), *InSelf->ArrayProp->Inner->GetClass()->GetName(), ResolvedIndex));
return -1;
}
@@ -640,9 +642,11 @@ Py_ssize_t FPyWrapperArray::Index(FPyWrapperArray* InSelf, PyObject* InValue, Py
FScriptArrayHelper SelfScriptArrayHelper(InSelf->ArrayProp, InSelf->ArrayInstance);
const int32 ElementCount = SelfScriptArrayHelper.Num();
const Py_ssize_t ResolvedStartIndex = PyUtil::ResolveContainerIndexParam(InStartIndex, ElementCount);
const Py_ssize_t ResolvedStopIndex = PyUtil::ResolveContainerIndexParam(InStopIndex, ElementCount);
const int32 StartIndex = FMath::Min((int32)InStartIndex, ElementCount);
const int32 StopIndex = FMath::Max((int32)InStopIndex, ElementCount);
const int32 StartIndex = FMath::Min((int32)ResolvedStartIndex, ElementCount);
const int32 StopIndex = FMath::Max((int32)ResolvedStopIndex, ElementCount);
int32 ReturnIndex = INDEX_NONE;
for (int32 ElementIndex = StartIndex; ElementIndex < StopIndex; ++ElementIndex)
@@ -672,8 +676,9 @@ int FPyWrapperArray::Insert(FPyWrapperArray* InSelf, Py_ssize_t InIndex, PyObjec
FScriptArrayHelper SelfScriptArrayHelper(InSelf->ArrayProp, InSelf->ArrayInstance);
const int32 ElementCount = SelfScriptArrayHelper.Num();
const Py_ssize_t ResolvedIndex = PyUtil::ResolveContainerIndexParam(InIndex, ElementCount);
const int32 InsertIndex = FMath::Min((int32)InIndex, ElementCount);
const int32 InsertIndex = FMath::Min((int32)ResolvedIndex, ElementCount);
SelfScriptArrayHelper.InsertValues(InsertIndex);
if (!PyConversion::NativizeProperty(InValue, InSelf->ArrayProp->Inner, SelfScriptArrayHelper.GetRawPtr(InsertIndex)))
@@ -695,21 +700,21 @@ PyObject* FPyWrapperArray::Pop(FPyWrapperArray* InSelf, Py_ssize_t InIndex)
FScriptArrayHelper SelfScriptArrayHelper(InSelf->ArrayProp, InSelf->ArrayInstance);
const int32 ElementCount = SelfScriptArrayHelper.Num();
const Py_ssize_t ResolvedIndex = PyUtil::ResolveContainerIndexParam(InIndex, ElementCount);
int32 ValueIndex = InIndex == -1 ? ElementCount - 1 : (int32)InIndex;
if (PyUtil::ValidateContainerIndexParam(ValueIndex, ElementCount, InSelf->ArrayProp, *PyUtil::GetErrorContext(InSelf)) != 0)
if (PyUtil::ValidateContainerIndexParam(ResolvedIndex, ElementCount, InSelf->ArrayProp, *PyUtil::GetErrorContext(InSelf)) != 0)
{
return nullptr;
}
PyObject* PyReturnValue = nullptr;
if (!PyConversion::PythonizeProperty(InSelf->ArrayProp->Inner, SelfScriptArrayHelper.GetRawPtr(ValueIndex), PyReturnValue))
if (!PyConversion::PythonizeProperty(InSelf->ArrayProp->Inner, SelfScriptArrayHelper.GetRawPtr(ResolvedIndex), PyReturnValue))
{
PyUtil::SetPythonError(PyExc_TypeError, InSelf, *FString::Printf(TEXT("Failed to convert element property '%s' (%s) at index %d"), *InSelf->ArrayProp->Inner->GetName(), *InSelf->ArrayProp->Inner->GetClass()->GetName(), ValueIndex));
PyUtil::SetPythonError(PyExc_TypeError, InSelf, *FString::Printf(TEXT("Failed to convert element property '%s' (%s) at index %d"), *InSelf->ArrayProp->Inner->GetName(), *InSelf->ArrayProp->Inner->GetClass()->GetName(), ResolvedIndex));
return nullptr;
}
SelfScriptArrayHelper.RemoveValues(ValueIndex);
SelfScriptArrayHelper.RemoveValues(ResolvedIndex);
return PyReturnValue;
}
@@ -991,7 +996,12 @@ PyTypeObject InitializePyWrapperArrayType()
return nullptr;
}
const Py_ssize_t SliceLen = FMath::Max(InSliceStop - InSliceStart, (Py_ssize_t)0);
FScriptArrayHelper SelfScriptArrayHelper(InSelf->ArrayProp, InSelf->ArrayInstance);
const int32 SelfElementCount = SelfScriptArrayHelper.Num();
const Py_ssize_t ResolvedSliceStart = PyUtil::ResolveContainerIndexParam(InSliceStart, SelfElementCount);
const Py_ssize_t ResolvedSliceStop = PyUtil::ResolveContainerIndexParam(InSliceStop, SelfElementCount);
const Py_ssize_t SliceLen = FMath::Max(ResolvedSliceStop - ResolvedSliceStart, (Py_ssize_t)0);
const PyUtil::FPropertyDef SelfElementDef = InSelf->ArrayProp->Inner;
FPyWrapperArrayPtr NewArray = FPyWrapperArrayPtr::StealReference(FPyWrapperArray::New(Py_TYPE(InSelf)));
@@ -1000,13 +1010,12 @@ PyTypeObject InitializePyWrapperArrayType()
return nullptr;
}
FScriptArrayHelper SelfScriptArrayHelper(InSelf->ArrayProp, InSelf->ArrayInstance);
FScriptArrayHelper NewScriptArrayHelper(NewArray->ArrayProp, NewArray->ArrayInstance);
NewScriptArrayHelper.Resize(SliceLen);
for (Py_ssize_t ElementIndex = InSliceStart; ElementIndex < InSliceStop; ++ElementIndex)
for (Py_ssize_t ElementIndex = ResolvedSliceStart; ElementIndex < ResolvedSliceStop; ++ElementIndex)
{
InSelf->ArrayProp->Inner->CopyCompleteValue(NewScriptArrayHelper.GetRawPtr((int32)(ElementIndex - InSliceStart)), SelfScriptArrayHelper.GetRawPtr((int32)ElementIndex));
InSelf->ArrayProp->Inner->CopyCompleteValue(NewScriptArrayHelper.GetRawPtr((int32)(ElementIndex - ResolvedSliceStart)), SelfScriptArrayHelper.GetRawPtr((int32)ElementIndex));
}
return (PyObject*)NewArray.Release();
}
@@ -1018,7 +1027,12 @@ PyTypeObject InitializePyWrapperArrayType()
return -1;
}
const Py_ssize_t SliceLen = FMath::Max(InSliceStop - InSliceStart, (Py_ssize_t)0);
FScriptArrayHelper SelfScriptArrayHelper(InSelf->ArrayProp, InSelf->ArrayInstance);
const int32 SelfElementCount = SelfScriptArrayHelper.Num();
const Py_ssize_t ResolvedSliceStart = PyUtil::ResolveContainerIndexParam(InSliceStart, SelfElementCount);
const Py_ssize_t ResolvedSliceStop = PyUtil::ResolveContainerIndexParam(InSliceStop, SelfElementCount);
const Py_ssize_t SliceLen = FMath::Max(ResolvedSliceStop - ResolvedSliceStart, (Py_ssize_t)0);
// Value will be null when performing a slice delete
FPyWrapperArrayPtr Value;
@@ -1033,18 +1047,17 @@ PyTypeObject InitializePyWrapperArrayType()
}
}
FScriptArrayHelper SelfScriptArrayHelper(InSelf->ArrayProp, InSelf->ArrayInstance);
SelfScriptArrayHelper.RemoveValues((int32)InSliceStart, (int32)SliceLen);
SelfScriptArrayHelper.RemoveValues((int32)ResolvedSliceStart, (int32)SliceLen);
if (Value)
{
FScriptArrayHelper ValueScriptArrayHelper(Value->ArrayProp, Value->ArrayInstance);
const int32 ValueElementCount = ValueScriptArrayHelper.Num();
SelfScriptArrayHelper.InsertValues((int32)InSliceStart, ValueElementCount);
SelfScriptArrayHelper.InsertValues((int32)ResolvedSliceStart, ValueElementCount);
for (int32 ElementIndex = 0; ElementIndex < ValueElementCount; ++ElementIndex)
{
InSelf->ArrayProp->Inner->CopyCompleteValue(SelfScriptArrayHelper.GetRawPtr(InSliceStart + ElementIndex), ValueScriptArrayHelper.GetRawPtr(ElementIndex));
InSelf->ArrayProp->Inner->CopyCompleteValue(SelfScriptArrayHelper.GetRawPtr(ResolvedSliceStart + ElementIndex), ValueScriptArrayHelper.GetRawPtr(ElementIndex));
}
}

View File

@@ -11,17 +11,53 @@
/** Python type for FPyWrapperEnum */
extern PyTypeObject PyWrapperEnumType;
/** Python type for FPyWrapperEnumValueDescrObject */
extern PyTypeObject PyWrapperEnumValueDescrType;
/** Initialize the PyWrapperEnum types and add them to the given Python module */
void InitializePyWrapperEnum(PyGenUtil::FNativePythonModule& ModuleInfo);
/** Type for all UE4 exposed enum instances */
/** Type for all UE4 exposed enum instances (an instance is created for each entry in the enum, before the enum type is locked for creating new instances) */
struct FPyWrapperEnum : public FPyWrapperBase
{
/** Name of this enum entry */
PyObject* EntryName;
/** Value of this enum entry */
PyObject* EntryValue;
/** New this wrapper instance (called via tp_new for Python, or directly in C++) */
static FPyWrapperEnum* New(PyTypeObject* InType);
/** Free this wrapper instance (called via tp_dealloc for Python) */
static void Free(FPyWrapperEnum* InSelf);
/** Initialize this wrapper instance (called via tp_init for Python, or directly in C++) */
static int Init(FPyWrapperEnum* InSelf);
/** Set the given enum entry value on the given enum type */
static void SetEnumEntryValue(PyTypeObject* InType, const int64 InEnumValue, const char* InEnumValueName, const char* InEnumValueDoc);
/** Initialize this wrapper instance (called directly in C++) */
static int Init(FPyWrapperEnum* InSelf, const int64 InEnumEntryValue, const char* InEnumEntryName);
/** Deinitialize this wrapper instance (called via Init and Free to restore the instance to its New state) */
static void Deinit(FPyWrapperEnum* InSelf);
/** Called to validate the internal state of this wrapper instance prior to operating on it (should be called by all functions that expect to operate on an initialized type; will set an error state on failure) */
static bool ValidateInternalState(FPyWrapperEnum* InSelf);
/** Cast the given Python object to this wrapped type (returns a new reference) */
static FPyWrapperEnum* CastPyObject(PyObject* InPyObject, FPyConversionResult* OutCastResult = nullptr);
/** Cast the given Python object to this wrapped type, or attempt to convert the type into a new wrapped instance (returns a new reference) */
static FPyWrapperEnum* CastPyObject(PyObject* InPyObject, PyTypeObject* InType, FPyConversionResult* OutCastResult = nullptr);
/** Get the name of the enum entry as a string */
static FString GetEnumEntryName(FPyWrapperEnum* InSelf);
/** Get the value of the enum entry as an int */
static int64 GetEnumEntryValue(FPyWrapperEnum* InSelf);
/** Add the given enum entry on the given enum type (returns borrowed reference) */
static FPyWrapperEnum* AddEnumEntry(PyTypeObject* InType, const int64 InEnumEntryValue, const char* InEnumEntryName, const char* InEnumEntryDoc);
};
/** Meta-data for all UE4 exposed enum types */
@@ -34,14 +70,32 @@ struct FPyWrapperEnumMetaData : public FPyWrapperBaseMetaData
/** Get the UEnum from the given type */
static UEnum* GetEnum(PyTypeObject* PyType);
/** Get the UEnum from the type of the given instance */
static UEnum* GetEnum(FPyWrapperEnum* Instance);
/** Check to see if the enum is deprecated, and optionally return its deprecation message */
static bool IsEnumDeprecated(PyTypeObject* PyType, FString* OutDeprecationMessage = nullptr);
/** Check to see if the enum is deprecated, and optionally return its deprecation message */
static bool IsEnumDeprecated(FPyWrapperEnum* Instance, FString* OutDeprecationMessage = nullptr);
/** Check to see if the enum is finalized */
static bool IsEnumFinalized(PyTypeObject* PyType);
/** Check to see if the enum is finalized */
static bool IsEnumFinalized(FPyWrapperEnum* Instance);
/** Unreal enum */
UEnum* Enum;
/** True if this enum type has been finalized after having all of its entries added to it */
bool bFinalized;
/** Set if this struct is deprecated and using it should emit a deprecation warning */
TOptional<FString> DeprecationMessage;
/** Array of enum entries in the order they were added (enum entries are stored as borrowed references) */
TArray<FPyWrapperEnum*> EnumEntries;
};
typedef TPyPtr<FPyWrapperEnum> FPyWrapperEnumPtr;

View File

@@ -386,15 +386,16 @@ PyObject* FPyWrapperFixedArray::GetItem(FPyWrapperFixedArray* InSelf, Py_ssize_t
return nullptr;
}
if (PyUtil::ValidateContainerIndexParam(InIndex, InSelf->ArrayProp->ArrayDim, InSelf->ArrayProp, *PyUtil::GetErrorContext(InSelf)) != 0)
const Py_ssize_t ResolvedIndex = PyUtil::ResolveContainerIndexParam(InIndex, InSelf->ArrayProp->ArrayDim);
if (PyUtil::ValidateContainerIndexParam(ResolvedIndex, InSelf->ArrayProp->ArrayDim, InSelf->ArrayProp, *PyUtil::GetErrorContext(InSelf)) != 0)
{
return nullptr;
}
PyObject* PyItemObj = nullptr;
if (!PyConversion::PythonizeProperty_Direct(InSelf->ArrayProp, GetItemPtr(InSelf, InIndex), PyItemObj))
if (!PyConversion::PythonizeProperty_Direct(InSelf->ArrayProp, GetItemPtr(InSelf, ResolvedIndex), PyItemObj))
{
PyUtil::SetPythonError(PyExc_TypeError, InSelf, *FString::Printf(TEXT("Failed to convert property '%s' (%s) at index %d"), *InSelf->ArrayProp->GetName(), *InSelf->ArrayProp->GetClass()->GetName(), InIndex));
PyUtil::SetPythonError(PyExc_TypeError, InSelf, *FString::Printf(TEXT("Failed to convert property '%s' (%s) at index %d"), *InSelf->ArrayProp->GetName(), *InSelf->ArrayProp->GetClass()->GetName(), ResolvedIndex));
return nullptr;
}
return PyItemObj;
@@ -407,15 +408,16 @@ int FPyWrapperFixedArray::SetItem(FPyWrapperFixedArray* InSelf, Py_ssize_t InInd
return -1;
}
const int ValidateIndexResult = PyUtil::ValidateContainerIndexParam(InIndex, InSelf->ArrayProp->ArrayDim, InSelf->ArrayProp, *PyUtil::GetErrorContext(InSelf));
const Py_ssize_t ResolvedIndex = PyUtil::ResolveContainerIndexParam(InIndex, InSelf->ArrayProp->ArrayDim);
const int ValidateIndexResult = PyUtil::ValidateContainerIndexParam(ResolvedIndex, InSelf->ArrayProp->ArrayDim, InSelf->ArrayProp, *PyUtil::GetErrorContext(InSelf));
if (ValidateIndexResult != 0)
{
return ValidateIndexResult;
}
if (!PyConversion::NativizeProperty_Direct(InValue, InSelf->ArrayProp, GetItemPtr(InSelf, InIndex)))
if (!PyConversion::NativizeProperty_Direct(InValue, InSelf->ArrayProp, GetItemPtr(InSelf, ResolvedIndex)))
{
PyUtil::SetPythonError(PyExc_TypeError, InSelf, *FString::Printf(TEXT("Failed to convert property '%s' (%s) at index %d"), *InSelf->ArrayProp->GetName(), *InSelf->ArrayProp->GetClass()->GetName(), InIndex));
PyUtil::SetPythonError(PyExc_TypeError, InSelf, *FString::Printf(TEXT("Failed to convert property '%s' (%s) at index %d"), *InSelf->ArrayProp->GetName(), *InSelf->ArrayProp->GetClass()->GetName(), ResolvedIndex));
return -1;
}
@@ -688,7 +690,9 @@ PyTypeObject InitializePyWrapperFixedArrayType()
return nullptr;
}
const Py_ssize_t SliceLen = InSliceStop - InSliceStart;
const Py_ssize_t ResolvedSliceStart = PyUtil::ResolveContainerIndexParam(InSliceStart, InSelf->ArrayProp->ArrayDim);
const Py_ssize_t ResolvedSliceStop = PyUtil::ResolveContainerIndexParam(InSliceStop, InSelf->ArrayProp->ArrayDim);
const Py_ssize_t SliceLen = ResolvedSliceStop - ResolvedSliceStart;
if (SliceLen <= 0)
{
PyUtil::SetPythonError(PyExc_Exception, InSelf, TEXT("Slice length must be greater than zero"));
@@ -702,9 +706,9 @@ PyTypeObject InitializePyWrapperFixedArrayType()
return nullptr;
}
for (Py_ssize_t ArrIndex = InSliceStart; ArrIndex < InSliceStop; ++ArrIndex)
for (Py_ssize_t ArrIndex = ResolvedSliceStart; ArrIndex < ResolvedSliceStop; ++ArrIndex)
{
InSelf->ArrayProp->CopySingleValue(FPyWrapperFixedArray::GetItemPtr(NewArray, ArrIndex - InSliceStart), FPyWrapperFixedArray::GetItemPtr(InSelf, ArrIndex));
InSelf->ArrayProp->CopySingleValue(FPyWrapperFixedArray::GetItemPtr(NewArray, ArrIndex - ResolvedSliceStart), FPyWrapperFixedArray::GetItemPtr(InSelf, ArrIndex));
}
return (PyObject*)NewArray.Release();
}

View File

@@ -50,7 +50,7 @@ struct FPyWrapperFixedArray : public FPyWrapperBase
/** Cast the given Python object to this wrapped type, or attempt to convert the type into a new wrapped instance (returns a new reference) */
static FPyWrapperFixedArray* CastPyObject(PyObject* InPyObject, PyTypeObject* InType, const PyUtil::FPropertyDef& InPropDef, FPyConversionResult* OutCastResult = nullptr);
/** Get the raw pointer to the element at index N */
/** Get the raw pointer to the element at index N (negative indexing not supported) */
static void* GetItemPtr(FPyWrapperFixedArray* InSelf, Py_ssize_t InIndex);
/** Get the length of this container (equivalent to 'len(x)' in Python) */

View File

@@ -2104,13 +2104,14 @@ void FPyWrapperTypeRegistry::GenerateStubCodeForWrappedTypes() const
void FPyWrapperTypeRegistry::GenerateStubCodeForWrappedType(PyTypeObject* PyType, const PyGenUtil::FGeneratedWrappedType* GeneratedTypeData, FPyFileWriter& OutPythonScript, FPyOnlineDocsSection* OutOnlineDocsSection)
{
OutPythonScript.WriteLine(FString::Printf(TEXT("class %s(%s):"), UTF8_TO_TCHAR(PyType->tp_name), UTF8_TO_TCHAR(PyType->tp_base->tp_name)));
const FString PyTypeName = UTF8_TO_TCHAR(PyType->tp_name);
OutPythonScript.WriteLine(FString::Printf(TEXT("class %s(%s):"), *PyTypeName, UTF8_TO_TCHAR(PyType->tp_base->tp_name)));
OutPythonScript.IncreaseIndent();
OutPythonScript.WriteDocString(UTF8_TO_TCHAR(PyType->tp_doc));
if (OutOnlineDocsSection)
{
OutOnlineDocsSection->AccumulateClass(UTF8_TO_TCHAR(PyType->tp_name));
OutOnlineDocsSection->AccumulateClass(*PyTypeName);
}
auto GetFunctionReturnValue = [](const void* InBaseParamsAddr, const TArray<PyGenUtil::FGeneratedWrappedMethodParameter>& InOutputParams) -> FString
@@ -2332,13 +2333,8 @@ void FPyWrapperTypeRegistry::GenerateStubCodeForWrappedType(PyTypeObject* PyType
if (MetaGuid == FPyWrapperObjectMetaData::StaticTypeId())
{
// Skip the __init__ function on derived object types as the base one is already correct
bWriteDefaultInit = false;
bHasExportedClassData = true;
OutPythonScript.WriteLine(TEXT("def __init__(self, outer=None, name=\"None\"):"));
OutPythonScript.IncreaseIndent();
OutPythonScript.WriteLine(TEXT("pass"));
OutPythonScript.DecreaseIndent();
}
else if (MetaGuid == FPyWrapperStructMetaData::StaticTypeId())
{
@@ -2394,11 +2390,55 @@ void FPyWrapperTypeRegistry::GenerateStubCodeForWrappedType(PyTypeObject* PyType
}
else if (MetaGuid == FPyWrapperEnumMetaData::StaticTypeId())
{
// Enums cannot be instanced, they don't have an __init__ function exposed
// Skip the __init__ function on derived enums
bWriteDefaultInit = false;
}
// todo: have correct __init__ signatures for the other intrinsic types?
}
else if (PyType == &PyWrapperObjectType)
{
bWriteDefaultInit = false;
bHasExportedClassData = true;
OutPythonScript.WriteLine(TEXT("def __init__(self, outer=None, name=\"None\"):"));
OutPythonScript.IncreaseIndent();
OutPythonScript.WriteLine(TEXT("pass"));
OutPythonScript.DecreaseIndent();
}
else if (PyType == &PyWrapperEnumType)
{
// Enums don't really have an __init__ function at runtime, so just give them a default one (with no arguments)
bWriteDefaultInit = false;
OutPythonScript.WriteLine(TEXT("def __init__(self):"));
OutPythonScript.IncreaseIndent();
OutPythonScript.WriteLine(TEXT("pass"));
OutPythonScript.DecreaseIndent();
}
else if (PyType == &PyWrapperEnumValueDescrType)
{
bWriteDefaultInit = false;
bHasExportedClassData = true;
// This is a special internal decorator type used to define enum entries, which is why it has __get__ as well as __init__
OutPythonScript.WriteLine(TEXT("def __init__(self, enum, name, value):"));
OutPythonScript.IncreaseIndent();
OutPythonScript.WriteLine(TEXT("self.enum = enum"));
OutPythonScript.WriteLine(TEXT("self.name = name"));
OutPythonScript.WriteLine(TEXT("self.value = value"));
OutPythonScript.DecreaseIndent();
OutPythonScript.WriteLine(TEXT("def __get__(self, obj, type=None):"));
OutPythonScript.IncreaseIndent();
OutPythonScript.WriteLine(TEXT("return self"));
OutPythonScript.DecreaseIndent();
// It also needs a __repr__ function for Sphinx to generate docs correctly
OutPythonScript.WriteLine(TEXT("def __repr__(self):"));
OutPythonScript.IncreaseIndent();
OutPythonScript.WriteLine(TEXT("return \"{0}.{1}\".format(self.enum, self.name)"));
OutPythonScript.DecreaseIndent();
}
if (bWriteDefaultInit)
{
@@ -2544,25 +2584,31 @@ void FPyWrapperTypeRegistry::GenerateStubCodeForWrappedType(PyTypeObject* PyType
}
else if (MetaGuid == FPyWrapperEnumMetaData::StaticTypeId())
{
// Export enum values
// Also see https://www.python.org/dev/peps/pep-0435/
// Export enum entries
const PyGenUtil::FGeneratedWrappedEnumType* EnumType = static_cast<const PyGenUtil::FGeneratedWrappedEnumType*>(GeneratedTypeData);
bool bFirstEnumMember = true;
for (const PyGenUtil::FGeneratedWrappedEnumEntry& EnumMember : EnumType->EnumEntries)
if (EnumType->EnumEntries.Num() > 0)
{
bHasExportedClassData = true;
// Add extra line break for first enum member
OutPythonScript.WriteNewLine();
if (bFirstEnumMember)
for (const PyGenUtil::FGeneratedWrappedEnumEntry& EnumMember : EnumType->EnumEntries)
{
// Add extra line break for first enum member
OutPythonScript.WriteNewLine();
bFirstEnumMember = false;
}
const FString EntryName = UTF8_TO_TCHAR(EnumMember.EntryName.GetData());
const FString EntryValue = LexToString(EnumMember.EntryValue);
ExportConstantValue(UTF8_TO_TCHAR(EnumMember.EntryName.GetData()), UTF8_TO_TCHAR(EnumMember.EntryDoc.GetData()), *LexToString(EnumMember.EntryValue));
FString EntryDoc = UTF8_TO_TCHAR(EnumMember.EntryDoc.GetData());
if (EntryDoc.IsEmpty())
{
EntryDoc = EntryValue;
}
else
{
EntryDoc.InsertAt(0, *FString::Printf(TEXT("%s: "), *EntryValue));
}
ExportConstantValue(*EntryName, *EntryDoc, *FString::Printf(TEXT("_EnumEntry(\"%s\", \"%s\", %s)"), *PyTypeName, *EntryName, *EntryValue));
}
}
}
}