ScriptFile.cpp 20 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645
  1. //
  2. // Copyright (c) 2008-2013 the Urho3D project.
  3. //
  4. // Permission is hereby granted, free of charge, to any person obtaining a copy
  5. // of this software and associated documentation files (the "Software"), to deal
  6. // in the Software without restriction, including without limitation the rights
  7. // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
  8. // copies of the Software, and to permit persons to whom the Software is
  9. // furnished to do so, subject to the following conditions:
  10. //
  11. // The above copyright notice and this permission notice shall be included in
  12. // all copies or substantial portions of the Software.
  13. //
  14. // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
  15. // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
  16. // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
  17. // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
  18. // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
  19. // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
  20. // THE SOFTWARE.
  21. //
  22. #include "Precompiled.h"
  23. #include "ArrayPtr.h"
  24. #include "Context.h"
  25. #include "FileSystem.h"
  26. #include "Log.h"
  27. #include "Profiler.h"
  28. #include "ResourceCache.h"
  29. #include "Script.h"
  30. #include "ScriptFile.h"
  31. #include "ScriptInstance.h"
  32. #include <angelscript.h>
  33. #include <cstring>
  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. virtual void Read(void* ptr, asUINT size)
  48. {
  49. // No-op, can not read from a Serializer
  50. }
  51. /// Write to stream.
  52. virtual void Write(const void* ptr, asUINT size)
  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(Deserializer& source) :
  66. source_(source)
  67. {
  68. }
  69. /// Read from stream.
  70. virtual void Read(void* ptr, asUINT size)
  71. {
  72. source_.Read(ptr, size);
  73. }
  74. /// Write to stream (no-op).
  75. virtual void Write(const void* ptr, asUINT size)
  76. {
  77. }
  78. private:
  79. /// Source stream.
  80. Deserializer& source_;
  81. };
  82. ScriptFile::ScriptFile(Context* context) :
  83. Resource(context),
  84. script_(GetSubsystem<Script>()),
  85. scriptModule_(0),
  86. compiled_(false)
  87. {
  88. }
  89. ScriptFile::~ScriptFile()
  90. {
  91. ReleaseModule();
  92. }
  93. void ScriptFile::RegisterObject(Context* context)
  94. {
  95. context->RegisterFactory<ScriptFile>();
  96. }
  97. bool ScriptFile::Load(Deserializer& source)
  98. {
  99. PROFILE(LoadScript);
  100. ReleaseModule();
  101. // Create the module. Discard previous module if there was one
  102. asIScriptEngine* engine = script_->GetScriptEngine();
  103. scriptModule_ = engine->GetModule(GetName().CString(), asGM_ALWAYS_CREATE);
  104. if (!scriptModule_)
  105. {
  106. LOGERROR("Failed to create script module " + GetName());
  107. return false;
  108. }
  109. // Check if this file is precompiled bytecode
  110. if (source.ReadFileID() == "ASBC")
  111. {
  112. ByteCodeDeserializer deserializer = ByteCodeDeserializer(source);
  113. if (scriptModule_->LoadByteCode(&deserializer) >= 0)
  114. {
  115. LOGINFO("Loaded script module " + GetName() + " from bytecode");
  116. compiled_ = true;
  117. // Map script module to script resource with userdata
  118. scriptModule_->SetUserData(this);
  119. return true;
  120. }
  121. else
  122. return false;
  123. }
  124. else
  125. source.Seek(0);
  126. // Not bytecode: add the initial section and check for includes
  127. if (!AddScriptSection(engine, source))
  128. return false;
  129. // Compile
  130. int result = scriptModule_->Build();
  131. if (result < 0)
  132. {
  133. LOGERROR("Failed to compile script module " + GetName());
  134. return false;
  135. }
  136. LOGINFO("Compiled script module " + GetName());
  137. compiled_ = true;
  138. // Map script module to script resource with userdata
  139. scriptModule_->SetUserData(this);
  140. return true;
  141. }
  142. void ScriptFile::AddEventHandler(StringHash eventType, const String& handlerName)
  143. {
  144. if (!compiled_)
  145. return;
  146. String declaration = "void " + handlerName + "(StringHash, VariantMap&)";
  147. asIScriptFunction* function = GetFunction(declaration);
  148. if (!function)
  149. {
  150. declaration = "void " + handlerName + "()";
  151. function = GetFunction(declaration);
  152. if (!function)
  153. {
  154. LOGERROR("Event handler function " + handlerName + " not found in " + GetName());
  155. return;
  156. }
  157. }
  158. SubscribeToEvent(eventType, HANDLER_USERDATA(ScriptFile, HandleScriptEvent, (void*)function));
  159. }
  160. void ScriptFile::AddEventHandler(Object* sender, StringHash eventType, const String& handlerName)
  161. {
  162. if (!compiled_)
  163. return;
  164. if (!sender)
  165. {
  166. LOGERROR("Null event sender for event " + String(eventType) + ", handler " + handlerName);
  167. return;
  168. }
  169. String declaration = "void " + handlerName + "(StringHash, VariantMap&)";
  170. asIScriptFunction* function = GetFunction(declaration);
  171. if (!function)
  172. {
  173. declaration = "void " + handlerName + "()";
  174. function = GetFunction(declaration);
  175. if (!function)
  176. {
  177. LOGERROR("Event handler function " + handlerName + " not found in " + GetName());
  178. return;
  179. }
  180. }
  181. SubscribeToEvent(sender, eventType, HANDLER_USERDATA(ScriptFile, HandleScriptEvent, (void*)function));
  182. }
  183. bool ScriptFile::Execute(const String& declaration, const VariantVector& parameters, bool unprepare)
  184. {
  185. asIScriptFunction* function = GetFunction(declaration);
  186. if (!function)
  187. {
  188. LOGERROR("Function " + declaration + " not found in " + GetName());
  189. return false;
  190. }
  191. return Execute(function, parameters, unprepare);
  192. }
  193. bool ScriptFile::Execute(asIScriptFunction* function, const VariantVector& parameters, bool unprepare)
  194. {
  195. PROFILE(ExecuteFunction);
  196. if (!compiled_ || !function)
  197. return false;
  198. // It is possible that executing the function causes us to unload. Therefore do not rely on member variables
  199. // However, we are not prepared for the whole script system getting destroyed during execution (should never happen)
  200. Script* scriptSystem = script_;
  201. asIScriptContext* context = scriptSystem->GetScriptFileContext();
  202. if (context->Prepare(function) < 0)
  203. return false;
  204. SetParameters(context, function, parameters);
  205. scriptSystem->IncScriptNestingLevel();
  206. bool success = context->Execute() >= 0;
  207. if (unprepare)
  208. context->Unprepare();
  209. scriptSystem->DecScriptNestingLevel();
  210. return success;
  211. }
  212. bool ScriptFile::Execute(asIScriptObject* object, const String& declaration, const VariantVector& parameters, bool unprepare)
  213. {
  214. asIScriptFunction* method = GetMethod(object, declaration);
  215. if (!method)
  216. {
  217. LOGERROR("Method " + declaration + " not found in " + GetName());
  218. return false;
  219. }
  220. return Execute(object, method, parameters, unprepare);
  221. }
  222. bool ScriptFile::Execute(asIScriptObject* object, asIScriptFunction* method, const VariantVector& parameters, bool unprepare)
  223. {
  224. PROFILE(ExecuteMethod);
  225. if (!compiled_ || !object || !method)
  226. return false;
  227. // It is possible that executing the method causes us to unload. Therefore do not rely on member variables
  228. // However, we are not prepared for the whole script system getting destroyed during execution (should never happen)
  229. Script* scriptSystem = script_;
  230. asIScriptContext* context = scriptSystem->GetScriptFileContext();
  231. if (context->Prepare(method) < 0)
  232. return false;
  233. context->SetObject(object);
  234. SetParameters(context, method, parameters);
  235. scriptSystem->IncScriptNestingLevel();
  236. bool success = context->Execute() >= 0;
  237. if (unprepare)
  238. context->Unprepare();
  239. scriptSystem->DecScriptNestingLevel();
  240. return success;
  241. }
  242. asIScriptObject* ScriptFile::CreateObject(const String& className)
  243. {
  244. PROFILE(CreateObject);
  245. if (!compiled_)
  246. return 0;
  247. asIScriptContext* context = script_->GetScriptFileContext();
  248. asIScriptEngine* engine = script_->GetScriptEngine();
  249. asIObjectType *type = engine->GetObjectTypeById(scriptModule_->GetTypeIdByDecl(className.CString()));
  250. if (!type)
  251. return 0;
  252. // Ensure that the type implements the "ScriptObject" interface, so it can be returned to script properly
  253. bool found = false;
  254. HashMap<asIObjectType*, bool>::ConstIterator i = validClasses_.Find(type);
  255. if (i != validClasses_.End())
  256. found = i->second_;
  257. else
  258. {
  259. unsigned numInterfaces = type->GetInterfaceCount();
  260. for (unsigned j = 0; j < numInterfaces; ++j)
  261. {
  262. asIObjectType* interfaceType = type->GetInterface(j);
  263. if (!strcmp(interfaceType->GetName(), "ScriptObject"))
  264. {
  265. found = true;
  266. break;
  267. }
  268. }
  269. validClasses_[type] = found;
  270. }
  271. if (!found)
  272. {
  273. LOGERROR("Script class " + className + " does not implement the ScriptObject interface");
  274. return 0;
  275. }
  276. // Get the factory function id from the object type
  277. String factoryName = className + "@ " + className + "()";
  278. asIScriptFunction* factory = type->GetFactoryByDecl(factoryName.CString());
  279. if (!factory || context->Prepare(factory) < 0 || context->Execute() < 0)
  280. return 0;
  281. asIScriptObject* obj = *(static_cast<asIScriptObject**>(context->GetAddressOfReturnValue()));
  282. if (obj)
  283. obj->AddRef();
  284. return obj;
  285. }
  286. bool ScriptFile::SaveByteCode(Serializer& dest)
  287. {
  288. if (compiled_)
  289. {
  290. dest.WriteFileID("ASBC");
  291. ByteCodeSerializer serializer = ByteCodeSerializer(dest);
  292. return scriptModule_->SaveByteCode(&serializer, true) >= 0;
  293. }
  294. else
  295. return false;
  296. }
  297. asIScriptFunction* ScriptFile::GetFunction(const String& declaration)
  298. {
  299. if (!compiled_)
  300. return 0;
  301. HashMap<String, asIScriptFunction*>::ConstIterator i = functions_.Find(declaration);
  302. if (i != functions_.End())
  303. return i->second_;
  304. asIScriptFunction* function = scriptModule_->GetFunctionByDecl(declaration.CString());
  305. functions_[declaration] = function;
  306. return function;
  307. }
  308. asIScriptFunction* ScriptFile::GetMethod(asIScriptObject* object, const String& declaration)
  309. {
  310. if (!compiled_ || !object)
  311. return 0;
  312. asIObjectType* type = object->GetObjectType();
  313. if (!type)
  314. return 0;
  315. HashMap<asIObjectType*, HashMap<String, asIScriptFunction*> >::ConstIterator i = methods_.Find(type);
  316. if (i != methods_.End())
  317. {
  318. HashMap<String, asIScriptFunction*>::ConstIterator j = i->second_.Find(declaration);
  319. if (j != i->second_.End())
  320. return j->second_;
  321. }
  322. asIScriptFunction* function = type->GetMethodByDecl(declaration.CString());
  323. methods_[type][declaration] = function;
  324. return function;
  325. }
  326. bool ScriptFile::AddScriptSection(asIScriptEngine* engine, Deserializer& source)
  327. {
  328. ResourceCache* cache = GetSubsystem<ResourceCache>();
  329. unsigned dataSize = source.GetSize();
  330. SharedArrayPtr<char> buffer(new char[dataSize]);
  331. source.Read((void*)buffer.Get(), dataSize);
  332. // Pre-parse for includes
  333. // Adapted from Angelscript's scriptbuilder add-on
  334. Vector<String> includeFiles;
  335. unsigned pos = 0;
  336. while(pos < dataSize)
  337. {
  338. int len;
  339. asETokenClass t = engine->ParseToken(&buffer[pos], dataSize - pos, &len);
  340. if (t == asTC_COMMENT || t == asTC_WHITESPACE)
  341. {
  342. pos += len;
  343. continue;
  344. }
  345. // Is this a preprocessor directive?
  346. if (buffer[pos] == '#')
  347. {
  348. int start = pos++;
  349. asETokenClass t = engine->ParseToken(&buffer[pos], dataSize - pos, &len);
  350. if (t == asTC_IDENTIFIER)
  351. {
  352. String token(&buffer[pos], len);
  353. if (token == "include")
  354. {
  355. pos += len;
  356. t = engine->ParseToken(&buffer[pos], dataSize - pos, &len);
  357. if (t == asTC_WHITESPACE)
  358. {
  359. pos += len;
  360. t = engine->ParseToken(&buffer[pos], dataSize - pos, &len);
  361. }
  362. if (t == asTC_VALUE && len > 2 && buffer[pos] == '"')
  363. {
  364. // Get the include file
  365. String includeFile(&buffer[pos+1], len - 2);
  366. pos += len;
  367. // If the file is not found as it is, add the path of current file but only if it is found there
  368. if (!cache->Exists(includeFile))
  369. {
  370. String prefixedIncludeFile = GetPath(GetName()) + includeFile;
  371. if (cache->Exists(prefixedIncludeFile))
  372. includeFile = prefixedIncludeFile;
  373. }
  374. String includeFileLower = includeFile.ToLower();
  375. // If not included yet, store it for later processing
  376. if (!includeFiles_.Contains(includeFileLower))
  377. {
  378. includeFiles_.Insert(includeFileLower);
  379. includeFiles.Push(includeFile);
  380. }
  381. // Overwrite the include directive with space characters to avoid compiler error
  382. memset(&buffer[start], ' ', pos - start);
  383. }
  384. }
  385. }
  386. }
  387. // Don't search includes within statement blocks or between tokens in statements
  388. else
  389. {
  390. int len;
  391. // Skip until ; or { whichever comes first
  392. while (pos < dataSize && buffer[pos] != ';' && buffer[pos] != '{')
  393. {
  394. engine->ParseToken(&buffer[pos], 0, &len);
  395. pos += len;
  396. }
  397. // Skip entire statement block
  398. if (pos < dataSize && buffer[pos] == '{')
  399. {
  400. ++pos;
  401. // Find the end of the statement block
  402. int level = 1;
  403. while (level > 0 && pos < dataSize)
  404. {
  405. asETokenClass t = engine->ParseToken(&buffer[pos], 0, &len);
  406. if (t == asTC_KEYWORD)
  407. {
  408. if (buffer[pos] == '{')
  409. ++level;
  410. else if(buffer[pos] == '}')
  411. --level;
  412. }
  413. pos += len;
  414. }
  415. }
  416. else
  417. ++pos;
  418. }
  419. }
  420. // Process includes first
  421. for (unsigned i = 0; i < includeFiles.Size(); ++i)
  422. {
  423. cache->StoreResourceDependency(this, includeFiles[i]);
  424. SharedPtr<File> file = cache->GetFile(includeFiles[i]);
  425. if (file)
  426. {
  427. if (!AddScriptSection(engine, *file))
  428. return false;
  429. }
  430. else
  431. {
  432. LOGERROR("Could not process all the include directives in " + GetName() + ": missing " + includeFiles[i]);
  433. return false;
  434. }
  435. }
  436. // Then add this section
  437. if (scriptModule_->AddScriptSection(source.GetName().CString(), (const char*)buffer.Get(), dataSize) < 0)
  438. {
  439. LOGERROR("Failed to add script section " + source.GetName());
  440. return false;
  441. }
  442. SetMemoryUse(GetMemoryUse() + dataSize);
  443. return true;
  444. }
  445. void ScriptFile::SetParameters(asIScriptContext* context, asIScriptFunction* function, const VariantVector& parameters)
  446. {
  447. unsigned paramCount = function->GetParamCount();
  448. for (unsigned i = 0; i < parameters.Size() && i < paramCount; ++i)
  449. {
  450. int paramType = function->GetParamTypeId(i);
  451. switch (paramType)
  452. {
  453. case asTYPEID_BOOL:
  454. context->SetArgByte(i, (unsigned char)parameters[i].GetBool());
  455. break;
  456. case asTYPEID_INT8:
  457. case asTYPEID_UINT8:
  458. context->SetArgByte(i, parameters[i].GetInt());
  459. break;
  460. case asTYPEID_INT16:
  461. case asTYPEID_UINT16:
  462. context->SetArgWord(i, parameters[i].GetInt());
  463. break;
  464. case asTYPEID_INT32:
  465. case asTYPEID_UINT32:
  466. context->SetArgDWord(i, parameters[i].GetInt());
  467. break;
  468. case asTYPEID_FLOAT:
  469. context->SetArgFloat(i, parameters[i].GetFloat());
  470. break;
  471. default:
  472. if (paramType & asTYPEID_APPOBJECT)
  473. {
  474. switch (parameters[i].GetType())
  475. {
  476. case VAR_VECTOR2:
  477. context->SetArgObject(i, (void *)&parameters[i].GetVector2());
  478. break;
  479. case VAR_VECTOR3:
  480. context->SetArgObject(i, (void *)&parameters[i].GetVector3());
  481. break;
  482. case VAR_VECTOR4:
  483. context->SetArgObject(i, (void *)&parameters[i].GetVector4());
  484. break;
  485. case VAR_QUATERNION:
  486. context->SetArgObject(i, (void *)&parameters[i].GetQuaternion());
  487. break;
  488. case VAR_STRING:
  489. context->SetArgObject(i, (void *)&parameters[i].GetString());
  490. break;
  491. case VAR_PTR:
  492. context->SetArgObject(i, (void *)parameters[i].GetPtr());
  493. break;
  494. default:
  495. break;
  496. }
  497. }
  498. break;
  499. }
  500. }
  501. }
  502. void ScriptFile::ReleaseModule()
  503. {
  504. if (scriptModule_)
  505. {
  506. script_->ClearObjectTypeCache();
  507. // Clear search caches and event handlers
  508. includeFiles_.Clear();
  509. validClasses_.Clear();
  510. functions_.Clear();
  511. methods_.Clear();
  512. UnsubscribeFromAllEventsExcept(PODVector<StringHash>(), true);
  513. // Remove the module
  514. scriptModule_->SetUserData(0);
  515. asIScriptEngine* engine = script_->GetScriptEngine();
  516. engine->DiscardModule(GetName().CString());
  517. scriptModule_ = 0;
  518. compiled_ = false;
  519. SetMemoryUse(0);
  520. ResourceCache* cache = GetSubsystem<ResourceCache>();
  521. if (cache)
  522. cache->ResetDependencies(this);
  523. }
  524. }
  525. void ScriptFile::HandleScriptEvent(StringHash eventType, VariantMap& eventData)
  526. {
  527. if (!compiled_)
  528. return;
  529. asIScriptFunction* function = static_cast<asIScriptFunction*>(GetEventHandler()->GetUserData());
  530. VariantVector parameters;
  531. if (function->GetParamCount() > 0)
  532. {
  533. parameters.Push(Variant((void*)&eventType));
  534. parameters.Push(Variant((void*)&eventData));
  535. }
  536. Execute(function, parameters);
  537. }
  538. ScriptFile* GetScriptContextFile()
  539. {
  540. asIScriptContext* context = asGetActiveContext();
  541. asIScriptFunction* function = context ? context->GetFunction() : 0;
  542. asIScriptModule* module = function ? function->GetEngine()->GetModule(function->GetModuleName()) : 0;
  543. if (module)
  544. return static_cast<ScriptFile*>(module->GetUserData());
  545. else
  546. return 0;
  547. }
  548. }