Addons.cpp 85 KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000100110021003100410051006100710081009101010111012101310141015101610171018101910201021102210231024102510261027102810291030103110321033103410351036103710381039104010411042104310441045104610471048104910501051105210531054105510561057105810591060106110621063106410651066106710681069107010711072107310741075107610771078107910801081108210831084108510861087108810891090109110921093109410951096109710981099110011011102110311041105110611071108110911101111111211131114111511161117111811191120112111221123112411251126112711281129113011311132113311341135113611371138113911401141114211431144114511461147114811491150115111521153115411551156115711581159116011611162116311641165116611671168116911701171117211731174117511761177117811791180118111821183118411851186118711881189119011911192119311941195119611971198119912001201120212031204120512061207120812091210121112121213121412151216121712181219122012211222122312241225122612271228122912301231123212331234123512361237123812391240124112421243124412451246124712481249125012511252125312541255125612571258125912601261126212631264126512661267126812691270127112721273127412751276127712781279128012811282128312841285128612871288128912901291129212931294129512961297129812991300130113021303130413051306130713081309131013111312131313141315131613171318131913201321132213231324132513261327132813291330133113321333133413351336133713381339134013411342134313441345134613471348134913501351135213531354135513561357135813591360136113621363136413651366136713681369137013711372137313741375137613771378137913801381138213831384138513861387138813891390139113921393139413951396139713981399140014011402140314041405140614071408140914101411141214131414141514161417141814191420142114221423142414251426142714281429143014311432143314341435143614371438143914401441144214431444144514461447144814491450145114521453145414551456145714581459146014611462146314641465146614671468146914701471147214731474147514761477147814791480148114821483148414851486148714881489149014911492149314941495149614971498149915001501150215031504150515061507150815091510151115121513151415151516151715181519152015211522152315241525152615271528152915301531153215331534153515361537153815391540154115421543154415451546154715481549155015511552155315541555155615571558155915601561156215631564156515661567156815691570157115721573157415751576157715781579158015811582158315841585158615871588158915901591159215931594159515961597159815991600160116021603160416051606160716081609161016111612161316141615161616171618161916201621162216231624162516261627162816291630163116321633163416351636163716381639164016411642164316441645164616471648164916501651165216531654165516561657165816591660166116621663166416651666166716681669167016711672167316741675167616771678167916801681168216831684168516861687168816891690169116921693169416951696169716981699170017011702170317041705170617071708170917101711171217131714171517161717171817191720172117221723172417251726172717281729173017311732173317341735173617371738173917401741174217431744174517461747174817491750175117521753175417551756175717581759176017611762176317641765176617671768176917701771177217731774177517761777177817791780178117821783178417851786178717881789179017911792179317941795179617971798179918001801180218031804180518061807180818091810181118121813181418151816181718181819182018211822182318241825182618271828182918301831183218331834183518361837183818391840184118421843184418451846184718481849185018511852185318541855185618571858185918601861186218631864186518661867186818691870187118721873187418751876187718781879188018811882188318841885188618871888188918901891189218931894189518961897189818991900190119021903190419051906190719081909191019111912191319141915191619171918191919201921192219231924192519261927192819291930193119321933193419351936193719381939194019411942194319441945194619471948194919501951195219531954195519561957195819591960196119621963196419651966196719681969197019711972197319741975197619771978197919801981198219831984198519861987198819891990199119921993199419951996199719981999200020012002200320042005200620072008200920102011201220132014201520162017201820192020202120222023202420252026202720282029203020312032203320342035203620372038203920402041204220432044204520462047204820492050205120522053205420552056205720582059206020612062206320642065206620672068206920702071207220732074207520762077207820792080208120822083208420852086208720882089209020912092209320942095209620972098209921002101210221032104210521062107210821092110211121122113211421152116211721182119212021212122212321242125212621272128212921302131213221332134213521362137213821392140214121422143214421452146214721482149215021512152215321542155215621572158215921602161216221632164216521662167216821692170217121722173217421752176217721782179218021812182218321842185218621872188218921902191219221932194219521962197219821992200220122022203220422052206220722082209221022112212221322142215221622172218221922202221222222232224222522262227222822292230223122322233223422352236223722382239224022412242224322442245224622472248224922502251225222532254225522562257225822592260226122622263226422652266226722682269227022712272227322742275227622772278227922802281228222832284228522862287228822892290229122922293229422952296229722982299230023012302230323042305230623072308230923102311231223132314231523162317231823192320232123222323232423252326232723282329233023312332233323342335233623372338233923402341234223432344234523462347234823492350235123522353235423552356235723582359236023612362236323642365236623672368236923702371237223732374237523762377237823792380238123822383238423852386238723882389239023912392239323942395239623972398239924002401240224032404240524062407240824092410241124122413241424152416241724182419242024212422242324242425242624272428242924302431243224332434243524362437243824392440244124422443244424452446244724482449245024512452245324542455245624572458245924602461246224632464246524662467246824692470247124722473247424752476247724782479248024812482248324842485248624872488
  1. //
  2. // Copyright (c) 2008-2020 the Urho3D project.
  3. //
  4. // Permission is hereby granted, free of charge, to any person obtaining a copy
  5. // of this software and associated documentation files (the "Software"), to deal
  6. // in the Software without restriction, including without limitation the rights
  7. // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
  8. // copies of the Software, and to permit persons to whom the Software is
  9. // furnished to do so, subject to the following conditions:
  10. //
  11. // The above copyright notice and this permission notice shall be included in
  12. // all copies or substantial portions of the Software.
  13. //
  14. // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
  15. // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
  16. // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
  17. // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
  18. // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
  19. // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
  20. // THE SOFTWARE.
  21. //
  22. #include "../Precompiled.h"
  23. #include "../AngelScript/Addons.h"
  24. #include "../AngelScript/RegistrationMacros.h"
  25. #include <cstring>
  26. #include <new>
  27. #include <cstdio>
  28. // Adapted from Angelscript's scriptarray & scriptstdstring add-ons, but with garbage collection disabled
  29. namespace Urho3D
  30. {
  31. using namespace std;
  32. // Set the default memory routines
  33. // Use the angelscript engine's memory routines by default
  34. static asALLOCFUNC_t userAlloc = asAllocMem;
  35. static asFREEFUNC_t userFree = asFreeMem;
  36. // Allows the application to set which memory routines should be used by the array object
  37. void CScriptArray::SetMemoryFunctions(asALLOCFUNC_t allocFunc, asFREEFUNC_t freeFunc)
  38. {
  39. userAlloc = allocFunc;
  40. userFree = freeFunc;
  41. }
  42. static void RegisterScriptArray_Native(asIScriptEngine *engine);
  43. static void RegisterScriptArray_Generic(asIScriptEngine *engine);
  44. /// %Script array buffer.
  45. struct SArrayBuffer
  46. {
  47. asDWORD maxElements;
  48. asDWORD numElements;
  49. asBYTE data[1];
  50. };
  51. /// %Script array cache.
  52. struct SArrayCache
  53. {
  54. asIScriptFunction *cmpFunc;
  55. asIScriptFunction *eqFunc;
  56. int cmpFuncReturnCode; // To allow better error message in case of multiple matches
  57. int eqFuncReturnCode;
  58. };
  59. // We just define a number here that we assume nobody else is using for
  60. // object type user data. The add-ons have reserved the numbers 1000
  61. // through 1999 for this purpose, so we should be fine.
  62. const asPWORD ARRAY_CACHE = 1000;
  63. static void CleanupTypeInfoArrayCache(asITypeInfo *type)
  64. {
  65. auto*cache = reinterpret_cast<SArrayCache*>(type->GetUserData(ARRAY_CACHE));
  66. if (cache)
  67. {
  68. cache->~SArrayCache();
  69. userFree(cache);
  70. }
  71. }
  72. CScriptArray* CScriptArray::Create(asITypeInfo *ot, asUINT length)
  73. {
  74. asIScriptContext *ctx = asGetActiveContext();
  75. // Allocate the memory
  76. void *mem = userAlloc(sizeof(CScriptArray));
  77. if( mem == nullptr )
  78. {
  79. if( ctx )
  80. ctx->SetException("Out of memory");
  81. return nullptr;
  82. }
  83. // Initialize the object
  84. auto*a = new(mem) CScriptArray(length, ot);
  85. // It's possible the constructor raised a script exception, in which case we
  86. // need to free the memory and return null instead, else we get a memory leak.
  87. if( ctx && ctx->GetState() == asEXECUTION_EXCEPTION )
  88. {
  89. a->Release();
  90. return nullptr;
  91. }
  92. return a;
  93. }
  94. CScriptArray* CScriptArray::Create(asITypeInfo *ot, void *initList)
  95. {
  96. asIScriptContext *ctx = asGetActiveContext();
  97. // Allocate the memory
  98. void *mem = userAlloc(sizeof(CScriptArray));
  99. if( mem == nullptr )
  100. {
  101. if( ctx )
  102. ctx->SetException("Out of memory");
  103. return nullptr;
  104. }
  105. // Initialize the object
  106. auto*a = new(mem) CScriptArray(ot, initList);
  107. // It's possible the constructor raised a script exception, in which case we
  108. // need to free the memory and return null instead, else we get a memory leak.
  109. if( ctx && ctx->GetState() == asEXECUTION_EXCEPTION )
  110. {
  111. a->Release();
  112. return nullptr;
  113. }
  114. return a;
  115. }
  116. CScriptArray* CScriptArray::Create(asITypeInfo *ot, asUINT length, void *defVal)
  117. {
  118. asIScriptContext *ctx = asGetActiveContext();
  119. // Allocate the memory
  120. void *mem = userAlloc(sizeof(CScriptArray));
  121. if( mem == nullptr )
  122. {
  123. if( ctx )
  124. ctx->SetException("Out of memory");
  125. return nullptr;
  126. }
  127. // Initialize the object
  128. auto*a = new(mem) CScriptArray(length, defVal, ot);
  129. // It's possible the constructor raised a script exception, in which case we
  130. // need to free the memory and return null instead, else we get a memory leak.
  131. if( ctx && ctx->GetState() == asEXECUTION_EXCEPTION )
  132. {
  133. a->Release();
  134. return nullptr;
  135. }
  136. return a;
  137. }
  138. CScriptArray* CScriptArray::Create(asITypeInfo *ot)
  139. {
  140. return CScriptArray::Create(ot, asUINT(0));
  141. }
  142. // This optional callback is called when the template type is first used by the compiler.
  143. // It allows the application to validate if the template can be instantiated for the requested
  144. // subtype at compile time, instead of at runtime. The output argument dontGarbageCollect
  145. // allow the callback to tell the engine if the template instance type shouldn't be garbage collected,
  146. // i.e. no asOBJ_GC flag.
  147. static bool ScriptArrayTemplateCallback(asITypeInfo *ti, bool &dontGarbageCollect)
  148. {
  149. // Make sure the subtype can be instantiated with a default factory/constructor,
  150. // otherwise we won't be able to instantiate the elements.
  151. int typeId = ti->GetSubTypeId();
  152. if (typeId == asTYPEID_VOID)
  153. return false;
  154. if ((typeId & asTYPEID_MASK_OBJECT) && !(typeId & asTYPEID_OBJHANDLE))
  155. {
  156. asITypeInfo *subtype = ti->GetEngine()->GetTypeInfoById(typeId);
  157. asDWORD flags = subtype->GetFlags();
  158. if ((flags & asOBJ_VALUE) && !(flags & asOBJ_POD))
  159. {
  160. // Verify that there is a default constructor
  161. bool found = false;
  162. for (asUINT n = 0; n < subtype->GetBehaviourCount(); n++)
  163. {
  164. asEBehaviours beh;
  165. asIScriptFunction *func = subtype->GetBehaviourByIndex(n, &beh);
  166. if (beh != asBEHAVE_CONSTRUCT) continue;
  167. if (func->GetParamCount() == 0)
  168. {
  169. // Found the default constructor
  170. found = true;
  171. break;
  172. }
  173. }
  174. if (!found)
  175. {
  176. // There is no default constructor
  177. // TODO: Should format the message to give the name of the subtype for better understanding
  178. ti->GetEngine()->WriteMessage("array", 0, 0, asMSGTYPE_ERROR, "The subtype has no default constructor");
  179. return false;
  180. }
  181. }
  182. else if ((flags & asOBJ_REF))
  183. {
  184. bool found = false;
  185. // If value assignment for ref type has been disabled then the array
  186. // can be created if the type has a default factory function
  187. if (!ti->GetEngine()->GetEngineProperty(asEP_DISALLOW_VALUE_ASSIGN_FOR_REF_TYPE))
  188. {
  189. // Verify that there is a default factory
  190. for (asUINT n = 0; n < subtype->GetFactoryCount(); n++)
  191. {
  192. asIScriptFunction *func = subtype->GetFactoryByIndex(n);
  193. if (func->GetParamCount() == 0)
  194. {
  195. // Found the default factory
  196. found = true;
  197. break;
  198. }
  199. }
  200. }
  201. if (!found)
  202. {
  203. // No default factory
  204. // TODO: Should format the message to give the name of the subtype for better understanding
  205. ti->GetEngine()->WriteMessage("array", 0, 0, asMSGTYPE_ERROR, "The subtype has no default factory");
  206. return false;
  207. }
  208. }
  209. // If the object type is not garbage collected then the array also doesn't need to be
  210. if (!(flags & asOBJ_GC))
  211. dontGarbageCollect = true;
  212. }
  213. else if (!(typeId & asTYPEID_OBJHANDLE))
  214. {
  215. // Arrays with primitives cannot form circular references,
  216. // thus there is no need to garbage collect them
  217. dontGarbageCollect = true;
  218. }
  219. else
  220. {
  221. assert(typeId & asTYPEID_OBJHANDLE);
  222. // It is not necessary to set the array as garbage collected for all handle types.
  223. // If it is possible to determine that the handle cannot refer to an object type
  224. // that can potentially form a circular reference with the array then it is not
  225. // necessary to make the array garbage collected.
  226. asITypeInfo *subtype = ti->GetEngine()->GetTypeInfoById(typeId);
  227. asDWORD flags = subtype->GetFlags();
  228. if (!(flags & asOBJ_GC))
  229. {
  230. if ((flags & asOBJ_SCRIPT_OBJECT))
  231. {
  232. // Even if a script class is by itself not garbage collected, it is possible
  233. // that classes that derive from it may be, so it is not possible to know
  234. // that no circular reference can occur.
  235. if ((flags & asOBJ_NOINHERIT))
  236. {
  237. // A script class declared as final cannot be inherited from, thus
  238. // we can be certain that the object cannot be garbage collected.
  239. dontGarbageCollect = true;
  240. }
  241. }
  242. else
  243. {
  244. // For application registered classes we assume the application knows
  245. // what it is doing and don't mark the array as garbage collected unless
  246. // the type is also garbage collected.
  247. dontGarbageCollect = true;
  248. }
  249. }
  250. }
  251. // The type is ok
  252. return true;
  253. }
  254. CScriptArray &CScriptArray::operator=(const CScriptArray &other)
  255. {
  256. // Only perform the copy if the array types are the same
  257. if( &other != this &&
  258. other.GetArrayObjectType() == GetArrayObjectType() )
  259. {
  260. // Make sure the arrays are of the same size
  261. Resize(other.buffer->numElements);
  262. // Copy the value of each element
  263. CopyBuffer(buffer, other.buffer);
  264. }
  265. return *this;
  266. }
  267. CScriptArray::CScriptArray(asITypeInfo *ot, void *buf)
  268. {
  269. refCount = 1;
  270. gcFlag = false;
  271. objType = ot;
  272. objType->AddRef();
  273. buffer = nullptr;
  274. Precache();
  275. asIScriptEngine *engine = ot->GetEngine();
  276. // Determine element size
  277. if( subTypeId & asTYPEID_MASK_OBJECT )
  278. elementSize = sizeof(asPWORD);
  279. else
  280. elementSize = engine->GetSizeOfPrimitiveType(subTypeId);
  281. // Determine the initial size from the buffer
  282. asUINT length = *(asUINT*)buf;
  283. // Make sure the array size isn't too large for us to handle
  284. if( !CheckMaxSize(length) )
  285. {
  286. // Don't continue with the initialization
  287. return;
  288. }
  289. // Copy the values of the array elements from the buffer
  290. if( (ot->GetSubTypeId() & asTYPEID_MASK_OBJECT) == 0 )
  291. {
  292. CreateBuffer(&buffer, length);
  293. // Copy the values of the primitive type into the internal buffer
  294. memcpy(At(0), (((asUINT*)buf)+1), length * elementSize);
  295. }
  296. else if( ot->GetSubTypeId() & asTYPEID_OBJHANDLE )
  297. {
  298. CreateBuffer(&buffer, length);
  299. // Copy the handles into the internal buffer
  300. memcpy(At(0), (((asUINT*)buf)+1), length * elementSize);
  301. // With object handles it is safe to clear the memory in the received buffer
  302. // instead of increasing the ref count. It will save time both by avoiding the
  303. // call the increase ref, and also relieve the engine from having to release
  304. // its references too
  305. memset((((asUINT*)buf)+1), 0, length * elementSize);
  306. }
  307. else if( ot->GetSubType()->GetFlags() & asOBJ_REF )
  308. {
  309. // Only allocate the buffer, but not the objects
  310. subTypeId |= asTYPEID_OBJHANDLE;
  311. CreateBuffer(&buffer, length);
  312. subTypeId &= ~asTYPEID_OBJHANDLE;
  313. // Copy the handles into the internal buffer
  314. memcpy(buffer->data, (((asUINT*)buf)+1), length * elementSize);
  315. // For ref types we can do the same as for handles, as they are
  316. // implicitly stored as handles.
  317. memset((((asUINT*)buf)+1), 0, length * elementSize);
  318. }
  319. else
  320. {
  321. // TODO: Optimize by calling the copy constructor of the object instead of
  322. // constructing with the default constructor and then assigning the value
  323. // TODO: With C++11 ideally we should be calling the move constructor, instead
  324. // of the copy constructor as the engine will just discard the objects in the
  325. // buffer afterwards.
  326. CreateBuffer(&buffer, length);
  327. // For value types we need to call the opAssign for each individual object
  328. for( asUINT n = 0; n < length; n++ )
  329. {
  330. void *obj = At(n);
  331. auto*srcObj = (asBYTE*)buf;
  332. srcObj += 4 + n*ot->GetSubType()->GetSize();
  333. engine->AssignScriptObject(obj, srcObj, ot->GetSubType());
  334. }
  335. }
  336. // Urho3D: garbage collection disabled
  337. // Notify the GC of the successful creation
  338. /*
  339. if( objType->GetFlags() & asOBJ_GC )
  340. objType->GetEngine()->NotifyGarbageCollectorOfNewObject(this, objType);
  341. */
  342. }
  343. CScriptArray::CScriptArray(asUINT length, asITypeInfo *ot)
  344. {
  345. refCount = 1;
  346. gcFlag = false;
  347. objType = ot;
  348. objType->AddRef();
  349. buffer = nullptr;
  350. Precache();
  351. // Determine element size
  352. if( subTypeId & asTYPEID_MASK_OBJECT )
  353. elementSize = sizeof(asPWORD);
  354. else
  355. elementSize = objType->GetEngine()->GetSizeOfPrimitiveType(subTypeId);
  356. // Make sure the array size isn't too large for us to handle
  357. if( !CheckMaxSize(length) )
  358. {
  359. // Don't continue with the initialization
  360. return;
  361. }
  362. CreateBuffer(&buffer, length);
  363. // Urho3D: garbage collection disabled
  364. /*
  365. // Notify the GC of the successful creation
  366. if( objType->GetFlags() & asOBJ_GC )
  367. objType->GetEngine()->NotifyGarbageCollectorOfNewObject(this, objType);
  368. */
  369. }
  370. CScriptArray::CScriptArray(const CScriptArray &other)
  371. {
  372. refCount = 1;
  373. gcFlag = false;
  374. objType = other.objType;
  375. objType->AddRef();
  376. buffer = nullptr;
  377. Precache();
  378. elementSize = other.elementSize;
  379. // Urho3D: garbage collection disabled
  380. /*
  381. if( objType->GetFlags() & asOBJ_GC )
  382. objType->GetEngine()->NotifyGarbageCollectorOfNewObject(this, objType);
  383. */
  384. CreateBuffer(&buffer, 0);
  385. // Copy the content
  386. *this = other;
  387. }
  388. CScriptArray::CScriptArray(asUINT length, void *defVal, asITypeInfo *ot)
  389. {
  390. refCount = 1;
  391. gcFlag = false;
  392. objType = ot;
  393. objType->AddRef();
  394. buffer = nullptr;
  395. Precache();
  396. // Determine element size
  397. if( subTypeId & asTYPEID_MASK_OBJECT )
  398. elementSize = sizeof(asPWORD);
  399. else
  400. elementSize = objType->GetEngine()->GetSizeOfPrimitiveType(subTypeId);
  401. // Make sure the array size isn't too large for us to handle
  402. if( !CheckMaxSize(length) )
  403. {
  404. // Don't continue with the initialization
  405. return;
  406. }
  407. CreateBuffer(&buffer, length);
  408. // Urho3D: garbage collection disabled
  409. /*
  410. // Notify the GC of the successful creation
  411. if( objType->GetFlags() & asOBJ_GC )
  412. objType->GetEngine()->NotifyGarbageCollectorOfNewObject(this, objType);
  413. */
  414. // Initialize the elements with the default value
  415. for( asUINT n = 0; n < GetSize(); n++ )
  416. SetValue(n, defVal);
  417. }
  418. void CScriptArray::SetValue(asUINT index, void *value)
  419. {
  420. // At() will take care of the out-of-bounds checking, though
  421. // if called from the application then nothing will be done
  422. void *ptr = At(index);
  423. if( ptr == nullptr ) return;
  424. if( (subTypeId & ~asTYPEID_MASK_SEQNBR) && !(subTypeId & asTYPEID_OBJHANDLE) )
  425. objType->GetEngine()->AssignScriptObject(ptr, value, objType->GetSubType());
  426. else if( subTypeId & asTYPEID_OBJHANDLE )
  427. {
  428. void *tmp = *(void**)ptr;
  429. *(void**)ptr = *(void**)value;
  430. objType->GetEngine()->AddRefScriptObject(*(void**)value, objType->GetSubType());
  431. if( tmp )
  432. objType->GetEngine()->ReleaseScriptObject(tmp, objType->GetSubType());
  433. }
  434. else if( subTypeId == asTYPEID_BOOL ||
  435. subTypeId == asTYPEID_INT8 ||
  436. subTypeId == asTYPEID_UINT8 )
  437. *(char*)ptr = *(char*)value;
  438. else if( subTypeId == asTYPEID_INT16 ||
  439. subTypeId == asTYPEID_UINT16 )
  440. *(short*)ptr = *(short*)value;
  441. else if( subTypeId == asTYPEID_INT32 ||
  442. subTypeId == asTYPEID_UINT32 ||
  443. subTypeId == asTYPEID_FLOAT ||
  444. subTypeId > asTYPEID_DOUBLE ) // enums have a type id larger than doubles
  445. *(int*)ptr = *(int*)value;
  446. else if( subTypeId == asTYPEID_INT64 ||
  447. subTypeId == asTYPEID_UINT64 ||
  448. subTypeId == asTYPEID_DOUBLE )
  449. *(double*)ptr = *(double*)value;
  450. }
  451. CScriptArray::~CScriptArray()
  452. {
  453. if( buffer )
  454. {
  455. DeleteBuffer(buffer);
  456. buffer = nullptr;
  457. }
  458. if( objType ) objType->Release();
  459. }
  460. asUINT CScriptArray::GetSize() const
  461. {
  462. return buffer->numElements;
  463. }
  464. bool CScriptArray::IsEmpty() const
  465. {
  466. return buffer->numElements == 0;
  467. }
  468. void CScriptArray::Reserve(asUINT maxElements)
  469. {
  470. if( maxElements <= buffer->maxElements )
  471. return;
  472. if( !CheckMaxSize(maxElements) )
  473. return;
  474. // Allocate memory for the buffer
  475. auto*newBuffer = reinterpret_cast<SArrayBuffer*>(userAlloc(sizeof(SArrayBuffer)-1 + elementSize*maxElements));
  476. if( newBuffer )
  477. {
  478. newBuffer->numElements = buffer->numElements;
  479. newBuffer->maxElements = maxElements;
  480. }
  481. else
  482. {
  483. // Out of memory
  484. asIScriptContext *ctx = asGetActiveContext();
  485. if( ctx )
  486. ctx->SetException("Out of memory");
  487. return;
  488. }
  489. // TODO: memcpy assumes the objects in the array doesn't hold pointers to themselves
  490. // This should really be using the objects copy/move constructor to copy each object
  491. // to the new location. It would most likely be a hit on the performance though.
  492. memcpy(newBuffer->data, buffer->data, buffer->numElements*elementSize);
  493. // Release the old buffer
  494. userFree(buffer);
  495. buffer = newBuffer;
  496. }
  497. void CScriptArray::Resize(asUINT numElements)
  498. {
  499. if( !CheckMaxSize(numElements) )
  500. return;
  501. Resize((int)numElements - (int)buffer->numElements, (asUINT)-1);
  502. }
  503. // Internal
  504. void CScriptArray::Resize(int delta, asUINT at)
  505. {
  506. if( delta < 0 )
  507. {
  508. if( -delta > (int)buffer->numElements )
  509. delta = -(int)buffer->numElements;
  510. if( at > buffer->numElements + delta )
  511. at = buffer->numElements + delta;
  512. }
  513. else if( delta > 0 )
  514. {
  515. // Make sure the array size isn't too large for us to handle
  516. if( delta > 0 && !CheckMaxSize(buffer->numElements + delta) )
  517. return;
  518. if( at > buffer->numElements )
  519. at = buffer->numElements;
  520. }
  521. if( delta == 0 ) return;
  522. if( buffer->maxElements < buffer->numElements + delta )
  523. {
  524. // Allocate memory for the buffer
  525. auto*newBuffer = reinterpret_cast<SArrayBuffer*>(userAlloc(sizeof(SArrayBuffer)-1 + elementSize*(buffer->numElements + delta)));
  526. if( newBuffer )
  527. {
  528. newBuffer->numElements = buffer->numElements + delta;
  529. newBuffer->maxElements = newBuffer->numElements;
  530. }
  531. else
  532. {
  533. // Out of memory
  534. asIScriptContext *ctx = asGetActiveContext();
  535. if( ctx )
  536. ctx->SetException("Out of memory");
  537. return;
  538. }
  539. // TODO: memcpy assumes the objects in the array doesn't hold pointers to themselves
  540. // This should really be using the objects copy/move constructor to copy each object
  541. // to the new location. It would most likely be a hit on the performance though.
  542. memcpy(newBuffer->data, buffer->data, at*elementSize);
  543. if( at < buffer->numElements )
  544. memcpy(newBuffer->data + (at+delta)*elementSize, buffer->data + at*elementSize, (buffer->numElements-at)*elementSize);
  545. // Initialize the new elements with default values
  546. Construct(newBuffer, at, at+delta);
  547. // Release the old buffer
  548. userFree(buffer);
  549. buffer = newBuffer;
  550. }
  551. else if( delta < 0 )
  552. {
  553. Destruct(buffer, at, at-delta);
  554. // TODO: memmove assumes the objects in the array doesn't hold pointers to themselves
  555. // This should really be using the objects copy/move constructor to copy each object
  556. // to the new location. It would most likely be a hit on the performance though.
  557. memmove(buffer->data + at*elementSize, buffer->data + (at-delta)*elementSize, (buffer->numElements - (at-delta))*elementSize);
  558. buffer->numElements += delta;
  559. }
  560. else
  561. {
  562. // TODO: memmove assumes the objects in the array doesn't hold pointers to themselves
  563. // This should really be using the objects copy/move constructor to copy each object
  564. // to the new location. It would most likely be a hit on the performance though.
  565. memmove(buffer->data + (at+delta)*elementSize, buffer->data + at*elementSize, (buffer->numElements - at)*elementSize);
  566. Construct(buffer, at, at+delta);
  567. buffer->numElements += delta;
  568. }
  569. }
  570. // internal
  571. bool CScriptArray::CheckMaxSize(asUINT numElements)
  572. {
  573. // This code makes sure the size of the buffer that is allocated
  574. // for the array doesn't overflow and becomes smaller than requested
  575. asUINT maxSize = 0xFFFFFFFFul - sizeof(SArrayBuffer) + 1;
  576. if( elementSize > 0 )
  577. maxSize /= elementSize;
  578. if( numElements > maxSize )
  579. {
  580. asIScriptContext *ctx = asGetActiveContext();
  581. if( ctx )
  582. ctx->SetException("Too large array size");
  583. return false;
  584. }
  585. // OK
  586. return true;
  587. }
  588. asITypeInfo *CScriptArray::GetArrayObjectType() const
  589. {
  590. return objType;
  591. }
  592. int CScriptArray::GetArrayTypeId() const
  593. {
  594. return objType->GetTypeId();
  595. }
  596. int CScriptArray::GetElementTypeId() const
  597. {
  598. return subTypeId;
  599. }
  600. void CScriptArray::InsertAt(asUINT index, void *value)
  601. {
  602. if( index > buffer->numElements )
  603. {
  604. // If this is called from a script we raise a script exception
  605. asIScriptContext *ctx = asGetActiveContext();
  606. if( ctx )
  607. ctx->SetException("Index out of bounds");
  608. return;
  609. }
  610. // Make room for the new element
  611. Resize(1, index);
  612. // Set the value of the new element
  613. SetValue(index, value);
  614. }
  615. void CScriptArray::InsertLast(void *value)
  616. {
  617. InsertAt(buffer->numElements, value);
  618. }
  619. void CScriptArray::RemoveAt(asUINT index)
  620. {
  621. if( index >= buffer->numElements )
  622. {
  623. // If this is called from a script we raise a script exception
  624. asIScriptContext *ctx = asGetActiveContext();
  625. if( ctx )
  626. ctx->SetException("Index out of bounds");
  627. return;
  628. }
  629. // Remove the element
  630. Resize(-1, index);
  631. }
  632. void CScriptArray::RemoveLast()
  633. {
  634. RemoveAt(buffer->numElements-1);
  635. }
  636. // Return a pointer to the array element. Returns 0 if the index is out of bounds
  637. const void *CScriptArray::At(asUINT index) const
  638. {
  639. if( buffer == nullptr || index >= buffer->numElements )
  640. {
  641. // If this is called from a script we raise a script exception
  642. asIScriptContext *ctx = asGetActiveContext();
  643. if( ctx )
  644. ctx->SetException("Index out of bounds");
  645. return nullptr;
  646. }
  647. if( (subTypeId & asTYPEID_MASK_OBJECT) && !(subTypeId & asTYPEID_OBJHANDLE) )
  648. return *(void**)(buffer->data + elementSize*index);
  649. else
  650. return buffer->data + elementSize*index;
  651. }
  652. void *CScriptArray::At(asUINT index)
  653. {
  654. return const_cast<void*>(const_cast<const CScriptArray *>(this)->At(index));
  655. }
  656. // internal
  657. void CScriptArray::CreateBuffer(SArrayBuffer **buf, asUINT numElements)
  658. {
  659. *buf = reinterpret_cast<SArrayBuffer*>(userAlloc(sizeof(SArrayBuffer)-1+elementSize*numElements));
  660. if( *buf )
  661. {
  662. (*buf)->numElements = numElements;
  663. (*buf)->maxElements = numElements;
  664. Construct(*buf, 0, numElements);
  665. }
  666. else
  667. {
  668. // Oops, out of memory
  669. asIScriptContext *ctx = asGetActiveContext();
  670. if( ctx )
  671. ctx->SetException("Out of memory");
  672. }
  673. }
  674. // internal
  675. void CScriptArray::DeleteBuffer(SArrayBuffer *buf)
  676. {
  677. Destruct(buf, 0, buf->numElements);
  678. // Free the buffer
  679. userFree(buf);
  680. }
  681. // internal
  682. void CScriptArray::Construct(SArrayBuffer *buf, asUINT start, asUINT end)
  683. {
  684. if( (subTypeId & asTYPEID_MASK_OBJECT) && !(subTypeId & asTYPEID_OBJHANDLE) )
  685. {
  686. // Create an object using the default constructor/factory for each element
  687. auto**max = (void**)(buf->data + end * sizeof(void*));
  688. auto**d = (void**)(buf->data + start * sizeof(void*));
  689. asIScriptEngine *engine = objType->GetEngine();
  690. asITypeInfo *subType = objType->GetSubType();
  691. for( ; d < max; d++ )
  692. {
  693. *d = (void*)engine->CreateScriptObject(subType);
  694. if( *d == nullptr )
  695. {
  696. // Set the remaining entries to null so the destructor
  697. // won't attempt to destroy invalid objects later
  698. memset(d, 0, sizeof(void*)*(max-d));
  699. // There is no need to set an exception on the context,
  700. // as CreateScriptObject has already done that
  701. return;
  702. }
  703. }
  704. }
  705. else
  706. {
  707. // Set all elements to zero whether they are handles or primitives
  708. auto*d = (void*)(buf->data + start * elementSize);
  709. memset(d, 0, (end-start)*elementSize);
  710. }
  711. }
  712. // internal
  713. void CScriptArray::Destruct(SArrayBuffer *buf, asUINT start, asUINT end)
  714. {
  715. if( subTypeId & asTYPEID_MASK_OBJECT )
  716. {
  717. asIScriptEngine *engine = objType->GetEngine();
  718. auto**max = (void**)(buf->data + end * sizeof(void*));
  719. auto**d = (void**)(buf->data + start * sizeof(void*));
  720. for( ; d < max; d++ )
  721. {
  722. if( *d )
  723. engine->ReleaseScriptObject(*d, objType->GetSubType());
  724. }
  725. }
  726. }
  727. // internal
  728. bool CScriptArray::Less(const void *a, const void *b, bool asc, asIScriptContext *ctx, SArrayCache *cache)
  729. {
  730. if( !asc )
  731. {
  732. // Swap items
  733. const void *TEMP = a;
  734. a = b;
  735. b = TEMP;
  736. }
  737. if( !(subTypeId & ~asTYPEID_MASK_SEQNBR) )
  738. {
  739. // Simple compare of values
  740. switch( subTypeId )
  741. {
  742. #define COMPARE(T) *((T*)a) < *((T*)b)
  743. case asTYPEID_BOOL: return COMPARE(bool);
  744. case asTYPEID_INT8: return COMPARE(signed char);
  745. case asTYPEID_UINT8: return COMPARE(unsigned char);
  746. case asTYPEID_INT16: return COMPARE(signed short);
  747. case asTYPEID_UINT16: return COMPARE(unsigned short);
  748. case asTYPEID_INT32: return COMPARE(signed int);
  749. case asTYPEID_UINT32: return COMPARE(unsigned int);
  750. case asTYPEID_FLOAT: return COMPARE(float);
  751. case asTYPEID_DOUBLE: return COMPARE(double);
  752. default: return COMPARE(signed int); // All enums fall in this case
  753. #undef COMPARE
  754. }
  755. }
  756. else
  757. {
  758. int r = 0;
  759. if( subTypeId & asTYPEID_OBJHANDLE )
  760. {
  761. // Allow sort to work even if the array contains null handles
  762. if( *(void**)a == nullptr ) return true;
  763. if( *(void**)b == nullptr ) return false;
  764. }
  765. // Execute object opCmp
  766. if( cache && cache->cmpFunc )
  767. {
  768. // TODO: Add proper error handling
  769. r = ctx->Prepare(cache->cmpFunc); assert(r >= 0);
  770. if( subTypeId & asTYPEID_OBJHANDLE )
  771. {
  772. r = ctx->SetObject(*((void**)a)); assert(r >= 0);
  773. r = ctx->SetArgObject(0, *((void**)b)); assert(r >= 0);
  774. }
  775. else
  776. {
  777. r = ctx->SetObject((void*)a); assert(r >= 0);
  778. r = ctx->SetArgObject(0, (void*)b); assert(r >= 0);
  779. }
  780. r = ctx->Execute();
  781. if( r == asEXECUTION_FINISHED )
  782. {
  783. return (int)ctx->GetReturnDWord() < 0;
  784. }
  785. }
  786. }
  787. return false;
  788. }
  789. void CScriptArray::Reverse()
  790. {
  791. asUINT size = GetSize();
  792. if( size >= 2 )
  793. {
  794. asBYTE TEMP[16];
  795. for( asUINT i = 0; i < size / 2; i++ )
  796. {
  797. Copy(TEMP, GetArrayItemPointer(i));
  798. Copy(GetArrayItemPointer(i), GetArrayItemPointer(size - i - 1));
  799. Copy(GetArrayItemPointer(size - i - 1), TEMP);
  800. }
  801. }
  802. }
  803. bool CScriptArray::operator==(const CScriptArray &other) const
  804. {
  805. if( objType != other.objType )
  806. return false;
  807. if( GetSize() != other.GetSize() )
  808. return false;
  809. asIScriptContext *cmpContext = nullptr;
  810. bool isNested = false;
  811. if( subTypeId & ~asTYPEID_MASK_SEQNBR )
  812. {
  813. // Try to reuse the active context
  814. cmpContext = asGetActiveContext();
  815. if( cmpContext )
  816. {
  817. if( cmpContext->GetEngine() == objType->GetEngine() && cmpContext->PushState() >= 0 )
  818. isNested = true;
  819. else
  820. cmpContext = nullptr;
  821. }
  822. if( cmpContext == nullptr )
  823. {
  824. // TODO: Ideally this context would be retrieved from a pool, so we don't have to
  825. // create a new one every time. We could keep a context with the array object
  826. // but that would consume a lot of resources as each context is quite heavy.
  827. cmpContext = objType->GetEngine()->CreateContext();
  828. }
  829. }
  830. // Check if all elements are equal
  831. bool isEqual = true;
  832. auto*cache = reinterpret_cast<SArrayCache*>(objType->GetUserData(ARRAY_CACHE));
  833. for( asUINT n = 0; n < GetSize(); n++ )
  834. if( !Equals(At(n), other.At(n), cmpContext, cache) )
  835. {
  836. isEqual = false;
  837. break;
  838. }
  839. if( cmpContext )
  840. {
  841. if( isNested )
  842. {
  843. asEContextState state = cmpContext->GetState();
  844. cmpContext->PopState();
  845. if( state == asEXECUTION_ABORTED )
  846. cmpContext->Abort();
  847. }
  848. else
  849. cmpContext->Release();
  850. }
  851. return isEqual;
  852. }
  853. // internal
  854. bool CScriptArray::Equals(const void *a, const void *b, asIScriptContext *ctx, SArrayCache *cache) const
  855. {
  856. if( !(subTypeId & ~asTYPEID_MASK_SEQNBR) )
  857. {
  858. // Simple compare of values
  859. switch( subTypeId )
  860. {
  861. #define COMPARE(T) *((T*)a) == *((T*)b)
  862. case asTYPEID_BOOL: return COMPARE(bool);
  863. case asTYPEID_INT8: return COMPARE(signed char);
  864. case asTYPEID_UINT8: return COMPARE(unsigned char);
  865. case asTYPEID_INT16: return COMPARE(signed short);
  866. case asTYPEID_UINT16: return COMPARE(unsigned short);
  867. case asTYPEID_INT32: return COMPARE(signed int);
  868. case asTYPEID_UINT32: return COMPARE(unsigned int);
  869. case asTYPEID_FLOAT: return COMPARE(float);
  870. case asTYPEID_DOUBLE: return COMPARE(double);
  871. default: return COMPARE(signed int); // All enums fall here
  872. #undef COMPARE
  873. }
  874. }
  875. else
  876. {
  877. int r = 0;
  878. if( subTypeId & asTYPEID_OBJHANDLE )
  879. {
  880. // Allow the find to work even if the array contains null handles
  881. if( *(void**)a == *(void**)b ) return true;
  882. }
  883. // Execute object opEquals if available
  884. if( cache && cache->eqFunc )
  885. {
  886. // TODO: Add proper error handling
  887. r = ctx->Prepare(cache->eqFunc); assert(r >= 0);
  888. if( subTypeId & asTYPEID_OBJHANDLE )
  889. {
  890. r = ctx->SetObject(*((void**)a)); assert(r >= 0);
  891. r = ctx->SetArgObject(0, *((void**)b)); assert(r >= 0);
  892. }
  893. else
  894. {
  895. r = ctx->SetObject((void*)a); assert(r >= 0);
  896. r = ctx->SetArgObject(0, (void*)b); assert(r >= 0);
  897. }
  898. r = ctx->Execute();
  899. if( r == asEXECUTION_FINISHED )
  900. return ctx->GetReturnByte() != 0;
  901. return false;
  902. }
  903. // Execute object opCmp if available
  904. if( cache && cache->cmpFunc )
  905. {
  906. // TODO: Add proper error handling
  907. r = ctx->Prepare(cache->cmpFunc); assert(r >= 0);
  908. if( subTypeId & asTYPEID_OBJHANDLE )
  909. {
  910. r = ctx->SetObject(*((void**)a)); assert(r >= 0);
  911. r = ctx->SetArgObject(0, *((void**)b)); assert(r >= 0);
  912. }
  913. else
  914. {
  915. r = ctx->SetObject((void*)a); assert(r >= 0);
  916. r = ctx->SetArgObject(0, (void*)b); assert(r >= 0);
  917. }
  918. r = ctx->Execute();
  919. if( r == asEXECUTION_FINISHED )
  920. return (int)ctx->GetReturnDWord() == 0;
  921. return false;
  922. }
  923. }
  924. return false;
  925. }
  926. int CScriptArray::FindByRef(void *ref) const
  927. {
  928. return FindByRef(0, ref);
  929. }
  930. int CScriptArray::FindByRef(asUINT startAt, void *ref) const
  931. {
  932. // Find the matching element by its reference
  933. asUINT size = GetSize();
  934. if( subTypeId & asTYPEID_OBJHANDLE )
  935. {
  936. // Dereference the pointer
  937. ref = *(void**)ref;
  938. for( asUINT i = startAt; i < size; i++ )
  939. {
  940. if( *(void**)At(i) == ref )
  941. return i;
  942. }
  943. }
  944. else
  945. {
  946. // Compare the reference directly
  947. for( asUINT i = startAt; i < size; i++ )
  948. {
  949. if( At(i) == ref )
  950. return i;
  951. }
  952. }
  953. return -1;
  954. }
  955. bool CScriptArray::Swap(CScriptArray& other)
  956. {
  957. if (other.GetArrayObjectType() == GetArrayObjectType())
  958. {
  959. Urho3D::Swap(buffer, other.buffer);
  960. return true;
  961. }
  962. return false;
  963. }
  964. int CScriptArray::Find(void *value) const
  965. {
  966. return Find(0, value);
  967. }
  968. int CScriptArray::Find(asUINT startAt, void *value) const
  969. {
  970. // Check if the subtype really supports find()
  971. // TODO: Can't this be done at compile time too by the template callback
  972. SArrayCache *cache = nullptr;
  973. if( subTypeId & ~asTYPEID_MASK_SEQNBR )
  974. {
  975. cache = reinterpret_cast<SArrayCache*>(objType->GetUserData(ARRAY_CACHE));
  976. if( !cache || (cache->cmpFunc == nullptr && cache->eqFunc == nullptr) )
  977. {
  978. asIScriptContext *ctx = asGetActiveContext();
  979. asITypeInfo* subType = objType->GetEngine()->GetTypeInfoById(subTypeId);
  980. // Throw an exception
  981. if( ctx )
  982. {
  983. char tmp[512];
  984. if( cache && cache->eqFuncReturnCode == asMULTIPLE_FUNCTIONS )
  985. #if defined(_MSC_VER) && _MSC_VER >= 1500 && !defined(__S3E__)
  986. sprintf_s(tmp, 512, "Type '%s' has multiple matching opEquals or opCmp methods", subType->GetName());
  987. #else
  988. sprintf(tmp, "Type '%s' has multiple matching opEquals or opCmp methods", subType->GetName());
  989. #endif
  990. else
  991. #if defined(_MSC_VER) && _MSC_VER >= 1500 && !defined(__S3E__)
  992. sprintf_s(tmp, 512, "Type '%s' does not have a matching opEquals or opCmp method", subType->GetName());
  993. #else
  994. sprintf(tmp, "Type '%s' does not have a matching opEquals or opCmp method", subType->GetName());
  995. #endif
  996. ctx->SetException(tmp);
  997. }
  998. return -1;
  999. }
  1000. }
  1001. asIScriptContext *cmpContext = nullptr;
  1002. bool isNested = false;
  1003. if( subTypeId & ~asTYPEID_MASK_SEQNBR )
  1004. {
  1005. // Try to reuse the active context
  1006. cmpContext = asGetActiveContext();
  1007. if( cmpContext )
  1008. {
  1009. if( cmpContext->GetEngine() == objType->GetEngine() && cmpContext->PushState() >= 0 )
  1010. isNested = true;
  1011. else
  1012. cmpContext = nullptr;
  1013. }
  1014. if( cmpContext == nullptr )
  1015. {
  1016. // TODO: Ideally this context would be retrieved from a pool, so we don't have to
  1017. // create a new one every time. We could keep a context with the array object
  1018. // but that would consume a lot of resources as each context is quite heavy.
  1019. cmpContext = objType->GetEngine()->CreateContext();
  1020. }
  1021. }
  1022. // Find the matching element
  1023. int ret = -1;
  1024. asUINT size = GetSize();
  1025. for( asUINT i = startAt; i < size; i++ )
  1026. {
  1027. // value passed by reference
  1028. if( Equals(At(i), value, cmpContext, cache) )
  1029. {
  1030. ret = (int)i;
  1031. break;
  1032. }
  1033. }
  1034. if( cmpContext )
  1035. {
  1036. if( isNested )
  1037. {
  1038. asEContextState state = cmpContext->GetState();
  1039. cmpContext->PopState();
  1040. if( state == asEXECUTION_ABORTED )
  1041. cmpContext->Abort();
  1042. }
  1043. else
  1044. cmpContext->Release();
  1045. }
  1046. return ret;
  1047. }
  1048. // internal
  1049. // Copy object handle or primitive value
  1050. void CScriptArray::Copy(void *dst, void *src)
  1051. {
  1052. memcpy(dst, src, elementSize);
  1053. }
  1054. // internal
  1055. // Return pointer to array item (object handle or primitive value)
  1056. void *CScriptArray::GetArrayItemPointer(int index)
  1057. {
  1058. return buffer->data + index * elementSize;
  1059. }
  1060. // internal
  1061. // Return pointer to data in buffer (object or primitive)
  1062. void *CScriptArray::GetDataPointer(void *buffer)
  1063. {
  1064. if ((subTypeId & asTYPEID_MASK_OBJECT) && !(subTypeId & asTYPEID_OBJHANDLE) )
  1065. {
  1066. // Real address of object
  1067. return reinterpret_cast<void*>(*(size_t*)buffer);
  1068. }
  1069. else
  1070. {
  1071. // Primitive is just a raw data
  1072. return buffer;
  1073. }
  1074. }
  1075. // Sort ascending
  1076. void CScriptArray::SortAsc()
  1077. {
  1078. Sort(0, GetSize(), true);
  1079. }
  1080. // Sort ascending
  1081. void CScriptArray::SortAsc(asUINT startAt, asUINT count)
  1082. {
  1083. Sort(startAt, count, true);
  1084. }
  1085. // Sort descending
  1086. void CScriptArray::SortDesc()
  1087. {
  1088. Sort(0, GetSize(), false);
  1089. }
  1090. // Sort descending
  1091. void CScriptArray::SortDesc(asUINT startAt, asUINT count)
  1092. {
  1093. Sort(startAt, count, false);
  1094. }
  1095. // internal
  1096. void CScriptArray::Sort(asUINT startAt, asUINT count, bool asc)
  1097. {
  1098. // Subtype isn't primitive and doesn't have opCmp
  1099. auto*cache = reinterpret_cast<SArrayCache*>(objType->GetUserData(ARRAY_CACHE));
  1100. if( subTypeId & ~asTYPEID_MASK_SEQNBR )
  1101. {
  1102. if( !cache || cache->cmpFunc == nullptr )
  1103. {
  1104. asIScriptContext *ctx = asGetActiveContext();
  1105. asITypeInfo* subType = objType->GetEngine()->GetTypeInfoById(subTypeId);
  1106. // Throw an exception
  1107. if( ctx )
  1108. {
  1109. char tmp[512];
  1110. if( cache && cache->cmpFuncReturnCode == asMULTIPLE_FUNCTIONS )
  1111. #if defined(_MSC_VER) && _MSC_VER >= 1500 && !defined(__S3E__)
  1112. sprintf_s(tmp, 512, "Type '%s' has multiple matching opCmp methods", subType->GetName());
  1113. #else
  1114. sprintf(tmp, "Type '%s' has multiple matching opCmp methods", subType->GetName());
  1115. #endif
  1116. else
  1117. #if defined(_MSC_VER) && _MSC_VER >= 1500 && !defined(__S3E__)
  1118. sprintf_s(tmp, 512, "Type '%s' does not have a matching opCmp method", subType->GetName());
  1119. #else
  1120. sprintf(tmp, "Type '%s' does not have a matching opCmp method", subType->GetName());
  1121. #endif
  1122. ctx->SetException(tmp);
  1123. }
  1124. return;
  1125. }
  1126. }
  1127. // No need to sort
  1128. if( count < 2 )
  1129. {
  1130. return;
  1131. }
  1132. int start = startAt;
  1133. int end = startAt + count;
  1134. // Check if we could access invalid item while sorting
  1135. if( start >= (int)buffer->numElements || end > (int)buffer->numElements )
  1136. {
  1137. asIScriptContext *ctx = asGetActiveContext();
  1138. // Throw an exception
  1139. if( ctx )
  1140. {
  1141. ctx->SetException("Index out of bounds");
  1142. }
  1143. return;
  1144. }
  1145. asBYTE tmp[16];
  1146. asIScriptContext *cmpContext = nullptr;
  1147. bool isNested = false;
  1148. if( subTypeId & ~asTYPEID_MASK_SEQNBR )
  1149. {
  1150. // Try to reuse the active context
  1151. cmpContext = asGetActiveContext();
  1152. if( cmpContext )
  1153. {
  1154. if( cmpContext->GetEngine() == objType->GetEngine() && cmpContext->PushState() >= 0 )
  1155. isNested = true;
  1156. else
  1157. cmpContext = nullptr;
  1158. }
  1159. if( cmpContext == nullptr )
  1160. {
  1161. // TODO: Ideally this context would be retrieved from a pool, so we don't have to
  1162. // create a new one every time. We could keep a context with the array object
  1163. // but that would consume a lot of resources as each context is quite heavy.
  1164. cmpContext = objType->GetEngine()->CreateContext();
  1165. }
  1166. }
  1167. // Insertion sort
  1168. for( int i = start + 1; i < end; i++ )
  1169. {
  1170. Copy(tmp, GetArrayItemPointer(i));
  1171. int j = i - 1;
  1172. while( j >= start && Less(GetDataPointer(tmp), At(j), asc, cmpContext, cache) )
  1173. {
  1174. Copy(GetArrayItemPointer(j + 1), GetArrayItemPointer(j));
  1175. j--;
  1176. }
  1177. Copy(GetArrayItemPointer(j + 1), tmp);
  1178. }
  1179. if( cmpContext )
  1180. {
  1181. if( isNested )
  1182. {
  1183. asEContextState state = cmpContext->GetState();
  1184. cmpContext->PopState();
  1185. if( state == asEXECUTION_ABORTED )
  1186. cmpContext->Abort();
  1187. }
  1188. else
  1189. cmpContext->Release();
  1190. }
  1191. }
  1192. // internal
  1193. void CScriptArray::CopyBuffer(SArrayBuffer *dst, SArrayBuffer *src)
  1194. {
  1195. asIScriptEngine *engine = objType->GetEngine();
  1196. if( subTypeId & asTYPEID_OBJHANDLE )
  1197. {
  1198. // Copy the references and increase the reference counters
  1199. if( dst->numElements > 0 && src->numElements > 0 )
  1200. {
  1201. int count = dst->numElements > src->numElements ? src->numElements : dst->numElements;
  1202. auto**max = (void**)(dst->data + count * sizeof(void*));
  1203. auto**d = (void**)dst->data;
  1204. auto**s = (void**)src->data;
  1205. for( ; d < max; d++, s++ )
  1206. {
  1207. void *tmp = *d;
  1208. *d = *s;
  1209. if( *d )
  1210. engine->AddRefScriptObject(*d, objType->GetSubType());
  1211. // Release the old ref after incrementing the new to avoid problem incase it is the same ref
  1212. if( tmp )
  1213. engine->ReleaseScriptObject(tmp, objType->GetSubType());
  1214. }
  1215. }
  1216. }
  1217. else
  1218. {
  1219. if( dst->numElements > 0 && src->numElements > 0 )
  1220. {
  1221. int count = dst->numElements > src->numElements ? src->numElements : dst->numElements;
  1222. if( subTypeId & asTYPEID_MASK_OBJECT )
  1223. {
  1224. // Call the assignment operator on all of the objects
  1225. auto**max = (void**)(dst->data + count * sizeof(void*));
  1226. auto**d = (void**)dst->data;
  1227. auto**s = (void**)src->data;
  1228. asITypeInfo *subType = objType->GetSubType();
  1229. for( ; d < max; d++, s++ )
  1230. engine->AssignScriptObject(*d, *s, subType);
  1231. }
  1232. else
  1233. {
  1234. // Primitives are copied byte for byte
  1235. memcpy(dst->data, src->data, count*elementSize);
  1236. }
  1237. }
  1238. }
  1239. }
  1240. // internal
  1241. // Precache some info
  1242. void CScriptArray::Precache()
  1243. {
  1244. subTypeId = objType->GetSubTypeId();
  1245. // Check if it is an array of objects. Only for these do we need to cache anything
  1246. // Type ids for primitives and enums only has the sequence number part
  1247. if( !(subTypeId & ~asTYPEID_MASK_SEQNBR) )
  1248. return;
  1249. // The opCmp and opEquals methods are cached because the searching for the
  1250. // methods is quite time consuming if a lot of array objects are created.
  1251. // First check if a cache already exists for this array type
  1252. auto*cache = reinterpret_cast<SArrayCache*>(objType->GetUserData(ARRAY_CACHE));
  1253. if( cache ) return;
  1254. // We need to make sure the cache is created only once, even
  1255. // if multiple threads reach the same point at the same time
  1256. asAcquireExclusiveLock();
  1257. // Now that we got the lock, we need to check again to make sure the
  1258. // cache wasn't created while we were waiting for the lock
  1259. cache = reinterpret_cast<SArrayCache*>(objType->GetUserData(ARRAY_CACHE));
  1260. if( cache )
  1261. {
  1262. asReleaseExclusiveLock();
  1263. return;
  1264. }
  1265. // Create the cache
  1266. cache = reinterpret_cast<SArrayCache*>(userAlloc(sizeof(SArrayCache)));
  1267. memset(cache, 0, sizeof(SArrayCache));
  1268. // If the sub type is a handle to const, then the methods must be const too
  1269. bool mustBeConst = (subTypeId & asTYPEID_HANDLETOCONST) ? true : false;
  1270. asITypeInfo *subType = objType->GetEngine()->GetTypeInfoById(subTypeId);
  1271. if( subType )
  1272. {
  1273. for( asUINT i = 0; i < subType->GetMethodCount(); i++ )
  1274. {
  1275. asIScriptFunction *func = subType->GetMethodByIndex(i);
  1276. if( func->GetParamCount() == 1 && (!mustBeConst || func->IsReadOnly()) )
  1277. {
  1278. asDWORD flags = 0;
  1279. int returnTypeId = func->GetReturnTypeId(&flags);
  1280. // The method must not return a reference
  1281. if( flags != asTM_NONE )
  1282. continue;
  1283. // opCmp returns an int and opEquals returns a bool
  1284. bool isCmp = false, isEq = false;
  1285. if( returnTypeId == asTYPEID_INT32 && strcmp(func->GetName(), "opCmp") == 0 )
  1286. isCmp = true;
  1287. if( returnTypeId == asTYPEID_BOOL && strcmp(func->GetName(), "opEquals") == 0 )
  1288. isEq = true;
  1289. if( !isCmp && !isEq )
  1290. continue;
  1291. // The parameter must either be a reference to the subtype or a handle to the subtype
  1292. int paramTypeId;
  1293. func->GetParam(0, &paramTypeId, &flags);
  1294. if( (paramTypeId & ~(asTYPEID_OBJHANDLE|asTYPEID_HANDLETOCONST)) != (subTypeId & ~(asTYPEID_OBJHANDLE|asTYPEID_HANDLETOCONST)) )
  1295. continue;
  1296. if( (flags & asTM_INREF) )
  1297. {
  1298. if( (paramTypeId & asTYPEID_OBJHANDLE) || (mustBeConst && !(flags & asTM_CONST)) )
  1299. continue;
  1300. }
  1301. else if( paramTypeId & asTYPEID_OBJHANDLE )
  1302. {
  1303. if( mustBeConst && !(paramTypeId & asTYPEID_HANDLETOCONST) )
  1304. continue;
  1305. }
  1306. else
  1307. continue;
  1308. if( isCmp )
  1309. {
  1310. if( cache->cmpFunc || cache->cmpFuncReturnCode )
  1311. {
  1312. cache->cmpFunc = nullptr;
  1313. cache->cmpFuncReturnCode = asMULTIPLE_FUNCTIONS;
  1314. }
  1315. else
  1316. cache->cmpFunc = func;
  1317. }
  1318. else if( isEq )
  1319. {
  1320. if( cache->eqFunc || cache->eqFuncReturnCode )
  1321. {
  1322. cache->eqFunc = nullptr;
  1323. cache->eqFuncReturnCode = asMULTIPLE_FUNCTIONS;
  1324. }
  1325. else
  1326. cache->eqFunc = func;
  1327. }
  1328. }
  1329. }
  1330. }
  1331. if( cache->eqFunc == nullptr && cache->eqFuncReturnCode == 0 )
  1332. cache->eqFuncReturnCode = asNO_FUNCTION;
  1333. if( cache->cmpFunc == nullptr && cache->cmpFuncReturnCode == 0 )
  1334. cache->cmpFuncReturnCode = asNO_FUNCTION;
  1335. // Set the user data only at the end so others that retrieve it will know it is complete
  1336. objType->SetUserData(cache, ARRAY_CACHE);
  1337. asReleaseExclusiveLock();
  1338. }
  1339. // GC behaviour
  1340. void CScriptArray::EnumReferences(asIScriptEngine *engine)
  1341. {
  1342. // If the array is holding handles, then we need to notify the GC of them
  1343. if( subTypeId & asTYPEID_MASK_OBJECT )
  1344. {
  1345. auto**d = (void**)buffer->data;
  1346. for( asUINT n = 0; n < buffer->numElements; n++ )
  1347. {
  1348. if( d[n] )
  1349. engine->GCEnumCallback(d[n]);
  1350. }
  1351. }
  1352. }
  1353. // GC behaviour
  1354. void CScriptArray::ReleaseAllHandles(asIScriptEngine *)
  1355. {
  1356. // Resizing to zero will release everything
  1357. Resize(0);
  1358. }
  1359. void CScriptArray::AddRef() const
  1360. {
  1361. // Clear the GC flag then increase the counter
  1362. gcFlag = false;
  1363. asAtomicInc(refCount);
  1364. }
  1365. void CScriptArray::Release() const
  1366. {
  1367. // Clearing the GC flag then descrease the counter
  1368. gcFlag = false;
  1369. if( asAtomicDec(refCount) == 0 )
  1370. {
  1371. // When reaching 0 no more references to this instance
  1372. // exists and the object should be destroyed
  1373. this->~CScriptArray();
  1374. userFree(const_cast<CScriptArray*>(this));
  1375. }
  1376. }
  1377. // GC behaviour
  1378. int CScriptArray::GetRefCount()
  1379. {
  1380. return refCount;
  1381. }
  1382. // GC behaviour
  1383. void CScriptArray::SetFlag()
  1384. {
  1385. gcFlag = true;
  1386. }
  1387. // GC behaviour
  1388. bool CScriptArray::GetFlag()
  1389. {
  1390. return gcFlag;
  1391. }
  1392. static void ScriptArrayClear(CScriptArray* ptr)
  1393. {
  1394. ptr->Resize(0);
  1395. }
  1396. void RegisterArray(asIScriptEngine* engine)
  1397. {
  1398. engine->RegisterObjectBehaviour("Array<T>", asBEHAVE_TEMPLATE_CALLBACK, "bool f(int&in, bool&out)", AS_FUNCTION(ScriptArrayTemplateCallback), AS_CALL_CDECL);
  1399. engine->RegisterObjectBehaviour("Array<T>", asBEHAVE_FACTORY, "Array<T>@ f(int&in)", AS_FUNCTIONPR(CScriptArray::Create, (asITypeInfo*), CScriptArray*), AS_CALL_CDECL);
  1400. engine->RegisterObjectBehaviour("Array<T>", asBEHAVE_FACTORY, "Array<T>@ f(int&in, uint)", AS_FUNCTIONPR(CScriptArray::Create, (asITypeInfo*, asUINT), CScriptArray*), AS_CALL_CDECL);
  1401. engine->RegisterObjectBehaviour("Array<T>", asBEHAVE_FACTORY, "Array<T>@ f(int&in, uint, const T &in)", AS_FUNCTIONPR(CScriptArray::Create, (asITypeInfo*, asUINT, void *), CScriptArray*), AS_CALL_CDECL);
  1402. engine->RegisterObjectBehaviour("Array<T>", asBEHAVE_LIST_FACTORY, "Array<T>@ f(int&in type, int&in list) {repeat T}", AS_FUNCTIONPR(CScriptArray::Create, (asITypeInfo*, void*), CScriptArray*), AS_CALL_CDECL);
  1403. engine->RegisterObjectBehaviour("Array<T>", asBEHAVE_ADDREF, "void f()", AS_METHOD(CScriptArray, AddRef), AS_CALL_THISCALL);
  1404. engine->RegisterObjectBehaviour("Array<T>", asBEHAVE_RELEASE, "void f()", AS_METHOD(CScriptArray, Release), AS_CALL_THISCALL);
  1405. engine->RegisterObjectMethod("Array<T>", "T& opIndex(uint)", AS_METHODPR(CScriptArray, At, (unsigned), void*), AS_CALL_THISCALL);
  1406. engine->RegisterObjectMethod("Array<T>", "const T& opIndex(uint) const", AS_METHODPR(CScriptArray, At, (unsigned), void*), AS_CALL_THISCALL);
  1407. engine->RegisterObjectMethod("Array<T>", "Array<T>& opAssign(const Array<T>& in)", AS_METHOD(CScriptArray, operator=), AS_CALL_THISCALL);
  1408. engine->RegisterObjectMethod("Array<T>", "void Insert(uint, const T& in)", AS_METHOD(CScriptArray, InsertAt), AS_CALL_THISCALL);
  1409. engine->RegisterObjectMethod("Array<T>", "void Erase(uint)", AS_METHOD(CScriptArray, RemoveAt), AS_CALL_THISCALL);
  1410. engine->RegisterObjectMethod("Array<T>", "void Push(const T& in)", AS_METHOD(CScriptArray, InsertLast), AS_CALL_THISCALL);
  1411. engine->RegisterObjectMethod("Array<T>", "void Pop()", AS_METHOD(CScriptArray, RemoveLast), AS_CALL_THISCALL);
  1412. engine->RegisterObjectMethod("Array<T>", "void Reserve(uint)", AS_METHOD(CScriptArray, Reserve), AS_CALL_THISCALL);
  1413. engine->RegisterObjectMethod("Array<T>", "void Resize(uint)", AS_METHODPR(CScriptArray, Resize, (asUINT), void), AS_CALL_THISCALL);
  1414. engine->RegisterObjectMethod("Array<T>", "void Clear()", AS_FUNCTION_OBJLAST(ScriptArrayClear), AS_CALL_CDECL_OBJLAST);
  1415. engine->RegisterObjectMethod("Array<T>", "void Sort()", AS_METHODPR(CScriptArray, SortAsc, (), void), AS_CALL_THISCALL);
  1416. engine->RegisterObjectMethod("Array<T>", "void Sort(uint, uint)", AS_METHODPR(CScriptArray, SortAsc, (asUINT, asUINT), void), AS_CALL_THISCALL);
  1417. engine->RegisterObjectMethod("Array<T>", "void SortReverse()", AS_METHODPR(CScriptArray, SortDesc, (), void), AS_CALL_THISCALL);
  1418. engine->RegisterObjectMethod("Array<T>", "void SortReverse(uint, uint)", AS_METHODPR(CScriptArray, SortDesc, (asUINT, asUINT), void), AS_CALL_THISCALL);
  1419. engine->RegisterObjectMethod("Array<T>", "void Reverse()", AS_METHOD(CScriptArray, Reverse), AS_CALL_THISCALL);
  1420. engine->RegisterObjectMethod("Array<T>", "int Find(const T&in) const", AS_METHODPR(CScriptArray, Find, (void*) const, int), AS_CALL_THISCALL);
  1421. engine->RegisterObjectMethod("Array<T>", "int Find(uint, const T&in) const", AS_METHODPR(CScriptArray, Find, (asUINT, void*) const, int), AS_CALL_THISCALL);
  1422. engine->RegisterObjectMethod("Array<T>", "int FindByRef(const T&in) const", AS_METHODPR(CScriptArray, FindByRef, (void*) const, int), AS_CALL_THISCALL);
  1423. engine->RegisterObjectMethod("Array<T>", "int FindByRef(uint, const T&in) const", AS_METHODPR(CScriptArray, FindByRef, (asUINT, void*) const, int), AS_CALL_THISCALL);
  1424. engine->RegisterObjectMethod("Array<T>", "bool Swap(Array<T>&inout)", AS_METHOD(CScriptArray, Swap), AS_CALL_THISCALL);
  1425. engine->RegisterObjectMethod("Array<T>", "bool opEquals(const Array<T>&in) const", AS_METHOD(CScriptArray, operator==), AS_CALL_THISCALL);
  1426. engine->RegisterObjectMethod("Array<T>", "uint get_length() const", AS_METHOD(CScriptArray, GetSize), AS_CALL_THISCALL);
  1427. engine->RegisterObjectMethod("Array<T>", "void set_length(uint)", AS_METHODPR(CScriptArray, Resize, (asUINT), void), AS_CALL_THISCALL);
  1428. engine->RegisterObjectMethod("Array<T>", "bool get_empty() const", AS_METHOD(CScriptArray, IsEmpty), AS_CALL_THISCALL);
  1429. engine->RegisterDefaultArrayType("Array<T>");
  1430. }
  1431. CScriptDictionary *CScriptDictionary::Create(asIScriptEngine *engine)
  1432. {
  1433. // Use the custom memory routine from AngelScript to allow application to better control how much memory is used
  1434. auto*obj = (CScriptDictionary*)asAllocMem(sizeof(CScriptDictionary));
  1435. new(obj) CScriptDictionary(engine);
  1436. return obj;
  1437. }
  1438. CScriptDictionary *CScriptDictionary::Create(asBYTE *buffer)
  1439. {
  1440. // Use the custom memory routine from AngelScript to allow application to better control how much memory is used
  1441. auto*obj = (CScriptDictionary*)asAllocMem(sizeof(CScriptDictionary));
  1442. new(obj) CScriptDictionary(buffer);
  1443. return obj;
  1444. }
  1445. CScriptDictionary::CScriptDictionary(asIScriptEngine *engine)
  1446. {
  1447. // We start with one reference
  1448. refCount = 1;
  1449. gcFlag = false;
  1450. // Keep a reference to the engine for as long as we live
  1451. // We don't increment the reference counter, because the
  1452. // engine will hold a pointer to the object.
  1453. this->engine = engine;
  1454. // Urho3D: garbage collection disabled
  1455. /*
  1456. // Notify the garbage collector of this object
  1457. // TODO: The object type should be cached
  1458. engine->NotifyGarbageCollectorOfNewObject(this, engine->GetObjectTypeByName("Dictionary"));
  1459. */
  1460. }
  1461. CScriptDictionary::CScriptDictionary(asBYTE *buffer)
  1462. {
  1463. // We start with one reference
  1464. refCount = 1;
  1465. gcFlag = false;
  1466. // This constructor will always be called from a script
  1467. // so we can get the engine from the active context
  1468. asIScriptContext *ctx = asGetActiveContext();
  1469. engine = ctx->GetEngine();
  1470. // Urho3D: garbage collection disabled
  1471. /*
  1472. // Notify the garbage collector of this object
  1473. // TODO: The type id should be cached
  1474. engine->NotifyGarbageCollectorOfNewObject(this, engine->GetObjectTypeByName("Dictionary"));
  1475. */
  1476. // Initialize the dictionary from the buffer
  1477. asUINT length = *(asUINT*)buffer;
  1478. buffer += 4;
  1479. while( length-- )
  1480. {
  1481. // Align the buffer pointer on a 4 byte boundary in
  1482. // case previous value was smaller than 4 bytes
  1483. if( asPWORD(buffer) & 0x3 )
  1484. buffer += 4 - (asPWORD(buffer) & 0x3);
  1485. // Get the name value pair from the buffer and insert it in the dictionary
  1486. String name = *(String*)buffer;
  1487. buffer += sizeof(String);
  1488. // Get the type id of the value
  1489. int typeId = *(int*)buffer;
  1490. buffer += sizeof(int);
  1491. // Depending on the type id, the value will inline in the buffer or a pointer
  1492. auto*ref = (void*)buffer;
  1493. if( typeId >= asTYPEID_INT8 && typeId <= asTYPEID_DOUBLE )
  1494. {
  1495. // Convert primitive values to either int64 or double, so we can use the overloaded Set methods
  1496. asINT64 i64;
  1497. double d;
  1498. switch( typeId )
  1499. {
  1500. case asTYPEID_INT8: i64 = *(char*)ref; break;
  1501. case asTYPEID_INT16: i64 = *(short*)ref; break;
  1502. case asTYPEID_INT32: i64 = *(int*)ref; break;
  1503. case asTYPEID_INT64: i64 = *(asINT64*)ref; break;
  1504. case asTYPEID_UINT8: i64 = *(unsigned char*)ref; break;
  1505. case asTYPEID_UINT16: i64 = *(unsigned short*)ref; break;
  1506. case asTYPEID_UINT32: i64 = *(unsigned int*)ref; break;
  1507. case asTYPEID_UINT64: i64 = *(asINT64*)ref; break;
  1508. case asTYPEID_FLOAT: d = *(float*)ref; break;
  1509. case asTYPEID_DOUBLE: d = *(double*)ref; break;
  1510. }
  1511. if( typeId >= asTYPEID_FLOAT )
  1512. Set(name, d);
  1513. else
  1514. Set(name, i64);
  1515. }
  1516. else
  1517. {
  1518. if( (typeId & asTYPEID_MASK_OBJECT) &&
  1519. !(typeId & asTYPEID_OBJHANDLE) &&
  1520. (engine->GetTypeInfoById(typeId)->GetFlags() & asOBJ_REF))
  1521. {
  1522. // Dereference the pointer to get the reference to the actual object
  1523. ref = *(void**)ref;
  1524. }
  1525. Set(name, ref, typeId);
  1526. }
  1527. // Advance the buffer pointer with the size of the value
  1528. if( typeId & asTYPEID_MASK_OBJECT )
  1529. {
  1530. asITypeInfo *ot = engine->GetTypeInfoById(typeId);
  1531. if( ot->GetFlags() & asOBJ_VALUE )
  1532. buffer += ot->GetSize();
  1533. else
  1534. buffer += sizeof(void*);
  1535. }
  1536. else if( typeId == 0 )
  1537. {
  1538. // null pointer
  1539. buffer += sizeof(void*);
  1540. }
  1541. else
  1542. {
  1543. buffer += engine->GetSizeOfPrimitiveType(typeId);
  1544. }
  1545. }
  1546. }
  1547. CScriptDictionary::~CScriptDictionary()
  1548. {
  1549. // Delete all keys and values
  1550. DeleteAll();
  1551. }
  1552. void CScriptDictionary::AddRef() const
  1553. {
  1554. // We need to clear the GC flag
  1555. gcFlag = false;
  1556. asAtomicInc(refCount);
  1557. }
  1558. void CScriptDictionary::Release() const
  1559. {
  1560. // We need to clear the GC flag
  1561. gcFlag = false;
  1562. if( asAtomicDec(refCount) == 0 )
  1563. {
  1564. this->~CScriptDictionary();
  1565. asFreeMem(const_cast<CScriptDictionary*>(this));
  1566. }
  1567. }
  1568. int CScriptDictionary::GetRefCount()
  1569. {
  1570. return refCount;
  1571. }
  1572. void CScriptDictionary::SetGCFlag()
  1573. {
  1574. gcFlag = true;
  1575. }
  1576. bool CScriptDictionary::GetGCFlag()
  1577. {
  1578. return gcFlag;
  1579. }
  1580. void CScriptDictionary::EnumReferences(asIScriptEngine *engine)
  1581. {
  1582. // Call the gc enum callback for each of the objects
  1583. HashMap<String, CScriptDictValue>::Iterator it;
  1584. for( it = dict.Begin(); it != dict.End(); it++ )
  1585. {
  1586. if( it->second_.m_typeId & asTYPEID_MASK_OBJECT )
  1587. engine->GCEnumCallback(it->second_.m_valueObj);
  1588. }
  1589. }
  1590. void CScriptDictionary::ReleaseAllReferences(asIScriptEngine * /*engine*/)
  1591. {
  1592. // We're being told to release all references in
  1593. // order to break circular references for dead objects
  1594. DeleteAll();
  1595. }
  1596. CScriptDictionary &CScriptDictionary::operator =(const CScriptDictionary &other)
  1597. {
  1598. // Clear everything we had before
  1599. DeleteAll();
  1600. // Do a shallow copy of the dictionary
  1601. HashMap<String, CScriptDictValue>::ConstIterator it;
  1602. for( it = other.dict.Begin(); it != other.dict.End(); it++ )
  1603. {
  1604. if( it->second_.m_typeId & asTYPEID_OBJHANDLE )
  1605. Set(it->first_, (void*)&it->second_.m_valueObj, it->second_.m_typeId);
  1606. else if( it->second_.m_typeId & asTYPEID_MASK_OBJECT )
  1607. Set(it->first_, (void*)it->second_.m_valueObj, it->second_.m_typeId);
  1608. else
  1609. Set(it->first_, (void*)&it->second_.m_valueInt, it->second_.m_typeId);
  1610. }
  1611. return *this;
  1612. }
  1613. CScriptDictValue *CScriptDictionary::operator[](const String &key)
  1614. {
  1615. // Return the existing value if it exists, else insert an empty value
  1616. HashMap<String, CScriptDictValue>::Iterator it;
  1617. it = dict.Find(key);
  1618. if( it == dict.End() )
  1619. it = dict.Insert(MakePair(key, CScriptDictValue()));
  1620. return &it->second_;
  1621. }
  1622. const CScriptDictValue *CScriptDictionary::operator[](const String &key) const
  1623. {
  1624. // Return the existing value if it exists
  1625. HashMap<String, CScriptDictValue>::ConstIterator it;
  1626. it = dict.Find(key);
  1627. if( it != dict.End() )
  1628. return &it->second_;
  1629. // Else raise an exception
  1630. asIScriptContext *ctx = asGetActiveContext();
  1631. if( ctx )
  1632. ctx->SetException("Invalid access to non-existing value");
  1633. return nullptr;
  1634. }
  1635. void CScriptDictionary::Set(const String &key, void *value, int typeId)
  1636. {
  1637. HashMap<String, CScriptDictValue>::Iterator it;
  1638. it = dict.Find(key);
  1639. if( it == dict.End() )
  1640. it = dict.Insert(MakePair(key, CScriptDictValue()));
  1641. it->second_.Set(engine, value, typeId);
  1642. }
  1643. // This overloaded method is implemented so that all integer and
  1644. // unsigned integers types will be stored in the dictionary as int64
  1645. // through implicit conversions. This simplifies the management of the
  1646. // numeric types when the script retrieves the stored value using a
  1647. // different type.
  1648. void CScriptDictionary::Set(const String &key, const asINT64 &value)
  1649. {
  1650. Set(key, const_cast<asINT64*>(&value), asTYPEID_INT64);
  1651. }
  1652. // This overloaded method is implemented so that all floating point types
  1653. // will be stored in the dictionary as double through implicit conversions.
  1654. // This simplifies the management of the numeric types when the script
  1655. // retrieves the stored value using a different type.
  1656. void CScriptDictionary::Set(const String &key, const double &value)
  1657. {
  1658. Set(key, const_cast<double*>(&value), asTYPEID_DOUBLE);
  1659. }
  1660. // Returns true if the value was successfully retrieved
  1661. bool CScriptDictionary::Get(const String &key, void *value, int typeId) const
  1662. {
  1663. HashMap<String, CScriptDictValue>::ConstIterator it;
  1664. it = dict.Find(key);
  1665. if( it != dict.End() )
  1666. return it->second_.Get(engine, value, typeId);
  1667. // AngelScript has already initialized the value with a default value,
  1668. // so we don't have to do anything if we don't find the element, or if
  1669. // the element is incompatible with the requested type.
  1670. return false;
  1671. }
  1672. // Returns the type id of the stored value
  1673. int CScriptDictionary::GetTypeId(const String &key) const
  1674. {
  1675. HashMap<String, CScriptDictValue>::ConstIterator it;
  1676. it = dict.Find(key);
  1677. if( it != dict.End() )
  1678. return it->second_.m_typeId;
  1679. return -1;
  1680. }
  1681. bool CScriptDictionary::Get(const String &key, asINT64 &value) const
  1682. {
  1683. return Get(key, &value, asTYPEID_INT64);
  1684. }
  1685. bool CScriptDictionary::Get(const String &key, double &value) const
  1686. {
  1687. return Get(key, &value, asTYPEID_DOUBLE);
  1688. }
  1689. bool CScriptDictionary::Exists(const String &key) const
  1690. {
  1691. HashMap<String, CScriptDictValue>::ConstIterator it;
  1692. it = dict.Find(key);
  1693. if( it != dict.End() )
  1694. return true;
  1695. return false;
  1696. }
  1697. bool CScriptDictionary::IsEmpty() const
  1698. {
  1699. if( dict.Size() == 0 )
  1700. return true;
  1701. return false;
  1702. }
  1703. asUINT CScriptDictionary::GetSize() const
  1704. {
  1705. return asUINT(dict.Size());
  1706. }
  1707. void CScriptDictionary::Delete(const String &key)
  1708. {
  1709. HashMap<String, CScriptDictValue>::Iterator it;
  1710. it = dict.Find(key);
  1711. if( it != dict.End() )
  1712. {
  1713. it->second_.FreeValue(engine);
  1714. dict.Erase(it);
  1715. }
  1716. }
  1717. void CScriptDictionary::DeleteAll()
  1718. {
  1719. HashMap<String, CScriptDictValue>::Iterator it;
  1720. for( it = dict.Begin(); it != dict.End(); it++ )
  1721. it->second_.FreeValue(engine);
  1722. dict.Clear();
  1723. }
  1724. CScriptArray* CScriptDictionary::GetKeys() const
  1725. {
  1726. // TODO: optimize: The string array type should only be determined once.
  1727. // It should be recomputed when registering the dictionary class.
  1728. // Only problem is if multiple engines are used, as they may not
  1729. // share the same type id. Alternatively it can be stored in the
  1730. // user data for the dictionary type.
  1731. asITypeInfo *ot = engine->GetTypeInfoByDecl("Array<String>");
  1732. // Create the array object
  1733. CScriptArray *array = CScriptArray::Create(ot, asUINT(dict.Size()));
  1734. long current = -1;
  1735. HashMap<String, CScriptDictValue>::ConstIterator it;
  1736. for( it = dict.Begin(); it != dict.End(); it++ )
  1737. {
  1738. current++;
  1739. *(String*)array->At(current) = it->first_;
  1740. }
  1741. return array;
  1742. }
  1743. void ScriptDictionaryFactory_Generic(asIScriptGeneric *gen)
  1744. {
  1745. *(CScriptDictionary**)gen->GetAddressOfReturnLocation() = CScriptDictionary::Create(gen->GetEngine());
  1746. }
  1747. void ScriptDictionaryListFactory_Generic(asIScriptGeneric *gen)
  1748. {
  1749. auto*buffer = (asBYTE*)gen->GetArgAddress(0);
  1750. *(CScriptDictionary**)gen->GetAddressOfReturnLocation() = CScriptDictionary::Create(buffer);
  1751. }
  1752. CScriptDictValue::CScriptDictValue() // NOLINT(hicpp-member-init)
  1753. {
  1754. m_valueObj = nullptr;
  1755. m_typeId = 0;
  1756. }
  1757. CScriptDictValue::CScriptDictValue(asIScriptEngine *engine, void *value, int typeId) // NOLINT(hicpp-member-init)
  1758. {
  1759. m_valueObj = nullptr;
  1760. m_typeId = 0;
  1761. Set(engine, value, typeId);
  1762. }
  1763. CScriptDictValue::~CScriptDictValue()
  1764. {
  1765. // Must not hold an object when destroyed, as then the object will never be freed
  1766. assert( (m_typeId & asTYPEID_MASK_OBJECT) == 0 );
  1767. }
  1768. void CScriptDictValue::FreeValue(asIScriptEngine *engine)
  1769. {
  1770. // If it is a handle or a ref counted object, call release
  1771. if( m_typeId & asTYPEID_MASK_OBJECT )
  1772. {
  1773. // Let the engine release the object
  1774. engine->ReleaseScriptObject(m_valueObj, engine->GetTypeInfoById(m_typeId));
  1775. m_valueObj = nullptr;
  1776. m_typeId = 0;
  1777. }
  1778. // For primitives, there's nothing to do
  1779. }
  1780. void CScriptDictValue::Set(asIScriptEngine *engine, void *value, int typeId)
  1781. {
  1782. FreeValue(engine);
  1783. m_typeId = typeId;
  1784. if( typeId & asTYPEID_OBJHANDLE )
  1785. {
  1786. // We're receiving a reference to the handle, so we need to dereference it
  1787. m_valueObj = *(void**)value;
  1788. engine->AddRefScriptObject(m_valueObj, engine->GetTypeInfoById(typeId));
  1789. }
  1790. else if( typeId & asTYPEID_MASK_OBJECT )
  1791. {
  1792. // Create a copy of the object
  1793. m_valueObj = engine->CreateScriptObjectCopy(value, engine->GetTypeInfoById(typeId));
  1794. }
  1795. else
  1796. {
  1797. // Copy the primitive value
  1798. // We receive a pointer to the value.
  1799. int size = engine->GetSizeOfPrimitiveType(typeId);
  1800. memcpy(&m_valueInt, value, size);
  1801. }
  1802. }
  1803. // This overloaded method is implemented so that all integer and
  1804. // unsigned integers types will be stored in the dictionary as int64
  1805. // through implicit conversions. This simplifies the management of the
  1806. // numeric types when the script retrieves the stored value using a
  1807. // different type.
  1808. void CScriptDictValue::Set(asIScriptEngine *engine, const asINT64 &value)
  1809. {
  1810. Set(engine, const_cast<asINT64*>(&value), asTYPEID_INT64);
  1811. }
  1812. // This overloaded method is implemented so that all floating point types
  1813. // will be stored in the dictionary as double through implicit conversions.
  1814. // This simplifies the management of the numeric types when the script
  1815. // retrieves the stored value using a different type.
  1816. void CScriptDictValue::Set(asIScriptEngine *engine, const double &value)
  1817. {
  1818. Set(engine, const_cast<double*>(&value), asTYPEID_DOUBLE);
  1819. }
  1820. bool CScriptDictValue::Get(asIScriptEngine *engine, void *value, int typeId) const
  1821. {
  1822. // Return the value
  1823. if( typeId & asTYPEID_OBJHANDLE )
  1824. {
  1825. // A handle can be retrieved if the stored type is a handle of same or compatible type
  1826. // or if the stored type is an object that implements the interface that the handle refer to.
  1827. void* cast = nullptr;
  1828. if ((m_typeId & asTYPEID_MASK_OBJECT) &&
  1829. engine->RefCastObject(m_valueObj, engine->GetTypeInfoById(m_typeId), engine->GetTypeInfoById(typeId), &cast) >= 0)
  1830. {
  1831. *(void**)value = m_valueObj;
  1832. return true;
  1833. }
  1834. }
  1835. else if( typeId & asTYPEID_MASK_OBJECT )
  1836. {
  1837. // Verify that the copy can be made
  1838. bool isCompatible = false;
  1839. if( m_typeId == typeId )
  1840. isCompatible = true;
  1841. // Copy the object into the given reference
  1842. if( isCompatible )
  1843. {
  1844. engine->AssignScriptObject(value, m_valueObj, engine->GetTypeInfoById(typeId));
  1845. return true;
  1846. }
  1847. }
  1848. else
  1849. {
  1850. if( m_typeId == typeId )
  1851. {
  1852. int size = engine->GetSizeOfPrimitiveType(typeId);
  1853. memcpy(value, &m_valueInt, size);
  1854. return true;
  1855. }
  1856. // We know all numbers are stored as either int64 or double, since we register overloaded functions for those
  1857. if( m_typeId == asTYPEID_INT64 && typeId == asTYPEID_DOUBLE )
  1858. {
  1859. *(double*)value = double(m_valueInt);
  1860. return true;
  1861. }
  1862. else if( m_typeId == asTYPEID_DOUBLE && typeId == asTYPEID_INT64 )
  1863. {
  1864. *(asINT64*)value = asINT64(m_valueFlt);
  1865. return true;
  1866. }
  1867. }
  1868. // It was not possible to retrieve the value using the desired typeId
  1869. return false;
  1870. }
  1871. bool CScriptDictValue::Get(asIScriptEngine *engine, asINT64 &value) const
  1872. {
  1873. return Get(engine, &value, asTYPEID_INT64);
  1874. }
  1875. bool CScriptDictValue::Get(asIScriptEngine *engine, double &value) const
  1876. {
  1877. return Get(engine, &value, asTYPEID_DOUBLE);
  1878. }
  1879. int CScriptDictValue::GetTypeId() const
  1880. {
  1881. return m_typeId;
  1882. }
  1883. static void CScriptDictValue_Construct(void *mem)
  1884. {
  1885. new(mem) CScriptDictValue();
  1886. }
  1887. static void CScriptDictValue_Destruct(CScriptDictValue *obj)
  1888. {
  1889. asIScriptContext *ctx = asGetActiveContext();
  1890. if( ctx )
  1891. {
  1892. asIScriptEngine *engine = ctx->GetEngine();
  1893. obj->FreeValue(engine);
  1894. }
  1895. obj->~CScriptDictValue();
  1896. }
  1897. static CScriptDictValue &CScriptDictValue_opAssign(void *ref, int typeId, CScriptDictValue *obj)
  1898. {
  1899. asIScriptContext *ctx = asGetActiveContext();
  1900. if( ctx )
  1901. {
  1902. asIScriptEngine *engine = ctx->GetEngine();
  1903. obj->Set(engine, ref, typeId);
  1904. }
  1905. return *obj;
  1906. }
  1907. static CScriptDictValue &CScriptDictValue_opAssign(double val, CScriptDictValue *obj)
  1908. {
  1909. return CScriptDictValue_opAssign(&val, asTYPEID_DOUBLE, obj);
  1910. }
  1911. static CScriptDictValue &CScriptDictValue_opAssign(asINT64 val, CScriptDictValue *obj)
  1912. {
  1913. return CScriptDictValue_opAssign(&val, asTYPEID_INT64, obj);
  1914. }
  1915. static void CScriptDictValue_opCast(void *ref, int typeId, CScriptDictValue *obj)
  1916. {
  1917. asIScriptContext *ctx = asGetActiveContext();
  1918. if( ctx )
  1919. {
  1920. asIScriptEngine *engine = ctx->GetEngine();
  1921. obj->Get(engine, ref, typeId);
  1922. }
  1923. }
  1924. static asINT64 CScriptDictValue_opConvInt(CScriptDictValue *obj)
  1925. {
  1926. asINT64 value;
  1927. CScriptDictValue_opCast(&value, asTYPEID_INT64, obj);
  1928. return value;
  1929. }
  1930. static double CScriptDictValue_opConvDouble(CScriptDictValue *obj)
  1931. {
  1932. double value;
  1933. CScriptDictValue_opCast(&value, asTYPEID_DOUBLE, obj);
  1934. return value;
  1935. }
  1936. #ifdef AS_MAX_PORTABILITY
  1937. static void CScriptDictValue_opAssign_gen(asIScriptGeneric* gen)
  1938. {
  1939. *static_cast<CScriptDictValue**>(gen->GetAddressOfReturnLocation()) =
  1940. &CScriptDictValue_opAssign(*static_cast<void**>(gen->GetAddressOfArg(0)),
  1941. gen->GetArgTypeId(0),
  1942. static_cast<CScriptDictValue*>(gen->GetObject()));
  1943. }
  1944. static void CScriptDictValue_opCast_gen(asIScriptGeneric* gen)
  1945. {
  1946. CScriptDictValue_opCast(
  1947. *static_cast<void**>(gen->GetAddressOfArg(0)),
  1948. gen->GetArgTypeId(0),
  1949. static_cast<CScriptDictValue*>(gen->GetObject()));
  1950. }
  1951. static void ScriptDictionarySet_Generic(asIScriptGeneric *gen)
  1952. {
  1953. CScriptDictionary* dict = (CScriptDictionary*)gen->GetObject();
  1954. String* key = *(String**)gen->GetAddressOfArg(0);
  1955. void* ref = *(void**)gen->GetAddressOfArg(1);
  1956. int type_id = gen->GetArgTypeId(1);
  1957. dict->Set(*key, ref, type_id);
  1958. }
  1959. static void ScriptDictionaryGet_Generic(asIScriptGeneric *gen)
  1960. {
  1961. CScriptDictionary* dict = (CScriptDictionary*)gen->GetObject();
  1962. String* key = *(String**)gen->GetAddressOfArg(0);
  1963. void* ref = *(void**)gen->GetAddressOfArg(1);
  1964. int type_id = gen->GetArgTypeId(1);
  1965. *(bool*)gen->GetAddressOfReturnLocation() = dict->Get(*key, ref, type_id);
  1966. }
  1967. #endif
  1968. void RegisterDictionary(asIScriptEngine *engine)
  1969. {
  1970. engine->RegisterObjectBehaviour("DictionaryValue", asBEHAVE_CONSTRUCT, "void f()", AS_FUNCTION_OBJLAST(CScriptDictValue_Construct), AS_CALL_CDECL_OBJLAST);
  1971. engine->RegisterObjectBehaviour("DictionaryValue", asBEHAVE_DESTRUCT, "void f()", AS_FUNCTION_OBJLAST(CScriptDictValue_Destruct), AS_CALL_CDECL_OBJLAST);
  1972. engine->RegisterObjectMethod("DictionaryValue", "DictionaryValue &opAssign(double)", AS_FUNCTIONPR_OBJLAST(CScriptDictValue_opAssign, (double, CScriptDictValue*), CScriptDictValue &), AS_CALL_CDECL_OBJLAST);
  1973. engine->RegisterObjectMethod("DictionaryValue", "DictionaryValue &opAssign(int64)", AS_FUNCTIONPR_OBJLAST(CScriptDictValue_opAssign, (asINT64, CScriptDictValue*), CScriptDictValue &), AS_CALL_CDECL_OBJLAST);
  1974. engine->RegisterObjectMethod("DictionaryValue", "int64 opConv()", AS_FUNCTIONPR_OBJLAST(CScriptDictValue_opConvInt, (CScriptDictValue*), asINT64), AS_CALL_CDECL_OBJLAST);
  1975. engine->RegisterObjectMethod("DictionaryValue", "double opConv()", AS_FUNCTIONPR_OBJLAST(CScriptDictValue_opConvDouble, (CScriptDictValue*), double), AS_CALL_CDECL_OBJLAST);
  1976. engine->RegisterObjectType("Dictionary", sizeof(CScriptDictionary), asOBJ_REF);
  1977. engine->RegisterObjectBehaviour("Dictionary", asBEHAVE_FACTORY, "Dictionary@ f()", asFUNCTION(ScriptDictionaryFactory_Generic), asCALL_GENERIC);
  1978. engine->RegisterObjectBehaviour("Dictionary", asBEHAVE_LIST_FACTORY, "Dictionary @f(int &in) {repeat {String, ?}}", asFUNCTION(ScriptDictionaryListFactory_Generic), asCALL_GENERIC);
  1979. engine->RegisterObjectBehaviour("Dictionary", asBEHAVE_ADDREF, "void f()", AS_METHOD(CScriptDictionary,AddRef), AS_CALL_THISCALL);
  1980. engine->RegisterObjectBehaviour("Dictionary", asBEHAVE_RELEASE, "void f()", AS_METHOD(CScriptDictionary,Release), AS_CALL_THISCALL);
  1981. engine->RegisterObjectMethod("Dictionary", "Dictionary &opAssign(const Dictionary &in)", AS_METHODPR(CScriptDictionary, operator=, (const CScriptDictionary &), CScriptDictionary&), AS_CALL_THISCALL);
  1982. engine->RegisterObjectMethod("Dictionary", "void Set(const String &in, const int64&in)", AS_METHODPR(CScriptDictionary,Set,(const String&,const asINT64&),void), AS_CALL_THISCALL);
  1983. engine->RegisterObjectMethod("Dictionary", "bool Get(const String &in, int64&out) const", AS_METHODPR(CScriptDictionary,Get,(const String&,asINT64&) const,bool), AS_CALL_THISCALL);
  1984. engine->RegisterObjectMethod("Dictionary", "void Set(const String &in, const double&in)", AS_METHODPR(CScriptDictionary,Set,(const String&,const double&),void), AS_CALL_THISCALL);
  1985. engine->RegisterObjectMethod("Dictionary", "bool Get(const String &in, double&out) const", AS_METHODPR(CScriptDictionary,Get,(const String&,double&) const,bool), AS_CALL_THISCALL);
  1986. engine->RegisterObjectMethod("Dictionary", "bool Exists(const String &in) const", AS_METHOD(CScriptDictionary,Exists), AS_CALL_THISCALL);
  1987. engine->RegisterObjectMethod("Dictionary", "bool get_empty() const", AS_METHOD(CScriptDictionary, IsEmpty), AS_CALL_THISCALL);
  1988. engine->RegisterObjectMethod("Dictionary", "uint get_length() const", AS_METHOD(CScriptDictionary, GetSize), AS_CALL_THISCALL);
  1989. engine->RegisterObjectMethod("Dictionary", "void Erase(const String &in)", AS_METHOD(CScriptDictionary,Delete), AS_CALL_THISCALL);
  1990. engine->RegisterObjectMethod("Dictionary", "void Clear()", AS_METHOD(CScriptDictionary,DeleteAll), AS_CALL_THISCALL);
  1991. engine->RegisterObjectMethod("Dictionary", "Array<String> @get_keys() const", AS_METHOD(CScriptDictionary,GetKeys), AS_CALL_THISCALL);
  1992. engine->RegisterObjectMethod("Dictionary", "DictionaryValue &opIndex(const String &in)", AS_METHODPR(CScriptDictionary, operator[], (const String &), CScriptDictValue*), AS_CALL_THISCALL);
  1993. engine->RegisterObjectMethod("Dictionary", "const DictionaryValue &opIndex(const String &in) const", AS_METHODPR(CScriptDictionary, operator[], (const String &) const, const CScriptDictValue*), AS_CALL_THISCALL);
  1994. #ifndef AS_MAX_PORTABILITY
  1995. engine->RegisterObjectMethod("DictionaryValue", "DictionaryValue &opHndlAssign(const ?&in)", AS_FUNCTIONPR_OBJLAST(CScriptDictValue_opAssign, (void *, int, CScriptDictValue*), CScriptDictValue &), AS_CALL_CDECL_OBJLAST);
  1996. engine->RegisterObjectMethod("DictionaryValue", "DictionaryValue &opAssign(const ?&in)", AS_FUNCTIONPR_OBJLAST(CScriptDictValue_opAssign, (void *, int, CScriptDictValue*), CScriptDictValue &), AS_CALL_CDECL_OBJLAST);
  1997. engine->RegisterObjectMethod("DictionaryValue", "void opCast(?&out)", AS_FUNCTIONPR_OBJLAST(CScriptDictValue_opCast, (void *, int, CScriptDictValue*), void), AS_CALL_CDECL_OBJLAST);
  1998. engine->RegisterObjectMethod("DictionaryValue", "void opConv(?&out)", AS_FUNCTIONPR_OBJLAST(CScriptDictValue_opCast, (void *, int, CScriptDictValue*), void), AS_CALL_CDECL_OBJLAST);
  1999. engine->RegisterObjectMethod("Dictionary", "void Set(const String &in, const ?&in)", AS_METHODPR(CScriptDictionary, Set, (const String&, void*, int), void), AS_CALL_THISCALL);
  2000. engine->RegisterObjectMethod("Dictionary", "bool Get(const String &in, ?&out) const", AS_METHODPR(CScriptDictionary, Get, (const String&, void*, int) const, bool), AS_CALL_THISCALL);
  2001. #else
  2002. engine->RegisterObjectMethod("DictionaryValue", "DictionaryValue &opHndlAssign(const ?&in)", asFUNCTION(CScriptDictValue_opAssign_gen), asCALL_GENERIC);
  2003. engine->RegisterObjectMethod("DictionaryValue", "DictionaryValue &opAssign(const ?&in)", asFUNCTION(CScriptDictValue_opAssign_gen), asCALL_GENERIC);
  2004. engine->RegisterObjectMethod("DictionaryValue", "void opCast(?&out)", asFUNCTION(CScriptDictValue_opCast_gen), asCALL_GENERIC);
  2005. engine->RegisterObjectMethod("DictionaryValue", "void opConv(?&out)", asFUNCTION(CScriptDictValue_opCast_gen), asCALL_GENERIC);
  2006. engine->RegisterObjectMethod("Dictionary", "void Set(const String &in, const ?&in)", asFUNCTION(ScriptDictionarySet_Generic), asCALL_GENERIC);
  2007. engine->RegisterObjectMethod("Dictionary", "bool Get(const String &in, ?&out) const", asFUNCTION(ScriptDictionaryGet_Generic), asCALL_GENERIC);
  2008. #endif
  2009. }
  2010. static void ConstructString(String* ptr)
  2011. {
  2012. new(ptr) String();
  2013. }
  2014. static void ConstructStringCopy(const String& str, String* ptr)
  2015. {
  2016. new(ptr) String(str);
  2017. }
  2018. static void DestructString(String* ptr)
  2019. {
  2020. ptr->~String();
  2021. }
  2022. static char* StringCharAt(unsigned i, String& str)
  2023. {
  2024. if (i >= str.Length())
  2025. {
  2026. asIScriptContext* context = asGetActiveContext();
  2027. if (context)
  2028. context->SetException("Index out of bounds");
  2029. return nullptr;
  2030. }
  2031. return &str[i];
  2032. }
  2033. static int StringCmp(const String& lhs, const String& rhs)
  2034. {
  2035. int cmp = 0;
  2036. if (lhs < rhs)
  2037. cmp = -1;
  2038. else if (lhs > rhs)
  2039. cmp = 1;
  2040. return cmp;
  2041. }
  2042. void StringResize(unsigned newSize, String& str)
  2043. {
  2044. unsigned oldSize = str.Length();
  2045. str.Resize(newSize);
  2046. for (unsigned i = oldSize; i < newSize; ++i)
  2047. str[i] = ' ';
  2048. }
  2049. static void ConstructStringInt(int value, String* ptr)
  2050. {
  2051. new(ptr) String(value);
  2052. }
  2053. static void ConstructStringUInt(unsigned value, String* ptr)
  2054. {
  2055. new(ptr) String(value);
  2056. }
  2057. static void ConstructStringFloat(float value, String* ptr)
  2058. {
  2059. new(ptr) String(value);
  2060. }
  2061. static void ConstructStringDouble(double value, String* ptr)
  2062. {
  2063. new(ptr)String(value);
  2064. }
  2065. static void ConstructStringBool(bool value, String* ptr)
  2066. {
  2067. new(ptr) String(value);
  2068. }
  2069. static void StringSetUTF8FromLatin1(const String& src, String& str)
  2070. {
  2071. str.SetUTF8FromLatin1(src.CString());
  2072. }
  2073. void RegisterString(asIScriptEngine *engine)
  2074. {
  2075. //static const unsigned NPOS = String::NPOS; // workaround for GCC
  2076. //static StringFactory stringFactory;
  2077. //engine->RegisterGlobalProperty("const uint NPOS", (void*)&NPOS);
  2078. //engine->RegisterStringFactory("String", &stringFactory);
  2079. // TODO Обертку с контролем размера массива
  2080. //engine->RegisterObjectMethod("String", "uint8 &opIndex(uint)", asFUNCTION(StringCharAt), asCALL_CDECL_OBJLAST);
  2081. //engine->RegisterObjectMethod("String", "const uint8 &opIndex(uint) const", asFUNCTION(StringCharAt), asCALL_CDECL_OBJLAST);
  2082. // TODO у меня int8
  2083. //engine->RegisterObjectMethod("String", "void Replace(uint8, uint8, bool caseSensitive = true)", asMETHODPR(String, Replace, (char, char, bool), void), asCALL_THISCALL);
  2084. //engine->RegisterObjectMethod("String", "String Replaced(uint8, uint8, bool caseSensitive = true) const", asMETHODPR(String, Replaced, (char, char, bool) const, String), asCALL_THISCALL);
  2085. //engine->RegisterObjectMethod("String", "uint Find(uint8, uint start = 0, bool caseSensitive = true) const", asMETHODPR(String, Find, (char, unsigned, bool) const, unsigned), asCALL_THISCALL);
  2086. // NPOS починить
  2087. //engine->RegisterObjectMethod("String", "uint FindLast(const String&in, uint start = 0xffffffff, bool caseSensitive = true) const", asMETHODPR(String, FindLast, (const String&, unsigned, bool) const, unsigned), asCALL_THISCALL);
  2088. //engine->RegisterObjectMethod("String", "uint FindLast(uint8, uint start = 0xffffffff, bool caseSensitive = true) const", asMETHODPR(String, FindLast, (char, unsigned, bool) const, unsigned), asCALL_THISCALL);
  2089. // ручная привязка
  2090. //engine->RegisterObjectMethod("String", "void SetUTF8FromLatin1(const String& in)", asFUNCTION(StringSetUTF8FromLatin1), asCALL_CDECL_OBJLAST);
  2091. // TODO у меня int8
  2092. //engine->RegisterObjectMethod("String", "bool Contains(uint8, bool caseSensitive = true) const", asMETHODPR(String, Contains, (char, bool) const, bool), asCALL_THISCALL);
  2093. }
  2094. const void* StringFactory::GetStringConstant(const char* data, asUINT length)
  2095. {
  2096. assert(strlen(data) == length);
  2097. StringHash hash(data);
  2098. auto iter = map_.Find(hash);
  2099. return reinterpret_cast<const void*>(&(iter == map_.End() ? map_.Insert(MakePair(hash, String(data))) : iter)->second_);
  2100. }
  2101. int StringFactory::ReleaseStringConstant(const void* str)
  2102. {
  2103. // Cache all the strings
  2104. return str ? asSUCCESS : asERROR;
  2105. }
  2106. int StringFactory::GetRawStringData(const void* str, char* data, asUINT* length) const
  2107. {
  2108. if (!str)
  2109. return asERROR;
  2110. auto p = reinterpret_cast<const String*>(str);
  2111. if (length)
  2112. *length = p->Length();
  2113. if (data)
  2114. memcpy(data, p->CString(), p->Length());
  2115. return asSUCCESS;
  2116. }
  2117. // This function is called before ASRegisterGenerated()
  2118. void ASRegisterManualFirst_Addons(asIScriptEngine* engine)
  2119. {
  2120. engine->SetTypeInfoUserDataCleanupCallback(CleanupTypeInfoArrayCache, ARRAY_CACHE);
  2121. engine->RegisterObjectType("Array<class T>", 0, asOBJ_REF | asOBJ_TEMPLATE);
  2122. RegisterArray(engine);
  2123. engine->RegisterObjectType("DictionaryValue", sizeof(CScriptDictValue), asOBJ_VALUE | asOBJ_ASHANDLE | asOBJ_APP_CLASS_CD);
  2124. }
  2125. // This function is called after ASRegisterGenerated()
  2126. void ASRegisterManualLast_Addons(asIScriptEngine* engine)
  2127. {
  2128. //RegisterArray(engine);
  2129. RegisterDictionary(engine);
  2130. }
  2131. }