Addons.cpp 81 KB

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