ScriptFile.cpp 34 KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000100110021003100410051006100710081009101010111012101310141015101610171018101910201021102210231024102510261027102810291030103110321033103410351036103710381039104010411042104310441045104610471048104910501051105210531054105510561057105810591060106110621063106410651066106710681069107010711072107310741075107610771078107910801081108210831084108510861087108810891090109110921093
  1. //
  2. // Copyright (c) 2008-2020 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/Script.h"
  24. #include "../AngelScript/ScriptFile.h"
  25. #include "../AngelScript/ScriptInstance.h"
  26. #include "../Core/Context.h"
  27. #include "../Core/CoreEvents.h"
  28. #include "../Core/Profiler.h"
  29. #include "../IO/FileSystem.h"
  30. #include "../IO/Log.h"
  31. #include "../IO/MemoryBuffer.h"
  32. #include "../Resource/ResourceCache.h"
  33. #include <AngelScript/angelscript.h>
  34. #include "../AngelScript/wrapmacros.h"
  35. #include "../DebugNew.h"
  36. namespace Urho3D
  37. {
  38. /// Helper class for saving AngelScript bytecode.
  39. class ByteCodeSerializer : public asIBinaryStream
  40. {
  41. public:
  42. /// Construct.
  43. explicit ByteCodeSerializer(Serializer& dest) :
  44. dest_(dest)
  45. {
  46. }
  47. /// Read from stream (no-op).
  48. int Read(void* ptr, asUINT size) override
  49. {
  50. // No-op, can not read from a Serializer
  51. return 0;
  52. }
  53. /// Write to stream.
  54. int Write(const void* ptr, asUINT size) override
  55. {
  56. return dest_.Write(ptr, size);
  57. }
  58. private:
  59. /// Destination stream.
  60. Serializer& dest_;
  61. };
  62. /// Helper class for loading AngelScript bytecode.
  63. class ByteCodeDeserializer : public asIBinaryStream
  64. {
  65. public:
  66. /// Construct.
  67. explicit ByteCodeDeserializer(MemoryBuffer& source) :
  68. source_(source)
  69. {
  70. }
  71. /// Read from stream.
  72. int Read(void* ptr, asUINT size) override
  73. {
  74. return source_.Read(ptr, size);
  75. }
  76. /// Write to stream (no-op).
  77. int Write(const void* ptr, asUINT size) override
  78. {
  79. return 0;
  80. }
  81. private:
  82. /// Source stream.
  83. MemoryBuffer& source_;
  84. };
  85. ScriptFile::ScriptFile(Context* context) :
  86. Resource(context),
  87. script_(GetSubsystem<Script>())
  88. {
  89. }
  90. ScriptFile::~ScriptFile()
  91. {
  92. ReleaseModule();
  93. }
  94. void ScriptFile::RegisterObject(Context* context)
  95. {
  96. context->RegisterFactory<ScriptFile>();
  97. }
  98. bool ScriptFile::BeginLoad(Deserializer& source)
  99. {
  100. ReleaseModule();
  101. loadByteCode_.Reset();
  102. asIScriptEngine* engine = script_->GetScriptEngine();
  103. {
  104. MutexLock lock(script_->GetModuleMutex());
  105. // Create the module. Discard previous module if there was one
  106. scriptModule_ = engine->GetModule(GetName().CString(), asGM_ALWAYS_CREATE);
  107. if (!scriptModule_)
  108. {
  109. URHO3D_LOGERROR("Failed to create script module " + GetName());
  110. return false;
  111. }
  112. }
  113. // Check if this file is precompiled bytecode
  114. if (source.ReadFileID() == "ASBC")
  115. {
  116. // Perform actual parsing in EndLoad(); read data now
  117. loadByteCodeSize_ = source.GetSize() - source.GetPosition();
  118. loadByteCode_ = new unsigned char[loadByteCodeSize_];
  119. source.Read(loadByteCode_.Get(), loadByteCodeSize_);
  120. return true;
  121. }
  122. else
  123. source.Seek(0);
  124. // Not bytecode: add the initial section and check for includes.
  125. // Perform actual building during EndLoad(), as AngelScript can not multithread module compilation,
  126. // and static initializers may access arbitrary engine functionality which may not be thread-safe
  127. return AddScriptSection(engine, source);
  128. }
  129. bool ScriptFile::EndLoad()
  130. {
  131. bool success = false;
  132. // Load from bytecode if available, else compile
  133. if (loadByteCode_)
  134. {
  135. MemoryBuffer buffer(loadByteCode_.Get(), loadByteCodeSize_);
  136. ByteCodeDeserializer deserializer = ByteCodeDeserializer(buffer);
  137. if (scriptModule_->LoadByteCode(&deserializer) >= 0)
  138. {
  139. URHO3D_LOGINFO("Loaded script module " + GetName() + " from bytecode");
  140. success = true;
  141. }
  142. }
  143. else
  144. {
  145. int result = scriptModule_->Build();
  146. if (result >= 0)
  147. {
  148. URHO3D_LOGINFO("Compiled script module " + GetName());
  149. success = true;
  150. }
  151. else
  152. URHO3D_LOGERROR("Failed to compile script module " + GetName());
  153. }
  154. if (success)
  155. {
  156. compiled_ = true;
  157. // Map script module to script resource with userdata
  158. scriptModule_->SetUserData(this);
  159. }
  160. loadByteCode_.Reset();
  161. return success;
  162. }
  163. void ScriptFile::AddEventHandler(StringHash eventType, const String& handlerName)
  164. {
  165. if (!compiled_)
  166. return;
  167. AddEventHandlerInternal(nullptr, eventType, handlerName);
  168. }
  169. void ScriptFile::AddEventHandler(Object* sender, StringHash eventType, const String& handlerName)
  170. {
  171. if (!compiled_)
  172. return;
  173. if (!sender)
  174. {
  175. URHO3D_LOGERROR("Null event sender for event " + String(eventType) + ", handler " + handlerName);
  176. return;
  177. }
  178. AddEventHandlerInternal(sender, eventType, handlerName);
  179. }
  180. void ScriptFile::RemoveEventHandler(StringHash eventType)
  181. {
  182. auto* receiver = static_cast<asIScriptObject*>(asGetActiveContext()->GetThisPointer());
  183. HashMap<asIScriptObject*, SharedPtr<ScriptEventInvoker> >::Iterator i = eventInvokers_.Find(receiver);
  184. if (i != eventInvokers_.End())
  185. {
  186. i->second_->UnsubscribeFromEvent(eventType);
  187. // If no longer have any subscribed events, remove the event invoker object
  188. if (!i->second_->HasEventHandlers())
  189. eventInvokers_.Erase(i);
  190. }
  191. }
  192. void ScriptFile::RemoveEventHandler(Object* sender, StringHash eventType)
  193. {
  194. auto* receiver = static_cast<asIScriptObject*>(asGetActiveContext()->GetThisPointer());
  195. HashMap<asIScriptObject*, SharedPtr<ScriptEventInvoker> >::Iterator i = eventInvokers_.Find(receiver);
  196. if (i != eventInvokers_.End())
  197. {
  198. i->second_->UnsubscribeFromEvent(sender, eventType);
  199. if (!i->second_->HasEventHandlers())
  200. eventInvokers_.Erase(i);
  201. }
  202. }
  203. void ScriptFile::RemoveEventHandlers(Object* sender)
  204. {
  205. auto* receiver = static_cast<asIScriptObject*>(asGetActiveContext()->GetThisPointer());
  206. HashMap<asIScriptObject*, SharedPtr<ScriptEventInvoker> >::Iterator i = eventInvokers_.Find(receiver);
  207. if (i != eventInvokers_.End())
  208. {
  209. i->second_->UnsubscribeFromEvents(sender);
  210. if (!i->second_->HasEventHandlers())
  211. eventInvokers_.Erase(i);
  212. }
  213. }
  214. void ScriptFile::RemoveEventHandlers()
  215. {
  216. auto* receiver = static_cast<asIScriptObject*>(asGetActiveContext()->GetThisPointer());
  217. HashMap<asIScriptObject*, SharedPtr<ScriptEventInvoker> >::Iterator i = eventInvokers_.Find(receiver);
  218. if (i != eventInvokers_.End())
  219. {
  220. i->second_->UnsubscribeFromAllEvents();
  221. if (!i->second_->HasEventHandlers())
  222. eventInvokers_.Erase(i);
  223. }
  224. }
  225. void ScriptFile::RemoveEventHandlersExcept(const PODVector<StringHash>& exceptions)
  226. {
  227. auto* receiver = static_cast<asIScriptObject*>(asGetActiveContext()->GetThisPointer());
  228. HashMap<asIScriptObject*, SharedPtr<ScriptEventInvoker> >::Iterator i = eventInvokers_.Find(receiver);
  229. if (i != eventInvokers_.End())
  230. {
  231. i->second_->UnsubscribeFromAllEventsExcept(exceptions, true);
  232. if (!i->second_->HasEventHandlers())
  233. eventInvokers_.Erase(i);
  234. }
  235. }
  236. bool ScriptFile::HasEventHandler(StringHash eventType) const
  237. {
  238. auto* receiver = static_cast<asIScriptObject*>(asGetActiveContext()->GetThisPointer());
  239. HashMap<asIScriptObject*, SharedPtr<ScriptEventInvoker> >::ConstIterator i = eventInvokers_.Find(receiver);
  240. if (i != eventInvokers_.End())
  241. return i->second_->HasSubscribedToEvent(eventType);
  242. else
  243. return false;
  244. }
  245. bool ScriptFile::HasEventHandler(Object* sender, StringHash eventType) const
  246. {
  247. auto* receiver = static_cast<asIScriptObject*>(asGetActiveContext()->GetThisPointer());
  248. HashMap<asIScriptObject*, SharedPtr<ScriptEventInvoker> >::ConstIterator i = eventInvokers_.Find(receiver);
  249. if (i != eventInvokers_.End())
  250. return i->second_->HasSubscribedToEvent(sender, eventType);
  251. else
  252. return false;
  253. }
  254. bool ScriptFile::Execute(const String& declaration, const VariantVector& parameters, Variant* functionReturn, bool unprepare)
  255. {
  256. asIScriptFunction* function = GetFunction(declaration);
  257. if (!function)
  258. {
  259. URHO3D_LOGERROR("Function " + declaration + " not found in " + GetName());
  260. return false;
  261. }
  262. return Execute(function, parameters, functionReturn, unprepare);
  263. }
  264. bool ScriptFile::Execute(asIScriptFunction* function, const VariantVector& parameters, Variant* functionReturn, bool unprepare)
  265. {
  266. URHO3D_PROFILE(ExecuteFunction);
  267. if (!compiled_ || !function)
  268. return false;
  269. // It is possible that executing the function causes us to unload. Therefore do not rely on member variables
  270. // However, we are not prepared for the whole script system getting destroyed during execution (should never happen)
  271. Script* scriptSystem = script_;
  272. asIScriptContext* context = scriptSystem->GetScriptFileContext();
  273. if (context->Prepare(function) < 0)
  274. return false;
  275. SetParameters(context, function, parameters);
  276. scriptSystem->IncScriptNestingLevel();
  277. bool success = (context->Execute() == asEXECUTION_FINISHED);
  278. if (success && (functionReturn != nullptr))
  279. {
  280. const int typeId = function->GetReturnTypeId();
  281. asIScriptEngine* engine = script_->GetScriptEngine();
  282. asITypeInfo* typeInfo = engine->GetTypeInfoById(typeId);
  283. // Built-in type
  284. if (typeInfo == nullptr)
  285. {
  286. switch (typeId)
  287. {
  288. case asTYPEID_VOID:
  289. *functionReturn = Variant::EMPTY;
  290. break;
  291. case asTYPEID_BOOL:
  292. *functionReturn = Variant(context->GetReturnByte() > 0);
  293. break;
  294. case asTYPEID_INT8:
  295. case asTYPEID_UINT8:
  296. case asTYPEID_INT16:
  297. case asTYPEID_UINT16:
  298. case asTYPEID_INT32:
  299. case asTYPEID_UINT32:
  300. *functionReturn = Variant(static_cast<int>(context->GetReturnDWord()));
  301. break;
  302. case asTYPEID_INT64:
  303. case asTYPEID_UINT64:
  304. *functionReturn = Variant(static_cast<long long>(context->GetReturnQWord()));
  305. break;
  306. case asTYPEID_FLOAT:
  307. *functionReturn = Variant(context->GetReturnFloat());
  308. break;
  309. case asTYPEID_DOUBLE:
  310. *functionReturn = Variant(context->GetReturnDouble());
  311. break;
  312. }
  313. }
  314. else if (typeInfo->GetFlags() & asOBJ_REF)
  315. {
  316. *functionReturn = Variant(static_cast<RefCounted*>(context->GetReturnObject()));
  317. }
  318. else if (typeInfo->GetFlags() & asOBJ_VALUE)
  319. {
  320. void* returnedObject = context->GetReturnObject();
  321. const VariantType variantType = Variant::GetTypeFromName(typeInfo->GetName());
  322. switch (variantType)
  323. {
  324. case VAR_STRING:
  325. *functionReturn = *static_cast<String*>(returnedObject);
  326. break;
  327. case VAR_VECTOR2:
  328. *functionReturn = *static_cast<Vector2*>(returnedObject);
  329. break;
  330. case VAR_VECTOR3:
  331. *functionReturn = *static_cast<Vector3*>(returnedObject);
  332. break;
  333. case VAR_VECTOR4:
  334. *functionReturn = *static_cast<Vector4*>(returnedObject);
  335. break;
  336. case VAR_QUATERNION:
  337. *functionReturn = *static_cast<Quaternion*>(returnedObject);
  338. break;
  339. case VAR_COLOR:
  340. *functionReturn = *static_cast<Color*>(returnedObject);
  341. break;
  342. case VAR_INTRECT:
  343. *functionReturn = *static_cast<IntRect*>(returnedObject);
  344. break;
  345. case VAR_INTVECTOR2:
  346. *functionReturn = *static_cast<IntVector2*>(returnedObject);
  347. break;
  348. case VAR_MATRIX3:
  349. *functionReturn = *static_cast<Matrix3*>(returnedObject);
  350. break;
  351. case VAR_MATRIX3X4:
  352. *functionReturn = *static_cast<Matrix3x4*>(returnedObject);
  353. break;
  354. case VAR_MATRIX4:
  355. *functionReturn = *static_cast<Matrix4*>(returnedObject);
  356. break;
  357. case VAR_RECT:
  358. *functionReturn = *static_cast<Rect*>(returnedObject);
  359. break;
  360. case VAR_INTVECTOR3:
  361. *functionReturn = *static_cast<IntVector3*>(returnedObject);
  362. break;
  363. default:
  364. URHO3D_LOGERRORF("Return type (%c) is not supported", typeInfo->GetName());
  365. break;
  366. }
  367. }
  368. else
  369. {
  370. URHO3D_LOGERRORF("Return type (%c)is not supported", typeInfo->GetName());
  371. }
  372. }
  373. if (unprepare)
  374. context->Unprepare();
  375. scriptSystem->DecScriptNestingLevel();
  376. return success;
  377. }
  378. bool ScriptFile::Execute(asIScriptObject* object, const String& declaration, const VariantVector& parameters, Variant* functionReturn,
  379. bool unprepare)
  380. {
  381. if (!object)
  382. return false;
  383. asIScriptFunction* method = GetMethod(object, declaration);
  384. if (!method)
  385. {
  386. URHO3D_LOGERROR("Method " + declaration + " not found in class " + String(object->GetObjectType()->GetName()));
  387. return false;
  388. }
  389. return Execute(object, method, parameters, functionReturn, unprepare);
  390. }
  391. bool ScriptFile::Execute(asIScriptObject* object, asIScriptFunction* method, const VariantVector& parameters, Variant* functionReturn,
  392. bool unprepare)
  393. {
  394. URHO3D_PROFILE(ExecuteMethod);
  395. if (!compiled_ || !object || !method)
  396. return false;
  397. // It is possible that executing the method causes us to unload. Therefore do not rely on member variables
  398. // However, we are not prepared for the whole script system getting destroyed during execution (should never happen)
  399. Script* scriptSystem = script_;
  400. asIScriptContext* context = scriptSystem->GetScriptFileContext();
  401. if (context->Prepare(method) < 0)
  402. return false;
  403. context->SetObject(object);
  404. SetParameters(context, method, parameters);
  405. scriptSystem->IncScriptNestingLevel();
  406. bool success = context->Execute() >= 0;
  407. if (unprepare)
  408. context->Unprepare();
  409. scriptSystem->DecScriptNestingLevel();
  410. return success;
  411. }
  412. void ScriptFile::DelayedExecute(float delay, bool repeat, const String& declaration, const VariantVector& parameters)
  413. {
  414. DelayedCall call;
  415. call.period_ = call.delay_ = Max(delay, 0.0f);
  416. call.repeat_ = repeat;
  417. call.declaration_ = declaration;
  418. call.parameters_ = parameters;
  419. delayedCalls_.Push(call);
  420. // Make sure we are registered to the application update event, because delayed calls are executed there
  421. if (!subscribed_)
  422. {
  423. SubscribeToEvent(E_UPDATE, URHO3D_HANDLER(ScriptFile, HandleUpdate));
  424. subscribed_ = true;
  425. }
  426. }
  427. void ScriptFile::ClearDelayedExecute(const String& declaration)
  428. {
  429. if (declaration.Empty())
  430. delayedCalls_.Clear();
  431. else
  432. {
  433. for (Vector<DelayedCall>::Iterator i = delayedCalls_.Begin(); i != delayedCalls_.End();)
  434. {
  435. if (declaration == i->declaration_)
  436. i = delayedCalls_.Erase(i);
  437. else
  438. ++i;
  439. }
  440. }
  441. }
  442. asIScriptObject* ScriptFile::CreateObject(const String& className, bool useInterface)
  443. {
  444. URHO3D_PROFILE(CreateObject);
  445. if (!compiled_)
  446. return nullptr;
  447. asIScriptContext* context = script_->GetScriptFileContext();
  448. asITypeInfo* type = nullptr;
  449. if (useInterface)
  450. {
  451. asITypeInfo* interfaceType = scriptModule_->GetTypeInfoByDecl(className.CString());
  452. if (!interfaceType)
  453. return nullptr;
  454. for (unsigned i = 0; i < scriptModule_->GetObjectTypeCount(); ++i)
  455. {
  456. asITypeInfo* t = scriptModule_->GetObjectTypeByIndex(i);
  457. if (t->Implements(interfaceType))
  458. {
  459. type = t;
  460. break;
  461. }
  462. }
  463. }
  464. else
  465. {
  466. type = scriptModule_->GetTypeInfoByDecl(className.CString());
  467. }
  468. if (!type)
  469. return nullptr;
  470. // Ensure that the type implements the "ScriptObject" interface, so it can be returned to script properly
  471. bool found;
  472. HashMap<asITypeInfo*, bool>::ConstIterator i = validClasses_.Find(type);
  473. if (i != validClasses_.End())
  474. found = i->second_;
  475. else
  476. {
  477. asITypeInfo* scriptObjectType = scriptModule_->GetTypeInfoByDecl("ScriptObject");
  478. found = type->Implements(scriptObjectType);
  479. validClasses_[type] = found;
  480. }
  481. if (!found)
  482. {
  483. URHO3D_LOGERRORF("Script class %s does not implement the ScriptObject interface", type->GetName());
  484. return nullptr;
  485. }
  486. // Get the factory function id from the object type
  487. String factoryName = String(type->GetName()) + "@ " + type->GetName() + "()";
  488. asIScriptFunction* factory = type->GetFactoryByDecl(factoryName.CString());
  489. if (!factory || context->Prepare(factory) < 0 || context->Execute() < 0)
  490. return nullptr;
  491. void* objAddress = context->GetAddressOfReturnValue();
  492. if (!objAddress)
  493. return nullptr;
  494. asIScriptObject* obj = *(static_cast<asIScriptObject**>(objAddress));
  495. if (obj)
  496. obj->AddRef();
  497. return obj;
  498. }
  499. bool ScriptFile::SaveByteCode(Serializer& dest)
  500. {
  501. if (compiled_)
  502. {
  503. dest.WriteFileID("ASBC");
  504. ByteCodeSerializer serializer = ByteCodeSerializer(dest);
  505. return scriptModule_->SaveByteCode(&serializer, true) >= 0;
  506. }
  507. else
  508. return false;
  509. }
  510. asIScriptFunction* ScriptFile::GetFunction(const String& declaration)
  511. {
  512. if (!compiled_)
  513. return nullptr;
  514. String trimDecl = declaration.Trimmed();
  515. // If not a full trimDecl, assume void with no parameters
  516. if (trimDecl.Find('(') == String::NPOS)
  517. trimDecl = "void " + trimDecl + "()";
  518. HashMap<String, asIScriptFunction*>::ConstIterator i = functions_.Find(trimDecl);
  519. if (i != functions_.End())
  520. return i->second_;
  521. asIScriptFunction* function = scriptModule_->GetFunctionByDecl(trimDecl.CString());
  522. functions_[trimDecl] = function;
  523. return function;
  524. }
  525. asIScriptFunction* ScriptFile::GetMethod(asIScriptObject* object, const String& declaration)
  526. {
  527. if (!compiled_ || !object)
  528. return nullptr;
  529. String trimDecl = declaration.Trimmed();
  530. // If not a full trimDecl, assume void with no parameters
  531. if (trimDecl.Find('(') == String::NPOS)
  532. trimDecl = "void " + trimDecl + "()";
  533. asITypeInfo* type = object->GetObjectType();
  534. if (!type)
  535. return nullptr;
  536. HashMap<asITypeInfo*, HashMap<String, asIScriptFunction*> >::ConstIterator i = methods_.Find(type);
  537. if (i != methods_.End())
  538. {
  539. HashMap<String, asIScriptFunction*>::ConstIterator j = i->second_.Find(trimDecl);
  540. if (j != i->second_.End())
  541. return j->second_;
  542. }
  543. asIScriptFunction* function = type->GetMethodByDecl(trimDecl.CString());
  544. methods_[type][trimDecl] = function;
  545. return function;
  546. }
  547. void ScriptFile::CleanupEventInvoker(asIScriptObject* object)
  548. {
  549. eventInvokers_.Erase(object);
  550. }
  551. void ScriptFile::AddEventHandlerInternal(Object* sender, StringHash eventType, const String& handlerName)
  552. {
  553. String declaration = "void " + handlerName + "(StringHash, VariantMap&)";
  554. asIScriptFunction* function = nullptr;
  555. auto* receiver = static_cast<asIScriptObject*>(asGetActiveContext()->GetThisPointer());
  556. if (receiver)
  557. function = GetMethod(receiver, declaration);
  558. else
  559. function = GetFunction(declaration);
  560. if (!function)
  561. {
  562. // Retry with parameterless signature
  563. if (receiver)
  564. function = GetMethod(receiver, handlerName);
  565. else
  566. function = GetFunction(handlerName);
  567. if (!function)
  568. {
  569. URHO3D_LOGERROR("Event handler function " + handlerName + " not found in " + GetName());
  570. return;
  571. }
  572. }
  573. HashMap<asIScriptObject*, SharedPtr<ScriptEventInvoker> >::Iterator i = eventInvokers_.Find(receiver);
  574. // Remove previous handler in case an object pointer gets reused
  575. if (i != eventInvokers_.End() && !i->second_->IsObjectAlive())
  576. {
  577. eventInvokers_.Erase(i);
  578. i = eventInvokers_.End();
  579. }
  580. if (i == eventInvokers_.End())
  581. i = eventInvokers_.Insert(MakePair(receiver, SharedPtr<ScriptEventInvoker>(new ScriptEventInvoker(this, receiver))));
  582. if (!sender)
  583. {
  584. i->second_->SubscribeToEvent(eventType, new EventHandlerImpl<ScriptEventInvoker>
  585. (i->second_, &ScriptEventInvoker::HandleScriptEvent, (void*)function));
  586. }
  587. else
  588. {
  589. i->second_->SubscribeToEvent(sender, eventType, new EventHandlerImpl<ScriptEventInvoker>
  590. (i->second_, &ScriptEventInvoker::HandleScriptEvent, (void*)function));
  591. }
  592. }
  593. bool ScriptFile::AddScriptSection(asIScriptEngine* engine, Deserializer& source)
  594. {
  595. auto* cache = GetSubsystem<ResourceCache>();
  596. unsigned dataSize = source.GetSize();
  597. SharedArrayPtr<char> buffer(new char[dataSize]);
  598. source.Read((void*)buffer.Get(), dataSize);
  599. // Pre-parse for includes
  600. // Adapted from Angelscript's scriptbuilder add-on
  601. Vector<String> includeFiles;
  602. unsigned pos = 0;
  603. while (pos < dataSize)
  604. {
  605. unsigned len;
  606. asETokenClass t = engine->ParseToken(&buffer[pos], dataSize - pos, &len);
  607. if (t == asTC_COMMENT || t == asTC_WHITESPACE)
  608. {
  609. pos += len;
  610. continue;
  611. }
  612. // Is this a preprocessor directive?
  613. if (buffer[pos] == '#')
  614. {
  615. int start = pos++;
  616. asETokenClass t = engine->ParseToken(&buffer[pos], dataSize - pos, &len);
  617. if (t == asTC_IDENTIFIER)
  618. {
  619. String token(&buffer[pos], (unsigned)len);
  620. if (token == "include")
  621. {
  622. pos += len;
  623. t = engine->ParseToken(&buffer[pos], dataSize - pos, &len);
  624. if (t == asTC_WHITESPACE)
  625. {
  626. pos += len;
  627. t = engine->ParseToken(&buffer[pos], dataSize - pos, &len);
  628. }
  629. if (t == asTC_VALUE && len > 2 && buffer[pos] == '"')
  630. {
  631. // Get the include file
  632. String includeFile(&buffer[pos + 1], (unsigned)(len - 2));
  633. pos += len;
  634. // If the file is not found as it is, add the path of current file but only if it is found there
  635. if (!cache->Exists(includeFile))
  636. {
  637. String prefixedIncludeFile = GetPath(GetName()) + includeFile;
  638. if (cache->Exists(prefixedIncludeFile))
  639. includeFile = prefixedIncludeFile;
  640. }
  641. String includeFileLower = includeFile.ToLower();
  642. // If not included yet, store it for later processing
  643. if (!includeFiles_.Contains(includeFileLower))
  644. {
  645. includeFiles_.Insert(includeFileLower);
  646. includeFiles.Push(includeFile);
  647. }
  648. // Overwrite the include directive with space characters to avoid compiler error
  649. memset(&buffer[start], ' ', pos - start);
  650. }
  651. }
  652. }
  653. }
  654. // Don't search includes within statement blocks or between tokens in statements
  655. else
  656. {
  657. unsigned len;
  658. // Skip until ; or { whichever comes first
  659. while (pos < dataSize && buffer[pos] != ';' && buffer[pos] != '{')
  660. {
  661. engine->ParseToken(&buffer[pos], dataSize - pos, &len);
  662. pos += len;
  663. }
  664. // Skip entire statement block
  665. if (pos < dataSize && buffer[pos] == '{')
  666. {
  667. ++pos;
  668. // Find the end of the statement block
  669. int level = 1;
  670. while (level > 0 && pos < dataSize)
  671. {
  672. asETokenClass t = engine->ParseToken(&buffer[pos], dataSize - pos, &len);
  673. if (t == asTC_KEYWORD)
  674. {
  675. if (buffer[pos] == '{')
  676. ++level;
  677. else if (buffer[pos] == '}')
  678. --level;
  679. }
  680. pos += len;
  681. }
  682. }
  683. else
  684. ++pos;
  685. }
  686. }
  687. // Process includes first
  688. for (unsigned i = 0; i < includeFiles.Size(); ++i)
  689. {
  690. cache->StoreResourceDependency(this, includeFiles[i]);
  691. SharedPtr<File> file = cache->GetFile(includeFiles[i]);
  692. if (file)
  693. {
  694. if (!AddScriptSection(engine, *file))
  695. return false;
  696. }
  697. else
  698. {
  699. URHO3D_LOGERROR("Could not process all the include directives in " + GetName() + ": missing " + includeFiles[i]);
  700. return false;
  701. }
  702. }
  703. // Then add this section
  704. if (scriptModule_->AddScriptSection(source.GetName().CString(), (const char*)buffer.Get(), dataSize) < 0)
  705. {
  706. URHO3D_LOGERROR("Failed to add script section " + source.GetName());
  707. return false;
  708. }
  709. SetMemoryUse(GetMemoryUse() + dataSize);
  710. return true;
  711. }
  712. void ScriptFile::SetParameters(asIScriptContext* context, asIScriptFunction* function, const VariantVector& parameters)
  713. {
  714. unsigned paramCount = function->GetParamCount();
  715. for (unsigned i = 0; i < parameters.Size() && i < paramCount; ++i)
  716. {
  717. int paramTypeId;
  718. function->GetParam(i, &paramTypeId);
  719. switch (paramTypeId)
  720. {
  721. case asTYPEID_BOOL:
  722. context->SetArgByte(i, (unsigned char)parameters[i].GetBool());
  723. break;
  724. case asTYPEID_INT8:
  725. case asTYPEID_UINT8:
  726. context->SetArgByte(i, (asBYTE)parameters[i].GetInt());
  727. break;
  728. case asTYPEID_INT16:
  729. case asTYPEID_UINT16:
  730. context->SetArgWord(i, (asWORD)parameters[i].GetInt());
  731. break;
  732. case asTYPEID_INT32:
  733. case asTYPEID_UINT32:
  734. context->SetArgDWord(i, (asDWORD)parameters[i].GetInt());
  735. break;
  736. case asTYPEID_INT64:
  737. case asTYPEID_UINT64:
  738. context->SetArgQWord(i, (asQWORD)parameters[i].GetInt64());
  739. break;
  740. case asTYPEID_FLOAT:
  741. context->SetArgFloat(i, parameters[i].GetFloat());
  742. break;
  743. default:
  744. if (paramTypeId & asTYPEID_APPOBJECT)
  745. {
  746. switch (parameters[i].GetType())
  747. {
  748. case VAR_VECTOR2:
  749. context->SetArgObject(i, (void*)&parameters[i].GetVector2());
  750. break;
  751. case VAR_VECTOR3:
  752. context->SetArgObject(i, (void*)&parameters[i].GetVector3());
  753. break;
  754. case VAR_VECTOR4:
  755. context->SetArgObject(i, (void*)&parameters[i].GetVector4());
  756. break;
  757. case VAR_QUATERNION:
  758. context->SetArgObject(i, (void*)&parameters[i].GetQuaternion());
  759. break;
  760. case VAR_STRING:
  761. context->SetArgObject(i, (void*)&parameters[i].GetString());
  762. break;
  763. case VAR_VARIANTMAP:
  764. context->SetArgObject(i, (void*)&parameters[i].GetVariantMap());
  765. break;
  766. case VAR_INTRECT:
  767. context->SetArgObject(i, (void*)&parameters[i].GetIntRect());
  768. break;
  769. case VAR_INTVECTOR2:
  770. context->SetArgObject(i, (void*)&parameters[i].GetIntVector2());
  771. break;
  772. case VAR_INTVECTOR3:
  773. context->SetArgObject(i, (void*)&parameters[i].GetIntVector3());
  774. break;
  775. case VAR_COLOR:
  776. context->SetArgObject(i, (void*)&parameters[i].GetColor());
  777. break;
  778. case VAR_MATRIX3:
  779. context->SetArgObject(i, (void*)&parameters[i].GetMatrix3());
  780. break;
  781. case VAR_MATRIX3X4:
  782. context->SetArgObject(i, (void*)&parameters[i].GetMatrix3x4());
  783. break;
  784. case VAR_MATRIX4:
  785. context->SetArgObject(i, (void*)&parameters[i].GetMatrix4());
  786. break;
  787. case VAR_RESOURCEREF:
  788. context->SetArgObject(i, (void*)&parameters[i].GetResourceRef());
  789. break;
  790. case VAR_RESOURCEREFLIST:
  791. context->SetArgObject(i, (void*)&parameters[i].GetResourceRefList());
  792. break;
  793. case VAR_VOIDPTR:
  794. context->SetArgObject(i, parameters[i].GetVoidPtr());
  795. break;
  796. case VAR_PTR:
  797. context->SetArgObject(i, (void*)parameters[i].GetPtr());
  798. break;
  799. default:
  800. break;
  801. }
  802. }
  803. break;
  804. }
  805. }
  806. }
  807. void ScriptFile::ReleaseModule()
  808. {
  809. if (scriptModule_)
  810. {
  811. // Clear search caches and event handlers
  812. includeFiles_.Clear();
  813. validClasses_.Clear();
  814. functions_.Clear();
  815. methods_.Clear();
  816. delayedCalls_.Clear();
  817. eventInvokers_.Clear();
  818. asIScriptEngine* engine = script_->GetScriptEngine();
  819. scriptModule_->SetUserData(nullptr);
  820. // Remove the module
  821. {
  822. MutexLock lock(script_->GetModuleMutex());
  823. script_->ClearObjectTypeCache();
  824. engine->DiscardModule(GetName().CString());
  825. }
  826. scriptModule_ = nullptr;
  827. compiled_ = false;
  828. SetMemoryUse(0);
  829. auto* cache = GetSubsystem<ResourceCache>();
  830. if (cache)
  831. cache->ResetDependencies(this);
  832. }
  833. }
  834. void ScriptFile::HandleUpdate(StringHash eventType, VariantMap& eventData)
  835. {
  836. if (!compiled_)
  837. return;
  838. using namespace Update;
  839. float timeStep = eventData[P_TIMESTEP].GetFloat();
  840. // Execute delayed calls
  841. for (unsigned i = 0; i < delayedCalls_.Size();)
  842. {
  843. DelayedCall& call = delayedCalls_[i];
  844. bool remove = false;
  845. call.delay_ -= timeStep;
  846. if (call.delay_ <= 0.0f)
  847. {
  848. if (!call.repeat_)
  849. remove = true;
  850. else
  851. call.delay_ += call.period_;
  852. Execute(call.declaration_, call.parameters_);
  853. }
  854. if (remove)
  855. delayedCalls_.Erase(i);
  856. else
  857. ++i;
  858. }
  859. }
  860. ScriptEventInvoker::ScriptEventInvoker(ScriptFile* file, asIScriptObject* object) :
  861. Object(file->GetContext()),
  862. file_(file),
  863. sharedBool_(nullptr),
  864. object_(object)
  865. {
  866. if (object_)
  867. {
  868. sharedBool_ = object_->GetEngine()->GetWeakRefFlagOfScriptObject(object_, object_->GetObjectType());
  869. if (sharedBool_)
  870. sharedBool_->AddRef();
  871. }
  872. }
  873. ScriptEventInvoker::~ScriptEventInvoker()
  874. {
  875. if (sharedBool_)
  876. sharedBool_->Release();
  877. sharedBool_ = nullptr;
  878. object_ = nullptr;
  879. }
  880. bool ScriptEventInvoker::IsObjectAlive() const
  881. {
  882. if (sharedBool_)
  883. {
  884. // Return inverse as Get returns true when an asIScriptObject is dead.
  885. return !sharedBool_->Get();
  886. }
  887. return true;
  888. }
  889. void ScriptEventInvoker::HandleScriptEvent(StringHash eventType, VariantMap& eventData)
  890. {
  891. if (!file_->IsCompiled())
  892. return;
  893. auto* method = static_cast<asIScriptFunction*>(GetEventHandler()->GetUserData());
  894. if (object_ && !IsObjectAlive())
  895. {
  896. file_->CleanupEventInvoker(object_);
  897. return;
  898. }
  899. VariantVector parameters;
  900. if (method->GetParamCount() > 0)
  901. {
  902. parameters.Push(Variant((void*)&eventType));
  903. parameters.Push(Variant((void*)&eventData));
  904. }
  905. if (object_)
  906. file_->Execute(object_, method, parameters);
  907. else
  908. file_->Execute(method, parameters);
  909. }
  910. ScriptFile* GetScriptContextFile()
  911. {
  912. asIScriptContext* context = asGetActiveContext();
  913. asIScriptFunction* function = context ? context->GetFunction() : nullptr;
  914. asIScriptModule* module = function ? function->GetEngine()->GetModule(function->GetModuleName()) : nullptr;
  915. if (module)
  916. return static_cast<ScriptFile*>(module->GetUserData());
  917. else
  918. return nullptr;
  919. }
  920. }