2
0

PythonMarshalComponent.cpp 85 KB

12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788899091929394959697989910010110210310410510610710810911011111211311411511611711811912012112212312412512612712812913013113213313413513613713813914014114214314414514614714814915015115215315415515615715815916016116216316416516616716816917017117217317417517617717817918018118218318418518618718818919019119219319419519619719819920020120220320420520620720820921021121221321421521621721821922022122222322422522622722822923023123223323423523623723823924024124224324424524624724824925025125225325425525625725825926026126226326426526626726826927027127227327427527627727827928028128228328428528628728828929029129229329429529629729829930030130230330430530630730830931031131231331431531631731831932032132232332432532632732832933033133233333433533633733833934034134234334434534634734834935035135235335435535635735835936036136236336436536636736836937037137237337437537637737837938038138238338438538638738838939039139239339439539639739839940040140240340440540640740840941041141241341441541641741841942042142242342442542642742842943043143243343443543643743843944044144244344444544644744844945045145245345445545645745845946046146246346446546646746846947047147247347447547647747847948048148248348448548648748848949049149249349449549649749849950050150250350450550650750850951051151251351451551651751851952052152252352452552652752852953053153253353453553653753853954054154254354454554654754854955055155255355455555655755855956056156256356456556656756856957057157257357457557657757857958058158258358458558658758858959059159259359459559659759859960060160260360460560660760860961061161261361461561661761861962062162262362462562662762862963063163263363463563663763863964064164264364464564664764864965065165265365465565665765865966066166266366466566666766866967067167267367467567667767867968068168268368468568668768868969069169269369469569669769869970070170270370470570670770870971071171271371471571671771871972072172272372472572672772872973073173273373473573673773873974074174274374474574674774874975075175275375475575675775875976076176276376476576676776876977077177277377477577677777877978078178278378478578678778878979079179279379479579679779879980080180280380480580680780880981081181281381481581681781881982082182282382482582682782882983083183283383483583683783883984084184284384484584684784884985085185285385485585685785885986086186286386486586686786886987087187287387487587687787887988088188288388488588688788888989089189289389489589689789889990090190290390490590690790890991091191291391491591691791891992092192292392492592692792892993093193293393493593693793893994094194294394494594694794894995095195295395495595695795895996096196296396496596696796896997097197297397497597697797897998098198298398498598698798898999099199299399499599699799899910001001100210031004100510061007100810091010101110121013101410151016101710181019102010211022102310241025102610271028102910301031103210331034103510361037103810391040104110421043104410451046104710481049105010511052105310541055105610571058105910601061106210631064106510661067106810691070107110721073107410751076107710781079108010811082108310841085108610871088108910901091109210931094109510961097109810991100110111021103110411051106110711081109111011111112111311141115111611171118111911201121112211231124112511261127112811291130113111321133113411351136113711381139114011411142114311441145114611471148114911501151115211531154115511561157115811591160116111621163116411651166116711681169117011711172117311741175117611771178117911801181118211831184118511861187118811891190119111921193119411951196119711981199120012011202120312041205120612071208120912101211121212131214121512161217121812191220122112221223122412251226122712281229123012311232123312341235123612371238123912401241124212431244124512461247124812491250125112521253125412551256125712581259126012611262126312641265126612671268126912701271127212731274127512761277127812791280128112821283128412851286128712881289129012911292129312941295129612971298129913001301130213031304130513061307130813091310131113121313131413151316131713181319132013211322132313241325132613271328132913301331133213331334133513361337133813391340134113421343134413451346134713481349135013511352135313541355135613571358135913601361136213631364136513661367136813691370137113721373137413751376137713781379138013811382138313841385138613871388138913901391139213931394139513961397139813991400140114021403140414051406140714081409141014111412141314141415141614171418141914201421142214231424142514261427142814291430143114321433143414351436143714381439144014411442144314441445144614471448144914501451145214531454145514561457145814591460146114621463146414651466146714681469147014711472147314741475147614771478147914801481148214831484148514861487148814891490149114921493149414951496149714981499150015011502150315041505150615071508150915101511151215131514151515161517151815191520152115221523152415251526152715281529153015311532153315341535153615371538153915401541154215431544154515461547154815491550155115521553155415551556155715581559156015611562156315641565156615671568156915701571157215731574157515761577157815791580158115821583158415851586158715881589159015911592159315941595159615971598159916001601160216031604160516061607160816091610161116121613161416151616161716181619162016211622162316241625162616271628162916301631163216331634163516361637163816391640164116421643164416451646164716481649165016511652165316541655165616571658165916601661166216631664166516661667166816691670167116721673167416751676167716781679168016811682168316841685168616871688168916901691169216931694169516961697169816991700170117021703170417051706170717081709171017111712171317141715171617171718171917201721172217231724172517261727172817291730173117321733173417351736173717381739174017411742174317441745174617471748174917501751175217531754175517561757175817591760176117621763176417651766176717681769177017711772177317741775177617771778177917801781178217831784178517861787178817891790
  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 <EditorPythonBindings/PythonUtility.h>
  9. #include <Source/PythonMarshalComponent.h>
  10. #include <Source/PythonMarshalTuple.h>
  11. #include <Source/PythonProxyObject.h>
  12. #include <Source/PythonTypeCasters.h>
  13. #include <AzCore/RTTI/BehaviorContext.h>
  14. #include <AzCore/RTTI/TypeInfo.h>
  15. #include <AzCore/Serialization/Utils.h>
  16. #include <AzCore/Serialization/EditContextConstants.inl>
  17. #include <AzCore/std/allocator.h>
  18. #include <AzCore/std/smart_ptr/make_shared.h>
  19. #include <AzFramework/StringFunc/StringFunc.h>
  20. #include <EditorPythonBindings/PythonCommon.h>
  21. #include <pybind11/embed.h>
  22. #include <pybind11/pytypes.h>
  23. namespace EditorPythonBindings
  24. {
  25. bool IsPointerType(PythonMarshalTypeRequests::BehaviorTraits traits)
  26. {
  27. return (((traits & AZ::BehaviorParameter::TR_POINTER) == AZ::BehaviorParameter::TR_POINTER) ||
  28. ((traits & AZ::BehaviorParameter::TR_REFERENCE) == AZ::BehaviorParameter::TR_REFERENCE));
  29. }
  30. template <typename TInput, typename TPythonType>
  31. pybind11::object MarshalBehaviorValueParameter(AZ::BehaviorArgument& result)
  32. {
  33. if (result.ConvertTo<TInput>())
  34. {
  35. TInput inputValue = static_cast<TInput>(*result.GetAsUnsafe<TInput>());
  36. return TPythonType(inputValue);
  37. }
  38. return pybind11::cast<pybind11::none>(Py_None);
  39. }
  40. void ReportMissingTypeId(AZ::TypeId typeId)
  41. {
  42. const AZ::BehaviorClass* behaviorClass = AZ::BehaviorContextHelper::GetClass(typeId);
  43. if (behaviorClass)
  44. {
  45. AZ_Warning("python", false, "Missing BehaviorClass for UUID:%s Name:%s", typeId.ToString<AZStd::string>().c_str(), behaviorClass->m_name.c_str());
  46. return;
  47. }
  48. AZ::SerializeContext* serializeContext = nullptr;
  49. AZ::ComponentApplicationBus::BroadcastResult(serializeContext, &AZ::ComponentApplicationRequests::GetSerializeContext);
  50. AZ_Error("python", serializeContext, "SerializeContext is missing");
  51. if (!serializeContext)
  52. {
  53. AZ_Warning("python", false, "Missing Serialize class for UUID:%s", typeId.ToString<AZStd::string>().c_str());
  54. return;
  55. }
  56. const AZ::SerializeContext::ClassData* classData = serializeContext->FindClassData(typeId);
  57. if(!classData)
  58. {
  59. AZ_Warning("python", false, "Missing Serialize class for UUID:%s", typeId.ToString<AZStd::string>().c_str());
  60. }
  61. else if (classData->m_container)
  62. {
  63. AZ_Warning("python", false, "Missing Serialize class container for UUID:%s Name:%s", typeId.ToString<AZStd::string>().c_str(), classData->m_name);
  64. }
  65. else
  66. {
  67. AZ_Warning("python", false, "Missing Serialize class for UUID:%s Name:%s", typeId.ToString<AZStd::string>().c_str(), classData->m_name);
  68. }
  69. }
  70. class TypeConverterAny
  71. : public PythonMarshalComponent::TypeConverter
  72. {
  73. public:
  74. AZStd::optional<PythonMarshalTypeRequests::BehaviorValueResult> PythonToBehaviorValueParameter(PythonMarshalTypeRequests::BehaviorTraits traits, pybind11::object pyObj, AZ::BehaviorArgument& outValue) override
  75. {
  76. if (!CanConvertPythonToBehaviorValue(traits, pyObj))
  77. {
  78. AZ_Warning("python", false, "AZStd::any<> handles Behavior Class types only.");
  79. return AZStd::nullopt;
  80. }
  81. if ((traits & AZ::BehaviorParameter::TR_POINTER) == AZ::BehaviorParameter::TR_POINTER)
  82. {
  83. AZ_Warning("python", false, "AZStd::any* pointer argument types are not supported; try 'AZStd::any' value or 'const AZStd::any&' instead");
  84. return AZStd::nullopt;
  85. }
  86. if (pybind11::isinstance<EditorPythonBindings::PythonProxyObject>(pyObj))
  87. {
  88. EditorPythonBindings::PythonProxyObject* proxyObj = pybind11::cast<EditorPythonBindings::PythonProxyObject*>(pyObj);
  89. if (proxyObj)
  90. {
  91. return PythonToParameterWithProxy(proxyObj, pyObj, outValue);
  92. }
  93. AZ_Warning("python", false, "Passed in PythonProxyObject is empty.");
  94. return AZStd::nullopt;
  95. }
  96. else if (pyObj.is_none())
  97. {
  98. AZStd::any* anyValue = aznew AZStd::any();
  99. outValue.Set<AZStd::any>(anyValue);
  100. auto deleteAny = [anyValue]()
  101. {
  102. delete anyValue;
  103. };
  104. return AZStd::make_optional(PythonMarshalTypeRequests::BehaviorValueResult{ true, deleteAny });
  105. }
  106. else if (PyList_Check(pyObj.ptr()))
  107. {
  108. return ReturnVectorFromList(traits, pybind11::cast<pybind11::list>(pyObj), outValue);
  109. }
  110. else if (PyBool_Check(pyObj.ptr()))
  111. {
  112. return ReturnSimpleType<bool>(Py_True == pyObj.ptr(), outValue);
  113. }
  114. else if (PyFloat_Check(pyObj.ptr()))
  115. {
  116. return ReturnSimpleType<double>(PyFloat_AsDouble(pyObj.ptr()), outValue);
  117. }
  118. else if (PyLong_Check(pyObj.ptr()))
  119. {
  120. return ReturnSimpleType<AZ::s64>(PyLong_AsLongLong(pyObj.ptr()), outValue);
  121. }
  122. else if (PyUnicode_Check(pyObj.ptr()))
  123. {
  124. // in the case of an error, NULL is returned with an exception set and no size is stored
  125. Py_ssize_t size = -1;
  126. const char* value = PyUnicode_AsUTF8AndSize(pyObj.ptr(), &size);
  127. if (value && size != -1)
  128. {
  129. return ReturnSimpleType<AZStd::string_view>(AZStd::string_view(value, size), outValue);
  130. }
  131. }
  132. return AZStd::nullopt;
  133. }
  134. AZStd::optional<PythonMarshalTypeRequests::PythonValueResult> BehaviorValueParameterToPython(AZ::BehaviorArgument& behaviorValue) override
  135. {
  136. if ((behaviorValue.m_traits & AZ::BehaviorParameter::TR_POINTER) == AZ::BehaviorParameter::TR_POINTER)
  137. {
  138. AZ_Warning("python", false, "Return value 'AZStd::any*' pointer argument types are not supported; try returning 'const AZStd::any&' instead");
  139. return AZStd::nullopt;
  140. }
  141. if (!behaviorValue.ConvertTo<AZStd::any>())
  142. {
  143. AZ_Warning("python", false, "Cannot convert the return value to a AZStd::any value.");
  144. return AZStd::nullopt;
  145. }
  146. AZStd::any* anyValue = static_cast<AZStd::any*>(behaviorValue.GetAsUnsafe<AZStd::any>());
  147. const AZ::TypeId anyValueTypId { anyValue->get_type_info().m_id };
  148. // is a registered convertible type?
  149. if (PythonMarshalTypeRequestBus::GetNumOfEventHandlers(anyValueTypId))
  150. {
  151. AZ::BehaviorArgument tempBehaviorValue;
  152. tempBehaviorValue.m_typeId = anyValueTypId;
  153. tempBehaviorValue.m_value = AZStd::any_cast<void>(anyValue);
  154. AZStd::optional<PythonMarshalTypeRequests::PythonValueResult> result;
  155. PythonMarshalTypeRequestBus::EventResult(result, anyValueTypId, &PythonMarshalTypeRequestBus::Events::BehaviorValueParameterToPython, tempBehaviorValue);
  156. return result;
  157. }
  158. else
  159. {
  160. AZ::BehaviorContext* behaviorContext {nullptr};
  161. AZ::ComponentApplicationBus::BroadcastResult(behaviorContext, &AZ::ComponentApplicationRequests::GetBehaviorContext);
  162. if (!behaviorContext)
  163. {
  164. AZ_Error("python", false, "A behavior context is required!");
  165. return AZStd::nullopt;
  166. }
  167. AZ::BehaviorClass* behaviorClass = AZ::BehaviorContextHelper::GetClass(behaviorContext, anyValueTypId);
  168. if (behaviorClass)
  169. {
  170. PythonMarshalTypeRequests::PythonValueResult result;
  171. result.first = PythonProxyObjectManagement::CreatePythonProxyObject(anyValueTypId, AZStd::any_cast<void>(anyValue));
  172. return result;
  173. }
  174. }
  175. return AZStd::nullopt;
  176. }
  177. bool CanConvertPythonToBehaviorValue([[maybe_unused]] PythonMarshalTypeRequests::BehaviorTraits traits, pybind11::object pyObj) const override
  178. {
  179. // supports Python native types None, Float, Long, Bool, List, or String
  180. if (pyObj.is_none() ||
  181. PyFloat_Check(pyObj.ptr()) ||
  182. PyLong_Check(pyObj.ptr()) ||
  183. PyBool_Check(pyObj.ptr()) ||
  184. PyList_Check(pyObj.ptr()) ||
  185. PyUnicode_Check(pyObj.ptr()))
  186. {
  187. return true;
  188. }
  189. return pybind11::isinstance<EditorPythonBindings::PythonProxyObject>(pyObj);
  190. }
  191. protected:
  192. template <typename T>
  193. AZStd::optional<PythonMarshalTypeRequests::BehaviorValueResult> ReturnSimpleType(T value, AZ::BehaviorArgument& outValue)
  194. {
  195. AZStd::any* anyValue = aznew AZStd::any(value);
  196. outValue.Set<AZStd::any>(anyValue);
  197. auto deleteAny = [anyValue]()
  198. {
  199. delete anyValue;
  200. };
  201. return AZStd::make_optional(PythonMarshalTypeRequests::BehaviorValueResult{ true, deleteAny });
  202. }
  203. AZStd::any* CreateAnyValue(AZ::TypeId typeId, void* address) const
  204. {
  205. const AZ::BehaviorClass* sourceClass = AZ::BehaviorContextHelper::GetClass(typeId);
  206. if (!sourceClass)
  207. {
  208. ReportMissingTypeId(typeId);
  209. return nullptr;
  210. }
  211. if (!sourceClass->m_allocate || !sourceClass->m_cloner || !sourceClass->m_mover || !sourceClass->m_destructor || !sourceClass->m_deallocate)
  212. {
  213. AZ_Warning("python", false, "BehaviorClass:%s must handle allocation", sourceClass->m_name.c_str());
  214. return nullptr;
  215. }
  216. AZStd::any::type_info valueInfo;
  217. valueInfo.m_id = typeId;
  218. valueInfo.m_isPointer = false;
  219. valueInfo.m_useHeap = true;
  220. valueInfo.m_handler = [sourceClass](AZStd::any::Action action, AZStd::any* dest, const AZStd::any* source)
  221. {
  222. if (action == AZStd::any::Action::Reserve)
  223. {
  224. *reinterpret_cast<void**>(dest) = sourceClass->Allocate();
  225. }
  226. else if (action == AZStd::any::Action::Copy)
  227. {
  228. sourceClass->m_cloner(AZStd::any_cast<void>(dest), AZStd::any_cast<void>(source), sourceClass->m_userData);
  229. }
  230. else if (action == AZStd::any::Action::Move)
  231. {
  232. sourceClass->m_mover(AZStd::any_cast<void>(dest), AZStd::any_cast<void>(const_cast<AZStd::any*>(source)), sourceClass->m_userData);
  233. }
  234. else if (action == AZStd::any::Action::Destroy)
  235. {
  236. sourceClass->Destroy(AZ::BehaviorObject(AZStd::any_cast<void>(dest), sourceClass->m_typeId));
  237. }
  238. };
  239. return aznew AZStd::any(address, valueInfo);
  240. }
  241. AZStd::optional<PythonMarshalTypeRequests::BehaviorValueResult> PythonToParameterWithProxy(EditorPythonBindings::PythonProxyObject* proxyObj, [[maybe_unused]] pybind11::object pyObj, AZ::BehaviorArgument& outValue)
  242. {
  243. auto behaviorObject = proxyObj->GetBehaviorObject();
  244. if (!behaviorObject)
  245. {
  246. AZ_Warning("python", false, "Empty behavior object sent in.");
  247. return AZStd::nullopt;
  248. }
  249. AZStd::any* anyValue = CreateAnyValue(behaviorObject.value()->m_typeId, behaviorObject.value()->m_address);
  250. if (!anyValue)
  251. {
  252. return AZStd::nullopt;
  253. }
  254. auto deleteAny = [anyValue]()
  255. {
  256. delete anyValue;
  257. };
  258. outValue.Set<AZStd::any>(anyValue);
  259. return AZStd::make_optional(PythonMarshalTypeRequests::BehaviorValueResult{ true, deleteAny });
  260. }
  261. AZStd::optional<PythonMarshalTypeRequests::BehaviorValueResult> ReturnVectorFromList(PythonMarshalTypeRequests::BehaviorTraits traits, pybind11::list pyList, AZ::BehaviorArgument& outValue)
  262. {
  263. // empty lists are okay, sending as an empty AZStd::any()
  264. if (pyList.size() == 0)
  265. {
  266. AZStd::any* anyValue = aznew AZStd::any();
  267. auto deleteAny = [anyValue]()
  268. {
  269. delete anyValue;
  270. };
  271. outValue.Set<AZStd::any>(anyValue);
  272. return AZStd::make_optional(PythonMarshalTypeRequests::BehaviorValueResult{ true, deleteAny });
  273. }
  274. // determine the type from the Python type
  275. pybind11::object pyListElement = pyList[0];
  276. AZ::TypeId vectorType;
  277. if (pybind11::isinstance<EditorPythonBindings::PythonProxyObject>(pyListElement))
  278. {
  279. // making a AZ::TypeId for a 'AZStd::vector<Element Type Id, AZStd::allocator>'
  280. // the vector TypeId equals "underlying element type" + "allocator type" + "vector type"
  281. auto* proxy = pybind11::cast<PythonProxyObject*>(pyListElement);
  282. if (!proxy || !proxy->GetWrappedType())
  283. {
  284. return AZStd::nullopt;
  285. }
  286. constexpr const char AZStdVectorTypeId[] = "{A60E3E61-1FF6-4982-B6B8-9E4350C4C679}";
  287. vectorType = proxy->GetWrappedType().value();
  288. vectorType += azrtti_typeid<AZStd::allocator>();
  289. vectorType += AZ::TypeId(AZStdVectorTypeId);
  290. }
  291. else if (PyBool_Check(pyListElement.ptr()))
  292. {
  293. vectorType = azrtti_typeid<AZStd::vector<bool>>();
  294. }
  295. else if (PyFloat_Check(pyListElement.ptr()))
  296. {
  297. vectorType = azrtti_typeid<AZStd::vector<double>>();
  298. }
  299. else if (PyNumber_Check(pyListElement.ptr()))
  300. {
  301. vectorType = azrtti_typeid<AZStd::vector<AZ::s64>>();
  302. }
  303. else if (PyUnicode_Check(pyListElement.ptr()))
  304. {
  305. vectorType = azrtti_typeid<AZStd::vector<AZStd::string>>();
  306. }
  307. AZStd::optional<PythonMarshalTypeRequests::BehaviorValueResult> vectorResult;
  308. PythonMarshalTypeRequestBus::EventResult(vectorResult, vectorType, &PythonMarshalTypeRequestBus::Events::PythonToBehaviorValueParameter, traits, pyList, outValue);
  309. if (vectorResult && vectorResult.value().first)
  310. {
  311. AZStd::any* anyValue = CreateAnyValue(vectorType, outValue.m_value);
  312. if (!anyValue)
  313. {
  314. return AZStd::nullopt;
  315. }
  316. outValue.Set<AZStd::any>(anyValue);
  317. auto deleteAny = [anyValue]()
  318. {
  319. delete anyValue;
  320. };
  321. return AZStd::make_optional(PythonMarshalTypeRequests::BehaviorValueResult{ true, deleteAny });
  322. }
  323. return AZStd::nullopt;
  324. }
  325. };
  326. class TypeConverterBool
  327. : public PythonMarshalComponent::TypeConverter
  328. {
  329. public:
  330. AZStd::optional<PythonMarshalTypeRequests::BehaviorValueResult> PythonToBehaviorValueParameter(PythonMarshalTypeRequests::BehaviorTraits traits, pybind11::object pyObj, AZ::BehaviorArgument& outValue) override
  331. {
  332. if (CanConvertPythonToBehaviorValue(traits,pyObj))
  333. {
  334. outValue.StoreInTempData(pybind11::cast<bool>(pyObj));
  335. return { { true, nullptr } };
  336. }
  337. return AZStd::nullopt;
  338. }
  339. AZStd::optional<PythonMarshalTypeRequests::PythonValueResult> BehaviorValueParameterToPython(AZ::BehaviorArgument& behaviorValue) override
  340. {
  341. PythonMarshalTypeRequests::PythonValueResult result;
  342. result.first = MarshalBehaviorValueParameter<bool, pybind11::bool_>(behaviorValue);
  343. return result;
  344. }
  345. bool CanConvertPythonToBehaviorValue([[maybe_unused]] PythonMarshalTypeRequests::BehaviorTraits traits, pybind11::object pyObj) const override
  346. {
  347. return (PyBool_Check(pyObj.ptr()) != false);
  348. }
  349. };
  350. template <typename T>
  351. class TypeConverterInteger
  352. : public PythonMarshalComponent::TypeConverter
  353. {
  354. public:
  355. AZStd::optional<PythonMarshalTypeRequests::BehaviorValueResult> PythonToBehaviorValueParameter(PythonMarshalTypeRequests::BehaviorTraits traits, pybind11::object pyObj, AZ::BehaviorArgument& outValue) override
  356. {
  357. if (CanConvertPythonToBehaviorValue(traits, pyObj))
  358. {
  359. outValue.StoreInTempData(pybind11::cast<T>(pyObj));
  360. return { { true, nullptr } };
  361. }
  362. return AZStd::nullopt;
  363. }
  364. AZStd::optional<PythonMarshalTypeRequests::PythonValueResult> BehaviorValueParameterToPython(AZ::BehaviorArgument& behaviorValue) override
  365. {
  366. PythonMarshalTypeRequests::PythonValueResult result;
  367. result.first = MarshalBehaviorValueParameter<T, pybind11::int_>(behaviorValue);
  368. return result;
  369. }
  370. bool CanConvertPythonToBehaviorValue([[maybe_unused]] PythonMarshalTypeRequests::BehaviorTraits traits, pybind11::object pyObj) const override
  371. {
  372. return PyLong_Check(pyObj.ptr()) != false;
  373. }
  374. };
  375. template <typename BehaviorType, typename NativeType, typename PythonType>
  376. class TypeConverterReal
  377. : public PythonMarshalComponent::TypeConverter
  378. {
  379. public:
  380. AZStd::optional<PythonMarshalTypeRequests::BehaviorValueResult> PythonToBehaviorValueParameter(PythonMarshalTypeRequests::BehaviorTraits traits, pybind11::object pyObj, AZ::BehaviorArgument& outValue) override
  381. {
  382. if (CanConvertPythonToBehaviorValue(traits, pyObj))
  383. {
  384. NativeType nativeType = pybind11::cast<NativeType>(pyObj);
  385. outValue.StoreInTempData(BehaviorType{ nativeType });
  386. return { { true, nullptr } };
  387. }
  388. return AZStd::nullopt;
  389. }
  390. AZStd::optional<PythonMarshalTypeRequests::PythonValueResult> BehaviorValueParameterToPython(AZ::BehaviorArgument& behaviorValue) override
  391. {
  392. PythonMarshalTypeRequests::PythonValueResult result;
  393. result.first = MarshalBehaviorValueParameter<BehaviorType, pybind11::float_>(behaviorValue);
  394. return result;
  395. }
  396. bool CanConvertPythonToBehaviorValue([[maybe_unused]] PythonMarshalTypeRequests::BehaviorTraits traits, pybind11::object pyObj) const override
  397. {
  398. return (PyFloat_Check(pyObj.ptr()) != false);
  399. }
  400. };
  401. template <typename T>
  402. class TypeConverterString
  403. : public PythonMarshalComponent::TypeConverter
  404. {
  405. public:
  406. AZStd::optional<PythonMarshalTypeRequests::BehaviorValueResult> PythonToBehaviorValueParameter(PythonMarshalTypeRequests::BehaviorTraits traits, pybind11::object pyObj, AZ::BehaviorArgument& outValue) override
  407. {
  408. if (!CanConvertPythonToBehaviorValue(traits, pyObj))
  409. {
  410. return AZStd::nullopt;
  411. }
  412. else if (AZ::AzTypeInfo<T>::Uuid() == AZ::AzTypeInfo<AZStd::string_view>::Uuid())
  413. {
  414. // in the case of an error, NULL is returned with an exception set and no size is stored
  415. Py_ssize_t size = -1;
  416. const char* value = PyUnicode_AsUTF8AndSize(pyObj.ptr(), &size);
  417. if (value || size != -1)
  418. {
  419. AZStd::string_view stringView(value, size);
  420. outValue.StoreInTempData<AZStd::string_view>(AZStd::move(stringView));
  421. return { { true, nullptr } };
  422. }
  423. }
  424. else if (AZ::AzTypeInfo<T>::Uuid() == AZ::AzTypeInfo<AZStd::string>::Uuid())
  425. {
  426. AZStd::string* stringValue = new AZStd::string(pybind11::cast<AZStd::string>(pyObj));
  427. outValue.Set<AZStd::string>(stringValue);
  428. return { {true, [stringValue]() { delete stringValue; }} };
  429. }
  430. else if (AZ::AzTypeInfo<T>::Uuid() == AZ::AzTypeInfo<AZ::IO::FixedMaxPathString>::Uuid())
  431. {
  432. auto* stringValue = new AZ::IO::FixedMaxPathString(pybind11::cast<AZ::IO::FixedMaxPathString>(pyObj));
  433. outValue.Set<AZ::IO::FixedMaxPathString>(stringValue);
  434. return { {true, [stringValue]() { delete stringValue; }} };
  435. }
  436. return AZStd::nullopt;
  437. }
  438. AZStd::optional<PythonMarshalTypeRequests::PythonValueResult> BehaviorValueParameterToPython(AZ::BehaviorArgument& behaviorValue) override
  439. {
  440. PythonMarshalTypeRequests::PythonValueResult result;
  441. result.first = pybind11::cast(behaviorValue.GetAsUnsafe<T>());
  442. return result;
  443. }
  444. bool CanConvertPythonToBehaviorValue([[maybe_unused]] PythonMarshalTypeRequests::BehaviorTraits traits, pybind11::object pyObj) const override
  445. {
  446. return(PyUnicode_Check(pyObj.ptr()) != false );
  447. }
  448. };
  449. // The 'char' type can come in with a variety of type traits:
  450. //
  451. class TypeConverterChar
  452. : public PythonMarshalComponent::TypeConverter
  453. {
  454. public:
  455. AZStd::optional<PythonMarshalTypeRequests::BehaviorValueResult> PythonToBehaviorValueParameter(PythonMarshalTypeRequests::BehaviorTraits traits, pybind11::object pyObj, AZ::BehaviorArgument& outValue) override
  456. {
  457. if (!CanConvertPythonToBehaviorValue(traits, pyObj))
  458. {
  459. return AZStd::nullopt;
  460. }
  461. // in the case of an error, NULL is returned with an exception set and no size is stored
  462. Py_ssize_t size = -1;
  463. const char* value = PyUnicode_AsUTF8AndSize(pyObj.ptr(), &size);
  464. if (!value || size == -1)
  465. {
  466. return AZStd::nullopt;
  467. }
  468. if (IsPointerType(traits))
  469. {
  470. outValue.StoreInTempData(value);
  471. }
  472. else
  473. {
  474. outValue.StoreInTempData(value[0]);
  475. }
  476. return { { true, nullptr } };
  477. }
  478. AZStd::optional<PythonMarshalTypeRequests::PythonValueResult> BehaviorValueParameterToPython(AZ::BehaviorArgument& behaviorValue) override
  479. {
  480. if (IsPointerType(static_cast<PythonMarshalTypeRequests::BehaviorTraits>(behaviorValue.m_traits)))
  481. {
  482. if (behaviorValue.ConvertTo<const char*>())
  483. {
  484. PythonMarshalTypeRequests::PythonValueResult resultString;
  485. resultString.first = pybind11::str(*behaviorValue.GetAsUnsafe<const char*>());
  486. return resultString;
  487. }
  488. }
  489. else
  490. {
  491. if (behaviorValue.ConvertTo<char>())
  492. {
  493. char characters[2];
  494. characters[0] = *behaviorValue.GetAsUnsafe<char>();
  495. characters[1] = 0;
  496. PythonMarshalTypeRequests::PythonValueResult resultCharNumber;
  497. resultCharNumber.first = pybind11::str(characters, 1);
  498. return resultCharNumber;
  499. }
  500. }
  501. return AZStd::nullopt;
  502. }
  503. bool CanConvertPythonToBehaviorValue([[maybe_unused]] PythonMarshalTypeRequests::BehaviorTraits traits, pybind11::object pyObj) const override
  504. {
  505. return (PyUnicode_Check(pyObj.ptr()) != false);
  506. }
  507. };
  508. namespace Container
  509. {
  510. class TypeConverterByteStream
  511. : public PythonMarshalComponent::TypeConverter
  512. {
  513. public:
  514. AZStd::optional<PythonMarshalTypeRequests::BehaviorValueResult> PythonToBehaviorValueParameter(PythonMarshalTypeRequests::BehaviorTraits traits, pybind11::object pyObj, AZ::BehaviorArgument& outValue) override
  515. {
  516. if (!CanConvertPythonToBehaviorValue(traits, pyObj))
  517. {
  518. AZ_Warning("python", false, "Expected a Python List as input");
  519. return AZStd::nullopt;
  520. }
  521. AZStd::vector<AZ::u8>* newByteStream = new AZStd::vector<AZ::u8>();
  522. pybind11::list pyList(pyObj);
  523. for (auto pyItem = pyList.begin(); pyItem != pyList.end(); ++pyItem)
  524. {
  525. AZ::u8 byte = (*pyItem).cast<AZ::u8>();
  526. newByteStream->push_back(byte);
  527. }
  528. outValue.m_name = "AZStd::vector<AZ::u8>";
  529. outValue.m_value = newByteStream;
  530. outValue.m_typeId = AZ::AzTypeInfo<AZStd::vector<AZ::u8>>::Uuid();
  531. outValue.m_traits = traits;
  532. auto deleteVector = [newByteStream]()
  533. {
  534. delete newByteStream;
  535. };
  536. return { { true, deleteVector } };
  537. }
  538. AZStd::optional<PythonMarshalTypeRequests::PythonValueResult> BehaviorValueParameterToPython(AZ::BehaviorArgument& behaviorValue) override
  539. {
  540. if (behaviorValue.ConvertTo(AZ::AzTypeInfo<AZStd::vector<AZ::u8>>::Uuid()))
  541. {
  542. pybind11::list pythonList;
  543. AZStd::vector<AZ::u8>* byteStream = behaviorValue.GetAsUnsafe<AZStd::vector<AZ::u8>>();
  544. for (AZ::u8 byte : *byteStream)
  545. {
  546. pythonList.append(pybind11::cast(byte));
  547. }
  548. PythonMarshalTypeRequests::PythonValueResult result;
  549. result.first = pythonList;
  550. return result;
  551. }
  552. return AZStd::nullopt;
  553. }
  554. bool CanConvertPythonToBehaviorValue([[maybe_unused]] PythonMarshalTypeRequests::BehaviorTraits traits, pybind11::object pyObj) const override
  555. {
  556. return (PyList_Check(pyObj.ptr()) != false);
  557. }
  558. };
  559. AZStd::optional<PythonMarshalTypeRequests::PythonValueResult> ProcessBehaviorObject(AZ::BehaviorObject& behaviorObject)
  560. {
  561. AZ::BehaviorArgument source;
  562. source.m_value = behaviorObject.m_address;
  563. source.m_typeId = behaviorObject.m_typeId;
  564. AZStd::optional<PythonMarshalTypeRequests::PythonValueResult> result;
  565. PythonMarshalTypeRequestBus::EventResult(result, source.m_typeId, &PythonMarshalTypeRequestBus::Events::BehaviorValueParameterToPython, source);
  566. if (result.has_value())
  567. {
  568. return result;
  569. }
  570. // return an opaque Behavior Objects to the caller if not a 'simple' type
  571. pybind11::object objectValue = PythonProxyObjectManagement::CreatePythonProxyObject(behaviorObject.m_typeId, behaviorObject.m_address);
  572. if (!objectValue.is_none())
  573. {
  574. return PythonMarshalTypeRequests::PythonValueResult( objectValue, {} );
  575. }
  576. return AZStd::nullopt;
  577. }
  578. AZStd::optional<PythonMarshalTypeRequests::BehaviorValueResult> ProcessPythonObject(PythonMarshalTypeRequests::BehaviorTraits traits, pybind11::object pythonObj, const AZ::TypeId& elementTypeId, AZ::BehaviorArgument& outValue)
  579. {
  580. // first try to convert using the element's type ID
  581. AZStd::optional<PythonMarshalTypeRequests::BehaviorValueResult> result;
  582. PythonMarshalTypeRequestBus::EventResult(result, elementTypeId, &PythonMarshalTypeRequestBus::Events::PythonToBehaviorValueParameter, traits, pythonObj, outValue);
  583. if (result)
  584. {
  585. return result;
  586. }
  587. else if (pybind11::isinstance<EditorPythonBindings::PythonProxyObject>(pythonObj))
  588. {
  589. AZ::BehaviorArgument behaviorArg;
  590. behaviorArg.m_traits = traits;
  591. behaviorArg.m_typeId = elementTypeId;
  592. if (Convert::PythonProxyObjectToBehaviorValueParameter(behaviorArg, pythonObj, outValue))
  593. {
  594. return PythonMarshalTypeRequests::BehaviorValueResult( { true, nullptr } );
  595. }
  596. }
  597. return AZStd::nullopt;
  598. }
  599. bool LoadPythonToPairElement(PyObject* pyItem, PythonMarshalTypeRequests::BehaviorTraits traits, const AZ::SerializeContext::ClassElement* itemElement,
  600. AZ::SerializeContext::IDataContainer* pairContainer, size_t index, AZ::SerializeContext* serializeContext, void* newPair)
  601. {
  602. pybind11::object pyObj{ pybind11::reinterpret_borrow<pybind11::object>(pyItem) };
  603. AZ::BehaviorArgument behaviorItem;
  604. auto behaviorResult = ProcessPythonObject(traits, pyObj, itemElement->m_typeId, behaviorItem);
  605. if (behaviorResult && behaviorResult.value().first)
  606. {
  607. void* itemAddress = pairContainer->GetElementByIndex(newPair, itemElement, index);
  608. AZ_Assert(itemAddress, "Element reserved for associative container's pair, but unable to retrieve address of the item:%d", index);
  609. serializeContext->CloneObjectInplace(itemAddress, behaviorItem.m_value, itemElement->m_typeId);
  610. }
  611. else
  612. {
  613. AZ_Warning("python", false, "Could not convert to pair element type %s for the pair<>; failed to marshal Python input %s", itemElement->m_name, Convert::GetPythonTypeName(pyObj).c_str());
  614. return false;
  615. }
  616. return true;
  617. }
  618. AZStd::optional<PythonMarshalTypeRequests::BehaviorValueResult> ConvertPythonElement(PythonMarshalTypeRequests::BehaviorTraits traits, pybind11::object pythonElement, const AZ::TypeId& elementTypeId, AZ::BehaviorArgument& outValue)
  619. {
  620. // first try to convert using the element's type ID
  621. AZStd::optional<PythonMarshalTypeRequests::BehaviorValueResult> result;
  622. PythonMarshalTypeRequestBus::EventResult(result, elementTypeId, &PythonMarshalTypeRequestBus::Events::PythonToBehaviorValueParameter, traits, pythonElement, outValue);
  623. if (result)
  624. {
  625. return result;
  626. }
  627. else if (pybind11::isinstance<EditorPythonBindings::PythonProxyObject>(pythonElement))
  628. {
  629. AZ::BehaviorArgument behaviorArg;
  630. behaviorArg.m_traits = traits;
  631. behaviorArg.m_typeId = elementTypeId;
  632. if (Convert::PythonProxyObjectToBehaviorValueParameter(behaviorArg, pythonElement, outValue))
  633. {
  634. return { { true, nullptr } };
  635. }
  636. }
  637. return AZStd::nullopt;
  638. }
  639. class TypeConverterDictionary final
  640. : public PythonMarshalComponent::TypeConverter
  641. {
  642. const AZ::SerializeContext::ClassData* m_classData = nullptr;
  643. const AZ::TypeId m_typeId = {};
  644. public:
  645. TypeConverterDictionary([[maybe_unused]] AZ::GenericClassInfo* genericClassInfo, const AZ::SerializeContext::ClassData* classData, const AZ::TypeId& typeId)
  646. : m_classData(classData)
  647. , m_typeId(typeId)
  648. {
  649. }
  650. AZStd::optional<PythonMarshalTypeRequests::BehaviorValueResult> PythonToBehaviorValueParameter(PythonMarshalTypeRequests::BehaviorTraits traits, pybind11::object pyObj, AZ::BehaviorArgument& outValue) override
  651. {
  652. if (!CanConvertPythonToBehaviorValue(traits, pyObj))
  653. {
  654. AZ_Warning("python", false, "The dictionary container type for %s", m_classData->m_name);
  655. return AZStd::nullopt;
  656. }
  657. const AZ::BehaviorClass* behaviorClass = AZ::BehaviorContextHelper::GetClass(m_typeId);
  658. if (!behaviorClass)
  659. {
  660. AZ_Warning("python", false, "Missing dictionary behavior class for %s", m_typeId.ToString<AZStd::string>().c_str());
  661. return AZStd::nullopt;
  662. }
  663. AZ::SerializeContext* serializeContext = nullptr;
  664. AZ::ComponentApplicationBus::BroadcastResult(serializeContext, &AZ::ComponentApplicationRequests::GetSerializeContext);
  665. if (!serializeContext)
  666. {
  667. return AZStd::nullopt;
  668. }
  669. // prepare the AZStd::unordered_map<> container
  670. AZ::BehaviorObject mapInstance = behaviorClass->Create();
  671. AZ::SerializeContext::IDataContainer* mapDataContainer = m_classData->m_container;
  672. const AZ::SerializeContext::ClassElement* pairElement = m_classData->m_container->GetElement(m_classData->m_container->GetDefaultElementNameCrc());
  673. const AZ::SerializeContext::ClassData* pairClass = serializeContext->FindClassData(pairElement->m_typeId);
  674. AZ_Assert(pairClass, "Associative container was registered but not the pair that's used for storage.");
  675. AZ::SerializeContext::IDataContainer* pairContainer = pairClass->m_container;
  676. AZ_Assert(pairContainer, "Associative container is missing the interface to the storage container.");
  677. // get the key/value element types
  678. const AZ::SerializeContext::ClassElement* keyElement = nullptr;
  679. const AZ::SerializeContext::ClassElement* valueElement = nullptr;
  680. auto keyValueTypeEnumCallback = [&keyElement, &valueElement](const AZ::Uuid&, const AZ::SerializeContext::ClassElement* genericClassElement)
  681. {
  682. if (genericClassElement->m_flags & AZ::SerializeContext::ClassElement::Flags::FLG_POINTER)
  683. {
  684. AZ_Error("python", false, "Python marshalling does not handle naked pointers; not converting dict's pair");
  685. return false;
  686. }
  687. else if (!keyElement)
  688. {
  689. keyElement = genericClassElement;
  690. }
  691. else if (!valueElement)
  692. {
  693. valueElement = genericClassElement;
  694. }
  695. else
  696. {
  697. AZ_Error("python", !valueElement, "The pair element in a container can't have more than 2 elements.");
  698. return false;
  699. }
  700. return true;
  701. };
  702. pairContainer->EnumTypes(keyValueTypeEnumCallback);
  703. if (!keyElement || !valueElement)
  704. {
  705. return AZStd::nullopt;
  706. }
  707. PyObject* key = nullptr;
  708. PyObject* value = nullptr;
  709. Py_ssize_t pos = 0;
  710. while (PyDict_Next(pyObj.ptr(), &pos, &key, &value))
  711. {
  712. void* newPair = mapDataContainer->ReserveElement(mapInstance.m_address, pairElement);
  713. AZ_Assert(newPair, "Could not allocate pair entry for map via ReserveElement()");
  714. if (newPair)
  715. {
  716. const bool didKey = LoadPythonToPairElement(key, traits, keyElement, pairContainer, 0, serializeContext, newPair);
  717. const bool didValue = LoadPythonToPairElement(value, traits, valueElement, pairContainer, 1, serializeContext, newPair);
  718. if (didKey && didValue)
  719. {
  720. // store the pair in the map
  721. mapDataContainer->StoreElement(mapInstance.m_address, newPair);
  722. }
  723. else
  724. {
  725. // release element, due to a failed pair conversion
  726. mapDataContainer->FreeReservedElement(mapInstance.m_address, newPair, serializeContext);
  727. }
  728. }
  729. }
  730. AZ_Warning("python", static_cast<size_t>(PyDict_Size(pyObj.ptr())) == mapDataContainer->Size(mapInstance.m_address), "Python Dict size:%d does not match the size of the unordered_map:%d", pos, mapDataContainer->Size(mapInstance.m_address));
  731. outValue.m_value = mapInstance.m_address;
  732. outValue.m_typeId = mapInstance.m_typeId;
  733. outValue.m_traits = traits;
  734. auto deleteMapInstance = [behaviorClass, mapInstance]()
  735. {
  736. behaviorClass->Destroy(mapInstance);
  737. };
  738. return PythonMarshalTypeRequests::BehaviorValueResult{ true, deleteMapInstance };
  739. }
  740. AZStd::optional<PythonMarshalTypeRequests::PythonValueResult> BehaviorValueParameterToPython(AZ::BehaviorArgument& behaviorValue) override
  741. {
  742. // the class data must have a container interface
  743. auto* containerInterface = m_classData->m_container;
  744. if (!containerInterface)
  745. {
  746. return AZStd::nullopt;
  747. }
  748. if (behaviorValue.ConvertTo(m_typeId))
  749. {
  750. auto cleanUpList = AZStd::make_shared<AZStd::vector<PythonMarshalTypeRequests::DeallocateFunction>>();
  751. pybind11::dict pythonDictionary;
  752. // visit each unordered_map<K,V> entry
  753. auto elementCallback = [pythonDictionary, cleanUpList](void* instancePointer, [[maybe_unused]] const auto& elementClassId, const auto* elementGenericClassData, [[maybe_unused]] const auto* genericClassElement)
  754. {
  755. pybind11::object pythonKey { pybind11::none() };
  756. pybind11::object pythonItem { pybind11::none() };
  757. // visit the AZStd::pair<K,V> elements
  758. auto pairCallback = [cleanUpList, &pythonKey, &pythonItem](void* instancePair, const auto& elementClassId, [[maybe_unused]] const auto* elementGenericClassData, [[maybe_unused]] const auto* genericClassElement)
  759. {
  760. AZ::BehaviorObject behaviorObjectValue(instancePair, elementClassId);
  761. auto result = ProcessBehaviorObject(behaviorObjectValue);
  762. if (result)
  763. {
  764. PythonMarshalTypeRequests::DeallocateFunction deallocateFunction = result.value().second;
  765. if (result.value().second)
  766. {
  767. cleanUpList->emplace_back(AZStd::move(result.value().second));
  768. }
  769. pybind11::object pythonResult = result.value().first;
  770. if (pythonKey.is_none())
  771. {
  772. pythonKey = pythonResult;
  773. }
  774. else if (pythonItem.is_none())
  775. {
  776. pythonItem = pythonResult;
  777. }
  778. }
  779. return true;
  780. };
  781. elementGenericClassData->m_container->EnumElements(instancePointer, pairCallback);
  782. // have a valid key and value?
  783. if (!pythonKey.is_none() && !pythonItem.is_none())
  784. {
  785. // assign the key's value in the dictionary?
  786. if (PyDict_SetItem(pythonDictionary.ptr(), pythonKey.ptr(), pythonItem.ptr()) < 0)
  787. {
  788. AZStd::string pythonKeyString = pybind11::cast<AZStd::string>(pythonKey);
  789. AZStd::string pythonItemString = pybind11::cast<AZStd::string>(pythonItem);
  790. AZ_Warning("python", false, "Could not add key:%s with item value:%s", pythonKeyString.c_str(), pythonKeyString.c_str());
  791. }
  792. }
  793. return true;
  794. };
  795. containerInterface->EnumElements(behaviorValue.m_value, elementCallback);
  796. PythonMarshalTypeRequests::PythonValueResult result;
  797. result.first = pythonDictionary;
  798. if (!cleanUpList->empty())
  799. {
  800. AZStd::weak_ptr<AZStd::vector<PythonMarshalTypeRequests::DeallocateFunction>> cleanUp(cleanUpList);
  801. result.second = [cleanUp]()
  802. {
  803. auto cleanupList = cleanUp.lock();
  804. if (cleanupList)
  805. {
  806. AZStd::for_each(cleanupList->begin(), cleanupList->end(), [](auto& deleteMe) { deleteMe(); });
  807. }
  808. };
  809. }
  810. return result;
  811. }
  812. return AZStd::nullopt;
  813. }
  814. bool CanConvertPythonToBehaviorValue([[maybe_unused]] PythonMarshalTypeRequests::BehaviorTraits traits, pybind11::object pyObj) const override
  815. {
  816. // the underlying types must have exactly two elements
  817. AZStd::vector<AZ::Uuid> typeList = AZ::Utils::GetContainedTypes(m_typeId);
  818. if (typeList.size() != 2)
  819. {
  820. return false;
  821. }
  822. return (PyDict_Check(pyObj.ptr()) != false);
  823. }
  824. };
  825. class TypeConverterVector
  826. : public PythonMarshalComponent::TypeConverter
  827. {
  828. public:
  829. AZ::GenericClassInfo* m_genericClassInfo = nullptr;
  830. const AZ::SerializeContext::ClassData* m_classData = nullptr;
  831. const AZ::TypeId m_typeId = {};
  832. TypeConverterVector(AZ::GenericClassInfo* genericClassInfo, const AZ::SerializeContext::ClassData* classData, const AZ::TypeId& typeId)
  833. : m_genericClassInfo(genericClassInfo)
  834. , m_classData(classData)
  835. , m_typeId(typeId)
  836. {
  837. }
  838. AZStd::optional<PythonMarshalTypeRequests::BehaviorValueResult> HandlePythonElement(PythonMarshalTypeRequests::BehaviorTraits traits, pybind11::object pythonElement, const AZ::TypeId& elementTypeId, AZ::BehaviorArgument& outValue)
  839. {
  840. // first try to convert using the element's type ID
  841. AZStd::optional<PythonMarshalTypeRequests::BehaviorValueResult> result;
  842. PythonMarshalTypeRequestBus::EventResult(result, elementTypeId, &PythonMarshalTypeRequestBus::Events::PythonToBehaviorValueParameter, traits, pythonElement, outValue);
  843. if (result)
  844. {
  845. return result;
  846. }
  847. else if (pybind11::isinstance<EditorPythonBindings::PythonProxyObject>(pythonElement))
  848. {
  849. AZ::BehaviorArgument behaviorArg;
  850. behaviorArg.m_traits = traits;
  851. behaviorArg.m_typeId = elementTypeId;
  852. if (Convert::PythonProxyObjectToBehaviorValueParameter(behaviorArg, pythonElement, outValue))
  853. {
  854. return { { true, nullptr } };
  855. }
  856. }
  857. return AZStd::nullopt;
  858. }
  859. // handle a vector of Behavior Class values
  860. AZStd::optional<PythonMarshalTypeRequests::BehaviorValueResult> PythonToBehaviorObjectList(const AZ::TypeId& elementType, const AZ::BehaviorClass* behaviorClass, PythonMarshalTypeRequests::BehaviorTraits traits, pybind11::object pyObj, AZ::BehaviorArgument& outValue)
  861. {
  862. auto iteratorToPushBackMethod = behaviorClass->m_methods.find("push_back");
  863. if (iteratorToPushBackMethod == behaviorClass->m_methods.end())
  864. {
  865. AZ_Warning("python", false, "BehaviorClass container missing push_back method");
  866. return AZStd::nullopt;
  867. }
  868. // prepare the AZStd::vector container
  869. AZ::BehaviorObject instance = behaviorClass->Create();
  870. AZ::BehaviorMethod* pushBackMethod = iteratorToPushBackMethod->second;
  871. [[maybe_unused]] size_t vectorCount = 0;
  872. pybind11::list pyList(pyObj);
  873. for (auto pyItem = pyList.begin(); pyItem != pyList.end(); ++pyItem)
  874. {
  875. auto pyObjItem = pybind11::cast<pybind11::object>(*pyItem);
  876. AZ::BehaviorArgument elementValue;
  877. auto result = HandlePythonElement(traits, pyObjItem, elementType, elementValue);
  878. if (result && result.value().first)
  879. {
  880. AZ::BehaviorArgument parameters[2];
  881. parameters[0].Set(&instance);
  882. parameters[1].Set(elementValue);
  883. pushBackMethod->Call(parameters, 2);
  884. ++vectorCount;
  885. }
  886. else
  887. {
  888. AZ_Warning("python", false, "Could not convert to behavior element type %s for the vector<>; failed to marshal Python input %s",
  889. elementType.ToString<AZStd::string>().c_str(), Convert::GetPythonTypeName(pyObjItem).c_str());
  890. return AZStd::nullopt;
  891. }
  892. }
  893. AZ_Warning("python", vectorCount == pyList.size(), "Python list size:%d does not match the size of the vector:%d", pyList.size(), vectorCount);
  894. outValue.m_value = instance.m_address;
  895. outValue.m_typeId = instance.m_typeId;
  896. outValue.m_traits = traits;
  897. auto deleteVector = [behaviorClass, instance]()
  898. {
  899. behaviorClass->Destroy(instance);
  900. };
  901. return { { true, deleteVector } };
  902. }
  903. // handle a vector of a data type not registered with the Behavior Context
  904. AZStd::optional<PythonMarshalTypeRequests::BehaviorValueResult> PythonToBehaviorSerializedList(const AZ::TypeId& elementType, PythonMarshalTypeRequests::BehaviorTraits traits, pybind11::object pyObj, AZ::BehaviorArgument& outValue)
  905. {
  906. // fetch the container parts
  907. const AZ::SerializeContext::ClassData* classData = m_genericClassInfo->GetClassData();
  908. const AZ::SerializeContext::ClassElement* classElement = classData->m_container->GetElement(classData->m_container->GetDefaultElementNameCrc());
  909. // prepare the AZStd::vector container
  910. AZ::SerializeContext* serializeContext = nullptr;
  911. AZ::ComponentApplicationBus::BroadcastResult(serializeContext, &AZ::ComponentApplicationRequests::GetSerializeContext);
  912. AZStd::any* newVector = new AZStd::any(serializeContext->CreateAny(m_typeId));
  913. void* instance = AZStd::any_cast<void>(newVector);
  914. [[maybe_unused]] size_t vectorCount = 0;
  915. pybind11::list pyList(pyObj);
  916. for (auto pyItem = pyList.begin(); pyItem != pyList.end(); ++pyItem)
  917. {
  918. auto pyObjItem = pybind11::cast<pybind11::object>(*pyItem);
  919. AZ::BehaviorArgument elementValue;
  920. auto elementResult = HandlePythonElement(traits, pyObjItem, elementType, elementValue);
  921. if (elementResult && elementResult.value().first)
  922. {
  923. void* destination = classData->m_container->ReserveElement(instance, classElement);
  924. AZ_Error("python", destination, "Could not allocate via ReserveElement()");
  925. if (destination)
  926. {
  927. serializeContext->CloneObjectInplace(destination, elementValue.m_value, elementType);
  928. ++vectorCount;
  929. }
  930. }
  931. else
  932. {
  933. AZ_Warning("python", false, "Could not convert to serialized element type %s for the vector<>; failed to marshal Python input %s",
  934. elementType.ToString<AZStd::string>().c_str(), Convert::GetPythonTypeName(pyObjItem).c_str());
  935. return AZStd::nullopt;
  936. }
  937. }
  938. AZ_Warning("python", vectorCount == pyList.size(), "Python list size:%d does not match the size of the vector:%d", pyList.size(), vectorCount);
  939. outValue.m_name = classData->m_name;
  940. outValue.m_value = instance;
  941. outValue.m_typeId = m_typeId;
  942. outValue.m_traits = traits;
  943. auto deleteVector = [newVector]()
  944. {
  945. delete newVector;
  946. };
  947. return { { true, deleteVector } };
  948. }
  949. AZStd::optional<PythonMarshalTypeRequests::BehaviorValueResult> PythonToBehaviorValueParameter(PythonMarshalTypeRequests::BehaviorTraits traits, pybind11::object pyObj, AZ::BehaviorArgument& outValue) override
  950. {
  951. AZStd::vector<AZ::Uuid> typeList = AZ::Utils::GetContainedTypes(m_typeId);
  952. if (typeList.empty())
  953. {
  954. AZ_Warning("python", false, "The list container type for %s had no types; expected one type", m_classData->m_name);
  955. return AZStd::nullopt;
  956. }
  957. else if (PyList_Check(pyObj.ptr()) == false)
  958. {
  959. AZ_Warning("python", false, "Expected a Python List as input");
  960. return AZStd::nullopt;
  961. }
  962. const AZ::BehaviorClass* behaviorClass = AZ::BehaviorContextHelper::GetClass(m_typeId);
  963. if (behaviorClass)
  964. {
  965. return PythonToBehaviorObjectList(typeList[0], behaviorClass, traits, pyObj, outValue);
  966. }
  967. return PythonToBehaviorSerializedList(typeList[0], traits, pyObj, outValue);
  968. }
  969. using HandleResult = AZStd::optional<PythonMarshalTypeRequests::DeallocateFunction>;
  970. HandleResult HandleElement(AZ::BehaviorObject& behaviorObject, pybind11::list pythonList)
  971. {
  972. AZ::BehaviorArgument source;
  973. source.m_value = behaviorObject.m_address;
  974. source.m_typeId = behaviorObject.m_typeId;
  975. AZStd::optional<PythonMarshalTypeRequests::PythonValueResult> result;
  976. PythonMarshalTypeRequestBus::EventResult(result, source.m_typeId, &PythonMarshalTypeRequestBus::Events::BehaviorValueParameterToPython, source);
  977. if (result.has_value())
  978. {
  979. pythonList.append(result.value().first);
  980. return AZStd::move(result.value().second);
  981. }
  982. // return back a 'list of opaque Behavior Objects' back to the caller if not a 'simple' type
  983. pybind11::object value = PythonProxyObjectManagement::CreatePythonProxyObject(behaviorObject.m_typeId, behaviorObject.m_address);
  984. if (!value.is_none())
  985. {
  986. pythonList.append(value);
  987. }
  988. return AZStd::nullopt;
  989. }
  990. AZStd::optional<PythonMarshalTypeRequests::PythonValueResult> BehaviorValueParameterToPython(AZ::BehaviorArgument& behaviorValue) override
  991. {
  992. auto* container = m_classData->m_container;
  993. if (behaviorValue.ConvertTo(m_typeId) && container)
  994. {
  995. auto deleterList = AZStd::make_shared<AZStd::vector<PythonMarshalTypeRequests::DeallocateFunction>>();
  996. pybind11::list pythonList;
  997. auto elementCallback = [this, pythonList, deleterList](void* instancePointer, const auto& elementClassId, [[maybe_unused]] const auto* elementGenericClassData, [[maybe_unused]] const auto* genericClassElement)
  998. {
  999. AZ::BehaviorObject behaviorObject(instancePointer, elementClassId);
  1000. auto result = this->HandleElement(behaviorObject, pythonList);
  1001. if (result)
  1002. {
  1003. if (result.value())
  1004. {
  1005. deleterList->emplace_back(AZStd::move(result.value()));
  1006. }
  1007. }
  1008. return true;
  1009. };
  1010. container->EnumElements(behaviorValue.m_value, elementCallback);
  1011. PythonMarshalTypeRequests::PythonValueResult result;
  1012. result.first = pythonList;
  1013. if (!deleterList->empty())
  1014. {
  1015. AZStd::weak_ptr<AZStd::vector<PythonMarshalTypeRequests::DeallocateFunction>> cleanUp(deleterList);
  1016. result.second = [cleanUp]()
  1017. {
  1018. auto cleanupList = cleanUp.lock();
  1019. if (cleanupList)
  1020. {
  1021. AZStd::for_each(cleanupList->begin(), cleanupList->end(), [](auto& deleteMe) { deleteMe(); });
  1022. }
  1023. };
  1024. }
  1025. return result;
  1026. }
  1027. return AZStd::nullopt;
  1028. }
  1029. bool CanConvertPythonToBehaviorValue([[maybe_unused]] PythonMarshalTypeRequests::BehaviorTraits traits, pybind11::object pyObj) const override
  1030. {
  1031. AZStd::vector<AZ::Uuid> typeList = AZ::Utils::GetContainedTypes(m_typeId);
  1032. if (typeList.empty())
  1033. {
  1034. return false;
  1035. }
  1036. return (PyList_Check(pyObj.ptr()) != false);
  1037. }
  1038. };
  1039. class TypeConverterSet
  1040. : public PythonMarshalComponent::TypeConverter
  1041. {
  1042. public:
  1043. AZ::GenericClassInfo* m_genericClassInfo = nullptr;
  1044. const AZ::SerializeContext::ClassData* m_classData = nullptr;
  1045. const AZ::TypeId m_typeId = {};
  1046. TypeConverterSet(AZ::GenericClassInfo* genericClassInfo, const AZ::SerializeContext::ClassData* classData, const AZ::TypeId& typeId)
  1047. : m_genericClassInfo(genericClassInfo)
  1048. , m_classData(classData)
  1049. , m_typeId(typeId)
  1050. {
  1051. }
  1052. // handle a vector of Behavior Class values
  1053. AZStd::optional<PythonMarshalTypeRequests::BehaviorValueResult> PythonToBehaviorObjectSet(const AZ::TypeId& elementType, const AZ::BehaviorClass* behaviorClass, PythonMarshalTypeRequests::BehaviorTraits traits, pybind11::object pyObj, AZ::BehaviorArgument& outValue)
  1054. {
  1055. auto iteratorToInsertMethod = behaviorClass->m_methods.find("Insert");
  1056. if (iteratorToInsertMethod == behaviorClass->m_methods.end())
  1057. {
  1058. AZ_Error("python", false, "The AZStd::unordered_set BehaviorClass reflection is missing the Insert method!");
  1059. return AZStd::nullopt;
  1060. }
  1061. // prepare the AZStd::unordered_set container
  1062. AZ::BehaviorObject instance = behaviorClass->Create();
  1063. AZ::BehaviorMethod* insertMethod = iteratorToInsertMethod->second;
  1064. [[maybe_unused]] size_t itemCount = 0;
  1065. pybind11::set pySet(pyObj);
  1066. for (auto pyItem = pySet.begin(); pyItem != pySet.end(); ++pyItem)
  1067. {
  1068. auto pyObjItem = pybind11::cast<pybind11::object>(*pyItem);
  1069. AZ::BehaviorArgument elementValue;
  1070. auto result = ConvertPythonElement(traits, pyObjItem, elementType, elementValue);
  1071. if (result && result.value().first)
  1072. {
  1073. AZ::BehaviorArgument parameters[2];
  1074. // set the 'this' pointer
  1075. parameters[0].m_value = instance.m_address;
  1076. parameters[0].m_typeId = instance.m_typeId;
  1077. // set the value element
  1078. parameters[1].Set(elementValue);
  1079. insertMethod->Call(parameters, 2);
  1080. ++itemCount;
  1081. }
  1082. else
  1083. {
  1084. AZ_Warning("python", false, "Convert to behavior element type %s for the unordered_set<> failed to marshal Python input %s",
  1085. elementType.ToString<AZStd::string>().c_str(), Convert::GetPythonTypeName(pyObjItem).c_str());
  1086. return AZStd::nullopt;
  1087. }
  1088. }
  1089. AZ_Warning("python", itemCount == pySet.size(), "Python Set size:%d does not match the size of the unordered_set:%d", pySet.size(), itemCount);
  1090. outValue.m_value = instance.m_address;
  1091. outValue.m_typeId = instance.m_typeId;
  1092. outValue.m_traits = traits;
  1093. auto deleteVector = [behaviorClass, instance]()
  1094. {
  1095. behaviorClass->Destroy(instance);
  1096. };
  1097. return { { true, deleteVector } };
  1098. }
  1099. AZStd::optional<PythonMarshalTypeRequests::BehaviorValueResult> PythonToBehaviorSerializedSet(const AZ::TypeId& elementType, PythonMarshalTypeRequests::BehaviorTraits traits, pybind11::object pyObj, AZ::BehaviorArgument& outValue)
  1100. {
  1101. // fetch the container parts
  1102. const AZ::SerializeContext::ClassData* classData = m_genericClassInfo->GetClassData();
  1103. const AZ::SerializeContext::ClassElement* classElement = classData->m_container->GetElement(classData->m_container->GetDefaultElementNameCrc());
  1104. // prepare the AZStd::unordered_set container
  1105. AZ::SerializeContext* serializeContext = nullptr;
  1106. AZ::ComponentApplicationBus::BroadcastResult(serializeContext, &AZ::ComponentApplicationRequests::GetSerializeContext);
  1107. AZStd::any* newVector = new AZStd::any(serializeContext->CreateAny(m_typeId));
  1108. void* instance = AZStd::any_cast<void>(newVector);
  1109. [[maybe_unused]] size_t itemCount = 0;
  1110. pybind11::set pySet(pyObj);
  1111. for (auto pyItem = pySet.begin(); pyItem != pySet.end(); ++pyItem)
  1112. {
  1113. auto pyObjItem = pybind11::cast<pybind11::object>(*pyItem);
  1114. AZ::BehaviorArgument elementValue;
  1115. auto elementResult = ConvertPythonElement(traits, pyObjItem, elementType, elementValue);
  1116. if (elementResult && elementResult.value().first)
  1117. {
  1118. void* destination = classData->m_container->ReserveElement(instance, classElement);
  1119. AZ_Error("python", destination, "Could not allocate via ReserveElement()");
  1120. if (destination)
  1121. {
  1122. serializeContext->CloneObjectInplace(destination, elementValue.m_value, elementType);
  1123. ++itemCount;
  1124. }
  1125. }
  1126. else
  1127. {
  1128. AZ_Warning("python", false, "Convert to serialized element type %s for the unordered_set<> failed to marshal Python input %s",
  1129. elementType.ToString<AZStd::string>().c_str(), Convert::GetPythonTypeName(pyObjItem).c_str());
  1130. return AZStd::nullopt;
  1131. }
  1132. }
  1133. AZ_Warning("python", itemCount == pySet.size(), "Python list size:%d does not match the size of the unordered_set:%d", pySet.size(), itemCount);
  1134. outValue.m_name = classData->m_name;
  1135. outValue.m_value = instance;
  1136. outValue.m_typeId = m_typeId;
  1137. outValue.m_traits = traits;
  1138. auto deleteVector = [newVector]()
  1139. {
  1140. delete newVector;
  1141. };
  1142. return { { true, deleteVector } };
  1143. }
  1144. AZStd::optional<PythonMarshalTypeRequests::BehaviorValueResult> PythonToBehaviorValueParameter(PythonMarshalTypeRequests::BehaviorTraits traits, pybind11::object pyObj, AZ::BehaviorArgument& outValue) override
  1145. {
  1146. AZStd::vector<AZ::Uuid> typeList = AZ::Utils::GetContainedTypes(m_typeId);
  1147. if (typeList.empty())
  1148. {
  1149. AZ_Warning("python", false, "The unordered_set container type for %s had no types; expected one type", m_classData->m_name);
  1150. return AZStd::nullopt;
  1151. }
  1152. else if (PySet_Check(pyObj.ptr()) == false)
  1153. {
  1154. AZ_Warning("python", false, "Expected a Python Set as input");
  1155. return AZStd::nullopt;
  1156. }
  1157. const AZ::BehaviorClass* behaviorClass = AZ::BehaviorContextHelper::GetClass(m_typeId);
  1158. if (behaviorClass)
  1159. {
  1160. return PythonToBehaviorObjectSet(typeList[0], behaviorClass, traits, pyObj, outValue);
  1161. }
  1162. return PythonToBehaviorSerializedSet(typeList[0], traits, pyObj, outValue);
  1163. }
  1164. AZStd::optional<PythonMarshalTypeRequests::DeallocateFunction> HandleSetElement(AZ::BehaviorObject& behaviorObject, pybind11::set pythonSet)
  1165. {
  1166. AZ::BehaviorArgument source;
  1167. source.m_value = behaviorObject.m_address;
  1168. source.m_typeId = behaviorObject.m_typeId;
  1169. AZStd::optional<PythonMarshalTypeRequests::PythonValueResult> result;
  1170. PythonMarshalTypeRequestBus::EventResult(result, source.m_typeId, &PythonMarshalTypeRequestBus::Events::BehaviorValueParameterToPython, source);
  1171. if (result.has_value())
  1172. {
  1173. pythonSet.add(result.value().first);
  1174. return AZStd::move(result.value().second);
  1175. }
  1176. // return back a 'list of opaque Behavior Objects' back to the caller if not a 'simple' type
  1177. pybind11::object value = PythonProxyObjectManagement::CreatePythonProxyObject(behaviorObject.m_typeId, behaviorObject.m_address);
  1178. if (!value.is_none())
  1179. {
  1180. pythonSet.add(value);
  1181. }
  1182. return AZStd::nullopt;
  1183. }
  1184. AZStd::optional<PythonMarshalTypeRequests::PythonValueResult> BehaviorValueParameterToPython(AZ::BehaviorArgument& behaviorValue) override
  1185. {
  1186. auto* container = m_classData->m_container;
  1187. AZ_Error("python", container, "Set container class data is missing");
  1188. if (container == nullptr)
  1189. {
  1190. return AZStd::nullopt;
  1191. }
  1192. if (behaviorValue.ConvertTo(m_typeId))
  1193. {
  1194. auto deleterList = AZStd::make_shared<AZStd::vector<PythonMarshalTypeRequests::DeallocateFunction>>();
  1195. pybind11::set pythonSet;
  1196. auto elementCallback = [this, pythonSet, deleterList](void* instancePointer, const auto& elementClassId, const auto*, const auto*)
  1197. {
  1198. AZ::BehaviorObject behaviorObject(instancePointer, elementClassId);
  1199. auto result = this->HandleSetElement(behaviorObject, pythonSet);
  1200. if (result)
  1201. {
  1202. if (result.value())
  1203. {
  1204. deleterList->emplace_back(AZStd::move(result.value()));
  1205. }
  1206. }
  1207. return true;
  1208. };
  1209. container->EnumElements(behaviorValue.m_value, elementCallback);
  1210. PythonMarshalTypeRequests::PythonValueResult result;
  1211. result.first = pythonSet;
  1212. if (!deleterList->empty())
  1213. {
  1214. AZStd::weak_ptr<AZStd::vector<PythonMarshalTypeRequests::DeallocateFunction>> cleanUp(deleterList);
  1215. result.second = [cleanUp]()
  1216. {
  1217. auto cleanupList = cleanUp.lock();
  1218. if (cleanupList)
  1219. {
  1220. AZStd::for_each(cleanupList->begin(), cleanupList->end(), [](auto& deleteMe) { deleteMe(); });
  1221. }
  1222. };
  1223. }
  1224. return result;
  1225. }
  1226. return AZStd::nullopt;
  1227. }
  1228. bool CanConvertPythonToBehaviorValue([[maybe_unused]] PythonMarshalTypeRequests::BehaviorTraits traits, pybind11::object pyObj) const override
  1229. {
  1230. AZStd::vector<AZ::Uuid> typeList = AZ::Utils::GetContainedTypes(m_typeId);
  1231. if (typeList.empty())
  1232. {
  1233. return false;
  1234. }
  1235. return (PySet_Check(pyObj.ptr()) != false);
  1236. }
  1237. };
  1238. class TypeConverterPair final
  1239. : public PythonMarshalComponent::TypeConverter
  1240. {
  1241. const AZ::SerializeContext::ClassData* m_classData = nullptr;
  1242. const AZ::TypeId m_typeId = {};
  1243. bool IsValidList(pybind11::object pyObj) const
  1244. {
  1245. return PyList_Check(pyObj.ptr()) != false && PyList_Size(pyObj.ptr()) == 2;
  1246. }
  1247. bool IsValidTuple(pybind11::object pyObj) const
  1248. {
  1249. return PyTuple_Check(pyObj.ptr()) != false && PyTuple_Size(pyObj.ptr()) == 2;
  1250. }
  1251. bool IsCompatibleProxy(pybind11::object pyObj) const
  1252. {
  1253. if (pybind11::isinstance<EditorPythonBindings::PythonProxyObject>(pyObj))
  1254. {
  1255. auto behaviorObject = pybind11::cast<EditorPythonBindings::PythonProxyObject*>(pyObj)->GetBehaviorObject();
  1256. AZ::Uuid typeId = behaviorObject.value()->m_typeId;
  1257. return AZ::Utils::IsPairContainerType(typeId);
  1258. }
  1259. return false;
  1260. }
  1261. public:
  1262. TypeConverterPair([[maybe_unused]] AZ::GenericClassInfo* genericClassInfo, const AZ::SerializeContext::ClassData* classData, const AZ::TypeId& typeId)
  1263. : m_classData(classData)
  1264. , m_typeId(typeId)
  1265. {
  1266. }
  1267. AZStd::optional<PythonMarshalTypeRequests::BehaviorValueResult> PythonToBehaviorValueParameter(PythonMarshalTypeRequests::BehaviorTraits traits, pybind11::object pyObj, AZ::BehaviorArgument& outValue) override
  1268. {
  1269. if (!CanConvertPythonToBehaviorValue(traits, pyObj))
  1270. {
  1271. AZ_Warning("python", false, "Cannot convert pair container for %s", m_classData->m_name);
  1272. return AZStd::nullopt;
  1273. }
  1274. const AZ::BehaviorClass* behaviorClass = AZ::BehaviorContextHelper::GetClass(m_typeId);
  1275. if (!behaviorClass)
  1276. {
  1277. AZ_Warning("python", false, "Missing pair behavior class for %s", m_typeId.ToString<AZStd::string>().c_str());
  1278. return AZStd::nullopt;
  1279. }
  1280. AZ::SerializeContext* serializeContext = nullptr;
  1281. AZ::ComponentApplicationBus::BroadcastResult(serializeContext, &AZ::ComponentApplicationRequests::GetSerializeContext);
  1282. if (!serializeContext)
  1283. {
  1284. return AZStd::nullopt;
  1285. }
  1286. // prepare the AZStd::pair<> container
  1287. AZ::BehaviorObject pairInstance = behaviorClass->Create();
  1288. AZ::SerializeContext::IDataContainer* pairDataContainer = m_classData->m_container;
  1289. // get the element types
  1290. const AZ::SerializeContext::ClassElement* element0 = nullptr;
  1291. const AZ::SerializeContext::ClassElement* element1 = nullptr;
  1292. auto elementTypeEnumCallback = [&element0, &element1](const AZ::Uuid&, const AZ::SerializeContext::ClassElement* genericClassElement)
  1293. {
  1294. if (genericClassElement->m_flags & AZ::SerializeContext::ClassElement::Flags::FLG_POINTER)
  1295. {
  1296. AZ_Error("python", false, "Python marshalling does not handle naked pointers; not converting the pair");
  1297. return false;
  1298. }
  1299. else if (!element0)
  1300. {
  1301. element0 = genericClassElement;
  1302. }
  1303. else if (!element1)
  1304. {
  1305. element1 = genericClassElement;
  1306. }
  1307. else
  1308. {
  1309. AZ_Error("python", false, "The pair container can't have more than 2 elements.");
  1310. return false;
  1311. }
  1312. return true;
  1313. };
  1314. pairDataContainer->EnumTypes(elementTypeEnumCallback);
  1315. if (!element0 || !element1)
  1316. {
  1317. AZ_Error("python", false, "Could not retrieve pair elements.");
  1318. return AZStd::nullopt;
  1319. }
  1320. // load python items into pair elements
  1321. PyObject* item0 = nullptr;
  1322. PyObject* item1 = nullptr;
  1323. if (IsValidList(pyObj))
  1324. {
  1325. pybind11::list pyList(pyObj);
  1326. item0 = pyList[0].ptr();
  1327. item1 = pyList[1].ptr();
  1328. }
  1329. else if (IsValidTuple(pyObj))
  1330. {
  1331. pybind11::tuple pyTuple(pyObj);
  1332. item0 = pyTuple[0].ptr();
  1333. item1 = pyTuple[1].ptr();
  1334. }
  1335. else if (IsCompatibleProxy(pyObj))
  1336. {
  1337. // OnDemandReflection<AZStd::pair<T1, T2>> exposes "first" and "second" in the proxy object
  1338. EditorPythonBindings::PythonProxyObject* proxy = pybind11::cast<EditorPythonBindings::PythonProxyObject*>(pyObj);
  1339. item0 = proxy->GetPropertyValue("first").ptr();
  1340. item1 = proxy->GetPropertyValue("second").ptr();
  1341. }
  1342. void* reserved0 = pairDataContainer->ReserveElement(pairInstance.m_address, element0);
  1343. AZ_Assert(reserved0, "Could not allocate pair's first item via ReserveElement()");
  1344. if (item0 && item1 && !LoadPythonToPairElement(item0, traits, element0, pairDataContainer, 0, serializeContext, pairInstance.m_address))
  1345. {
  1346. pairDataContainer->FreeReservedElement(pairInstance.m_address, reserved0, serializeContext);
  1347. return AZStd::nullopt;
  1348. }
  1349. void* reserved1 = pairDataContainer->ReserveElement(pairInstance.m_address, element1);
  1350. AZ_Assert(reserved1, "Could not allocate pair's second item via ReserveElement()");
  1351. if (item1 && !LoadPythonToPairElement(item1, traits, element1, pairDataContainer, 1, serializeContext, pairInstance.m_address))
  1352. {
  1353. pairDataContainer->FreeReservedElement(pairInstance.m_address, reserved0, serializeContext);
  1354. pairDataContainer->FreeReservedElement(pairInstance.m_address, reserved1, serializeContext);
  1355. return AZStd::nullopt;
  1356. }
  1357. outValue.m_value = pairInstance.m_address;
  1358. outValue.m_typeId = pairInstance.m_typeId;
  1359. outValue.m_traits = traits;
  1360. auto pairInstanceDeleter = [behaviorClass, pairInstance]()
  1361. {
  1362. behaviorClass->Destroy(pairInstance);
  1363. };
  1364. return PythonMarshalTypeRequests::BehaviorValueResult{ true, pairInstanceDeleter };
  1365. }
  1366. AZStd::optional<PythonMarshalTypeRequests::PythonValueResult> BehaviorValueParameterToPython(AZ::BehaviorArgument& behaviorValue) override
  1367. {
  1368. // the class data must have a container interface
  1369. AZ::SerializeContext::IDataContainer* containerInterface = m_classData->m_container;
  1370. if (!containerInterface)
  1371. {
  1372. AZ_Warning("python", false, "Container interface is missing from class %s.", m_classData->m_name);
  1373. return AZStd::nullopt;
  1374. }
  1375. if (!behaviorValue.ConvertTo(m_typeId))
  1376. {
  1377. AZ_Warning("python", false, "Cannot convert behavior value %s.", behaviorValue.m_name);
  1378. return AZStd::nullopt;
  1379. }
  1380. auto cleanUpList = AZStd::make_shared<AZStd::vector<PythonMarshalTypeRequests::DeallocateFunction>>();
  1381. // return pair as list, if conversion failed for an item it will remain as 'none'
  1382. pybind11::list pythonList;
  1383. pybind11::object pythonItem0{ pybind11::none() };
  1384. pybind11::object pythonItem1{ pybind11::none() };
  1385. size_t itemCount = 0;
  1386. auto pairElementCallback = [cleanUpList, &pythonItem0, &pythonItem1, &itemCount](void* instancePair, const AZ::Uuid& elementClassId, [[maybe_unused]] const AZ::SerializeContext::ClassData* elementGenericClassData, [[maybe_unused]] const AZ::SerializeContext::ClassElement* genericClassElement)
  1387. {
  1388. AZ::BehaviorObject behaviorObjectValue(instancePair, elementClassId);
  1389. auto result = ProcessBehaviorObject(behaviorObjectValue);
  1390. if (result.has_value())
  1391. {
  1392. PythonMarshalTypeRequests::DeallocateFunction deallocateFunction = result.value().second;
  1393. if (result.value().second)
  1394. {
  1395. cleanUpList->emplace_back(AZStd::move(result.value().second));
  1396. }
  1397. pybind11::object pythonResult = result.value().first;
  1398. if (itemCount == 0)
  1399. {
  1400. pythonItem0 = pythonResult;
  1401. }
  1402. else
  1403. {
  1404. pythonItem1 = pythonResult;
  1405. }
  1406. itemCount++;
  1407. }
  1408. else
  1409. {
  1410. AZ_Warning("python", false, "BehaviorObject was not processed, python item will remain 'none'.");
  1411. }
  1412. return true;
  1413. };
  1414. containerInterface->EnumElements(behaviorValue.m_value, pairElementCallback);
  1415. pythonList.append(pythonItem0);
  1416. pythonList.append(pythonItem1);
  1417. PythonMarshalTypeRequests::PythonValueResult result;
  1418. result.first = pythonList;
  1419. if (!cleanUpList->empty())
  1420. {
  1421. AZStd::weak_ptr<AZStd::vector<PythonMarshalTypeRequests::DeallocateFunction>> cleanUp(cleanUpList);
  1422. result.second = [cleanUp]()
  1423. {
  1424. auto cleanupList = cleanUp.lock();
  1425. if (cleanupList)
  1426. {
  1427. AZStd::for_each(cleanupList->begin(), cleanupList->end(), [](auto& deleteMe) { deleteMe(); });
  1428. }
  1429. };
  1430. }
  1431. return result;
  1432. }
  1433. bool CanConvertPythonToBehaviorValue([[maybe_unused]] PythonMarshalTypeRequests::BehaviorTraits traits, pybind11::object pyObj) const override
  1434. {
  1435. AZStd::vector<AZ::Uuid> typeList = AZ::Utils::GetContainedTypes(m_typeId);
  1436. bool isList = IsValidList(pyObj);
  1437. bool isTuple = IsValidTuple(pyObj);
  1438. bool isCompatibleProxy = IsCompatibleProxy(pyObj);
  1439. if (typeList.empty() || typeList.size() != 2)
  1440. {
  1441. return false;
  1442. }
  1443. return isList || isTuple || isCompatibleProxy;
  1444. }
  1445. };
  1446. using TypeConverterRegistrant = AZStd::function<void(const AZ::TypeId& typeId, PythonMarshalComponent::TypeConverterPointer typeConverterPointer)>;
  1447. void RegisterContainerTypes(TypeConverterRegistrant registrant)
  1448. {
  1449. AZ::SerializeContext* serializeContext = nullptr;
  1450. AZ::ComponentApplicationBus::BroadcastResult(serializeContext, &AZ::ComponentApplicationRequests::GetSerializeContext);
  1451. if (!serializeContext)
  1452. {
  1453. return;
  1454. }
  1455. // handle the generic container types and create type converters for each found
  1456. auto handleTypeInfo = [registrant, serializeContext](const AZ::SerializeContext::ClassData* classData, const AZ::TypeId& typeId)
  1457. {
  1458. if (typeId == AZ::AzTypeInfo<AZStd::vector<AZ::u8>>::Uuid())
  1459. {
  1460. // AZStd::vector<AZ::u8> is registered in the Serialization Context as a ByteStream, so it fails on IsVectorContainerType()
  1461. registrant(typeId, AZStd::make_unique<TypeConverterByteStream>());
  1462. }
  1463. else if (AZ::Utils::IsVectorContainerType(typeId))
  1464. {
  1465. registrant(typeId, AZStd::make_unique<TypeConverterVector>(serializeContext->FindGenericClassInfo(typeId), classData, typeId));
  1466. }
  1467. else if (AZ::Utils::IsMapContainerType(typeId))
  1468. {
  1469. registrant(typeId, AZStd::make_unique<TypeConverterDictionary>(serializeContext->FindGenericClassInfo(typeId), classData, typeId));
  1470. }
  1471. else if (AZ::Utils::IsPairContainerType(typeId))
  1472. {
  1473. registrant(typeId, AZStd::make_unique<TypeConverterPair>(serializeContext->FindGenericClassInfo(typeId), classData, typeId));
  1474. }
  1475. else if (AZ::Utils::IsTupleContainerType(typeId))
  1476. {
  1477. registrant(
  1478. typeId, AZStd::make_unique<TypeConverterTuple>(serializeContext->FindGenericClassInfo(typeId), classData, typeId));
  1479. }
  1480. else if (AZ::Utils::IsSetContainerType(typeId))
  1481. {
  1482. registrant(typeId, AZStd::make_unique<TypeConverterSet>(serializeContext->FindGenericClassInfo(typeId), classData, typeId));
  1483. }
  1484. return true;
  1485. };
  1486. const bool includeGenerics = true;
  1487. serializeContext->EnumerateAll(handleTypeInfo, includeGenerics);
  1488. }
  1489. }
  1490. AZStd::optional<PythonMarshalComponent::BehaviorValueResult> PythonMarshalComponent::PythonToBehaviorValueParameter(PythonMarshalTypeRequests::BehaviorTraits traits, pybind11::object pyObj, AZ::BehaviorArgument& outValue)
  1491. {
  1492. const auto* typeId = PythonMarshalTypeRequestBus::GetCurrentBusId();
  1493. AZ_Error("python", typeId, "Requires a valid non-null AZ::TypeId pointer");
  1494. if (!typeId)
  1495. {
  1496. return AZStd::nullopt;
  1497. }
  1498. auto converterEntry = m_typeConverterMap.find(*typeId);
  1499. if (m_typeConverterMap.end() == converterEntry)
  1500. {
  1501. return AZStd::nullopt;
  1502. }
  1503. return converterEntry->second->PythonToBehaviorValueParameter(traits, pyObj, outValue);
  1504. }
  1505. AZStd::optional<PythonMarshalComponent::PythonValueResult> PythonMarshalComponent::BehaviorValueParameterToPython(AZ::BehaviorArgument& behaviorValue)
  1506. {
  1507. const auto* typeId = PythonMarshalTypeRequestBus::GetCurrentBusId();
  1508. AZ_Error("python", typeId, "Requires a valid non-null AZ::TypeId pointer");
  1509. if (!typeId)
  1510. {
  1511. return AZStd::nullopt;
  1512. }
  1513. auto converterEntry = m_typeConverterMap.find(*typeId);
  1514. if (m_typeConverterMap.end() == converterEntry)
  1515. {
  1516. return AZStd::nullopt;
  1517. }
  1518. return converterEntry->second->BehaviorValueParameterToPython(behaviorValue);
  1519. }
  1520. //////////////////////////////////////////////////////////////////////////
  1521. // PythonMarshalComponent
  1522. void PythonMarshalComponent::GetProvidedServices(AZ::ComponentDescriptor::DependencyArrayType& provided)
  1523. {
  1524. provided.push_back(PythonMarshalingService);
  1525. }
  1526. void PythonMarshalComponent::GetIncompatibleServices(AZ::ComponentDescriptor::DependencyArrayType& incompatible)
  1527. {
  1528. incompatible.push_back(PythonMarshalingService);
  1529. }
  1530. void PythonMarshalComponent::GetRequiredServices(AZ::ComponentDescriptor::DependencyArrayType& required)
  1531. {
  1532. required.push_back(PythonEmbeddedService);
  1533. }
  1534. bool PythonMarshalComponent::CanConvertPythonToBehaviorValue(BehaviorTraits traits, pybind11::object pyObj) const
  1535. {
  1536. const auto* typeId = PythonMarshalTypeRequestBus::GetCurrentBusId();
  1537. AZ_Error("python", typeId, "Requires a valid non-null AZ::TypeId pointer");
  1538. if (!typeId)
  1539. {
  1540. return false;
  1541. }
  1542. auto converterEntry = m_typeConverterMap.find(*typeId);
  1543. if (converterEntry == m_typeConverterMap.end())
  1544. {
  1545. return false;
  1546. }
  1547. return converterEntry->second->CanConvertPythonToBehaviorValue(traits, pyObj);
  1548. }
  1549. void PythonMarshalComponent::RegisterTypeConverter(const AZ::TypeId& typeId, TypeConverterPointer typeConverterPointer)
  1550. {
  1551. PythonMarshalTypeRequestBus::MultiHandler::BusConnect(typeId);
  1552. m_typeConverterMap[typeId] = AZStd::move(typeConverterPointer);
  1553. }
  1554. void PythonMarshalComponent::Reflect(AZ::ReflectContext* context)
  1555. {
  1556. if (auto&& serialize = azrtti_cast<AZ::SerializeContext*>(context))
  1557. {
  1558. serialize->Class<PythonMarshalComponent, AZ::Component>()
  1559. ->Version(1)
  1560. ->Attribute(AZ::Edit::Attributes::SystemComponentTags, AZStd::vector<AZ::Crc32>{AZ_CRC_CE("AssetBuilder")})
  1561. ;
  1562. }
  1563. }
  1564. void PythonMarshalComponent::Activate()
  1565. {
  1566. RegisterTypeConverter(AZ::AzTypeInfo<bool>::Uuid(), AZStd::make_unique<TypeConverterBool>());
  1567. RegisterTypeConverter(AZ::AzTypeInfo<char>::Uuid(), AZStd::make_unique<TypeConverterChar>());
  1568. RegisterTypeConverter(AZ::AzTypeInfo<AZ::s8>::Uuid(), AZStd::make_unique<TypeConverterInteger<AZ::s8>>());
  1569. RegisterTypeConverter(AZ::AzTypeInfo<AZ::u8>::Uuid(), AZStd::make_unique<TypeConverterInteger<AZ::u8>>());
  1570. RegisterTypeConverter(AZ::AzTypeInfo<AZ::s16>::Uuid(), AZStd::make_unique<TypeConverterInteger<AZ::s16>>());
  1571. RegisterTypeConverter(AZ::AzTypeInfo<AZ::u16>::Uuid(), AZStd::make_unique<TypeConverterInteger<AZ::u16>>());
  1572. RegisterTypeConverter(AZ::AzTypeInfo<AZ::s32>::Uuid(), AZStd::make_unique<TypeConverterInteger<AZ::s32>>());
  1573. RegisterTypeConverter(AZ::AzTypeInfo<AZ::u32>::Uuid(), AZStd::make_unique<TypeConverterInteger<AZ::u32>>());
  1574. RegisterTypeConverter(AZ::AzTypeInfo<AZ::s64>::Uuid(), AZStd::make_unique<TypeConverterInteger<AZ::s64>>());
  1575. RegisterTypeConverter(AZ::AzTypeInfo<AZ::u64>::Uuid(), AZStd::make_unique<TypeConverterInteger<AZ::u64>>());
  1576. RegisterTypeConverter(AZ::AzTypeInfo<long>::Uuid(), AZStd::make_unique<TypeConverterInteger<long>>());
  1577. RegisterTypeConverter(AZ::AzTypeInfo<unsigned long>::Uuid(), AZStd::make_unique<TypeConverterInteger<unsigned long>>());
  1578. RegisterTypeConverter(AZ::AzTypeInfo<float>::Uuid(), AZStd::make_unique<TypeConverterReal<float, float, float>>());
  1579. RegisterTypeConverter(AZ::AzTypeInfo<double>::Uuid(), AZStd::make_unique<TypeConverterReal<double, double, double>>());
  1580. RegisterTypeConverter(AZ::AzTypeInfo<AZStd::string>::Uuid(), AZStd::make_unique<TypeConverterString<AZStd::string>>());
  1581. RegisterTypeConverter(AZ::AzTypeInfo<AZ::IO::FixedMaxPathString>::Uuid(), AZStd::make_unique<TypeConverterString<AZ::IO::FixedMaxPathString>>());
  1582. RegisterTypeConverter(AZ::AzTypeInfo<AZStd::string_view>::Uuid(), AZStd::make_unique<TypeConverterString<AZStd::string_view>>());
  1583. RegisterTypeConverter(AZ::AzTypeInfo<AZStd::any>::Uuid(), AZStd::make_unique<TypeConverterAny>());
  1584. Container::RegisterContainerTypes([this](const AZ::TypeId& typeId, auto containerConverter)
  1585. {
  1586. this->RegisterTypeConverter(typeId, AZStd::move(containerConverter));
  1587. });
  1588. }
  1589. void PythonMarshalComponent::Deactivate()
  1590. {
  1591. PythonMarshalTypeRequestBus::MultiHandler::BusDisconnect();
  1592. m_typeConverterMap.clear();
  1593. }
  1594. }