Addons.cpp 89 KB

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