as_scriptobject.cpp 17 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557
  1. /*
  2. AngelCode Scripting Library
  3. Copyright (c) 2003-2012 Andreas Jonsson
  4. This software is provided 'as-is', without any express or implied
  5. warranty. In no event will the authors be held liable for any
  6. damages arising from the use of this software.
  7. Permission is granted to anyone to use this software for any
  8. purpose, including commercial applications, and to alter it and
  9. redistribute it freely, subject to the following restrictions:
  10. 1. The origin of this software must not be misrepresented; you
  11. must not claim that you wrote the original software. If you use
  12. this software in a product, an acknowledgment in the product
  13. documentation would be appreciated but is not required.
  14. 2. Altered source versions must be plainly marked as such, and
  15. must not be misrepresented as being the original software.
  16. 3. This notice may not be removed or altered from any source
  17. distribution.
  18. The original version of this library can be located at:
  19. http://www.angelcode.com/angelscript/
  20. Andreas Jonsson
  21. [email protected]
  22. */
  23. // Modified by Lasse Öörni for Urho3D
  24. #include <new>
  25. #include "as_config.h"
  26. #include "as_scriptengine.h"
  27. #include "as_scriptobject.h"
  28. BEGIN_AS_NAMESPACE
  29. // This helper function will call the default factory, that is a script function
  30. asIScriptObject *ScriptObjectFactory(const asCObjectType *objType, asCScriptEngine *engine)
  31. {
  32. asIScriptContext *ctx;
  33. // TODO: optimize: There should be a pool for the context so it doesn't
  34. // have to be allocated just for creating the script object
  35. // TODO: It must be possible for the application to debug the creation of the object too
  36. int r = engine->CreateContext(&ctx, true);
  37. if( r < 0 )
  38. return 0;
  39. r = ctx->Prepare(objType->beh.factory);
  40. if( r < 0 )
  41. {
  42. ctx->Release();
  43. return 0;
  44. }
  45. r = ctx->Execute();
  46. if( r != asEXECUTION_FINISHED )
  47. {
  48. ctx->Release();
  49. return 0;
  50. }
  51. asIScriptObject *ptr = (asIScriptObject*)ctx->GetReturnAddress();
  52. // Increase the reference, because the context will release it's pointer
  53. ptr->AddRef();
  54. ctx->Release();
  55. return ptr;
  56. }
  57. #ifdef AS_MAX_PORTABILITY
  58. static void ScriptObject_AddRef_Generic(asIScriptGeneric *gen)
  59. {
  60. asCScriptObject *self = (asCScriptObject*)gen->GetObject();
  61. self->AddRef();
  62. }
  63. static void ScriptObject_Release_Generic(asIScriptGeneric *gen)
  64. {
  65. asCScriptObject *self = (asCScriptObject*)gen->GetObject();
  66. self->Release();
  67. }
  68. static void ScriptObject_GetRefCount_Generic(asIScriptGeneric *gen)
  69. {
  70. asCScriptObject *self = (asCScriptObject*)gen->GetObject();
  71. *(int*)gen->GetAddressOfReturnLocation() = self->GetRefCount();
  72. }
  73. static void ScriptObject_SetFlag_Generic(asIScriptGeneric *gen)
  74. {
  75. asCScriptObject *self = (asCScriptObject*)gen->GetObject();
  76. self->SetFlag();
  77. }
  78. static void ScriptObject_GetFlag_Generic(asIScriptGeneric *gen)
  79. {
  80. asCScriptObject *self = (asCScriptObject*)gen->GetObject();
  81. *(bool*)gen->GetAddressOfReturnLocation() = self->GetFlag();
  82. }
  83. static void ScriptObject_EnumReferences_Generic(asIScriptGeneric *gen)
  84. {
  85. asCScriptObject *self = (asCScriptObject*)gen->GetObject();
  86. asIScriptEngine *engine = *(asIScriptEngine**)gen->GetAddressOfArg(0);
  87. self->EnumReferences(engine);
  88. }
  89. static void ScriptObject_ReleaseAllHandles_Generic(asIScriptGeneric *gen)
  90. {
  91. asCScriptObject *self = (asCScriptObject*)gen->GetObject();
  92. asIScriptEngine *engine = *(asIScriptEngine**)gen->GetAddressOfArg(0);
  93. self->ReleaseAllHandles(engine);
  94. }
  95. #endif
  96. void RegisterScriptObject(asCScriptEngine *engine)
  97. {
  98. // Register the default script class behaviours
  99. int r = 0;
  100. UNUSED_VAR(r); // It is only used in debug mode
  101. engine->scriptTypeBehaviours.engine = engine;
  102. engine->scriptTypeBehaviours.flags = asOBJ_SCRIPT_OBJECT | asOBJ_REF | asOBJ_GC;
  103. engine->scriptTypeBehaviours.name = "_builtin_object_";
  104. #ifndef AS_MAX_PORTABILITY
  105. r = engine->RegisterBehaviourToObjectType(&engine->scriptTypeBehaviours, asBEHAVE_CONSTRUCT, "void f(int&in)", asFUNCTION(ScriptObject_Construct), asCALL_CDECL_OBJLAST); asASSERT( r >= 0 );
  106. r = engine->RegisterBehaviourToObjectType(&engine->scriptTypeBehaviours, asBEHAVE_ADDREF, "void f()", asMETHOD(asCScriptObject,AddRef), asCALL_THISCALL); asASSERT( r >= 0 );
  107. r = engine->RegisterBehaviourToObjectType(&engine->scriptTypeBehaviours, asBEHAVE_RELEASE, "void f()", asMETHOD(asCScriptObject,Release), asCALL_THISCALL); asASSERT( r >= 0 );
  108. r = engine->RegisterMethodToObjectType(&engine->scriptTypeBehaviours, "int &opAssign(int &in)", asFUNCTION(ScriptObject_Assignment), asCALL_CDECL_OBJLAST); asASSERT( r >= 0 );
  109. // Register GC behaviours
  110. r = engine->RegisterBehaviourToObjectType(&engine->scriptTypeBehaviours, asBEHAVE_GETREFCOUNT, "int f()", asMETHOD(asCScriptObject,GetRefCount), asCALL_THISCALL); asASSERT( r >= 0 );
  111. r = engine->RegisterBehaviourToObjectType(&engine->scriptTypeBehaviours, asBEHAVE_SETGCFLAG, "void f()", asMETHOD(asCScriptObject,SetFlag), asCALL_THISCALL); asASSERT( r >= 0 );
  112. r = engine->RegisterBehaviourToObjectType(&engine->scriptTypeBehaviours, asBEHAVE_GETGCFLAG, "bool f()", asMETHOD(asCScriptObject,GetFlag), asCALL_THISCALL); asASSERT( r >= 0 );
  113. r = engine->RegisterBehaviourToObjectType(&engine->scriptTypeBehaviours, asBEHAVE_ENUMREFS, "void f(int&in)", asMETHOD(asCScriptObject,EnumReferences), asCALL_THISCALL); asASSERT( r >= 0 );
  114. r = engine->RegisterBehaviourToObjectType(&engine->scriptTypeBehaviours, asBEHAVE_RELEASEREFS, "void f(int&in)", asMETHOD(asCScriptObject,ReleaseAllHandles), asCALL_THISCALL); asASSERT( r >= 0 );
  115. #else
  116. r = engine->RegisterBehaviourToObjectType(&engine->scriptTypeBehaviours, asBEHAVE_CONSTRUCT, "void f(int&in)", asFUNCTION(ScriptObject_Construct_Generic), asCALL_GENERIC); asASSERT( r >= 0 );
  117. r = engine->RegisterBehaviourToObjectType(&engine->scriptTypeBehaviours, asBEHAVE_ADDREF, "void f()", asFUNCTION(ScriptObject_AddRef_Generic), asCALL_GENERIC); asASSERT( r >= 0 );
  118. r = engine->RegisterBehaviourToObjectType(&engine->scriptTypeBehaviours, asBEHAVE_RELEASE, "void f()", asFUNCTION(ScriptObject_Release_Generic), asCALL_GENERIC); asASSERT( r >= 0 );
  119. r = engine->RegisterMethodToObjectType(&engine->scriptTypeBehaviours, "int &opAssign(int &in)", asFUNCTION(ScriptObject_Assignment_Generic), asCALL_GENERIC); asASSERT( r >= 0 );
  120. // Register GC behaviours
  121. r = engine->RegisterBehaviourToObjectType(&engine->scriptTypeBehaviours, asBEHAVE_GETREFCOUNT, "int f()", asFUNCTION(ScriptObject_GetRefCount_Generic), asCALL_GENERIC); asASSERT( r >= 0 );
  122. r = engine->RegisterBehaviourToObjectType(&engine->scriptTypeBehaviours, asBEHAVE_SETGCFLAG, "void f()", asFUNCTION(ScriptObject_SetFlag_Generic), asCALL_GENERIC); asASSERT( r >= 0 );
  123. r = engine->RegisterBehaviourToObjectType(&engine->scriptTypeBehaviours, asBEHAVE_GETGCFLAG, "bool f()", asFUNCTION(ScriptObject_GetFlag_Generic), asCALL_GENERIC); asASSERT( r >= 0 );
  124. r = engine->RegisterBehaviourToObjectType(&engine->scriptTypeBehaviours, asBEHAVE_ENUMREFS, "void f(int&in)", asFUNCTION(ScriptObject_EnumReferences_Generic), asCALL_GENERIC); asASSERT( r >= 0 );
  125. r = engine->RegisterBehaviourToObjectType(&engine->scriptTypeBehaviours, asBEHAVE_RELEASEREFS, "void f(int&in)", asFUNCTION(ScriptObject_ReleaseAllHandles_Generic), asCALL_GENERIC); asASSERT( r >= 0 );
  126. #endif
  127. }
  128. void ScriptObject_Construct_Generic(asIScriptGeneric *gen)
  129. {
  130. asCObjectType *objType = *(asCObjectType**)gen->GetAddressOfArg(0);
  131. asCScriptObject *self = (asCScriptObject*)gen->GetObject();
  132. ScriptObject_Construct(objType, self);
  133. }
  134. void ScriptObject_Construct(asCObjectType *objType, asCScriptObject *self)
  135. {
  136. new(self) asCScriptObject(objType);
  137. }
  138. asCScriptObject::asCScriptObject(asCObjectType *ot)
  139. {
  140. refCount.set(1);
  141. objType = ot;
  142. objType->AddRef();
  143. isDestructCalled = false;
  144. // Notify the garbage collector of this object
  145. if( objType->flags & asOBJ_GC )
  146. objType->engine->gc.AddScriptObjectToGC(this, objType);
  147. // Construct all properties
  148. asCScriptEngine *engine = objType->engine;
  149. for( asUINT n = 0; n < objType->properties.GetLength(); n++ )
  150. {
  151. asCObjectProperty *prop = objType->properties[n];
  152. if( prop->type.IsObject() )
  153. {
  154. asPWORD *ptr = (asPWORD*)(((char*)this) + prop->byteOffset);
  155. if( prop->type.IsObjectHandle() )
  156. *ptr = 0;
  157. else
  158. {
  159. // Allocate the object and call it's constructor
  160. *ptr = (asPWORD)AllocateObject(prop->type.GetObjectType(), engine);
  161. }
  162. }
  163. }
  164. // Urho3D: initialize userdata
  165. userData = 0;
  166. }
  167. void asCScriptObject::Destruct()
  168. {
  169. // Call the destructor, which will also call the GCObject's destructor
  170. this->~asCScriptObject();
  171. // Free the memory
  172. userFree(this);
  173. }
  174. asCScriptObject::~asCScriptObject()
  175. {
  176. objType->Release();
  177. // The engine pointer should be available from the objectType
  178. asCScriptEngine *engine = objType->engine;
  179. // Destroy all properties
  180. for( asUINT n = 0; n < objType->properties.GetLength(); n++ )
  181. {
  182. asCObjectProperty *prop = objType->properties[n];
  183. if( prop->type.IsObject() )
  184. {
  185. // Destroy the object
  186. void **ptr = (void**)(((char*)this) + prop->byteOffset);
  187. if( *ptr )
  188. {
  189. FreeObject(*ptr, prop->type.GetObjectType(), engine);
  190. *(asDWORD*)ptr = 0;
  191. }
  192. }
  193. }
  194. }
  195. asIScriptEngine *asCScriptObject::GetEngine() const
  196. {
  197. return objType->engine;
  198. }
  199. int asCScriptObject::AddRef() const
  200. {
  201. // Increase counter and clear flag set by GC
  202. gcFlag = false;
  203. return refCount.atomicInc();
  204. }
  205. int asCScriptObject::Release() const
  206. {
  207. // Clear the flag set by the GC
  208. gcFlag = false;
  209. // Call the script destructor behaviour if the reference counter is 1.
  210. if( refCount.get() == 1 && !isDestructCalled )
  211. {
  212. // This cast is OK since we are the last reference
  213. const_cast<asCScriptObject*>(this)->CallDestructor();
  214. }
  215. // Now do the actual releasing
  216. int r = refCount.atomicDec();
  217. if( r == 0 )
  218. {
  219. // This cast is OK since we are the last reference
  220. const_cast<asCScriptObject*>(this)->Destruct();
  221. return 0;
  222. }
  223. return r;
  224. }
  225. void asCScriptObject::CallDestructor()
  226. {
  227. // Make sure the destructor is called once only, even if the
  228. // reference count is increased and then decreased again
  229. isDestructCalled = true;
  230. asIScriptContext *ctx = 0;
  231. // Call the destructor for this class and all the super classes
  232. asCObjectType *ot = objType;
  233. while( ot )
  234. {
  235. int funcIndex = ot->beh.destruct;
  236. if( funcIndex )
  237. {
  238. if( ctx == 0 )
  239. {
  240. // Setup a context for calling the default constructor
  241. asCScriptEngine *engine = objType->engine;
  242. int r = engine->CreateContext(&ctx, true);
  243. if( r < 0 ) return;
  244. }
  245. int r = ctx->Prepare(funcIndex);
  246. if( r >= 0 )
  247. {
  248. ctx->SetObject(this);
  249. ctx->Execute();
  250. // There's not much to do if the execution doesn't
  251. // finish, so we just ignore the result
  252. }
  253. }
  254. ot = ot->derivedFrom;
  255. }
  256. if( ctx )
  257. {
  258. ctx->Release();
  259. }
  260. }
  261. asIObjectType *asCScriptObject::GetObjectType() const
  262. {
  263. return objType;
  264. }
  265. int asCScriptObject::GetRefCount()
  266. {
  267. return refCount.get();
  268. }
  269. void asCScriptObject::SetFlag()
  270. {
  271. gcFlag = true;
  272. }
  273. bool asCScriptObject::GetFlag()
  274. {
  275. return gcFlag;
  276. }
  277. // interface
  278. int asCScriptObject::GetTypeId() const
  279. {
  280. asCDataType dt = asCDataType::CreateObject(objType, false);
  281. return objType->engine->GetTypeIdFromDataType(dt);
  282. }
  283. // interface
  284. void *asCScriptObject::SetUserData(void *data)
  285. {
  286. void *oldData = userData;
  287. userData = data;
  288. return oldData;
  289. }
  290. // interface
  291. void *asCScriptObject::GetUserData() const
  292. {
  293. return userData;
  294. }
  295. asUINT asCScriptObject::GetPropertyCount() const
  296. {
  297. return asUINT(objType->properties.GetLength());
  298. }
  299. int asCScriptObject::GetPropertyTypeId(asUINT prop) const
  300. {
  301. if( prop >= objType->properties.GetLength() )
  302. return asINVALID_ARG;
  303. return objType->engine->GetTypeIdFromDataType(objType->properties[prop]->type);
  304. }
  305. const char *asCScriptObject::GetPropertyName(asUINT prop) const
  306. {
  307. if( prop >= objType->properties.GetLength() )
  308. return 0;
  309. return objType->properties[prop]->name.AddressOf();
  310. }
  311. void *asCScriptObject::GetAddressOfProperty(asUINT prop)
  312. {
  313. if( prop >= objType->properties.GetLength() )
  314. return 0;
  315. // Objects are stored by reference, so this must be dereferenced
  316. asCDataType *dt = &objType->properties[prop]->type;
  317. if( dt->IsObject() && !dt->IsObjectHandle() )
  318. return *(void**)(((char*)this) + objType->properties[prop]->byteOffset);
  319. return (void*)(((char*)this) + objType->properties[prop]->byteOffset);
  320. }
  321. void asCScriptObject::EnumReferences(asIScriptEngine *engine)
  322. {
  323. // We'll notify the GC of all object handles that we're holding
  324. for( asUINT n = 0; n < objType->properties.GetLength(); n++ )
  325. {
  326. asCObjectProperty *prop = objType->properties[n];
  327. if( prop->type.IsObject() )
  328. {
  329. void *ptr = *(void**)(((char*)this) + prop->byteOffset);
  330. if( ptr )
  331. ((asCScriptEngine*)engine)->GCEnumCallback(ptr);
  332. }
  333. }
  334. }
  335. void asCScriptObject::ReleaseAllHandles(asIScriptEngine *engine)
  336. {
  337. for( asUINT n = 0; n < objType->properties.GetLength(); n++ )
  338. {
  339. asCObjectProperty *prop = objType->properties[n];
  340. if( prop->type.IsObject() && prop->type.IsObjectHandle() )
  341. {
  342. void **ptr = (void**)(((char*)this) + prop->byteOffset);
  343. if( *ptr )
  344. {
  345. asASSERT( (prop->type.GetObjectType()->flags & asOBJ_NOCOUNT) || prop->type.GetBehaviour()->release );
  346. if( prop->type.GetBehaviour()->release )
  347. ((asCScriptEngine*)engine)->CallObjectMethod(*ptr, prop->type.GetBehaviour()->release);
  348. *ptr = 0;
  349. }
  350. }
  351. }
  352. }
  353. void ScriptObject_Assignment_Generic(asIScriptGeneric *gen)
  354. {
  355. asCScriptObject *other = *(asCScriptObject**)gen->GetAddressOfArg(0);
  356. asCScriptObject *self = (asCScriptObject*)gen->GetObject();
  357. *self = *other;
  358. *(asCScriptObject**)gen->GetAddressOfReturnLocation() = self;
  359. }
  360. asCScriptObject &ScriptObject_Assignment(asCScriptObject *other, asCScriptObject *self)
  361. {
  362. return (*self = *other);
  363. }
  364. asCScriptObject &asCScriptObject::operator=(const asCScriptObject &other)
  365. {
  366. if( &other != this )
  367. {
  368. asASSERT( other.objType->DerivesFrom(objType) );
  369. asCScriptEngine *engine = objType->engine;
  370. // Copy all properties
  371. for( asUINT n = 0; n < objType->properties.GetLength(); n++ )
  372. {
  373. asCObjectProperty *prop = objType->properties[n];
  374. if( prop->type.IsObject() )
  375. {
  376. void **dst = (void**)(((char*)this) + prop->byteOffset);
  377. void **src = (void**)(((char*)&other) + prop->byteOffset);
  378. if( !prop->type.IsObjectHandle() )
  379. CopyObject(*src, *dst, prop->type.GetObjectType(), engine);
  380. else
  381. CopyHandle((asPWORD*)src, (asPWORD*)dst, prop->type.GetObjectType(), engine);
  382. }
  383. else
  384. {
  385. void *dst = ((char*)this) + prop->byteOffset;
  386. void *src = ((char*)&other) + prop->byteOffset;
  387. memcpy(dst, src, prop->type.GetSizeInMemoryBytes());
  388. }
  389. }
  390. }
  391. return *this;
  392. }
  393. int asCScriptObject::CopyFrom(asIScriptObject *other)
  394. {
  395. if( other == 0 ) return asINVALID_ARG;
  396. if( GetTypeId() != other->GetTypeId() )
  397. return asINVALID_TYPE;
  398. *this = *(asCScriptObject*)other;
  399. return 0;
  400. }
  401. void *asCScriptObject::AllocateObject(asCObjectType *objType, asCScriptEngine *engine)
  402. {
  403. void *ptr = 0;
  404. if( objType->flags & asOBJ_SCRIPT_OBJECT )
  405. {
  406. ptr = ScriptObjectFactory(objType, engine);
  407. }
  408. else if( objType->flags & asOBJ_TEMPLATE )
  409. {
  410. // Templates store the original factory that takes the object
  411. // type as a hidden parameter in the construct behaviour
  412. ptr = engine->CallGlobalFunctionRetPtr(objType->beh.construct, objType);
  413. }
  414. else if( objType->flags & asOBJ_REF )
  415. {
  416. ptr = engine->CallGlobalFunctionRetPtr(objType->beh.factory);
  417. }
  418. else
  419. {
  420. ptr = engine->CallAlloc(objType);
  421. int funcIndex = objType->beh.construct;
  422. if( funcIndex )
  423. engine->CallObjectMethod(ptr, funcIndex);
  424. }
  425. return ptr;
  426. }
  427. void asCScriptObject::FreeObject(void *ptr, asCObjectType *objType, asCScriptEngine *engine)
  428. {
  429. if( objType->flags & asOBJ_REF )
  430. {
  431. asASSERT( (objType->flags & asOBJ_NOCOUNT) || objType->beh.release );
  432. if( objType->beh.release )
  433. engine->CallObjectMethod(ptr, objType->beh.release);
  434. }
  435. else
  436. {
  437. if( objType->beh.destruct )
  438. engine->CallObjectMethod(ptr, objType->beh.destruct);
  439. engine->CallFree(ptr);
  440. }
  441. }
  442. void asCScriptObject::CopyObject(void *src, void *dst, asCObjectType *objType, asCScriptEngine *engine)
  443. {
  444. // TODO: If the object doesn't have the copy behaviour, and it is not a
  445. // POD object then the copy must not be performed
  446. int funcIndex = objType->beh.copy;
  447. if( funcIndex )
  448. engine->CallObjectMethod(dst, src, funcIndex);
  449. else
  450. memcpy(dst, src, objType->size);
  451. }
  452. void asCScriptObject::CopyHandle(asPWORD *src, asPWORD *dst, asCObjectType *objType, asCScriptEngine *engine)
  453. {
  454. if( *dst )
  455. engine->CallObjectMethod(*(void**)dst, objType->beh.release);
  456. *dst = *src;
  457. if( *dst )
  458. engine->CallObjectMethod(*(void**)dst, objType->beh.addref);
  459. }
  460. END_AS_NAMESPACE