Addons.cpp 93 KB

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