Addons.cpp 53 KB

12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788899091929394959697989910010110210310410510610710810911011111211311411511611711811912012112212312412512612712812913013113213313413513613713813914014114214314414514614714814915015115215315415515615715815916016116216316416516616716816917017117217317417517617717817918018118218318418518618718818919019119219319419519619719819920020120220320420520620720820921021121221321421521621721821922022122222322422522622722822923023123223323423523623723823924024124224324424524624724824925025125225325425525625725825926026126226326426526626726826927027127227327427527627727827928028128228328428528628728828929029129229329429529629729829930030130230330430530630730830931031131231331431531631731831932032132232332432532632732832933033133233333433533633733833934034134234334434534634734834935035135235335435535635735835936036136236336436536636736836937037137237337437537637737837938038138238338438538638738838939039139239339439539639739839940040140240340440540640740840941041141241341441541641741841942042142242342442542642742842943043143243343443543643743843944044144244344444544644744844945045145245345445545645745845946046146246346446546646746846947047147247347447547647747847948048148248348448548648748848949049149249349449549649749849950050150250350450550650750850951051151251351451551651751851952052152252352452552652752852953053153253353453553653753853954054154254354454554654754854955055155255355455555655755855956056156256356456556656756856957057157257357457557657757857958058158258358458558658758858959059159259359459559659759859960060160260360460560660760860961061161261361461561661761861962062162262362462562662762862963063163263363463563663763863964064164264364464564664764864965065165265365465565665765865966066166266366466566666766866967067167267367467567667767867968068168268368468568668768868969069169269369469569669769869970070170270370470570670770870971071171271371471571671771871972072172272372472572672772872973073173273373473573673773873974074174274374474574674774874975075175275375475575675775875976076176276376476576676776876977077177277377477577677777877978078178278378478578678778878979079179279379479579679779879980080180280380480580680780880981081181281381481581681781881982082182282382482582682782882983083183283383483583683783883984084184284384484584684784884985085185285385485585685785885986086186286386486586686786886987087187287387487587687787887988088188288388488588688788888989089189289389489589689789889990090190290390490590690790890991091191291391491591691791891992092192292392492592692792892993093193293393493593693793893994094194294394494594694794894995095195295395495595695795895996096196296396496596696796896997097197297397497597697797897998098198298398498598698798898999099199299399499599699799899910001001100210031004100510061007100810091010101110121013101410151016101710181019102010211022102310241025102610271028102910301031103210331034103510361037103810391040104110421043104410451046104710481049105010511052105310541055105610571058105910601061106210631064106510661067106810691070107110721073107410751076107710781079108010811082108310841085108610871088108910901091109210931094109510961097109810991100110111021103110411051106110711081109111011111112111311141115111611171118111911201121112211231124112511261127112811291130113111321133113411351136113711381139114011411142114311441145114611471148114911501151115211531154115511561157115811591160116111621163116411651166116711681169117011711172117311741175117611771178117911801181118211831184118511861187118811891190119111921193119411951196119711981199120012011202120312041205120612071208120912101211121212131214121512161217121812191220122112221223122412251226122712281229123012311232123312341235123612371238123912401241124212431244124512461247124812491250125112521253125412551256125712581259126012611262126312641265126612671268126912701271127212731274127512761277127812791280128112821283128412851286128712881289129012911292129312941295129612971298129913001301130213031304130513061307130813091310131113121313131413151316131713181319132013211322132313241325132613271328132913301331133213331334133513361337133813391340134113421343134413451346134713481349135013511352135313541355135613571358135913601361136213631364136513661367136813691370137113721373137413751376137713781379138013811382138313841385138613871388138913901391139213931394139513961397139813991400140114021403140414051406140714081409141014111412141314141415141614171418141914201421142214231424142514261427142814291430143114321433143414351436143714381439144014411442144314441445144614471448144914501451145214531454145514561457145814591460146114621463146414651466146714681469147014711472147314741475147614771478147914801481148214831484148514861487148814891490149114921493149414951496149714981499150015011502150315041505150615071508150915101511151215131514151515161517151815191520152115221523152415251526152715281529153015311532153315341535153615371538153915401541
  1. //
  2. // Copyright (c) 2008-2013 the Urho3D project.
  3. //
  4. // Permission is hereby granted, free of charge, to any person obtaining a copy
  5. // of this software and associated documentation files (the "Software"), to deal
  6. // in the Software without restriction, including without limitation the rights
  7. // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
  8. // copies of the Software, and to permit persons to whom the Software is
  9. // furnished to do so, subject to the following conditions:
  10. //
  11. // The above copyright notice and this permission notice shall be included in
  12. // all copies or substantial portions of the Software.
  13. //
  14. // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
  15. // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
  16. // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
  17. // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
  18. // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
  19. // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
  20. // THE SOFTWARE.
  21. //
  22. #include "Precompiled.h"
  23. #include "Addons.h"
  24. #include <cstring>
  25. #include <new>
  26. #include <stdio.h>
  27. // Adapted from Angelscript's scriptarray & scriptstdstring add-ons, but with garbage collection disabled
  28. namespace Urho3D
  29. {
  30. using namespace std;
  31. /// %Script array buffer.
  32. struct SArrayBuffer
  33. {
  34. asDWORD maxElements;
  35. asDWORD numElements;
  36. asBYTE data[1];
  37. };
  38. /// %Script array cache.
  39. struct SArrayCache
  40. {
  41. asIScriptFunction *cmpFunc;
  42. asIScriptFunction *eqFunc;
  43. };
  44. // We just define a number here that we assume nobody else is using for
  45. // object type user data. The add-ons have reserved the numbers 1000
  46. // through 1999 for this purpose, so we should be fine.
  47. const asPWORD ARRAY_CACHE = 1000;
  48. static void CleanupObjectTypeArrayCache(asIObjectType *type)
  49. {
  50. SArrayCache *cache = reinterpret_cast<SArrayCache*>(type->GetUserData(ARRAY_CACHE));
  51. if( cache )
  52. delete cache;
  53. }
  54. static CScriptArray* ScriptArrayFactory2(asIObjectType *ot, asUINT length)
  55. {
  56. CScriptArray *a = new CScriptArray(length, ot);
  57. // It's possible the constructor raised a script exception, in which case we
  58. // need to free the memory and return null instead, else we get a memory leak.
  59. asIScriptContext *ctx = asGetActiveContext();
  60. if( ctx && ctx->GetState() == asEXECUTION_EXCEPTION )
  61. {
  62. delete a;
  63. return 0;
  64. }
  65. return a;
  66. }
  67. static CScriptArray* ScriptArrayFactoryDefVal(asIObjectType *ot, asUINT length, void *defVal)
  68. {
  69. CScriptArray *a = new CScriptArray(length, defVal, ot);
  70. // It's possible the constructor raised a script exception, in which case we
  71. // need to free the memory and return null instead, else we get a memory leak.
  72. asIScriptContext *ctx = asGetActiveContext();
  73. if( ctx && ctx->GetState() == asEXECUTION_EXCEPTION )
  74. {
  75. delete a;
  76. return 0;
  77. }
  78. return a;
  79. }
  80. static CScriptArray* ScriptArrayFactory(asIObjectType *ot)
  81. {
  82. return ScriptArrayFactory2(ot, 0);
  83. }
  84. // This optional callback is called when the template type is first used by the compiler.
  85. // It allows the application to validate if the template can be instanciated for the requested
  86. // subtype at compile time, instead of at runtime. The output argument dontGarbageCollect
  87. // allow the callback to tell the engine if the template instance type shouldn't be garbage collected,
  88. // i.e. no asOBJ_GC flag.
  89. static bool ScriptArrayTemplateCallback(asIObjectType *ot, bool &dontGarbageCollect)
  90. {
  91. // Urho3D: nothing is garbage collected
  92. dontGarbageCollect = true;
  93. // Make sure the subtype can be instanciated with a default factory/constructor,
  94. // otherwise we won't be able to instanciate the elements.
  95. int typeId = ot->GetSubTypeId();
  96. if( typeId == asTYPEID_VOID )
  97. return false;
  98. if( (typeId & asTYPEID_MASK_OBJECT) && !(typeId & asTYPEID_OBJHANDLE) )
  99. {
  100. asIObjectType *subtype = ot->GetEngine()->GetObjectTypeById(typeId);
  101. asDWORD flags = subtype->GetFlags();
  102. if( (flags & asOBJ_VALUE) && !(flags & asOBJ_POD) )
  103. {
  104. // Verify that there is a default constructor
  105. bool found = false;
  106. for( asUINT n = 0; n < subtype->GetBehaviourCount(); n++ )
  107. {
  108. asEBehaviours beh;
  109. asIScriptFunction *func = subtype->GetBehaviourByIndex(n, &beh);
  110. if( beh != asBEHAVE_CONSTRUCT ) continue;
  111. if( func->GetParamCount() == 0 )
  112. {
  113. // Found the default constructor
  114. found = true;
  115. break;
  116. }
  117. }
  118. if( !found )
  119. {
  120. // There is no default constructor
  121. return false;
  122. }
  123. }
  124. else if( (flags & asOBJ_REF) )
  125. {
  126. // Verify that there is a default factory
  127. bool found = false;
  128. for( asUINT n = 0; n < subtype->GetFactoryCount(); n++ )
  129. {
  130. asIScriptFunction *func = subtype->GetFactoryByIndex(n);
  131. if( func->GetParamCount() == 0 )
  132. {
  133. // Found the default factory
  134. found = true;
  135. break;
  136. }
  137. }
  138. if( !found )
  139. {
  140. // No default factory
  141. return false;
  142. }
  143. }
  144. // If the object type is not garbage collected then the array also doesn't need to be
  145. if( !(flags & asOBJ_GC) )
  146. dontGarbageCollect = true;
  147. }
  148. // The type is ok
  149. return true;
  150. }
  151. CScriptArray &CScriptArray::operator=(const CScriptArray &other)
  152. {
  153. // Only perform the copy if the array types are the same
  154. if( &other != this &&
  155. other.GetArrayObjectType() == GetArrayObjectType() )
  156. {
  157. // Make sure the arrays are of the same size
  158. Resize(other.buffer->numElements);
  159. // Copy the value of each element
  160. CopyBuffer(buffer, other.buffer);
  161. }
  162. return *this;
  163. }
  164. CScriptArray::CScriptArray(asUINT length, asIObjectType *ot)
  165. {
  166. refCount = 1;
  167. gcFlag = false;
  168. objType = ot;
  169. objType->AddRef();
  170. buffer = 0;
  171. Precache();
  172. // Determine element size
  173. if( subTypeId & asTYPEID_MASK_OBJECT )
  174. elementSize = sizeof(asPWORD);
  175. else
  176. elementSize = objType->GetEngine()->GetSizeOfPrimitiveType(subTypeId);
  177. // Make sure the array size isn't too large for us to handle
  178. if( !CheckMaxSize(length) )
  179. {
  180. // Don't continue with the initialization
  181. return;
  182. }
  183. CreateBuffer(&buffer, length);
  184. // Urho3D: garbage collection disabled
  185. /*
  186. // Notify the GC of the successful creation
  187. if( objType->GetFlags() & asOBJ_GC )
  188. objType->GetEngine()->NotifyGarbageCollectorOfNewObject(this, objType);
  189. */
  190. }
  191. CScriptArray::CScriptArray(const CScriptArray &other)
  192. {
  193. refCount = 1;
  194. gcFlag = false;
  195. objType = other.objType;
  196. objType->AddRef();
  197. buffer = 0;
  198. Precache();
  199. elementSize = other.elementSize;
  200. if( objType->GetFlags() & asOBJ_GC )
  201. objType->GetEngine()->NotifyGarbageCollectorOfNewObject(this, objType);
  202. CreateBuffer(&buffer, 0);
  203. // Copy the content
  204. *this = other;
  205. }
  206. CScriptArray::CScriptArray(asUINT length, void *defVal, asIObjectType *ot)
  207. {
  208. refCount = 1;
  209. gcFlag = false;
  210. objType = ot;
  211. objType->AddRef();
  212. buffer = 0;
  213. Precache();
  214. // Determine element size
  215. if( subTypeId & asTYPEID_MASK_OBJECT )
  216. elementSize = sizeof(asPWORD);
  217. else
  218. elementSize = objType->GetEngine()->GetSizeOfPrimitiveType(subTypeId);
  219. // Make sure the array size isn't too large for us to handle
  220. if( !CheckMaxSize(length) )
  221. {
  222. // Don't continue with the initialization
  223. return;
  224. }
  225. CreateBuffer(&buffer, length);
  226. // Notify the GC of the successful creation
  227. if( objType->GetFlags() & asOBJ_GC )
  228. objType->GetEngine()->NotifyGarbageCollectorOfNewObject(this, objType);
  229. // Initialize the elements with the default value
  230. for( asUINT n = 0; n < GetSize(); n++ )
  231. SetValue(n, defVal);
  232. }
  233. void CScriptArray::SetValue(asUINT index, void *value)
  234. {
  235. // At() will take care of the out-of-bounds checking, though
  236. // if called from the application then nothing will be done
  237. void *ptr = At(index);
  238. if( ptr == 0 ) return;
  239. if( (subTypeId & ~asTYPEID_MASK_SEQNBR) && !(subTypeId & asTYPEID_OBJHANDLE) )
  240. objType->GetEngine()->AssignScriptObject(ptr, value, subTypeId);
  241. else if( subTypeId & asTYPEID_OBJHANDLE )
  242. {
  243. void *tmp = *(void**)ptr;
  244. *(void**)ptr = *(void**)value;
  245. objType->GetEngine()->AddRefScriptObject(*(void**)value, objType->GetSubType());
  246. if( tmp )
  247. objType->GetEngine()->ReleaseScriptObject(tmp, objType->GetSubType());
  248. }
  249. else if( subTypeId == asTYPEID_BOOL ||
  250. subTypeId == asTYPEID_INT8 ||
  251. subTypeId == asTYPEID_UINT8 )
  252. *(char*)ptr = *(char*)value;
  253. else if( subTypeId == asTYPEID_INT16 ||
  254. subTypeId == asTYPEID_UINT16 )
  255. *(short*)ptr = *(short*)value;
  256. else if( subTypeId == asTYPEID_INT32 ||
  257. subTypeId == asTYPEID_UINT32 ||
  258. subTypeId == asTYPEID_FLOAT ||
  259. subTypeId > asTYPEID_DOUBLE ) // enums have a type id larger than doubles
  260. *(int*)ptr = *(int*)value;
  261. else if( subTypeId == asTYPEID_INT64 ||
  262. subTypeId == asTYPEID_UINT64 ||
  263. subTypeId == asTYPEID_DOUBLE )
  264. *(double*)ptr = *(double*)value;
  265. }
  266. CScriptArray::~CScriptArray()
  267. {
  268. if( buffer )
  269. {
  270. DeleteBuffer(buffer);
  271. buffer = 0;
  272. }
  273. if( objType ) objType->Release();
  274. }
  275. asUINT CScriptArray::GetSize() const
  276. {
  277. return buffer->numElements;
  278. }
  279. bool CScriptArray::IsEmpty() const
  280. {
  281. return buffer->numElements == 0;
  282. }
  283. void CScriptArray::Reserve(asUINT maxElements)
  284. {
  285. if( maxElements <= buffer->maxElements )
  286. return;
  287. // Allocate memory for the buffer
  288. SArrayBuffer *newBuffer;
  289. #if defined(__S3E__) // Marmalade doesn't understand (nothrow)
  290. newBuffer = (SArrayBuffer*)new asBYTE[sizeof(SArrayBuffer)-1 + elementSize*maxElements];
  291. #else
  292. newBuffer = (SArrayBuffer*)new (nothrow) asBYTE[sizeof(SArrayBuffer)-1 + elementSize*maxElements];
  293. #endif
  294. if( newBuffer )
  295. {
  296. newBuffer->numElements = buffer->numElements;
  297. newBuffer->maxElements = maxElements;
  298. }
  299. else
  300. {
  301. // Out of memory
  302. asIScriptContext *ctx = asGetActiveContext();
  303. if( ctx )
  304. ctx->SetException("Out of memory");
  305. return;
  306. }
  307. memcpy(newBuffer->data, buffer->data, buffer->numElements*elementSize);
  308. // Release the old buffer
  309. delete[] (asBYTE*)buffer;
  310. buffer = newBuffer;
  311. }
  312. void CScriptArray::Resize(asUINT numElements)
  313. {
  314. if( numElements & 0x80000000 )
  315. {
  316. CheckMaxSize(numElements);
  317. return;
  318. }
  319. Resize((int)numElements - (int)buffer->numElements, (asUINT)-1);
  320. }
  321. // Internal
  322. void CScriptArray::Resize(int delta, asUINT at)
  323. {
  324. if( delta < 0 )
  325. {
  326. if( -delta > (int)buffer->numElements )
  327. delta = -(int)buffer->numElements;
  328. if( at > buffer->numElements + delta )
  329. at = buffer->numElements + delta;
  330. }
  331. else if( delta > 0 )
  332. {
  333. // Make sure the array size isn't too large for us to handle
  334. if( delta > 0 && !CheckMaxSize(buffer->numElements + delta) )
  335. return;
  336. if( at > buffer->numElements )
  337. at = buffer->numElements;
  338. }
  339. if( delta == 0 ) return;
  340. if( buffer->maxElements < buffer->numElements + delta )
  341. {
  342. // Allocate memory for the buffer
  343. SArrayBuffer *newBuffer;
  344. #if defined(__S3E__) // Marmalade doesn't understand (nothrow)
  345. newBuffer = (SArrayBuffer*)new asBYTE[sizeof(SArrayBuffer)-1 + elementSize*(buffer->numElements + delta)];
  346. #else
  347. newBuffer = (SArrayBuffer*)new (nothrow) asBYTE[sizeof(SArrayBuffer)-1 + elementSize*(buffer->numElements + delta)];
  348. #endif
  349. if( newBuffer )
  350. {
  351. newBuffer->numElements = buffer->numElements + delta;
  352. newBuffer->maxElements = newBuffer->numElements;
  353. }
  354. else
  355. {
  356. // Out of memory
  357. asIScriptContext *ctx = asGetActiveContext();
  358. if( ctx )
  359. ctx->SetException("Out of memory");
  360. return;
  361. }
  362. memcpy(newBuffer->data, buffer->data, at*elementSize);
  363. if( at < buffer->numElements )
  364. memcpy(newBuffer->data + (at+delta)*elementSize, buffer->data + at*elementSize, (buffer->numElements-at)*elementSize);
  365. if( subTypeId & asTYPEID_MASK_OBJECT )
  366. Construct(newBuffer, at, at+delta);
  367. // Release the old buffer
  368. delete[] (asBYTE*)buffer;
  369. buffer = newBuffer;
  370. }
  371. else if( delta < 0 )
  372. {
  373. Destruct(buffer, at, at-delta);
  374. memmove(buffer->data + at*elementSize, buffer->data + (at-delta)*elementSize, (buffer->numElements - (at-delta))*elementSize);
  375. buffer->numElements += delta;
  376. }
  377. else
  378. {
  379. memmove(buffer->data + (at+delta)*elementSize, buffer->data + at*elementSize, (buffer->numElements - at)*elementSize);
  380. Construct(buffer, at, at+delta);
  381. buffer->numElements += delta;
  382. }
  383. }
  384. // internal
  385. bool CScriptArray::CheckMaxSize(asUINT numElements)
  386. {
  387. // This code makes sure the size of the buffer that is allocated
  388. // for the array doesn't overflow and becomes smaller than requested
  389. asUINT maxSize = 0xFFFFFFFFul - sizeof(SArrayBuffer) + 1;
  390. if( subTypeId & asTYPEID_MASK_OBJECT )
  391. maxSize /= sizeof(void*);
  392. else if( elementSize > 0 )
  393. maxSize /= elementSize;
  394. if( numElements > maxSize )
  395. {
  396. asIScriptContext *ctx = asGetActiveContext();
  397. if( ctx )
  398. {
  399. // Set a script exception
  400. ctx->SetException("Too large array size");
  401. }
  402. return false;
  403. }
  404. // OK
  405. return true;
  406. }
  407. asIObjectType *CScriptArray::GetArrayObjectType() const
  408. {
  409. return objType;
  410. }
  411. int CScriptArray::GetArrayTypeId() const
  412. {
  413. return objType->GetTypeId();
  414. }
  415. int CScriptArray::GetElementTypeId() const
  416. {
  417. return subTypeId;
  418. }
  419. void CScriptArray::InsertAt(asUINT index, void *value)
  420. {
  421. if( index > buffer->numElements )
  422. {
  423. // If this is called from a script we raise a script exception
  424. asIScriptContext *ctx = asGetActiveContext();
  425. if( ctx )
  426. ctx->SetException("Index out of bounds");
  427. return;
  428. }
  429. // Make room for the new element
  430. Resize(1, index);
  431. // Set the value of the new element
  432. SetValue(index, value);
  433. }
  434. void CScriptArray::InsertLast(void *value)
  435. {
  436. InsertAt(buffer->numElements, value);
  437. }
  438. void CScriptArray::RemoveAt(asUINT index)
  439. {
  440. if( index >= buffer->numElements )
  441. {
  442. // If this is called from a script we raise a script exception
  443. asIScriptContext *ctx = asGetActiveContext();
  444. if( ctx )
  445. ctx->SetException("Index out of bounds");
  446. return;
  447. }
  448. // Remove the element
  449. Resize(-1, index);
  450. }
  451. void CScriptArray::RemoveLast()
  452. {
  453. RemoveAt(buffer->numElements-1);
  454. }
  455. // Return a pointer to the array element. Returns 0 if the index is out of bounds
  456. const void *CScriptArray::At(asUINT index) const
  457. {
  458. if( buffer == 0 || index >= buffer->numElements )
  459. {
  460. // If this is called from a script we raise a script exception
  461. asIScriptContext *ctx = asGetActiveContext();
  462. if( ctx )
  463. ctx->SetException("Index out of bounds");
  464. return 0;
  465. }
  466. if( (subTypeId & asTYPEID_MASK_OBJECT) && !(subTypeId & asTYPEID_OBJHANDLE) )
  467. return (void*)((size_t*)buffer->data)[index];
  468. else
  469. return buffer->data + elementSize*index;
  470. }
  471. void *CScriptArray::At(asUINT index)
  472. {
  473. return const_cast<void*>(const_cast<const CScriptArray *>(this)->At(index));
  474. }
  475. // internal
  476. void CScriptArray::CreateBuffer(SArrayBuffer **buf, asUINT numElements)
  477. {
  478. if( subTypeId & asTYPEID_MASK_OBJECT )
  479. {
  480. #if defined(__S3E__) // Marmalade doesn't understand (nothrow)
  481. *buf = (SArrayBuffer*)new asBYTE[sizeof(SArrayBuffer)-1+sizeof(void*)*numElements];
  482. #else
  483. *buf = (SArrayBuffer*)new (nothrow) asBYTE[sizeof(SArrayBuffer)-1+sizeof(void*)*numElements];
  484. #endif
  485. }
  486. else
  487. {
  488. #if defined(__S3E__)
  489. *buf = (SArrayBuffer*)new asBYTE[sizeof(SArrayBuffer)-1+elementSize*numElements];
  490. #else
  491. *buf = (SArrayBuffer*)new (nothrow) asBYTE[sizeof(SArrayBuffer)-1+elementSize*numElements];
  492. #endif
  493. }
  494. if( *buf )
  495. {
  496. (*buf)->numElements = numElements;
  497. (*buf)->maxElements = numElements;
  498. Construct(*buf, 0, numElements);
  499. }
  500. else
  501. {
  502. // Oops, out of memory
  503. asIScriptContext *ctx = asGetActiveContext();
  504. if( ctx )
  505. ctx->SetException("Out of memory");
  506. }
  507. }
  508. // internal
  509. void CScriptArray::DeleteBuffer(SArrayBuffer *buf)
  510. {
  511. Destruct(buf, 0, buf->numElements);
  512. // Free the buffer
  513. delete[] (asBYTE*)buf;
  514. }
  515. // internal
  516. void CScriptArray::Construct(SArrayBuffer *buf, asUINT start, asUINT end)
  517. {
  518. if( subTypeId & asTYPEID_OBJHANDLE )
  519. {
  520. // Set all object handles to null
  521. void *d = (void*)(buf->data + start * sizeof(void*));
  522. memset(d, 0, (end-start)*sizeof(void*));
  523. }
  524. else if( subTypeId & asTYPEID_MASK_OBJECT )
  525. {
  526. void **max = (void**)(buf->data + end * sizeof(void*));
  527. void **d = (void**)(buf->data + start * sizeof(void*));
  528. asIScriptEngine *engine = objType->GetEngine();
  529. for( ; d < max; d++ )
  530. *d = (void*)engine->CreateScriptObject(subTypeId);
  531. }
  532. }
  533. // internal
  534. void CScriptArray::Destruct(SArrayBuffer *buf, asUINT start, asUINT end)
  535. {
  536. if( subTypeId & asTYPEID_MASK_OBJECT )
  537. {
  538. asIScriptEngine *engine = objType->GetEngine();
  539. void **max = (void**)(buf->data + end * sizeof(void*));
  540. void **d = (void**)(buf->data + start * sizeof(void*));
  541. for( ; d < max; d++ )
  542. {
  543. if( *d )
  544. engine->ReleaseScriptObject(*d, objType->GetSubType());
  545. }
  546. }
  547. }
  548. // internal
  549. bool CScriptArray::Less(const void *a, const void *b, bool asc, asIScriptContext *ctx)
  550. {
  551. if( !asc )
  552. {
  553. // Swap items
  554. const void *TEMP = a;
  555. a = b;
  556. b = TEMP;
  557. }
  558. if( !(subTypeId & ~asTYPEID_MASK_SEQNBR) )
  559. {
  560. // Simple compare of values
  561. switch( subTypeId )
  562. {
  563. #define COMPARE(T) *((T*)a) < *((T*)b)
  564. case asTYPEID_BOOL: return COMPARE(bool);
  565. case asTYPEID_INT8: return COMPARE(signed char);
  566. case asTYPEID_UINT8: return COMPARE(unsigned char);
  567. case asTYPEID_INT16: return COMPARE(signed short);
  568. case asTYPEID_UINT16: return COMPARE(unsigned short);
  569. case asTYPEID_INT32: return COMPARE(signed int);
  570. case asTYPEID_UINT32: return COMPARE(unsigned int);
  571. case asTYPEID_FLOAT: return COMPARE(float);
  572. case asTYPEID_DOUBLE: return COMPARE(double);
  573. default: return COMPARE(signed int); // All enums fall in this case
  574. #undef COMPARE
  575. }
  576. }
  577. else if( subTypeId & asTYPEID_OBJHANDLE )
  578. {
  579. return *((void**)a) < *((void**)b);
  580. }
  581. else
  582. {
  583. int r = 0;
  584. // Execute object opCmp
  585. SArrayCache *cache = reinterpret_cast<SArrayCache*>(objType->GetUserData(ARRAY_CACHE));
  586. if( cache && cache->cmpFunc )
  587. {
  588. // TODO: Add proper error handling
  589. r = ctx->Prepare(cache->cmpFunc); assert(r >= 0);
  590. r = ctx->SetObject((void*)a); assert(r >= 0);
  591. r = ctx->SetArgObject(0, (void*)b); assert(r >= 0);
  592. r = ctx->Execute();
  593. if( r == asEXECUTION_FINISHED )
  594. {
  595. return (int)ctx->GetReturnDWord() < 0;
  596. }
  597. }
  598. }
  599. return false;
  600. }
  601. void CScriptArray::Reverse()
  602. {
  603. asUINT size = GetSize();
  604. if( size >= 2 )
  605. {
  606. asBYTE TEMP[16];
  607. for( asUINT i = 0; i < size / 2; i++ )
  608. {
  609. Copy(TEMP, GetArrayItemPointer(i));
  610. Copy(GetArrayItemPointer(i), GetArrayItemPointer(size - i - 1));
  611. Copy(GetArrayItemPointer(size - i - 1), TEMP);
  612. }
  613. }
  614. }
  615. bool CScriptArray::operator==(const CScriptArray &other) const
  616. {
  617. if( objType != other.objType )
  618. return false;
  619. if( GetSize() != other.GetSize() )
  620. return false;
  621. asIScriptContext *cmpContext = 0;
  622. bool isNested = false;
  623. if( (subTypeId & ~asTYPEID_MASK_SEQNBR) && !(subTypeId & asTYPEID_OBJHANDLE) )
  624. {
  625. // Try to reuse the active context
  626. cmpContext = asGetActiveContext();
  627. if( cmpContext )
  628. {
  629. if( cmpContext->PushState() >= 0 )
  630. isNested = true;
  631. else
  632. cmpContext = 0;
  633. }
  634. if( cmpContext == 0 )
  635. {
  636. // TODO: Ideally this context would be retrieved from a pool, so we don't have to
  637. // create a new one everytime. We could keep a context with the array object
  638. // but that would consume a lot of resources as each context is quite heavy.
  639. cmpContext = objType->GetEngine()->CreateContext();
  640. }
  641. }
  642. // Check if all elements are equal
  643. bool isEqual = true;
  644. SArrayCache *cache = reinterpret_cast<SArrayCache*>(objType->GetUserData(ARRAY_CACHE));
  645. for( asUINT n = 0; n < GetSize(); n++ )
  646. if( !Equals(At(n), other.At(n), cmpContext, cache) )
  647. {
  648. isEqual = false;
  649. break;
  650. }
  651. if( cmpContext )
  652. if( isNested )
  653. {
  654. asEContextState state = cmpContext->GetState();
  655. cmpContext->PopState();
  656. if( state == asEXECUTION_ABORTED )
  657. cmpContext->Abort();
  658. }
  659. else
  660. cmpContext->Release();
  661. return isEqual;
  662. }
  663. // internal
  664. bool CScriptArray::Equals(const void *a, const void *b, asIScriptContext *ctx, SArrayCache *cache) const
  665. {
  666. if( !(subTypeId & ~asTYPEID_MASK_SEQNBR) )
  667. {
  668. // Simple compare of values
  669. switch( subTypeId )
  670. {
  671. #define COMPARE(T) *((T*)a) == *((T*)b)
  672. case asTYPEID_BOOL: return COMPARE(bool);
  673. case asTYPEID_INT8: return COMPARE(signed char);
  674. case asTYPEID_UINT8: return COMPARE(unsigned char);
  675. case asTYPEID_INT16: return COMPARE(signed short);
  676. case asTYPEID_UINT16: return COMPARE(unsigned short);
  677. case asTYPEID_INT32: return COMPARE(signed int);
  678. case asTYPEID_UINT32: return COMPARE(unsigned int);
  679. case asTYPEID_FLOAT: return COMPARE(float);
  680. case asTYPEID_DOUBLE: return COMPARE(double);
  681. default: return COMPARE(signed int); // All enums fall here
  682. #undef COMPARE
  683. }
  684. }
  685. else if( subTypeId & asTYPEID_OBJHANDLE )
  686. {
  687. return *((void**)a) == *((void**)b);
  688. }
  689. else
  690. {
  691. int r = 0;
  692. // Execute object opEquals if available
  693. if( cache && cache->eqFunc )
  694. {
  695. // TODO: Add proper error handling
  696. r = ctx->Prepare(cache->eqFunc); assert(r >= 0);
  697. r = ctx->SetObject((void*)a); assert(r >= 0);
  698. r = ctx->SetArgObject(0, (void*)b); assert(r >= 0);
  699. r = ctx->Execute();
  700. if( r == asEXECUTION_FINISHED )
  701. {
  702. return ctx->GetReturnByte() != 0;
  703. }
  704. }
  705. // Execute object opCmp if available
  706. if( cache && cache->cmpFunc )
  707. {
  708. // TODO: Add proper error handling
  709. r = ctx->Prepare(cache->cmpFunc); assert(r >= 0);
  710. r = ctx->SetObject((void*)a); assert(r >= 0);
  711. r = ctx->SetArgObject(0, (void*)b); assert(r >= 0);
  712. r = ctx->Execute();
  713. if( r == asEXECUTION_FINISHED )
  714. {
  715. return (int)ctx->GetReturnDWord() == 0;
  716. }
  717. }
  718. }
  719. return false;
  720. }
  721. int CScriptArray::Find(void *value) const
  722. {
  723. return Find(0, value);
  724. }
  725. int CScriptArray::Find(asUINT index, void *value) const
  726. {
  727. // Check if the subtype really supports find()
  728. // TODO: Can't this be done at compile time too by the template callback
  729. SArrayCache *cache = 0;
  730. if( (subTypeId & ~asTYPEID_MASK_SEQNBR) && !(subTypeId & asTYPEID_OBJHANDLE) )
  731. {
  732. cache = reinterpret_cast<SArrayCache*>(objType->GetUserData(ARRAY_CACHE));
  733. if( !cache || (cache->cmpFunc == 0 && cache->eqFunc == 0) )
  734. {
  735. asIScriptContext *ctx = asGetActiveContext();
  736. asIObjectType* subType = objType->GetEngine()->GetObjectTypeById(subTypeId);
  737. // Throw an exception
  738. if( ctx )
  739. {
  740. char tmp[512];
  741. #if defined(_MSC_VER) && _MSC_VER >= 1500 && !defined(__S3E__)
  742. sprintf_s(tmp, 512, "Type '%s' does not have opEquals / opCmp", subType->GetName());
  743. #else
  744. sprintf(tmp, "Type '%s' does not have opEquals / opCmp", subType->GetName());
  745. #endif
  746. ctx->SetException(tmp);
  747. }
  748. return -1;
  749. }
  750. }
  751. asIScriptContext *cmpContext = 0;
  752. bool isNested = false;
  753. if( (subTypeId & ~asTYPEID_MASK_SEQNBR) && !(subTypeId & asTYPEID_OBJHANDLE) )
  754. {
  755. // Try to reuse the active context
  756. cmpContext = asGetActiveContext();
  757. if( cmpContext )
  758. {
  759. if( cmpContext->PushState() >= 0 )
  760. isNested = true;
  761. else
  762. cmpContext = 0;
  763. }
  764. if( cmpContext == 0 )
  765. {
  766. // TODO: Ideally this context would be retrieved from a pool, so we don't have to
  767. // create a new one everytime. We could keep a context with the array object
  768. // but that would consume a lot of resources as each context is quite heavy.
  769. cmpContext = objType->GetEngine()->CreateContext();
  770. }
  771. }
  772. // Find the matching element
  773. int ret = -1;
  774. asUINT size = GetSize();
  775. if( index < size )
  776. {
  777. for( asUINT i = index; i < size; i++ )
  778. {
  779. // value passed by reference
  780. if( Equals(At(i), (value), cmpContext, cache) )
  781. {
  782. ret = (int)i;
  783. break;
  784. }
  785. }
  786. }
  787. if( cmpContext )
  788. if( isNested )
  789. {
  790. asEContextState state = cmpContext->GetState();
  791. cmpContext->PopState();
  792. if( state == asEXECUTION_ABORTED )
  793. cmpContext->Abort();
  794. }
  795. else
  796. cmpContext->Release();
  797. return ret;
  798. }
  799. // internal
  800. // Copy object handle or primitive value
  801. void CScriptArray::Copy(void *dst, void *src)
  802. {
  803. memcpy(dst, src, elementSize);
  804. }
  805. // internal
  806. // Return pointer to array item (object handle or primitive value)
  807. void *CScriptArray::GetArrayItemPointer(int index)
  808. {
  809. return buffer->data + index * elementSize;
  810. }
  811. // internal
  812. // Return pointer to data in buffer (object or primitive)
  813. void *CScriptArray::GetDataPointer(void *buffer)
  814. {
  815. if ((subTypeId & asTYPEID_MASK_OBJECT) && !(subTypeId & asTYPEID_OBJHANDLE) )
  816. {
  817. // Real address of object
  818. return reinterpret_cast<void*>(*(size_t*)buffer);
  819. }
  820. else
  821. {
  822. // Primitive is just a raw data
  823. return buffer;
  824. }
  825. }
  826. // Sort ascending
  827. void CScriptArray::SortAsc()
  828. {
  829. Sort(0, GetSize(), true);
  830. }
  831. // Sort ascending
  832. void CScriptArray::SortAsc(asUINT index, asUINT count)
  833. {
  834. Sort(index, count, true);
  835. }
  836. // Sort descending
  837. void CScriptArray::SortDesc()
  838. {
  839. Sort(0, GetSize(), false);
  840. }
  841. // Sort descending
  842. void CScriptArray::SortDesc(asUINT index, asUINT count)
  843. {
  844. Sort(index, count, false);
  845. }
  846. // internal
  847. void CScriptArray::Sort(asUINT index, asUINT count, bool asc)
  848. {
  849. // Subtype isn't primitive and doesn't have opCmp
  850. SArrayCache *cache = reinterpret_cast<SArrayCache*>(objType->GetUserData(ARRAY_CACHE));
  851. if( (subTypeId & ~asTYPEID_MASK_SEQNBR) && !(subTypeId & asTYPEID_OBJHANDLE) )
  852. {
  853. if( !cache || cache->cmpFunc == 0 )
  854. {
  855. asIScriptContext *ctx = asGetActiveContext();
  856. asIObjectType* subType = objType->GetEngine()->GetObjectTypeById(subTypeId);
  857. // Throw an exception
  858. if( ctx )
  859. {
  860. char tmp[512];
  861. #if defined(_MSC_VER) && _MSC_VER >= 1500 && !defined(__S3E__)
  862. sprintf_s(tmp, 512, "Type '%s' does not have opCmp", subType->GetName());
  863. #else
  864. sprintf(tmp, "Type '%s' does not have opCmp", subType->GetName());
  865. #endif
  866. ctx->SetException(tmp);
  867. }
  868. return;
  869. }
  870. }
  871. // No need to sort
  872. if( count < 2 )
  873. {
  874. return;
  875. }
  876. int start = index;
  877. int end = index + count;
  878. // Check if we could access invalid item while sorting
  879. if( start >= (int)buffer->numElements || end > (int)buffer->numElements )
  880. {
  881. asIScriptContext *ctx = asGetActiveContext();
  882. // Throw an exception
  883. if( ctx )
  884. {
  885. ctx->SetException("Index out of bounds");
  886. }
  887. return;
  888. }
  889. asBYTE tmp[16];
  890. asIScriptContext *cmpContext = 0;
  891. bool isNested = false;
  892. if( (subTypeId & ~asTYPEID_MASK_SEQNBR) && !(subTypeId & asTYPEID_OBJHANDLE) )
  893. {
  894. // Try to reuse the active context
  895. cmpContext = asGetActiveContext();
  896. if( cmpContext )
  897. {
  898. if( cmpContext->PushState() >= 0 )
  899. isNested = true;
  900. else
  901. cmpContext = 0;
  902. }
  903. if( cmpContext == 0 )
  904. {
  905. // TODO: Ideally this context would be retrieved from a pool, so we don't have to
  906. // create a new one everytime. We could keep a context with the array object
  907. // but that would consume a lot of resources as each context is quite heavy.
  908. cmpContext = objType->GetEngine()->CreateContext();
  909. }
  910. }
  911. // Insertion sort
  912. for( int i = start + 1; i < end; i++ )
  913. {
  914. Copy(tmp, GetArrayItemPointer(i));
  915. int j = i - 1;
  916. while( j >= start && Less(GetDataPointer(tmp), At(j), asc, cmpContext) )
  917. {
  918. Copy(GetArrayItemPointer(j + 1), GetArrayItemPointer(j));
  919. j--;
  920. }
  921. Copy(GetArrayItemPointer(j + 1), tmp);
  922. }
  923. if( cmpContext )
  924. if( isNested )
  925. {
  926. asEContextState state = cmpContext->GetState();
  927. cmpContext->PopState();
  928. if( state == asEXECUTION_ABORTED )
  929. cmpContext->Abort();
  930. }
  931. else
  932. cmpContext->Release();
  933. }
  934. // internal
  935. void CScriptArray::CopyBuffer(SArrayBuffer *dst, SArrayBuffer *src)
  936. {
  937. asIScriptEngine *engine = objType->GetEngine();
  938. if( subTypeId & asTYPEID_OBJHANDLE )
  939. {
  940. // Copy the references and increase the reference counters
  941. if( dst->numElements > 0 && src->numElements > 0 )
  942. {
  943. int count = dst->numElements > src->numElements ? src->numElements : dst->numElements;
  944. void **max = (void**)(dst->data + count * sizeof(void*));
  945. void **d = (void**)dst->data;
  946. void **s = (void**)src->data;
  947. for( ; d < max; d++, s++ )
  948. {
  949. void *tmp = *d;
  950. *d = *s;
  951. if( *d )
  952. engine->AddRefScriptObject(*d, objType->GetSubType());
  953. // Release the old ref after incrementing the new to avoid problem incase it is the same ref
  954. if( tmp )
  955. engine->ReleaseScriptObject(tmp, objType->GetSubType());
  956. }
  957. }
  958. }
  959. else
  960. {
  961. if( dst->numElements > 0 && src->numElements > 0 )
  962. {
  963. int count = dst->numElements > src->numElements ? src->numElements : dst->numElements;
  964. if( subTypeId & asTYPEID_MASK_OBJECT )
  965. {
  966. // Call the assignment operator on all of the objects
  967. void **max = (void**)(dst->data + count * sizeof(void*));
  968. void **d = (void**)dst->data;
  969. void **s = (void**)src->data;
  970. for( ; d < max; d++, s++ )
  971. engine->AssignScriptObject(*d, *s, subTypeId);
  972. }
  973. else
  974. {
  975. // Primitives are copied byte for byte
  976. memcpy(dst->data, src->data, count*elementSize);
  977. }
  978. }
  979. }
  980. }
  981. // internal
  982. // Precache some info
  983. void CScriptArray::Precache()
  984. {
  985. subTypeId = objType->GetSubTypeId();
  986. // Check if it is an array of objects. Only for these do we need to cache anything
  987. // Type ids for primitives and enums only has the sequence number part
  988. if( !(subTypeId & ~asTYPEID_MASK_SEQNBR) )
  989. return;
  990. // The opCmp and opEquals methods are cached because the searching for the
  991. // methods is quite time consuming if a lot of array objects are created.
  992. // First check if a cache already exists for this array type
  993. SArrayCache *cache = reinterpret_cast<SArrayCache*>(objType->GetUserData(ARRAY_CACHE));
  994. if( cache ) return;
  995. // We need to make sure the cache is created only once, even
  996. // if multiple threads reach the same point at the same time
  997. asAcquireExclusiveLock();
  998. // Now that we got the lock, we need to check again to make sure the
  999. // cache wasn't created while we were waiting for the lock
  1000. cache = reinterpret_cast<SArrayCache*>(objType->GetUserData(ARRAY_CACHE));
  1001. if( cache )
  1002. {
  1003. asReleaseExclusiveLock();
  1004. return;
  1005. }
  1006. // Create the cache
  1007. cache = new SArrayCache();
  1008. memset(cache, 0, sizeof(SArrayCache));
  1009. asIObjectType *subType = objType->GetEngine()->GetObjectTypeById(subTypeId);
  1010. if( subType )
  1011. {
  1012. for( asUINT i = 0; i < subType->GetMethodCount(); i++ )
  1013. {
  1014. asIScriptFunction *func = subType->GetMethodByIndex(i);
  1015. if( func->GetParamCount() == 1 /* && func->IsReadOnly() */ )
  1016. {
  1017. asDWORD flags = 0;
  1018. int returnTypeId = func->GetReturnTypeId();
  1019. int paramTypeId = func->GetParamTypeId(0, &flags);
  1020. // The parameter must either be a reference to the subtype or a handle to the subtype
  1021. if( ((flags & asTM_INREF) && paramTypeId == subTypeId) ||
  1022. (flags == 0 &&
  1023. (paramTypeId & asTYPEID_OBJHANDLE) &&
  1024. (paramTypeId & ~(asTYPEID_OBJHANDLE | asTYPEID_HANDLETOCONST)) == (subTypeId & ~(asTYPEID_OBJHANDLE | asTYPEID_HANDLETOCONST))) )
  1025. {
  1026. if( returnTypeId == asTYPEID_INT32 && strcmp(func->GetName(), "opCmp") == 0 )
  1027. cache->cmpFunc = subType->GetMethodByIndex(i);
  1028. if( returnTypeId == asTYPEID_BOOL && strcmp(func->GetName(), "opEquals") == 0 )
  1029. cache->eqFunc = subType->GetMethodByIndex(i);
  1030. if( cache->cmpFunc && cache->eqFunc )
  1031. break;
  1032. }
  1033. }
  1034. }
  1035. }
  1036. // Set the user data only at the end so others that retrieve it will know it is complete
  1037. objType->SetUserData(cache, ARRAY_CACHE);
  1038. asReleaseExclusiveLock();
  1039. }
  1040. // GC behaviour
  1041. void CScriptArray::EnumReferences(asIScriptEngine *engine)
  1042. {
  1043. // If the array is holding handles, then we need to notify the GC of them
  1044. if( subTypeId & asTYPEID_MASK_OBJECT )
  1045. {
  1046. void **d = (void**)buffer->data;
  1047. for( asUINT n = 0; n < buffer->numElements; n++ )
  1048. {
  1049. if( d[n] )
  1050. engine->GCEnumCallback(d[n]);
  1051. }
  1052. }
  1053. }
  1054. // GC behaviour
  1055. void CScriptArray::ReleaseAllHandles(asIScriptEngine *engine)
  1056. {
  1057. // Resizing to zero will release everything
  1058. Resize(0);
  1059. }
  1060. void CScriptArray::AddRef() const
  1061. {
  1062. // Clear the GC flag then increase the counter
  1063. gcFlag = false;
  1064. asAtomicInc(refCount);
  1065. }
  1066. void CScriptArray::Release() const
  1067. {
  1068. // Clearing the GC flag then descrease the counter
  1069. gcFlag = false;
  1070. if( asAtomicDec(refCount) == 0 )
  1071. {
  1072. // When reaching 0 no more references to this instance
  1073. // exists and the object should be destroyed
  1074. delete this;
  1075. }
  1076. }
  1077. // GC behaviour
  1078. int CScriptArray::GetRefCount()
  1079. {
  1080. return refCount;
  1081. }
  1082. // GC behaviour
  1083. void CScriptArray::SetFlag()
  1084. {
  1085. gcFlag = true;
  1086. }
  1087. // GC behaviour
  1088. bool CScriptArray::GetFlag()
  1089. {
  1090. return gcFlag;
  1091. }
  1092. static void ScriptArrayClear(CScriptArray* ptr)
  1093. {
  1094. ptr->Resize(0);
  1095. }
  1096. void RegisterArray(asIScriptEngine* engine)
  1097. {
  1098. // Register the object type user data clean up
  1099. engine->SetObjectTypeUserDataCleanupCallback(CleanupObjectTypeArrayCache, ARRAY_CACHE);
  1100. engine->RegisterObjectType("Array<class T>", 0, asOBJ_REF | asOBJ_TEMPLATE);
  1101. engine->RegisterObjectBehaviour("Array<T>", asBEHAVE_TEMPLATE_CALLBACK, "bool f(int&in, bool&out)", asFUNCTION(ScriptArrayTemplateCallback), asCALL_CDECL);
  1102. engine->RegisterObjectBehaviour("Array<T>", asBEHAVE_FACTORY, "Array<T>@ f(int& in)", asFUNCTIONPR(ScriptArrayFactory, (asIObjectType*), CScriptArray*), asCALL_CDECL);
  1103. engine->RegisterObjectBehaviour("Array<T>", asBEHAVE_FACTORY, "Array<T>@ f(int& in, uint)", asFUNCTIONPR(ScriptArrayFactory2, (asIObjectType*, asUINT), CScriptArray*), asCALL_CDECL);
  1104. engine->RegisterObjectBehaviour("Array<T>", asBEHAVE_FACTORY, "Array<T>@ f(int& in, uint, const T& in)", asFUNCTIONPR(ScriptArrayFactoryDefVal, (asIObjectType*, asUINT, void *), CScriptArray*), asCALL_CDECL);
  1105. engine->RegisterObjectBehaviour("Array<T>", asBEHAVE_LIST_FACTORY, "Array<T>@ f(int& in, uint)", asFUNCTIONPR(ScriptArrayFactory2, (asIObjectType*, asUINT), CScriptArray*), asCALL_CDECL);
  1106. engine->RegisterObjectBehaviour("Array<T>", asBEHAVE_ADDREF, "void f()", asMETHOD(CScriptArray,AddRef), asCALL_THISCALL);
  1107. engine->RegisterObjectBehaviour("Array<T>", asBEHAVE_RELEASE, "void f()", asMETHOD(CScriptArray,Release), asCALL_THISCALL);
  1108. engine->RegisterObjectMethod("Array<T>", "T& opIndex(uint)", asMETHODPR(CScriptArray, At, (unsigned), void*), asCALL_THISCALL);
  1109. engine->RegisterObjectMethod("Array<T>", "const T& opIndex(uint) const", asMETHODPR(CScriptArray, At, (unsigned), void*), asCALL_THISCALL);
  1110. engine->RegisterObjectMethod("Array<T>", "Array<T>& opAssign(const Array<T>& in)", asMETHOD(CScriptArray, operator=), asCALL_THISCALL);
  1111. engine->RegisterObjectMethod("Array<T>", "void Insert(uint, const T& in)", asMETHOD(CScriptArray, InsertAt), asCALL_THISCALL);
  1112. engine->RegisterObjectMethod("Array<T>", "void Erase(uint)", asMETHOD(CScriptArray, RemoveAt), asCALL_THISCALL);
  1113. engine->RegisterObjectMethod("Array<T>", "void Push(const T& in)", asMETHOD(CScriptArray, InsertLast), asCALL_THISCALL);
  1114. engine->RegisterObjectMethod("Array<T>", "void Pop()", asMETHOD(CScriptArray, RemoveLast), asCALL_THISCALL);
  1115. engine->RegisterObjectMethod("Array<T>", "void Resize(uint)", asMETHODPR(CScriptArray, Resize, (asUINT), void), asCALL_THISCALL);
  1116. engine->RegisterObjectMethod("Array<T>", "void Clear()", asFUNCTION(ScriptArrayClear), asCALL_CDECL_OBJLAST);
  1117. engine->RegisterObjectMethod("Array<T>", "void Sort()", asMETHODPR(CScriptArray, SortAsc, (), void), asCALL_THISCALL);
  1118. engine->RegisterObjectMethod("Array<T>", "void Sort(uint, uint)", asMETHODPR(CScriptArray, SortAsc, (asUINT, asUINT), void), asCALL_THISCALL);
  1119. engine->RegisterObjectMethod("Array<T>", "void SortReverse()", asMETHODPR(CScriptArray, SortDesc, (), void), asCALL_THISCALL);
  1120. engine->RegisterObjectMethod("Array<T>", "void SortReverse(uint, uint)", asMETHODPR(CScriptArray, SortDesc, (asUINT, asUINT), void), asCALL_THISCALL);
  1121. engine->RegisterObjectMethod("Array<T>", "void Reverse()", asMETHOD(CScriptArray, Reverse), asCALL_THISCALL);
  1122. engine->RegisterObjectMethod("Array<T>", "int Find(const T&in) const", asMETHODPR(CScriptArray, Find, (void*) const, int), asCALL_THISCALL);
  1123. engine->RegisterObjectMethod("Array<T>", "int Find(uint, const T&in) const", asMETHODPR(CScriptArray, Find, (asUINT, void*) const, int), asCALL_THISCALL);
  1124. engine->RegisterObjectMethod("Array<T>", "bool opEquals(const Array<T>&in) const", asMETHOD(CScriptArray, operator==), asCALL_THISCALL);
  1125. engine->RegisterObjectMethod("Array<T>", "uint get_length() const", asMETHOD(CScriptArray, GetSize), asCALL_THISCALL);
  1126. engine->RegisterObjectMethod("Array<T>", "void set_length(uint)", asMETHODPR(CScriptArray, Resize, (asUINT), void), asCALL_THISCALL);
  1127. engine->RegisterObjectMethod("Array<T>", "bool get_empty() const", asMETHOD(CScriptArray, IsEmpty), asCALL_THISCALL);
  1128. engine->RegisterDefaultArrayType("Array<T>");
  1129. }
  1130. static String StringFactory(asUINT length, const char* s)
  1131. {
  1132. return String(s, length);
  1133. }
  1134. static void ConstructString(String* ptr)
  1135. {
  1136. new(ptr) String();
  1137. }
  1138. static void ConstructStringCopy(const String& str, String* ptr)
  1139. {
  1140. new(ptr) String(str);
  1141. }
  1142. static void DestructString(String* ptr)
  1143. {
  1144. ptr->~String();
  1145. }
  1146. static char* StringCharAt(unsigned int i, String& str)
  1147. {
  1148. if (i >= str.Length())
  1149. {
  1150. asIScriptContext* context = asGetActiveContext();
  1151. if (context)
  1152. context->SetException("Index out of bounds");
  1153. return 0;
  1154. }
  1155. return &str[i];
  1156. }
  1157. static int StringCmp(const String& lhs, const String& rhs)
  1158. {
  1159. int cmp = 0;
  1160. if (lhs < rhs)
  1161. cmp = -1;
  1162. else if (lhs > rhs)
  1163. cmp = 1;
  1164. return cmp;
  1165. }
  1166. void StringResize(unsigned newSize, String& str)
  1167. {
  1168. unsigned oldSize = str.Length();
  1169. str.Resize(newSize);
  1170. for (unsigned i = oldSize; i < newSize; ++i)
  1171. str[i] = ' ';
  1172. }
  1173. static void ConstructStringInt(int value, String* ptr)
  1174. {
  1175. new(ptr) String(value);
  1176. }
  1177. static void ConstructStringUInt(unsigned value, String* ptr)
  1178. {
  1179. new(ptr) String(value);
  1180. }
  1181. static void ConstructStringFloat(float value, String* ptr)
  1182. {
  1183. new(ptr) String(value);
  1184. }
  1185. static void ConstructStringBool(bool value, String* ptr)
  1186. {
  1187. new(ptr) String(value);
  1188. }
  1189. static String& StringAssignInt(int value, String& str)
  1190. {
  1191. str = String(value);
  1192. return str;
  1193. }
  1194. static String& StringAddAssignInt(int value, String& str)
  1195. {
  1196. str += String(value);
  1197. return str;
  1198. }
  1199. static String StringAddInt(int value, const String& str)
  1200. {
  1201. return str + String(value);
  1202. }
  1203. static String StringAddIntReverse(int value, const String& str)
  1204. {
  1205. return String(value) + str;
  1206. }
  1207. static String& StringAssignUInt(unsigned value, String& str)
  1208. {
  1209. str = String(value);
  1210. return str;
  1211. }
  1212. static String& StringAddAssignUInt(unsigned value, String& str)
  1213. {
  1214. str += String(value);
  1215. return str;
  1216. }
  1217. static String StringAddUInt(unsigned value, const String& str)
  1218. {
  1219. return str + String(value);
  1220. }
  1221. static String StringAddUIntReverse(unsigned value, const String& str)
  1222. {
  1223. return String(value) + str;
  1224. }
  1225. static String& StringAssignFloat(float value, String& str)
  1226. {
  1227. str = String(value);
  1228. return str;
  1229. }
  1230. static String& StringAddAssignFloat(float value, String& str)
  1231. {
  1232. str += String(value);
  1233. return str;
  1234. }
  1235. static String StringAddFloat(float value, const String& str)
  1236. {
  1237. return str + String(value);
  1238. }
  1239. static String StringAddFloatReverse(float value, const String& str)
  1240. {
  1241. return String(value) + str;
  1242. }
  1243. static String& StringAssignBool(bool value, String& str)
  1244. {
  1245. str = String(value);
  1246. return str;
  1247. }
  1248. static String& StringAddAssignBool(bool value, String& str)
  1249. {
  1250. str += String(value);
  1251. return str;
  1252. }
  1253. static String StringAddBool(bool value, const String& str)
  1254. {
  1255. return str + String(value);
  1256. }
  1257. static String StringAddBoolReverse(bool value, const String& str)
  1258. {
  1259. return String(value) + str;
  1260. }
  1261. static void StringSetUTF8FromLatin1(const String& src, String& str)
  1262. {
  1263. str.SetUTF8FromLatin1(src.CString());
  1264. }
  1265. void RegisterString(asIScriptEngine *engine)
  1266. {
  1267. engine->RegisterObjectType("String", sizeof(String), asOBJ_VALUE | asOBJ_APP_CLASS_CDAK);
  1268. engine->RegisterStringFactory("String", asFUNCTION(StringFactory), asCALL_CDECL);
  1269. engine->RegisterObjectBehaviour("String", asBEHAVE_CONSTRUCT, "void f()", asFUNCTION(ConstructString), asCALL_CDECL_OBJLAST);
  1270. engine->RegisterObjectBehaviour("String", asBEHAVE_CONSTRUCT, "void f(const String&in)", asFUNCTION(ConstructStringCopy), asCALL_CDECL_OBJLAST);
  1271. engine->RegisterObjectBehaviour("String", asBEHAVE_DESTRUCT, "void f()", asFUNCTION(DestructString), asCALL_CDECL_OBJLAST);
  1272. engine->RegisterObjectMethod("String", "String& opAssign(const String&in)", asMETHODPR(String, operator =, (const String&), String&), asCALL_THISCALL);
  1273. engine->RegisterObjectMethod("String", "String& opAddAssign(const String&in)", asMETHODPR(String, operator +=, (const String&), String&), asCALL_THISCALL);
  1274. engine->RegisterObjectMethod("String", "bool opEquals(const String&in) const", asMETHODPR(String, operator ==, (const String&) const, bool), asCALL_THISCALL);
  1275. engine->RegisterObjectMethod("String", "int opCmp(const String&in) const", asFUNCTION(StringCmp), asCALL_CDECL_OBJFIRST);
  1276. engine->RegisterObjectMethod("String", "String opAdd(const String&in) const", asMETHODPR(String, operator +, (const String&) const, String), asCALL_THISCALL);
  1277. engine->RegisterObjectMethod("String", "uint8 &opIndex(uint)", asFUNCTION(StringCharAt), asCALL_CDECL_OBJLAST);
  1278. engine->RegisterObjectMethod("String", "const uint8 &opIndex(uint) const", asFUNCTION(StringCharAt), asCALL_CDECL_OBJLAST);
  1279. engine->RegisterObjectMethod("String", "void Replace(uint8, uint8)", asMETHODPR(String, Replace, (char, char), void), asCALL_THISCALL);
  1280. engine->RegisterObjectMethod("String", "void Replace(const String&in, const String&in)", asMETHODPR(String, Replace, (const String&, const String&), void), asCALL_THISCALL);
  1281. engine->RegisterObjectMethod("String", "String Replaced(uint8, uint8) const", asMETHODPR(String, Replaced, (char, char) const, String), asCALL_THISCALL);
  1282. engine->RegisterObjectMethod("String", "String Replaced(const String&in, const String&in) const", asMETHODPR(String, Replaced, (const String&, const String&) const, String), asCALL_THISCALL);
  1283. engine->RegisterObjectMethod("String", "void Resize(uint)", asFUNCTION(StringResize), asCALL_CDECL_OBJLAST);
  1284. engine->RegisterObjectMethod("String", "uint Find(const String&in, uint start = 0) const", asMETHODPR(String, Find, (const String&, unsigned) const, unsigned), asCALL_THISCALL);
  1285. engine->RegisterObjectMethod("String", "uint Find(uint8, uint start = 0) const", asMETHODPR(String, Find, (char, unsigned) const, unsigned), asCALL_THISCALL);
  1286. engine->RegisterObjectMethod("String", "uint FindLast(const String&in, uint start = 0xffffffff) const", asMETHODPR(String, FindLast, (const String&, unsigned) const, unsigned), asCALL_THISCALL);
  1287. engine->RegisterObjectMethod("String", "uint FindLast(uint8, uint start = 0xffffffff) const", asMETHODPR(String, FindLast, (char, unsigned) const, unsigned), asCALL_THISCALL);
  1288. engine->RegisterObjectMethod("String", "bool StartsWith(const String&in) const", asMETHOD(String, StartsWith), asCALL_THISCALL);
  1289. engine->RegisterObjectMethod("String", "bool EndsWith(const String&in) const", asMETHOD(String, EndsWith), asCALL_THISCALL);
  1290. engine->RegisterObjectMethod("String", "String Substring(uint) const", asMETHODPR(String, Substring, (unsigned) const, String), asCALL_THISCALL);
  1291. engine->RegisterObjectMethod("String", "String Substring(uint, uint) const", asMETHODPR(String, Substring, (unsigned, unsigned) const, String), asCALL_THISCALL);
  1292. engine->RegisterObjectMethod("String", "String ToUpper() const", asMETHOD(String, ToUpper), asCALL_THISCALL);
  1293. engine->RegisterObjectMethod("String", "String ToLower() const", asMETHOD(String, ToLower), asCALL_THISCALL);
  1294. engine->RegisterObjectMethod("String", "String Trimmed() const", asMETHOD(String, Trimmed), asCALL_THISCALL);
  1295. engine->RegisterObjectMethod("String", "void SetUTF8FromLatin1(const String& in)", asFUNCTION(StringSetUTF8FromLatin1), asCALL_CDECL_OBJLAST);
  1296. engine->RegisterObjectMethod("String", "uint get_utf8Length() const", asMETHOD(String, LengthUTF8), asCALL_THISCALL);
  1297. engine->RegisterObjectMethod("String", "uint ByteOffsetUTF8(uint) const", asMETHOD(String, ByteOffsetUTF8), asCALL_THISCALL);
  1298. engine->RegisterObjectMethod("String", "uint NextUTF8Char(uint&) const", asMETHOD(String, NextUTF8Char), asCALL_THISCALL);
  1299. engine->RegisterObjectMethod("String", "uint AtUTF8(uint) const", asMETHOD(String, AtUTF8), asCALL_THISCALL);
  1300. engine->RegisterObjectMethod("String", "void ReplaceUTF8(uint, uint)", asMETHOD(String, ReplaceUTF8), asCALL_THISCALL);
  1301. engine->RegisterObjectMethod("String", "void AppendUTF8(uint)", asMETHOD(String, AppendUTF8), asCALL_THISCALL);
  1302. engine->RegisterObjectMethod("String", "String SubstringUTF8(uint) const", asMETHODPR(String, SubstringUTF8, (unsigned) const, String), asCALL_THISCALL);
  1303. engine->RegisterObjectMethod("String", "String SubstringUTF8(uint, uint) const", asMETHODPR(String, SubstringUTF8, (unsigned, unsigned) const, String), asCALL_THISCALL);
  1304. engine->RegisterObjectMethod("String", "uint get_length() const", asMETHOD(String, Length), asCALL_THISCALL);
  1305. engine->RegisterObjectMethod("String", "bool get_empty() const", asMETHOD(String, Empty), asCALL_THISCALL);
  1306. engine->RegisterObjectMethod("String", "int Compare(const String&in, bool caseSensitive = true) const", asMETHODPR(String, Compare, (const String&, bool) const, int), asCALL_THISCALL);
  1307. engine->RegisterObjectMethod("String", "bool Contains(const String&in) const", asMETHODPR(String, Contains, (const String&) const, bool), asCALL_THISCALL);
  1308. engine->RegisterObjectMethod("String", "bool Contains(uint8) const", asMETHODPR(String, Contains, (char) const, bool), asCALL_THISCALL);
  1309. engine->RegisterObjectMethod("String", "void Clear()", asMETHOD(String, Clear), asCALL_THISCALL);
  1310. // Register automatic conversion functions for convenience
  1311. engine->RegisterObjectBehaviour("String", asBEHAVE_CONSTRUCT, "void f(int)", asFUNCTION(ConstructStringInt), asCALL_CDECL_OBJLAST);
  1312. engine->RegisterObjectBehaviour("String", asBEHAVE_CONSTRUCT, "void f(uint)", asFUNCTION(ConstructStringUInt), asCALL_CDECL_OBJLAST);
  1313. engine->RegisterObjectBehaviour("String", asBEHAVE_CONSTRUCT, "void f(float)", asFUNCTION(ConstructStringFloat), asCALL_CDECL_OBJLAST);
  1314. engine->RegisterObjectBehaviour("String", asBEHAVE_CONSTRUCT, "void f(bool)", asFUNCTION(ConstructStringBool), asCALL_CDECL_OBJLAST);
  1315. engine->RegisterObjectMethod("String", "String& opAssign(int)", asFUNCTION(StringAssignInt), asCALL_CDECL_OBJLAST);
  1316. engine->RegisterObjectMethod("String", "String& opAddAssign(int)", asFUNCTION(StringAddAssignInt), asCALL_CDECL_OBJLAST);
  1317. engine->RegisterObjectMethod("String", "String opAdd(int) const", asFUNCTION(StringAddInt), asCALL_CDECL_OBJLAST);
  1318. engine->RegisterObjectMethod("String", "String opAdd_r(int) const", asFUNCTION(StringAddIntReverse), asCALL_CDECL_OBJLAST);
  1319. engine->RegisterObjectMethod("String", "String& opAssign(uint)", asFUNCTION(StringAssignUInt), asCALL_CDECL_OBJLAST);
  1320. engine->RegisterObjectMethod("String", "String& opAddAssign(uint)", asFUNCTION(StringAddAssignUInt), asCALL_CDECL_OBJLAST);
  1321. engine->RegisterObjectMethod("String", "String opAdd(uint) const", asFUNCTION(StringAddUInt), asCALL_CDECL_OBJLAST);
  1322. engine->RegisterObjectMethod("String", "String opAdd_r(uint) const", asFUNCTION(StringAddUIntReverse), asCALL_CDECL_OBJLAST);
  1323. engine->RegisterObjectMethod("String", "String& opAssign(float)", asFUNCTION(StringAssignFloat), asCALL_CDECL_OBJLAST);
  1324. engine->RegisterObjectMethod("String", "String& opAddAssign(float)", asFUNCTION(StringAddAssignFloat), asCALL_CDECL_OBJLAST);
  1325. engine->RegisterObjectMethod("String", "String opAdd(float) const", asFUNCTION(StringAddFloat), asCALL_CDECL_OBJLAST);
  1326. engine->RegisterObjectMethod("String", "String opAdd_r(float) const", asFUNCTION(StringAddFloatReverse), asCALL_CDECL_OBJLAST);
  1327. engine->RegisterObjectMethod("String", "String& opAssign(bool)", asFUNCTION(StringAssignBool), asCALL_CDECL_OBJLAST);
  1328. engine->RegisterObjectMethod("String", "String& opAddAssign(bool)", asFUNCTION(StringAddAssignBool), asCALL_CDECL_OBJLAST);
  1329. engine->RegisterObjectMethod("String", "String opAdd(bool) const", asFUNCTION(StringAddBool), asCALL_CDECL_OBJLAST);
  1330. engine->RegisterObjectMethod("String", "String opAdd_r(bool) const", asFUNCTION(StringAddBoolReverse), asCALL_CDECL_OBJLAST);
  1331. }
  1332. }