AEJavascript.cpp 7.8 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326
  1. // Copyright (c) 2014-2015, THUNDERBEAST GAMES LLC All rights reserved
  2. // Please see LICENSE.md in repository root for license information
  3. // https://github.com/AtomicGameEngine/AtomicGameEngine
  4. #include "AtomicEditor.h"
  5. #include <Duktape/duktape.h>
  6. #include <Atomic/IO/Log.h>
  7. #include <Atomic/Core/Context.h>
  8. #include <Atomic/IO/FileSystem.h>
  9. #include <Atomic/Resource/ResourceEvents.h>
  10. #include <Atomic/Resource/ResourceCache.h>
  11. #include "AEEditor.h"
  12. #include "AEEvents.h"
  13. #include "Project/AEProject.h"
  14. #include "AEJavascript.h"
  15. #include "Javascript/JSErrorChecker.h"
  16. namespace AtomicEditor
  17. {
  18. WeakPtr<AEJavascript> AEJavascript::instance_;
  19. AEJavascript::AEJavascript(Context* context) :
  20. Object(context)
  21. {
  22. context->RegisterSubsystem(this);
  23. instance_ = this;
  24. ctx_ = duk_create_heap_default();
  25. duk_get_global_string(ctx_, "Duktape");
  26. duk_push_c_function(ctx_, js_module_search, 1);
  27. duk_put_prop_string(ctx_, -2, "modSearch");
  28. duk_push_c_function(ctx_, js_read_source, 1);
  29. duk_put_prop_string(ctx_, -2, "readSource");
  30. duk_pop(ctx_);
  31. errorChecker_ = new JSErrorChecker(context, ctx_);
  32. SubscribeToEvent(E_FILECHANGED, HANDLER(AEJavascript, HandleFileChanged));
  33. SubscribeToEvent(E_EDITORSHUTDOWN, HANDLER(AEJavascript, HandleEditorShutdown));
  34. ExecuteFile("AtomicEditor/javascript/AtomicEditor.js");
  35. }
  36. AEJavascript::~AEJavascript()
  37. {
  38. duk_destroy_heap(ctx_);
  39. instance_ = NULL;
  40. }
  41. bool AEJavascript::ExecuteFile(const String& path)
  42. {
  43. SharedPtr<File> file (GetSubsystem<ResourceCache>()->GetFile(path));
  44. if (file.Null())
  45. return false;
  46. String source;
  47. file->ReadText(source);
  48. duk_push_string(ctx_, file->GetFullPath().CString());
  49. if (duk_eval_raw(ctx_, source.CString(), 0,
  50. DUK_COMPILE_EVAL | DUK_COMPILE_SAFE | DUK_COMPILE_NOSOURCE | DUK_COMPILE_STRLEN) != 0)
  51. {
  52. if (duk_is_object(ctx_, -1))
  53. {
  54. LOGERRORF("Error: %s\n", duk_safe_to_string(ctx_, -1));
  55. }
  56. assert(0);
  57. duk_pop(ctx_);
  58. return false;
  59. }
  60. duk_pop(ctx_);
  61. return true;
  62. }
  63. void AEJavascript::HandleFileChanged(StringHash eventType, VariantMap& eventData)
  64. {
  65. Editor* editor = GetSubsystem<Editor>();
  66. Project* project = editor->GetProject();
  67. if (!project)
  68. return;
  69. using namespace FileChanged;
  70. const String& fileName = eventData[P_FILENAME].GetString();
  71. //const String& resourceName = eventData[P_RESOURCENAME].GetString();
  72. if (GetExtension(fileName) != ".js")
  73. return;
  74. if (modifiedJS_.Contains(fileName))
  75. return;
  76. String componentsPath = AddTrailingSlash(project->GetComponentsPath());
  77. String scriptsPath = AddTrailingSlash(project->GetScriptsPath());
  78. String modulesPath = AddTrailingSlash(project->GetModulesPath());
  79. if (fileName.StartsWith(componentsPath) || fileName.StartsWith(scriptsPath) || fileName.StartsWith(modulesPath))
  80. {
  81. modifiedJS_.Push(fileName);
  82. }
  83. }
  84. bool AEJavascript::CheckJSErrors(bool fullCheck)
  85. {
  86. errors_.Clear();
  87. Editor* editor = GetSubsystem<Editor>();
  88. Project* project = editor->GetProject();
  89. FileSystem* fileSystem = GetSubsystem<FileSystem>();
  90. if (!project)
  91. {
  92. modifiedJS_.Clear();
  93. return true;
  94. }
  95. Vector<String>& filesToCheck = modifiedJS_;
  96. Vector<String> allFiles;
  97. if (fullCheck)
  98. {
  99. filesToCheck = allFiles;
  100. String componentsPath = AddTrailingSlash(project->GetComponentsPath());
  101. String scriptsPath = AddTrailingSlash(project->GetScriptsPath());
  102. String modulesPath = AddTrailingSlash(project->GetModulesPath());
  103. Vector<String> files;
  104. fileSystem->ScanDir(files, componentsPath, "*.js", SCAN_FILES, true );
  105. for (unsigned i = 0; i < files.Size(); i++)
  106. allFiles.Push(componentsPath + files[i]);
  107. fileSystem->ScanDir(files, scriptsPath, "*.js", SCAN_FILES, true );
  108. for (unsigned i = 0; i < files.Size(); i++)
  109. allFiles.Push(scriptsPath + files[i]);
  110. fileSystem->ScanDir(files, modulesPath, "*.js", SCAN_FILES, true );
  111. for (unsigned i = 0; i < files.Size(); i++)
  112. allFiles.Push(modulesPath + files[i]);
  113. }
  114. bool ok = true;
  115. for (unsigned j = 0; j < filesToCheck.Size(); j++)
  116. {
  117. String source;
  118. String fullpath = filesToCheck[j];
  119. ReadZeroTerminatedSourceFile(fullpath, source);
  120. duk_get_global_string(ctx_, "__atomic_parse_error_check");
  121. duk_push_string(ctx_, source.CString());
  122. if (duk_pcall(ctx_, 1))
  123. {
  124. printf("Error: %s\n", duk_safe_to_string(ctx_, -1));
  125. }
  126. else
  127. {
  128. if (duk_is_boolean(ctx_, -1))
  129. {
  130. // error
  131. if (duk_to_boolean(ctx_, -1))
  132. ok = false;
  133. }
  134. else if (duk_is_object(ctx_, -1))
  135. {
  136. ok = false;
  137. JSError error;
  138. error.fullpath = fullpath;
  139. duk_get_prop_string(ctx_, -1, "message");
  140. error.message = duk_to_string(ctx_, -1);
  141. duk_pop(ctx_);
  142. duk_get_prop_string(ctx_, -1, "loc");
  143. duk_get_prop_string(ctx_, -1, "line");
  144. error.line = duk_to_number(ctx_, -1);
  145. duk_get_prop_string(ctx_, -2, "column");
  146. error.column = duk_to_number(ctx_, -1);
  147. duk_get_prop_string(ctx_, -4, "raisedAt");
  148. error.tokenPos = duk_to_number(ctx_, -1);
  149. duk_pop_3(ctx_);
  150. errors_.Push(error);
  151. }
  152. else
  153. {
  154. // what to do?
  155. }
  156. }
  157. // ignore result
  158. duk_pop(ctx_);
  159. }
  160. modifiedJS_.Clear();
  161. return !ok;
  162. }
  163. bool AEJavascript::ParseJavascriptToJSON(const char* source, String& json, bool loose)
  164. {
  165. json.Clear();
  166. // forcing loose to false, as issues with MSVC release - 3/3/2015 First Early Access Release
  167. loose = false;
  168. duk_get_global_string(ctx_, loose ? "__atomic_parse_to_json_loose" : "__atomic_parse_to_json");
  169. duk_push_string(ctx_, source);
  170. bool ok = true;
  171. if (duk_pcall(ctx_, 1))
  172. {
  173. ok = false;
  174. printf("Error: %s\n", duk_safe_to_string(ctx_, -1));
  175. }
  176. else
  177. {
  178. json = duk_to_string(ctx_, -1);
  179. }
  180. // ignore result
  181. duk_pop(ctx_);
  182. return ok;
  183. }
  184. bool AEJavascript::BeautifyJavascript(const char* source, String& output)
  185. {
  186. output.Clear();
  187. duk_get_global_string(ctx_, "__atomic_js_beautify");
  188. duk_push_string(ctx_, source);
  189. bool ok = true;
  190. if (duk_pcall(ctx_, 1))
  191. {
  192. ok = false;
  193. printf("Error: %s\n", duk_safe_to_string(ctx_, -1));
  194. }
  195. else
  196. {
  197. output = duk_to_string(ctx_, -1);
  198. }
  199. // ignore result
  200. duk_pop(ctx_);
  201. return ok;
  202. }
  203. bool AEJavascript::ReadZeroTerminatedSourceFile(const String& path, String& source)
  204. {
  205. ResourceCache* cache = GetSubsystem<ResourceCache>();
  206. SharedPtr<File> file = cache->GetFile(path);
  207. if (file.Null() || !file->IsOpen())
  208. return false;
  209. file->ReadText(source);
  210. return true;
  211. }
  212. int AEJavascript::js_read_source(duk_context* ctx)
  213. {
  214. String path = duk_to_string(ctx, 0);
  215. String source;
  216. instance_->ReadZeroTerminatedSourceFile(path, source);
  217. duk_push_string(ctx, source.CString());
  218. return 1;
  219. }
  220. int AEJavascript::js_module_search(duk_context* ctx)
  221. {
  222. String path = duk_to_string(ctx, 0);
  223. path = "AtomicEditor/javascript/modules/" + path + ".js";
  224. String source;
  225. instance_->ReadZeroTerminatedSourceFile(path, source);
  226. duk_push_string(ctx, source.CString());
  227. return 1;
  228. }
  229. void AEJavascript::HandleEditorShutdown(StringHash eventType, VariantMap& eventData)
  230. {
  231. context_->RemoveSubsystem(GetType());
  232. }
  233. }