Addons.cpp 77 KB

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