ScriptFile.cpp 30 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970
  1. //
  2. // Copyright (c) 2008-2017 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 "../DebugNew.h"
  35. namespace Urho3D
  36. {
  37. /// Helper class for saving AngelScript bytecode.
  38. class ByteCodeSerializer : public asIBinaryStream
  39. {
  40. public:
  41. /// Construct.
  42. ByteCodeSerializer(Serializer& dest) :
  43. dest_(dest)
  44. {
  45. }
  46. /// Read from stream (no-op).
  47. void Read(void* ptr, asUINT size) override
  48. {
  49. // No-op, can not read from a Serializer
  50. }
  51. /// Write to stream.
  52. void Write(const void* ptr, asUINT size) override
  53. {
  54. dest_.Write(ptr, size);
  55. }
  56. private:
  57. /// Destination stream.
  58. Serializer& dest_;
  59. };
  60. /// Helper class for loading AngelScript bytecode.
  61. class ByteCodeDeserializer : public asIBinaryStream
  62. {
  63. public:
  64. /// Construct.
  65. ByteCodeDeserializer(MemoryBuffer& source) :
  66. source_(source)
  67. {
  68. }
  69. /// Read from stream.
  70. void Read(void* ptr, asUINT size) override
  71. {
  72. source_.Read(ptr, size);
  73. }
  74. /// Write to stream (no-op).
  75. void Write(const void* ptr, asUINT size) override
  76. {
  77. }
  78. private:
  79. /// Source stream.
  80. MemoryBuffer& source_;
  81. };
  82. ScriptFile::ScriptFile(Context* context) :
  83. Resource(context),
  84. script_(GetSubsystem<Script>()),
  85. scriptModule_(nullptr),
  86. compiled_(false),
  87. subscribed_(false)
  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, 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, unprepare);
  263. }
  264. bool ScriptFile::Execute(asIScriptFunction* function, const VariantVector& parameters, 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() >= 0;
  278. if (unprepare)
  279. context->Unprepare();
  280. scriptSystem->DecScriptNestingLevel();
  281. return success;
  282. }
  283. bool ScriptFile::Execute(asIScriptObject* object, const String& declaration, const VariantVector& parameters, bool unprepare)
  284. {
  285. if (!object)
  286. return false;
  287. asIScriptFunction* method = GetMethod(object, declaration);
  288. if (!method)
  289. {
  290. URHO3D_LOGERROR("Method " + declaration + " not found in class " + String(object->GetObjectType()->GetName()));
  291. return false;
  292. }
  293. return Execute(object, method, parameters, unprepare);
  294. }
  295. bool ScriptFile::Execute(asIScriptObject* object, asIScriptFunction* method, const VariantVector& parameters, bool unprepare)
  296. {
  297. URHO3D_PROFILE(ExecuteMethod);
  298. if (!compiled_ || !object || !method)
  299. return false;
  300. // It is possible that executing the method causes us to unload. Therefore do not rely on member variables
  301. // However, we are not prepared for the whole script system getting destroyed during execution (should never happen)
  302. Script* scriptSystem = script_;
  303. asIScriptContext* context = scriptSystem->GetScriptFileContext();
  304. if (context->Prepare(method) < 0)
  305. return false;
  306. context->SetObject(object);
  307. SetParameters(context, method, parameters);
  308. scriptSystem->IncScriptNestingLevel();
  309. bool success = context->Execute() >= 0;
  310. if (unprepare)
  311. context->Unprepare();
  312. scriptSystem->DecScriptNestingLevel();
  313. return success;
  314. }
  315. void ScriptFile::DelayedExecute(float delay, bool repeat, const String& declaration, const VariantVector& parameters)
  316. {
  317. DelayedCall call;
  318. call.period_ = call.delay_ = Max(delay, 0.0f);
  319. call.repeat_ = repeat;
  320. call.declaration_ = declaration;
  321. call.parameters_ = parameters;
  322. delayedCalls_.Push(call);
  323. // Make sure we are registered to the application update event, because delayed calls are executed there
  324. if (!subscribed_)
  325. {
  326. SubscribeToEvent(E_UPDATE, URHO3D_HANDLER(ScriptFile, HandleUpdate));
  327. subscribed_ = true;
  328. }
  329. }
  330. void ScriptFile::ClearDelayedExecute(const String& declaration)
  331. {
  332. if (declaration.Empty())
  333. delayedCalls_.Clear();
  334. else
  335. {
  336. for (Vector<DelayedCall>::Iterator i = delayedCalls_.Begin(); i != delayedCalls_.End();)
  337. {
  338. if (declaration == i->declaration_)
  339. i = delayedCalls_.Erase(i);
  340. else
  341. ++i;
  342. }
  343. }
  344. }
  345. asIScriptObject* ScriptFile::CreateObject(const String& className, bool useInterface)
  346. {
  347. URHO3D_PROFILE(CreateObject);
  348. if (!compiled_)
  349. return nullptr;
  350. asIScriptContext* context = script_->GetScriptFileContext();
  351. asITypeInfo* type = nullptr;
  352. if (useInterface)
  353. {
  354. asITypeInfo* interfaceType = scriptModule_->GetTypeInfoByDecl(className.CString());
  355. if (!interfaceType)
  356. return nullptr;
  357. for (unsigned i = 0; i < scriptModule_->GetObjectTypeCount(); ++i)
  358. {
  359. asITypeInfo* t = scriptModule_->GetObjectTypeByIndex(i);
  360. if (t->Implements(interfaceType))
  361. {
  362. type = t;
  363. break;
  364. }
  365. }
  366. }
  367. else
  368. {
  369. type = scriptModule_->GetTypeInfoByDecl(className.CString());
  370. }
  371. if (!type)
  372. return nullptr;
  373. // Ensure that the type implements the "ScriptObject" interface, so it can be returned to script properly
  374. bool found;
  375. HashMap<asITypeInfo*, bool>::ConstIterator i = validClasses_.Find(type);
  376. if (i != validClasses_.End())
  377. found = i->second_;
  378. else
  379. {
  380. asITypeInfo* scriptObjectType = scriptModule_->GetTypeInfoByDecl("ScriptObject");
  381. found = type->Implements(scriptObjectType);
  382. validClasses_[type] = found;
  383. }
  384. if (!found)
  385. {
  386. URHO3D_LOGERRORF("Script class %s does not implement the ScriptObject interface", type->GetName());
  387. return nullptr;
  388. }
  389. // Get the factory function id from the object type
  390. String factoryName = String(type->GetName()) + "@ " + type->GetName() + "()";
  391. asIScriptFunction* factory = type->GetFactoryByDecl(factoryName.CString());
  392. if (!factory || context->Prepare(factory) < 0 || context->Execute() < 0)
  393. return nullptr;
  394. void* objAddress = context->GetAddressOfReturnValue();
  395. if (!objAddress)
  396. return nullptr;
  397. asIScriptObject* obj = *(static_cast<asIScriptObject**>(objAddress));
  398. if (obj)
  399. obj->AddRef();
  400. return obj;
  401. }
  402. bool ScriptFile::SaveByteCode(Serializer& dest)
  403. {
  404. if (compiled_)
  405. {
  406. dest.WriteFileID("ASBC");
  407. ByteCodeSerializer serializer = ByteCodeSerializer(dest);
  408. return scriptModule_->SaveByteCode(&serializer, true) >= 0;
  409. }
  410. else
  411. return false;
  412. }
  413. asIScriptFunction* ScriptFile::GetFunction(const String& declarationIn)
  414. {
  415. if (!compiled_)
  416. return nullptr;
  417. String declaration = declarationIn.Trimmed();
  418. // If not a full declaration, assume void with no parameters
  419. if (declaration.Find('(') == String::NPOS)
  420. declaration = "void " + declaration + "()";
  421. HashMap<String, asIScriptFunction*>::ConstIterator i = functions_.Find(declaration);
  422. if (i != functions_.End())
  423. return i->second_;
  424. asIScriptFunction* function = scriptModule_->GetFunctionByDecl(declaration.CString());
  425. functions_[declaration] = function;
  426. return function;
  427. }
  428. asIScriptFunction* ScriptFile::GetMethod(asIScriptObject* object, const String& declarationIn)
  429. {
  430. if (!compiled_ || !object)
  431. return nullptr;
  432. String declaration = declarationIn.Trimmed();
  433. // If not a full declaration, assume void with no parameters
  434. if (declaration.Find('(') == String::NPOS)
  435. declaration = "void " + declaration + "()";
  436. asITypeInfo* type = object->GetObjectType();
  437. if (!type)
  438. return nullptr;
  439. HashMap<asITypeInfo*, HashMap<String, asIScriptFunction*> >::ConstIterator i = methods_.Find(type);
  440. if (i != methods_.End())
  441. {
  442. HashMap<String, asIScriptFunction*>::ConstIterator j = i->second_.Find(declaration);
  443. if (j != i->second_.End())
  444. return j->second_;
  445. }
  446. asIScriptFunction* function = type->GetMethodByDecl(declaration.CString());
  447. methods_[type][declaration] = function;
  448. return function;
  449. }
  450. void ScriptFile::CleanupEventInvoker(asIScriptObject* object)
  451. {
  452. eventInvokers_.Erase(object);
  453. }
  454. void ScriptFile::AddEventHandlerInternal(Object* sender, StringHash eventType, const String& handlerName)
  455. {
  456. String declaration = "void " + handlerName + "(StringHash, VariantMap&)";
  457. asIScriptFunction* function = nullptr;
  458. auto* receiver = static_cast<asIScriptObject*>(asGetActiveContext()->GetThisPointer());
  459. if (receiver)
  460. function = GetMethod(receiver, declaration);
  461. else
  462. function = GetFunction(declaration);
  463. if (!function)
  464. {
  465. // Retry with parameterless signature
  466. if (receiver)
  467. function = GetMethod(receiver, handlerName);
  468. else
  469. function = GetFunction(handlerName);
  470. if (!function)
  471. {
  472. URHO3D_LOGERROR("Event handler function " + handlerName + " not found in " + GetName());
  473. return;
  474. }
  475. }
  476. HashMap<asIScriptObject*, SharedPtr<ScriptEventInvoker> >::Iterator i = eventInvokers_.Find(receiver);
  477. // Remove previous handler in case an object pointer gets reused
  478. if (i != eventInvokers_.End() && !i->second_->IsObjectAlive())
  479. {
  480. eventInvokers_.Erase(i);
  481. i = eventInvokers_.End();
  482. }
  483. if (i == eventInvokers_.End())
  484. i = eventInvokers_.Insert(MakePair(receiver, SharedPtr<ScriptEventInvoker>(new ScriptEventInvoker(this, receiver))));
  485. if (!sender)
  486. {
  487. i->second_->SubscribeToEvent(eventType, new EventHandlerImpl<ScriptEventInvoker>
  488. (i->second_, &ScriptEventInvoker::HandleScriptEvent, (void*)function));
  489. }
  490. else
  491. {
  492. i->second_->SubscribeToEvent(sender, eventType, new EventHandlerImpl<ScriptEventInvoker>
  493. (i->second_, &ScriptEventInvoker::HandleScriptEvent, (void*)function));
  494. }
  495. }
  496. bool ScriptFile::AddScriptSection(asIScriptEngine* engine, Deserializer& source)
  497. {
  498. auto* cache = GetSubsystem<ResourceCache>();
  499. unsigned dataSize = source.GetSize();
  500. SharedArrayPtr<char> buffer(new char[dataSize]);
  501. source.Read((void*)buffer.Get(), dataSize);
  502. // Pre-parse for includes
  503. // Adapted from Angelscript's scriptbuilder add-on
  504. Vector<String> includeFiles;
  505. unsigned pos = 0;
  506. while (pos < dataSize)
  507. {
  508. unsigned len;
  509. asETokenClass t = engine->ParseToken(&buffer[pos], dataSize - pos, &len);
  510. if (t == asTC_COMMENT || t == asTC_WHITESPACE)
  511. {
  512. pos += len;
  513. continue;
  514. }
  515. // Is this a preprocessor directive?
  516. if (buffer[pos] == '#')
  517. {
  518. int start = pos++;
  519. asETokenClass t = engine->ParseToken(&buffer[pos], dataSize - pos, &len);
  520. if (t == asTC_IDENTIFIER)
  521. {
  522. String token(&buffer[pos], (unsigned)len);
  523. if (token == "include")
  524. {
  525. pos += len;
  526. t = engine->ParseToken(&buffer[pos], dataSize - pos, &len);
  527. if (t == asTC_WHITESPACE)
  528. {
  529. pos += len;
  530. t = engine->ParseToken(&buffer[pos], dataSize - pos, &len);
  531. }
  532. if (t == asTC_VALUE && len > 2 && buffer[pos] == '"')
  533. {
  534. // Get the include file
  535. String includeFile(&buffer[pos + 1], (unsigned)(len - 2));
  536. pos += len;
  537. // If the file is not found as it is, add the path of current file but only if it is found there
  538. if (!cache->Exists(includeFile))
  539. {
  540. String prefixedIncludeFile = GetPath(GetName()) + includeFile;
  541. if (cache->Exists(prefixedIncludeFile))
  542. includeFile = prefixedIncludeFile;
  543. }
  544. String includeFileLower = includeFile.ToLower();
  545. // If not included yet, store it for later processing
  546. if (!includeFiles_.Contains(includeFileLower))
  547. {
  548. includeFiles_.Insert(includeFileLower);
  549. includeFiles.Push(includeFile);
  550. }
  551. // Overwrite the include directive with space characters to avoid compiler error
  552. memset(&buffer[start], ' ', pos - start);
  553. }
  554. }
  555. }
  556. }
  557. // Don't search includes within statement blocks or between tokens in statements
  558. else
  559. {
  560. unsigned len;
  561. // Skip until ; or { whichever comes first
  562. while (pos < dataSize && buffer[pos] != ';' && buffer[pos] != '{')
  563. {
  564. engine->ParseToken(&buffer[pos], dataSize - pos, &len);
  565. pos += len;
  566. }
  567. // Skip entire statement block
  568. if (pos < dataSize && buffer[pos] == '{')
  569. {
  570. ++pos;
  571. // Find the end of the statement block
  572. int level = 1;
  573. while (level > 0 && pos < dataSize)
  574. {
  575. asETokenClass t = engine->ParseToken(&buffer[pos], dataSize - pos, &len);
  576. if (t == asTC_KEYWORD)
  577. {
  578. if (buffer[pos] == '{')
  579. ++level;
  580. else if (buffer[pos] == '}')
  581. --level;
  582. }
  583. pos += len;
  584. }
  585. }
  586. else
  587. ++pos;
  588. }
  589. }
  590. // Process includes first
  591. for (unsigned i = 0; i < includeFiles.Size(); ++i)
  592. {
  593. cache->StoreResourceDependency(this, includeFiles[i]);
  594. SharedPtr<File> file = cache->GetFile(includeFiles[i]);
  595. if (file)
  596. {
  597. if (!AddScriptSection(engine, *file))
  598. return false;
  599. }
  600. else
  601. {
  602. URHO3D_LOGERROR("Could not process all the include directives in " + GetName() + ": missing " + includeFiles[i]);
  603. return false;
  604. }
  605. }
  606. // Then add this section
  607. if (scriptModule_->AddScriptSection(source.GetName().CString(), (const char*)buffer.Get(), dataSize) < 0)
  608. {
  609. URHO3D_LOGERROR("Failed to add script section " + source.GetName());
  610. return false;
  611. }
  612. SetMemoryUse(GetMemoryUse() + dataSize);
  613. return true;
  614. }
  615. void ScriptFile::SetParameters(asIScriptContext* context, asIScriptFunction* function, const VariantVector& parameters)
  616. {
  617. unsigned paramCount = function->GetParamCount();
  618. for (unsigned i = 0; i < parameters.Size() && i < paramCount; ++i)
  619. {
  620. int paramTypeId;
  621. function->GetParam(i, &paramTypeId);
  622. switch (paramTypeId)
  623. {
  624. case asTYPEID_BOOL:
  625. context->SetArgByte(i, (unsigned char)parameters[i].GetBool());
  626. break;
  627. case asTYPEID_INT8:
  628. case asTYPEID_UINT8:
  629. context->SetArgByte(i, (asBYTE)parameters[i].GetInt());
  630. break;
  631. case asTYPEID_INT16:
  632. case asTYPEID_UINT16:
  633. context->SetArgWord(i, (asWORD)parameters[i].GetInt());
  634. break;
  635. case asTYPEID_INT32:
  636. case asTYPEID_UINT32:
  637. context->SetArgDWord(i, (asDWORD)parameters[i].GetInt());
  638. break;
  639. case asTYPEID_FLOAT:
  640. context->SetArgFloat(i, parameters[i].GetFloat());
  641. break;
  642. default:
  643. if (paramTypeId & asTYPEID_APPOBJECT)
  644. {
  645. switch (parameters[i].GetType())
  646. {
  647. case VAR_VECTOR2:
  648. context->SetArgObject(i, (void*)&parameters[i].GetVector2());
  649. break;
  650. case VAR_VECTOR3:
  651. context->SetArgObject(i, (void*)&parameters[i].GetVector3());
  652. break;
  653. case VAR_VECTOR4:
  654. context->SetArgObject(i, (void*)&parameters[i].GetVector4());
  655. break;
  656. case VAR_QUATERNION:
  657. context->SetArgObject(i, (void*)&parameters[i].GetQuaternion());
  658. break;
  659. case VAR_STRING:
  660. context->SetArgObject(i, (void*)&parameters[i].GetString());
  661. break;
  662. case VAR_VARIANTMAP:
  663. context->SetArgObject(i, (void*)&parameters[i].GetVariantMap());
  664. break;
  665. case VAR_INTRECT:
  666. context->SetArgObject(i, (void*)&parameters[i].GetIntRect());
  667. break;
  668. case VAR_INTVECTOR2:
  669. context->SetArgObject(i, (void*)&parameters[i].GetIntVector2());
  670. break;
  671. case VAR_INTVECTOR3:
  672. context->SetArgObject(i, (void*)&parameters[i].GetIntVector3());
  673. break;
  674. case VAR_COLOR:
  675. context->SetArgObject(i, (void*)&parameters[i].GetColor());
  676. break;
  677. case VAR_MATRIX3:
  678. context->SetArgObject(i, (void*)&parameters[i].GetMatrix3());
  679. break;
  680. case VAR_MATRIX3X4:
  681. context->SetArgObject(i, (void*)&parameters[i].GetMatrix3x4());
  682. break;
  683. case VAR_MATRIX4:
  684. context->SetArgObject(i, (void*)&parameters[i].GetMatrix4());
  685. break;
  686. case VAR_RESOURCEREF:
  687. context->SetArgObject(i, (void*)&parameters[i].GetResourceRef());
  688. break;
  689. case VAR_RESOURCEREFLIST:
  690. context->SetArgObject(i, (void*)&parameters[i].GetResourceRefList());
  691. break;
  692. case VAR_VOIDPTR:
  693. context->SetArgObject(i, parameters[i].GetVoidPtr());
  694. break;
  695. case VAR_PTR:
  696. context->SetArgObject(i, (void*)parameters[i].GetPtr());
  697. break;
  698. default:
  699. break;
  700. }
  701. }
  702. break;
  703. }
  704. }
  705. }
  706. void ScriptFile::ReleaseModule()
  707. {
  708. if (scriptModule_)
  709. {
  710. // Clear search caches and event handlers
  711. includeFiles_.Clear();
  712. validClasses_.Clear();
  713. functions_.Clear();
  714. methods_.Clear();
  715. delayedCalls_.Clear();
  716. eventInvokers_.Clear();
  717. asIScriptEngine* engine = script_->GetScriptEngine();
  718. scriptModule_->SetUserData(nullptr);
  719. // Remove the module
  720. {
  721. MutexLock lock(script_->GetModuleMutex());
  722. script_->ClearObjectTypeCache();
  723. engine->DiscardModule(GetName().CString());
  724. }
  725. scriptModule_ = nullptr;
  726. compiled_ = false;
  727. SetMemoryUse(0);
  728. auto* cache = GetSubsystem<ResourceCache>();
  729. if (cache)
  730. cache->ResetDependencies(this);
  731. }
  732. }
  733. void ScriptFile::HandleUpdate(StringHash eventType, VariantMap& eventData)
  734. {
  735. if (!compiled_)
  736. return;
  737. using namespace Update;
  738. float timeStep = eventData[P_TIMESTEP].GetFloat();
  739. // Execute delayed calls
  740. for (unsigned i = 0; i < delayedCalls_.Size();)
  741. {
  742. DelayedCall& call = delayedCalls_[i];
  743. bool remove = false;
  744. call.delay_ -= timeStep;
  745. if (call.delay_ <= 0.0f)
  746. {
  747. if (!call.repeat_)
  748. remove = true;
  749. else
  750. call.delay_ += call.period_;
  751. Execute(call.declaration_, call.parameters_);
  752. }
  753. if (remove)
  754. delayedCalls_.Erase(i);
  755. else
  756. ++i;
  757. }
  758. }
  759. ScriptEventInvoker::ScriptEventInvoker(ScriptFile* file, asIScriptObject* object) :
  760. Object(file->GetContext()),
  761. file_(file),
  762. sharedBool_(nullptr),
  763. object_(object)
  764. {
  765. if (object_)
  766. {
  767. sharedBool_ = object_->GetEngine()->GetWeakRefFlagOfScriptObject(object_, object_->GetObjectType());
  768. if (sharedBool_)
  769. sharedBool_->AddRef();
  770. }
  771. }
  772. ScriptEventInvoker::~ScriptEventInvoker()
  773. {
  774. if (sharedBool_)
  775. sharedBool_->Release();
  776. sharedBool_ = nullptr;
  777. object_ = nullptr;
  778. }
  779. bool ScriptEventInvoker::IsObjectAlive() const
  780. {
  781. if (sharedBool_)
  782. {
  783. // Return inverse as Get returns true when an asIScriptObject is dead.
  784. return !sharedBool_->Get();
  785. }
  786. return true;
  787. }
  788. void ScriptEventInvoker::HandleScriptEvent(StringHash eventType, VariantMap& eventData)
  789. {
  790. if (!file_->IsCompiled())
  791. return;
  792. auto* method = static_cast<asIScriptFunction*>(GetEventHandler()->GetUserData());
  793. if (object_ && !IsObjectAlive())
  794. {
  795. file_->CleanupEventInvoker(object_);
  796. return;
  797. }
  798. VariantVector parameters;
  799. if (method->GetParamCount() > 0)
  800. {
  801. parameters.Push(Variant((void*)&eventType));
  802. parameters.Push(Variant((void*)&eventData));
  803. }
  804. if (object_)
  805. file_->Execute(object_, method, parameters);
  806. else
  807. file_->Execute(method, parameters);
  808. }
  809. ScriptFile* GetScriptContextFile()
  810. {
  811. asIScriptContext* context = asGetActiveContext();
  812. asIScriptFunction* function = context ? context->GetFunction() : nullptr;
  813. asIScriptModule* module = function ? function->GetEngine()->GetModule(function->GetModuleName()) : nullptr;
  814. if (module)
  815. return static_cast<ScriptFile*>(module->GetUserData());
  816. else
  817. return nullptr;
  818. }
  819. }