ScriptFile.cpp 30 KB

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