@@ -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
0 commit comments