Skip to content

Commit 798c082

Browse files
committed
all unit tests pass
1 parent f8f0fcc commit 798c082

File tree

2 files changed

+55
-67
lines changed

2 files changed

+55
-67
lines changed

Source/UnrealEnginePython/Private/PythonFunction.cpp

Lines changed: 47 additions & 64 deletions
Original file line numberDiff line numberDiff line change
@@ -55,15 +55,10 @@ void UPythonFunction::CallPythonCallable(FFrame& Stack, RESULT_DECL)
5555
uint8 *frame = Stack.Locals;
5656

5757
// is it a blueprint call ?
58-
UProperty *first_out_prop = nullptr;
5958
if (*Stack.Code == EX_EndFunctionParms) {
6059
for (UProperty *prop = (UProperty *)function->Children; prop; prop = (UProperty *)prop->Next) {
6160
if (prop->PropertyFlags & CPF_OutParm)
62-
{
63-
if (!first_out_prop)
64-
first_out_prop = prop;
6561
continue;
66-
}
6762
if (!on_error) {
6863
PyObject *arg = ue_py_convert_property(prop, (uint8 *)Stack.Locals, 0);
6964
if (!arg) {
@@ -76,16 +71,24 @@ void UPythonFunction::CallPythonCallable(FFrame& Stack, RESULT_DECL)
7671
}
7772
}
7873
}
79-
else {
74+
else
75+
{
8076
//UE_LOG(LogPython, Warning, TEXT("BLUEPRINT CALL"));
77+
// largely copied from ScriptCore.cpp::CallFunction
78+
// for BP calls, we need to set up the FOutParmRec stuff ourselves
79+
Stack.OutParms = NULL;
8180
frame = (uint8 *)FMemory_Alloca(function->PropertiesSize);
8281
FMemory::Memzero(frame, function->PropertiesSize);
83-
for (UProperty *prop = (UProperty *)function->Children; *Stack.Code != EX_EndFunctionParms; prop = (UProperty *)prop->Next) {
82+
for (UProperty *prop = (UProperty *)function->Children; *Stack.Code != EX_EndFunctionParms; prop = (UProperty *)prop->Next)
83+
{
8484
Stack.Step(Stack.Object, prop->ContainerPtrToValuePtr<uint8>(frame));
8585
if (prop->PropertyFlags & CPF_OutParm)
8686
{
87-
if (!first_out_prop)
88-
first_out_prop = prop;
87+
FOutParmRec *rec = (FOutParmRec*)FMemory_Alloca(sizeof(FOutParmRec));
88+
rec->Property = prop;
89+
rec->PropAddr = Stack.MostRecentPropertyAddress;
90+
rec->NextOutParm = Stack.OutParms;
91+
Stack.OutParms = rec;
8992
continue;
9093
}
9194
if (!on_error) {
@@ -115,75 +118,55 @@ void UPythonFunction::CallPythonCallable(FFrame& Stack, RESULT_DECL)
115118
return;
116119
}
117120

118-
// get return value and/or any out params
119-
if (PyTuple_Check(ret))
120-
{ // function has multiple output params or a return value and one or more output params
121-
int nret = PyTuple_Size(ret);
122-
int tuple_index = 0;
123-
for( TFieldIterator<UProperty> It(function); It && (It->PropertyFlags & CPF_Parm); ++It )
121+
// get return value and/or any out params - for convenience, if a single item is returned, wrap it in a tuple so that we can process
122+
// multi-out params and single out params with one block of code
123+
bool wrapped_ret = false;
124+
if (!PyTuple_Check(ret))
125+
{
126+
PyObject *wrapped = PyTuple_New(1);
127+
PyTuple_SetItem(wrapped, 0, ret);
128+
ret = wrapped;
129+
}
130+
131+
int nret = PyTuple_Size(ret);
132+
int tuple_index = 0;
133+
for (TFieldIterator<UProperty> It(function); It && (It->PropertyFlags & CPF_Parm); ++It)
134+
{
135+
if (!(It->PropertyFlags & CPF_OutParm))
136+
continue;
137+
if (tuple_index >= nret)
124138
{
125-
if( It->PropertyFlags & CPF_OutParm )
139+
UE_LOG(LogPython, Error, TEXT("Python function %s didn't return enough values"), *function->GetFName().ToString());
140+
break;
141+
}
142+
143+
UProperty *prop = *It;
144+
PyObject *py_obj = PyTuple_GetItem(ret, tuple_index);
145+
if (prop->PropertyFlags & CPF_ReturnParm)
146+
{ // handle the return value specially by have it write directly to the stack
147+
if (!ue_py_convert_pyobject(py_obj, prop, (uint8*)RESULT_PARAM - prop->GetOffset_ForUFunction(), 0))
126148
{
127-
if (tuple_index >= nret)
128-
{
129-
UE_LOG(LogPython, Error, TEXT("Python function %s didn't return enough values"), *function->GetFName().ToString());
130-
}
131-
else
132-
{
133-
UProperty *prop = *It;
134-
PyObject *py_obj = PyTuple_GetItem(ret, tuple_index);
135-
uint8 *out_frame = frame;
136-
for (FOutParmRec *rec = Stack.OutParms; rec != nullptr; rec = rec->NextOutParm)
137-
{
138-
if (rec->Property == prop)
139-
{
140-
out_frame = rec->PropAddr - prop->GetOffset_ForUFunction();
141-
break;
142-
}
143-
}
144-
if (ue_py_convert_pyobject(py_obj, prop, out_frame, 0))
145-
{
146-
if (prop->PropertyFlags & CPF_ReturnParm)
147-
{
148-
// copy value to stack result value
149-
//FMemory::Memcpy(RESULT_PARAM, frame + function->ReturnValueOffset, prop->ArrayDim * prop->ElementSize);
150-
}
151-
}
152-
else {
153-
UE_LOG(LogPython, Error, TEXT("Invalid return value type for function %s"), *function->GetFName().ToString());
154-
}
155-
tuple_index++;
156-
}
149+
UE_LOG(LogPython, Error, TEXT("Invalid return value type for function %s"), *function->GetFName().ToString());
157150
}
158151
}
159-
160-
}
161-
else
162-
{ // no output params, but maybe a return value
163-
if (first_out_prop)
152+
else
164153
{
165-
uint8 *out_frame = frame;
154+
uint8 *out_frame = frame;
166155
for (FOutParmRec *rec = Stack.OutParms; rec != nullptr; rec = rec->NextOutParm)
167156
{
168-
if (rec->Property == first_out_prop)
157+
if (rec->Property == prop)
169158
{
170-
out_frame = rec->PropAddr - first_out_prop->GetOffset_ForUFunction();
159+
out_frame = rec->PropAddr - prop->GetOffset_ForUFunction();
171160
break;
172161
}
173162
}
174-
if (ue_py_convert_pyobject(ret, first_out_prop, out_frame, 0))
163+
if (!ue_py_convert_pyobject(py_obj, prop, out_frame, 0))
175164
{
176-
if (function->ReturnValueOffset != MAX_uint16)
177-
{
178-
// copy value to stack result value
179-
//FMemory::Memcpy(RESULT_PARAM, frame + function->ReturnValueOffset, first_out_prop->ArrayDim * first_out_prop->ElementSize);
180-
}
181-
}
182-
else {
183-
UE_LOG(LogPython, Error, TEXT("Invalid return value type for function %s"), *function->GetFName().ToString());
165+
UE_LOG(LogPython, Error, TEXT("Failed to convert output property for function %s"), *function->GetFName().ToString());
184166
}
185167
}
186-
}
168+
tuple_index++;
169+
}
187170
Py_DECREF(ret);
188171
}
189172

Source/UnrealEnginePython/Private/UEPyModule.cpp

Lines changed: 8 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -3401,23 +3401,28 @@ UFunction *unreal_engine_add_function(UClass *u_class, char *name, PyObject *py_
34013401

34023402
// allocate properties storage (ignore super)
34033403
TFieldIterator<UProperty> props(function, EFieldIteratorFlags::ExcludeSuper);
3404-
bool has_out_params = false;
3404+
int num_out_params = 0;
3405+
int num_return_params = 0;
34053406
for (; props; ++props)
34063407
{
34073408
UProperty *p = *props;
34083409
if (p->HasAnyPropertyFlags(CPF_Parm))
34093410
{
34103411
if (p->HasAnyPropertyFlags(CPF_OutParm))
3411-
has_out_params = true;
3412+
num_out_params++;
34123413
function->NumParms++;
34133414
function->ParmsSize = p->GetOffset_ForUFunction() + p->GetSize();
34143415
if (p->HasAnyPropertyFlags(CPF_ReturnParm))
34153416
{
34163417
function->ReturnValueOffset = p->GetOffset_ForUFunction();
3418+
num_return_params++;
34173419
}
34183420
}
34193421
}
3420-
if (has_out_params)
3422+
3423+
// UProps have both out + return flags set on the property that is the return value, but a function with a return value but no other
3424+
// out properties does not get HasOutParms set.
3425+
if (num_out_params > 0) //num_return_params)
34213426
function_flags |= FUNC_HasOutParms;
34223427

34233428
if (parent_function)

0 commit comments

Comments
 (0)