AEJavascript.cpp 7.4 KB

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