Addons.cpp 89 KB

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