3
0

PythonUtility.cpp 34 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730
  1. /*
  2. * Copyright (c) Contributors to the Open 3D Engine Project.
  3. * For complete copyright and license terms please see the LICENSE at the root of this distribution.
  4. *
  5. * SPDX-License-Identifier: Apache-2.0 OR MIT
  6. *
  7. */
  8. #include <Source/PythonUtility.h>
  9. #include <Source/PythonProxyObject.h>
  10. #include <Source/PythonTypeCasters.h>
  11. #include <Source/PythonMarshalComponent.h>
  12. #include <AzCore/RTTI/BehaviorContext.h>
  13. #include <AzCore/RTTI/TypeInfo.h>
  14. #include <AzCore/Serialization/SerializeContext.h>
  15. #include <AzFramework/StringFunc/StringFunc.h>
  16. #include <EditorPythonBindings/CustomTypeBindingBus.h>
  17. #include <pybind11/embed.h>
  18. namespace EditorPythonBindings
  19. {
  20. namespace Module
  21. {
  22. pybind11::module DeterminePackageModule(PackageMapType& modulePackageMap, AZStd::string_view moduleName, pybind11::module parentModule, pybind11::module fallbackModule, [[maybe_unused]] bool alertUsingFallback)
  23. {
  24. if (moduleName.empty() || !moduleName[0])
  25. {
  26. AZ_Warning("python", !alertUsingFallback, "Could not determine missing or empty module; using fallback module");
  27. return fallbackModule;
  28. }
  29. else if (parentModule.is_none())
  30. {
  31. AZ_Warning("python", !alertUsingFallback, "Could not determine using None parent module; using fallback module");
  32. return fallbackModule;
  33. }
  34. AZStd::string parentModuleName(PyModule_GetName(parentModule.ptr()));
  35. modulePackageMap[parentModuleName] = parentModule;
  36. pybind11::module currentModule = parentModule;
  37. AZStd::string fullModuleName(parentModuleName);
  38. fullModuleName.append(".");
  39. fullModuleName.append(moduleName);
  40. AZStd::vector<AZStd::string> moduleParts;
  41. AzFramework::StringFunc::Tokenize(fullModuleName.c_str(), moduleParts, ".", false, false);
  42. for (int modulePartsIndex = 0; modulePartsIndex < moduleParts.size(); ++modulePartsIndex)
  43. {
  44. AZStd::string currentModulePath;
  45. AzFramework::StringFunc::Join(currentModulePath, moduleParts.begin(), moduleParts.begin() + modulePartsIndex + 1, ".");
  46. auto itPackageEntry = modulePackageMap.find(currentModulePath.c_str());
  47. if (itPackageEntry != modulePackageMap.end())
  48. {
  49. currentModule = itPackageEntry->second;
  50. }
  51. else
  52. {
  53. PyObject* newModule = PyImport_AddModule(currentModulePath.c_str());
  54. if (!newModule)
  55. {
  56. AZ_Warning("python", false, "Could not add module named %s; using fallback module", currentModulePath.c_str());
  57. return fallbackModule;
  58. }
  59. else
  60. {
  61. auto newSubModule = pybind11::reinterpret_borrow<pybind11::module>(newModule);
  62. modulePackageMap[currentModulePath] = newSubModule;
  63. const char* subModuleName = moduleParts[modulePartsIndex].c_str();
  64. currentModule.attr(subModuleName) = newSubModule;
  65. currentModule = newSubModule;
  66. }
  67. }
  68. }
  69. return currentModule;
  70. }
  71. }
  72. namespace Internal
  73. {
  74. void LogSerializeTypeInfo(const AZ::TypeId& typeId)
  75. {
  76. AZStd::string info = AZStd::string::format("Serialize class info for typeId %s (", typeId.ToString<AZStd::string>().c_str());
  77. AZ::SerializeContext* serializeContext{ nullptr };
  78. AZ::ComponentApplicationBus::BroadcastResult(serializeContext, &AZ::ComponentApplicationRequests::GetSerializeContext);
  79. if (serializeContext)
  80. {
  81. auto&& classInfo = serializeContext->FindClassData(typeId);
  82. if (classInfo)
  83. {
  84. info = AZStd::string::format("name:%s version:%d isContainer:%s",
  85. classInfo->m_name, classInfo->m_version, classInfo->m_container ? "true" : "false");
  86. }
  87. auto&& genericClassInfo = serializeContext->FindGenericClassInfo(typeId);
  88. if (genericClassInfo)
  89. {
  90. info += " generic:true";
  91. info += AZStd::string::format(" specialized typeId: %s",
  92. genericClassInfo->GetSpecializedTypeId().ToString<AZStd::string>().c_str());
  93. info += AZStd::string::format(" generic typeId: %s",
  94. genericClassInfo->GetGenericTypeId().ToString<AZStd::string>().c_str());
  95. size_t numTemplatedArguments = genericClassInfo->GetNumTemplatedArguments();
  96. info += AZStd::string::format(" template arguments %zu", genericClassInfo->GetNumTemplatedArguments());
  97. for (size_t index = 0; index < numTemplatedArguments; ++index)
  98. {
  99. info += AZStd::string::format(" [%zu] template type: %s",
  100. index,
  101. genericClassInfo->GetTemplatedTypeId(index).ToString<AZStd::string>().c_str());
  102. }
  103. }
  104. }
  105. info += ")";
  106. AZ_Warning("python", false, "Serialize generic class info %s", info.c_str());
  107. }
  108. AZStd::optional<AZ::TypeId> IsEnumClass(const AZ::BehaviorParameter& behaviorParameter)
  109. {
  110. if (behaviorParameter.m_azRtti)
  111. {
  112. // If the underlying type of the supplied type is different, then T is an enum
  113. AZ::TypeId underlyingTypeId = AZ::Internal::GetUnderlyingTypeId(*behaviorParameter.m_azRtti);
  114. if (underlyingTypeId != behaviorParameter.m_typeId)
  115. {
  116. return AZStd::make_optional(underlyingTypeId);
  117. }
  118. }
  119. return AZStd::nullopt;
  120. }
  121. template <typename T>
  122. bool ConvertPythonFromEnumClass(const AZ::TypeId& underlyingTypeId, AZ::BehaviorArgument& behaviorValue, AZ::s64& outboundPythonValue)
  123. {
  124. if (underlyingTypeId == AZ::AzTypeInfo<T>::Uuid())
  125. {
  126. outboundPythonValue = aznumeric_cast<AZ::s64>(*behaviorValue.GetAsUnsafe<T>());
  127. return true;
  128. }
  129. return false;
  130. }
  131. AZStd::optional<pybind11::object> ConvertFromEnumClass(AZ::BehaviorArgument& behaviorValue)
  132. {
  133. if (!behaviorValue.m_azRtti)
  134. {
  135. return AZStd::nullopt;
  136. }
  137. AZ::TypeId underlyingTypeId = AZ::Internal::GetUnderlyingTypeId(*behaviorValue.m_azRtti);
  138. if (underlyingTypeId != behaviorValue.m_typeId)
  139. {
  140. AZ::s64 outboundPythonValue = 0;
  141. bool converted =
  142. ConvertPythonFromEnumClass<long>(underlyingTypeId, behaviorValue, outboundPythonValue) ||
  143. ConvertPythonFromEnumClass<unsigned long>(underlyingTypeId, behaviorValue, outboundPythonValue) ||
  144. ConvertPythonFromEnumClass<AZ::u8>(underlyingTypeId, behaviorValue, outboundPythonValue) ||
  145. ConvertPythonFromEnumClass<AZ::u16>(underlyingTypeId, behaviorValue, outboundPythonValue) ||
  146. ConvertPythonFromEnumClass<AZ::u32>(underlyingTypeId, behaviorValue, outboundPythonValue) ||
  147. ConvertPythonFromEnumClass<AZ::u64>(underlyingTypeId, behaviorValue, outboundPythonValue) ||
  148. ConvertPythonFromEnumClass<AZ::s8>(underlyingTypeId, behaviorValue, outboundPythonValue) ||
  149. ConvertPythonFromEnumClass<AZ::s16>(underlyingTypeId, behaviorValue, outboundPythonValue) ||
  150. ConvertPythonFromEnumClass<AZ::s32>(underlyingTypeId, behaviorValue, outboundPythonValue) ||
  151. ConvertPythonFromEnumClass<AZ::s64>(underlyingTypeId, behaviorValue, outboundPythonValue);
  152. AZ_Error("python", converted, "Enumeration backed by a non-numeric integer type.");
  153. return converted ? AZStd::make_optional(pybind11::cast<AZ::s64>(AZStd::move(outboundPythonValue))) : AZStd::nullopt;
  154. }
  155. return AZStd::nullopt;
  156. }
  157. template <typename T>
  158. bool ConvertBehaviorParameterEnum(pybind11::object obj, const AZ::TypeId& underlyingTypeId, AZ::BehaviorArgument& parameter)
  159. {
  160. if (underlyingTypeId == AZ::AzTypeInfo<T>::Uuid())
  161. {
  162. void* value = parameter.m_tempData.allocate(sizeof(T), AZStd::alignment_of<T>::value, 0);
  163. *reinterpret_cast<T*>(value) = pybind11::cast<T>(obj);
  164. if (parameter.m_traits & AZ::BehaviorParameter::TR_POINTER)
  165. {
  166. *reinterpret_cast<void**>(parameter.m_value) = reinterpret_cast<T*>(&value);
  167. }
  168. else
  169. {
  170. parameter.m_value = value;
  171. }
  172. return true;
  173. }
  174. return false;
  175. }
  176. bool ConvertEnumClassFromPython(pybind11::object obj, const AZ::BehaviorParameter& behaviorArgument, AZ::BehaviorArgument& parameter)
  177. {
  178. if (behaviorArgument.m_azRtti)
  179. {
  180. // If the underlying type of the supplied type is different, then T is an enum
  181. const AZ::TypeId underlyingTypeId = AZ::Internal::GetUnderlyingTypeId(*behaviorArgument.m_azRtti);
  182. if (underlyingTypeId != behaviorArgument.m_typeId)
  183. {
  184. parameter.m_name = behaviorArgument.m_name;
  185. parameter.m_azRtti = behaviorArgument.m_azRtti;
  186. parameter.m_traits = behaviorArgument.m_traits;
  187. parameter.m_typeId = behaviorArgument.m_typeId;
  188. bool handled =
  189. ConvertBehaviorParameterEnum<long>(obj, underlyingTypeId, parameter) ||
  190. ConvertBehaviorParameterEnum<unsigned long>(obj, underlyingTypeId, parameter) ||
  191. ConvertBehaviorParameterEnum<AZ::u8>(obj, underlyingTypeId, parameter) ||
  192. ConvertBehaviorParameterEnum<AZ::u16>(obj, underlyingTypeId, parameter) ||
  193. ConvertBehaviorParameterEnum<AZ::u32>(obj, underlyingTypeId, parameter) ||
  194. ConvertBehaviorParameterEnum<AZ::u64>(obj, underlyingTypeId, parameter) ||
  195. ConvertBehaviorParameterEnum<AZ::s8>(obj, underlyingTypeId, parameter) ||
  196. ConvertBehaviorParameterEnum<AZ::s16>(obj, underlyingTypeId, parameter) ||
  197. ConvertBehaviorParameterEnum<AZ::s32>(obj, underlyingTypeId, parameter) ||
  198. ConvertBehaviorParameterEnum<AZ::s64>(obj, underlyingTypeId, parameter) ;
  199. AZ_Error("python", handled, "Enumeration backed by a non-numeric integer type.");
  200. return handled;
  201. }
  202. }
  203. return false;
  204. }
  205. // type checks
  206. bool IsPrimitiveType(const AZ::TypeId& typeId)
  207. {
  208. return (
  209. typeId == AZ::AzTypeInfo<bool>::Uuid() ||
  210. typeId == AZ::AzTypeInfo<char>::Uuid() ||
  211. typeId == AZ::AzTypeInfo<float>::Uuid() ||
  212. typeId == AZ::AzTypeInfo<double>::Uuid() ||
  213. typeId == AZ::AzTypeInfo<long>::Uuid() ||
  214. typeId == AZ::AzTypeInfo<unsigned long>::Uuid() ||
  215. typeId == AZ::AzTypeInfo<AZ::s8>::Uuid() ||
  216. typeId == AZ::AzTypeInfo<AZ::u8>::Uuid() ||
  217. typeId == AZ::AzTypeInfo<AZ::s16>::Uuid() ||
  218. typeId == AZ::AzTypeInfo<AZ::u16>::Uuid() ||
  219. typeId == AZ::AzTypeInfo<AZ::s32>::Uuid() ||
  220. typeId == AZ::AzTypeInfo<AZ::u32>::Uuid() ||
  221. typeId == AZ::AzTypeInfo<AZ::s64>::Uuid() ||
  222. typeId == AZ::AzTypeInfo<AZ::u64>::Uuid());
  223. }
  224. bool IsPointerType(const AZ::u32 traits)
  225. {
  226. return (((traits & AZ::BehaviorParameter::TR_POINTER) == AZ::BehaviorParameter::TR_POINTER) ||
  227. ((traits & AZ::BehaviorParameter::TR_REFERENCE) == AZ::BehaviorParameter::TR_REFERENCE));
  228. }
  229. // allocation patterns
  230. void StoreVariableCustomTypeDeleter(
  231. CustomTypeBindingNotifications::ValueHandle handle,
  232. AZ::TypeId typeId,
  233. Convert::StackVariableAllocator& stackVariableAllocator)
  234. {
  235. auto deallocateValue = [typeId = std::move(typeId), handle]() mutable
  236. {
  237. CustomTypeBindingNotificationBus::Event(
  238. typeId,
  239. &CustomTypeBindingNotificationBus::Events::CleanUpValue,
  240. handle);
  241. };
  242. stackVariableAllocator.StoreVariableDeleter(deallocateValue);
  243. }
  244. bool AllocateBehaviorObjectByClass(const AZ::BehaviorClass* behaviorClass, AZ::BehaviorObject& behaviorObject)
  245. {
  246. if (behaviorClass)
  247. {
  248. if (behaviorClass->m_defaultConstructor)
  249. {
  250. AZ::BehaviorObject newBehaviorObject = behaviorClass->Create();
  251. behaviorObject.m_typeId = newBehaviorObject.m_typeId;
  252. behaviorObject.m_address = newBehaviorObject.m_address;
  253. return true;
  254. }
  255. else
  256. {
  257. AZ_Warning("python", behaviorClass->m_defaultConstructor, "Missing default constructor for AZ::BehaviorClass for typeId:%s", behaviorClass->m_name.c_str());
  258. }
  259. }
  260. return false;
  261. }
  262. bool AllocateBehaviorValueParameter(const AZ::BehaviorMethod* behaviorMethod, AZ::BehaviorArgument& result, Convert::StackVariableAllocator& stackVariableAllocator)
  263. {
  264. if (const AZ::BehaviorParameter* resultType = behaviorMethod->GetResult())
  265. {
  266. result.Set(*resultType);
  267. if (auto underlyingTypeId = Internal::IsEnumClass(result); underlyingTypeId)
  268. {
  269. result.m_typeId = underlyingTypeId.value();
  270. }
  271. if (resultType->m_traits & AZ::BehaviorParameter::TR_POINTER)
  272. {
  273. result.m_value = result.m_tempData.allocate(sizeof(intptr_t), alignof(intptr_t));
  274. return true;
  275. }
  276. if (resultType->m_traits & AZ::BehaviorParameter::TR_REFERENCE)
  277. {
  278. return true;
  279. }
  280. if (IsPrimitiveType(resultType->m_typeId))
  281. {
  282. result.m_value = result.m_tempData.allocate(sizeof(intptr_t), alignof(intptr_t));
  283. return true;
  284. }
  285. AZ::BehaviorContext* behaviorContext(nullptr);
  286. AZ::ComponentApplicationBus::BroadcastResult(behaviorContext, &AZ::ComponentApplicationRequests::GetBehaviorContext);
  287. if (!behaviorContext)
  288. {
  289. AZ_Assert(false, "A behavior context is required!");
  290. return false;
  291. }
  292. const AZ::BehaviorClass* behaviorClass = AZ::BehaviorContextHelper::GetClass(behaviorContext, resultType->m_typeId);
  293. if (behaviorClass)
  294. {
  295. AZ::BehaviorObject behaviorObject;
  296. if (AllocateBehaviorObjectByClass(behaviorClass, behaviorObject))
  297. {
  298. result.m_value = behaviorObject.m_address;
  299. result.m_typeId = resultType->m_typeId;
  300. return true;
  301. }
  302. }
  303. else
  304. {
  305. CustomTypeBindingNotifications::AllocationHandle allocationHandleResult;
  306. CustomTypeBindingNotificationBus::EventResult(
  307. allocationHandleResult,
  308. result.m_typeId,
  309. &CustomTypeBindingNotificationBus::Events::AllocateDefault);
  310. if (allocationHandleResult)
  311. {
  312. CustomTypeBindingNotifications::ValueHandle handle = allocationHandleResult.value().first;
  313. const AZ::BehaviorObject& behaviorObject = allocationHandleResult.value().second;
  314. StoreVariableCustomTypeDeleter(handle, behaviorObject.m_typeId, stackVariableAllocator);
  315. result.m_value = behaviorObject.m_address;
  316. result.m_typeId = behaviorObject.m_typeId;
  317. return true;
  318. }
  319. // So far no allocation scheme has been found for this typeId, but the SerializeContext might have more information
  320. // so this code tries to pull out more type information about the typeId so that the user can get more human readable
  321. // information than a UUID
  322. LogSerializeTypeInfo(resultType->m_typeId);
  323. AZ_Error("python", behaviorClass, "A behavior class for method %s is missing for type '%s' (%s)!",
  324. behaviorMethod->m_name.c_str(),
  325. resultType->m_name,
  326. resultType->m_typeId.ToString<AZStd::string>().c_str());
  327. }
  328. }
  329. return false;
  330. }
  331. void DeallocateBehaviorValueParameter(AZ::BehaviorArgument& valueParameter)
  332. {
  333. if (IsPointerType(valueParameter.m_traits) || IsPrimitiveType(valueParameter.m_typeId))
  334. {
  335. // no constructor was used
  336. return;
  337. }
  338. AZ::BehaviorContext* behaviorContext(nullptr);
  339. AZ::ComponentApplicationBus::BroadcastResult(behaviorContext, &AZ::ComponentApplicationRequests::GetBehaviorContext);
  340. if (!behaviorContext)
  341. {
  342. AZ_Assert(false, "A behavior context is required!");
  343. }
  344. const AZ::BehaviorClass* behaviorClass = AZ::BehaviorContextHelper::GetClass(behaviorContext, valueParameter.m_typeId);
  345. if (behaviorClass)
  346. {
  347. AZ::BehaviorObject behaviorObject;
  348. behaviorObject.m_address = valueParameter.m_value;
  349. behaviorObject.m_typeId = valueParameter.m_typeId;
  350. behaviorClass->Destroy(behaviorObject);
  351. }
  352. }
  353. }
  354. namespace Convert
  355. {
  356. // StackVariableAllocator
  357. StackVariableAllocator::~StackVariableAllocator()
  358. {
  359. for (auto& cleanUp : m_cleanUpItems)
  360. {
  361. cleanUp();
  362. }
  363. }
  364. void StackVariableAllocator::StoreVariableDeleter(VariableDeleter&& deleter)
  365. {
  366. m_cleanUpItems.emplace_back(deleter);
  367. }
  368. // from Python to BehaviorArgument
  369. bool PythonProxyObjectToBehaviorValueParameter(const AZ::BehaviorParameter& behaviorArgument, pybind11::object pyObj, AZ::BehaviorArgument& parameter)
  370. {
  371. auto behaviorObject = pybind11::cast<EditorPythonBindings::PythonProxyObject*>(pyObj)->GetBehaviorObject();
  372. if (behaviorObject)
  373. {
  374. const AZ::BehaviorClass* behaviorClass = AZ::BehaviorContextHelper::GetClass(behaviorObject.value()->m_typeId);
  375. if (!behaviorClass)
  376. {
  377. AZ_Warning("python", false, "Missing BehaviorClass for typeId %s", behaviorObject.value()->m_typeId.ToString<AZStd::string>().c_str());
  378. return false;
  379. }
  380. if (behaviorClass->m_azRtti)
  381. {
  382. // is exact type or can be down casted?
  383. if (!behaviorClass->m_azRtti->IsTypeOf(behaviorArgument.m_typeId))
  384. {
  385. return false;
  386. }
  387. }
  388. else if (behaviorObject.value()->m_typeId != behaviorArgument.m_typeId)
  389. {
  390. // type mismatch detected
  391. return false;
  392. }
  393. if ((behaviorArgument.m_traits & AZ::BehaviorParameter::TR_POINTER) == AZ::BehaviorParameter::TR_POINTER)
  394. {
  395. parameter.m_value = &behaviorObject.value()->m_address;
  396. }
  397. else
  398. {
  399. parameter.m_value = behaviorObject.value()->m_address;
  400. }
  401. parameter.m_typeId = behaviorClass->m_typeId;
  402. parameter.m_azRtti = behaviorClass->m_azRtti;
  403. parameter.m_traits = behaviorArgument.m_traits;
  404. parameter.m_name = behaviorArgument.m_name;
  405. return true;
  406. }
  407. return false;
  408. }
  409. bool CustomPythonToBehavior(
  410. const AZ::BehaviorParameter& behaviorArgument,
  411. pybind11::object pyObj,
  412. AZ::BehaviorArgument& outBehavior,
  413. StackVariableAllocator& stackVariableAllocator)
  414. {
  415. AZStd::optional<CustomTypeBindingNotifications::ValueHandle> handle;
  416. CustomTypeBindingNotificationBus::EventResult(
  417. handle,
  418. behaviorArgument.m_typeId,
  419. &CustomTypeBindingNotificationBus::Events::PythonToBehavior,
  420. pyObj.ptr(),
  421. static_cast<AZ::BehaviorParameter::Traits>(behaviorArgument.m_traits),
  422. outBehavior);
  423. if (handle)
  424. {
  425. Internal::StoreVariableCustomTypeDeleter(handle.value(), behaviorArgument.m_typeId, stackVariableAllocator);
  426. outBehavior.m_typeId = behaviorArgument.m_typeId;
  427. outBehavior.m_traits = behaviorArgument.m_traits;
  428. outBehavior.m_name = behaviorArgument.m_name;
  429. outBehavior.m_azRtti = behaviorArgument.m_azRtti;
  430. return true;
  431. }
  432. return false;
  433. }
  434. bool PythonToBehaviorValueParameter(const AZ::BehaviorParameter& behaviorArgument, pybind11::object pyObj, AZ::BehaviorArgument& parameter, Convert::StackVariableAllocator& stackVariableAllocator)
  435. {
  436. AZStd::optional<PythonMarshalTypeRequests::BehaviorValueResult> result;
  437. PythonMarshalTypeRequests::BehaviorTraits traits = static_cast<PythonMarshalTypeRequests::BehaviorTraits>(behaviorArgument.m_traits);
  438. PythonMarshalTypeRequestBus::EventResult(result, behaviorArgument.m_typeId, &PythonMarshalTypeRequestBus::Events::PythonToBehaviorValueParameter, traits, pyObj, parameter);
  439. if (result && result.value().first)
  440. {
  441. auto deleter = AZStd::move(result.value().second);
  442. if (deleter)
  443. {
  444. stackVariableAllocator.StoreVariableDeleter(AZStd::move(deleter));
  445. }
  446. parameter.m_typeId = behaviorArgument.m_typeId;
  447. parameter.m_traits = behaviorArgument.m_traits;
  448. parameter.m_name = behaviorArgument.m_name;
  449. parameter.m_azRtti = behaviorArgument.m_azRtti;
  450. return true;
  451. }
  452. else if (auto underlyingTypeId = Internal::IsEnumClass(behaviorArgument); underlyingTypeId)
  453. {
  454. AZ::BehaviorParameter tempArg;
  455. tempArg.m_azRtti = behaviorArgument.m_azRtti;
  456. tempArg.m_traits = behaviorArgument.m_traits;
  457. tempArg.m_name = behaviorArgument.m_name;
  458. tempArg.m_typeId = underlyingTypeId.value();
  459. if (PythonToBehaviorValueParameter(tempArg, pyObj, parameter, stackVariableAllocator))
  460. {
  461. parameter.m_typeId = behaviorArgument.m_typeId;
  462. return true;
  463. }
  464. }
  465. else if (pybind11::isinstance<EditorPythonBindings::PythonProxyObject>(pyObj))
  466. {
  467. return PythonProxyObjectToBehaviorValueParameter(behaviorArgument, pyObj, parameter);
  468. }
  469. else if (CustomPythonToBehavior(behaviorArgument, pyObj, parameter, stackVariableAllocator))
  470. {
  471. return true;
  472. }
  473. return false;
  474. }
  475. // from BehaviorArgument to Python
  476. AZStd::optional<pybind11::object> CustomBehaviorToPython(AZ::BehaviorArgument& behaviorValue, Convert::StackVariableAllocator& stackVariableAllocator)
  477. {
  478. AZStd::optional<CustomTypeBindingNotifications::ValueHandle> handle;
  479. PyObject* outPyObj = nullptr;
  480. CustomTypeBindingNotificationBus::EventResult(
  481. handle,
  482. behaviorValue.m_typeId,
  483. &CustomTypeBindingNotificationBus::Events::BehaviorToPython,
  484. behaviorValue,
  485. outPyObj);
  486. if (outPyObj != nullptr && handle)
  487. {
  488. Internal::StoreVariableCustomTypeDeleter(handle.value(), behaviorValue.m_typeId, stackVariableAllocator);
  489. return { pybind11::reinterpret_borrow<pybind11::object>(outPyObj) };
  490. }
  491. return AZStd::nullopt;
  492. }
  493. pybind11::object BehaviorValueParameterToPython(AZ::BehaviorArgument& behaviorValue, Convert::StackVariableAllocator& stackVariableAllocator)
  494. {
  495. auto pyValue = Internal::ConvertFromEnumClass(behaviorValue);
  496. if (pyValue.has_value())
  497. {
  498. return pyValue.value();
  499. }
  500. AZStd::optional<PythonMarshalTypeRequests::PythonValueResult> result;
  501. PythonMarshalTypeRequestBus::EventResult(result, behaviorValue.m_typeId, &PythonMarshalTypeRequestBus::Events::BehaviorValueParameterToPython, behaviorValue);
  502. if (result.has_value())
  503. {
  504. auto deleter = AZStd::move(result.value().second);
  505. if (deleter)
  506. {
  507. stackVariableAllocator.StoreVariableDeleter(AZStd::move(deleter));
  508. }
  509. return result.value().first;
  510. }
  511. else if (auto customResult = CustomBehaviorToPython(behaviorValue, stackVariableAllocator); customResult)
  512. {
  513. return customResult.value();
  514. }
  515. else if (behaviorValue.m_typeId != AZ::Uuid::CreateNull() && behaviorValue.GetValueAddress())
  516. {
  517. return PythonProxyObjectManagement::CreatePythonProxyObject(behaviorValue.m_typeId, behaviorValue.GetValueAddress());
  518. }
  519. AZ_Warning("python", false, "Cannot convert type %s",
  520. behaviorValue.m_name ? behaviorValue.m_name : behaviorValue.m_typeId.ToString<AZStd::string>().c_str());
  521. return pybind11::cast<pybind11::none>(Py_None);
  522. }
  523. AZStd::string GetPythonTypeName(pybind11::object pyObj)
  524. {
  525. if (pybind11::isinstance<PythonProxyObject>(pyObj))
  526. {
  527. return pybind11::cast<PythonProxyObject*>(pyObj)->GetWrappedTypeName();
  528. }
  529. return pybind11::cast<AZStd::string>(pybind11::str(pyObj.get_type()));
  530. }
  531. }
  532. namespace Call
  533. {
  534. constexpr size_t MaxBehaviorMethodArguments = 32;
  535. using BehaviorMethodArgumentArray = AZStd::array<AZ::BehaviorArgument, MaxBehaviorMethodArguments>;
  536. pybind11::object InvokeBehaviorMethodWithResult(AZ::BehaviorMethod* behaviorMethod, pybind11::args pythonInputArgs, AZ::BehaviorObject self, AZ::BehaviorArgument& result)
  537. {
  538. if (behaviorMethod->GetNumArguments() > MaxBehaviorMethodArguments || pythonInputArgs.size() > MaxBehaviorMethodArguments)
  539. {
  540. AZ_Error("python", false, "Too many arguments for class method; set:%zu max:%zu", behaviorMethod->GetMinNumberOfArguments(), MaxBehaviorMethodArguments);
  541. return pybind11::cast<pybind11::none>(Py_None);
  542. }
  543. Convert::StackVariableAllocator stackVariableAllocator;
  544. BehaviorMethodArgumentArray parameters;
  545. int parameterCount = 0;
  546. if (self.IsValid())
  547. {
  548. // record the "this" pointer's metadata like its RTTI so that it can be
  549. // down casted to a parent class type if needed to invoke a parent method
  550. AZ::BehaviorArgument theThisPointer;
  551. if (const AZ::BehaviorParameter* thisInfo = behaviorMethod->GetArgument(0))
  552. {
  553. // avoiding the "Special handling for the generic object holder." since it assumes
  554. // the BehaviorObject.m_value is a pointer; the reference version is already dereferenced
  555. if ((thisInfo->m_traits & AZ::BehaviorParameter::TR_POINTER) == AZ::BehaviorParameter::TR_POINTER)
  556. {
  557. theThisPointer.m_value = &self.m_address;
  558. }
  559. else
  560. {
  561. theThisPointer.m_value = self.m_address;
  562. }
  563. theThisPointer.Set(*thisInfo);
  564. parameters[0].Set(theThisPointer);
  565. ++parameterCount;
  566. }
  567. else
  568. {
  569. AZ_Warning("python", false, "Missing self info index 0 in class method %s", behaviorMethod->m_name.c_str());
  570. return pybind11::cast<pybind11::none>(Py_None);
  571. }
  572. }
  573. // prepare the parameters for the BehaviorMethod
  574. for (auto pythonArg : pythonInputArgs)
  575. {
  576. if (parameterCount < behaviorMethod->GetNumArguments())
  577. {
  578. auto currentPythonArg = pybind11::cast<pybind11::object>(pythonArg);
  579. const AZ::BehaviorParameter* behaviorArgument = behaviorMethod->GetArgument(parameterCount);
  580. if (!behaviorArgument)
  581. {
  582. AZ_Warning("python", false, "Missing argument at index %d in class method %s", parameterCount, behaviorMethod->m_name.c_str());
  583. return pybind11::cast<pybind11::none>(Py_None);
  584. }
  585. if (!Convert::PythonToBehaviorValueParameter(*behaviorArgument, currentPythonArg, parameters[parameterCount], stackVariableAllocator))
  586. {
  587. AZ_Warning("python", false, "BehaviorMethod %s: Parameter at [%d] index expects (%s:%s) for method but got type (%s)",
  588. behaviorMethod->m_name.c_str(), parameterCount,
  589. behaviorArgument->m_name, behaviorArgument->m_typeId.ToString<AZStd::string>().c_str(),
  590. Convert::GetPythonTypeName(currentPythonArg).c_str());
  591. return pybind11::cast<pybind11::none>(Py_None);
  592. }
  593. ++parameterCount;
  594. }
  595. }
  596. // did the Python script send the right amount of arguments?
  597. const auto totalPythonArgs = pythonInputArgs.size() + (self.IsValid() ? 1 : 0); // +1 for the 'this' coming in from a marshaled Python/BehaviorObject
  598. if (totalPythonArgs < behaviorMethod->GetMinNumberOfArguments())
  599. {
  600. AZ_Warning("python", false, "Method %s requires at least %zu parameters got %zu", behaviorMethod->m_name.c_str(), behaviorMethod->GetMinNumberOfArguments(), totalPythonArgs);
  601. return pybind11::cast<pybind11::none>(Py_None);
  602. }
  603. else if (totalPythonArgs > behaviorMethod->GetNumArguments())
  604. {
  605. AZ_Warning("python", false, "Method %s requires %zu parameters but it got more (%zu) - excess parameters will not be used.", behaviorMethod->m_name.c_str(), behaviorMethod->GetMinNumberOfArguments(), totalPythonArgs);
  606. }
  607. if (behaviorMethod->HasResult())
  608. {
  609. if (Internal::AllocateBehaviorValueParameter(behaviorMethod, result, stackVariableAllocator))
  610. {
  611. if (behaviorMethod->Call(parameters.begin(), static_cast<unsigned int>(totalPythonArgs), &result))
  612. {
  613. result.m_azRtti = behaviorMethod->GetResult()->m_azRtti;
  614. result.m_typeId = behaviorMethod->GetResult()->m_typeId;
  615. result.m_traits = behaviorMethod->GetResult()->m_traits;
  616. return Convert::BehaviorValueParameterToPython(result, stackVariableAllocator);
  617. }
  618. else
  619. {
  620. AZ_Warning("python", false, "Failed to call class method %s", behaviorMethod->m_name.c_str());
  621. }
  622. }
  623. else
  624. {
  625. AZ_Warning("python", false, "Failed to allocate return value for method %s", behaviorMethod->m_name.c_str());
  626. }
  627. }
  628. else if (!behaviorMethod->Call(parameters.begin(), static_cast<unsigned int>(totalPythonArgs)))
  629. {
  630. AZ_Warning("python", false, "Failed to invoke class method %s", behaviorMethod->m_name.c_str());
  631. }
  632. return pybind11::cast<pybind11::none>(Py_None);
  633. }
  634. pybind11::object InvokeBehaviorMethod(AZ::BehaviorMethod* behaviorMethod, pybind11::args pythonInputArgs, AZ::BehaviorObject self)
  635. {
  636. AZ::BehaviorArgument result;
  637. result.m_value = nullptr;
  638. pybind11::object pythonOutput = InvokeBehaviorMethodWithResult(behaviorMethod, pythonInputArgs, self, result);
  639. if (result.m_value)
  640. {
  641. Internal::DeallocateBehaviorValueParameter(result);
  642. }
  643. return pythonOutput;
  644. }
  645. pybind11::object StaticMethod(AZ::BehaviorMethod* behaviorMethod, pybind11::args pythonInputArgs)
  646. {
  647. return InvokeBehaviorMethod(behaviorMethod, pythonInputArgs, {});
  648. }
  649. pybind11::object ClassMethod(AZ::BehaviorMethod* behaviorMethod, AZ::BehaviorObject self, pybind11::args pythonInputArgs)
  650. {
  651. if (behaviorMethod->GetNumArguments() == 0)
  652. {
  653. AZ_Error("python", false, "A member level function should require at least one argument");
  654. }
  655. else if (!self.IsValid())
  656. {
  657. AZ_Error("python", false, "Method %s requires at valid self object to invoke", behaviorMethod->m_name.c_str());
  658. }
  659. else
  660. {
  661. return InvokeBehaviorMethod(behaviorMethod, pythonInputArgs, self);
  662. }
  663. return pybind11::cast<pybind11::none>(Py_None);
  664. }
  665. }
  666. }