/Tests/DetectMissingImports.js

http://github.com/cacaodev/cappuccino · JavaScript · 224 lines · 176 code · 30 blank · 18 comment · 24 complexity · 6779f7eb36c5e66f8f425a69c6bc19a6 MD5 · raw file

  1. var FILE = require("file"),
  2. FileList = require("jake").FileList,
  3. OS = require("os"),
  4. stream = require("narwhal/term").stream;
  5. var cPreprocessedFileContents = function(aFilePath)
  6. {
  7. var INCLUDES = new FileList(FILE.join(FILE.dirname(aFilePath), "**", "*.h")).map(function(aFilename)
  8. {
  9. return "--include \"" + aFilename + "\"";
  10. }).join(" ");
  11. var gcc = OS.popen("gcc -E -x c -P -DPLATFORM_COMMONJS " + INCLUDES + " " + OS.enquote(aFilePath), { charset:"UTF-8" }),
  12. chunk,
  13. fileContents = "";
  14. while (chunk = gcc.stdout.read())
  15. fileContents += chunk;
  16. return fileContents;
  17. };
  18. var checkImportsForFile = function (fileName)
  19. {
  20. var classNames = [],
  21. errorMessageHashSet = {},
  22. matches = fileName.match(new RegExp("([^\\/]+)\\/([^\\/]+)\\.j$"));
  23. if (!FILE.exists(fileName) ||
  24. FILE.isDirectory(fileName) ||
  25. fileName.indexOf("+") !== -1 ||
  26. (matches && matches[1] === matches[2]))
  27. return;
  28. // this is a hackish way to clear the previous file's @imports
  29. for (var g in global)
  30. if (g !== "SyntaxError")
  31. delete global[g];
  32. // reload Objective-J and get a reference to its StaticResource export at
  33. // the same time
  34. global.StaticResource = require.force("objective-j").StaticResource;
  35. ObjectiveJ.OBJJ_INCLUDE_PATHS = [FILE.canonical(".")];
  36. var og_resolveResourceAtURL = StaticResource.resolveResourceAtURL;
  37. // this function gets called for all "File.j" imports
  38. // in case the import can't be resolved, use JAKE.FileList to try and find
  39. // it.
  40. StaticResource.resolveResourceAtURL = function(/*CFURL|String*/ aURL, /*BOOL*/ isDirectory, /*Function*/ aCallback)
  41. {
  42. var og_callback = arguments[2];
  43. arguments[2] = function (resource)
  44. {
  45. if (!resource)
  46. {
  47. var basename = FILE.basename(aURL.toString()),
  48. newURL = (new FileList("Foundation/**/" + basename)).include("AppKit/**/" + basename).items()[0];
  49. if (newURL)
  50. return og_resolveResourceAtURL(FILE.canonical(newURL), isDirectory, og_callback)
  51. else
  52. og_callback(resource);
  53. }
  54. else
  55. og_callback(resource);
  56. };
  57. og_resolveResourceAtURL.apply(this, arguments);
  58. };
  59. // this function gets called for <Framework/File.j> imports
  60. // since this script looks at the un-compiled frameworks,
  61. // some trickery for finding the correct file is sometimes necessary
  62. var resolveResourceAtURLSearchingIncludeURLs = function (/*CFURL*/ aURL, /*Number*/ anIndex, /*Function*/ aCallback)
  63. {
  64. var includeURLs = StaticResource.includeURLs(),
  65. searchURL = new CFURL(aURL, includeURLs[anIndex]).absoluteURL();
  66. og_resolveResourceAtURL(searchURL, NO, function(/*StaticResource*/ aStaticResource)
  67. {
  68. if (!aStaticResource)
  69. {
  70. if (anIndex + 1 < includeURLs.length)
  71. resolveResourceAtURLSearchingIncludeURLs(aURL, anIndex + 1, aCallback);
  72. else
  73. {
  74. var basename = FILE.basename(aURL),
  75. newURL = (new FileList("Foundation/**/" + basename)).include("AppKit/**/" + basename).items()[0];
  76. if (newURL)
  77. resolveResourceAtURLSearchingIncludeURLS(FILE.canonical(newURL), anIndex, aCallback);
  78. else
  79. aCallback(NULL);
  80. }
  81. return;
  82. }
  83. aCallback(aStaticResource);
  84. });
  85. }
  86. StaticResource.resolveResourceAtURLSearchingIncludeURLs = function(/*CFURL*/ aURL, /*Function*/ aCallback)
  87. {
  88. resolveResourceAtURLSearchingIncludeURLs(aURL, 0, aCallback);
  89. };
  90. // find all the @implementations by running through the CPP and then
  91. // calling ObjectiveJ.preprocess with a decorated @implementation handler
  92. // on the Preprocessor
  93. var fileNameContents = cPreprocessedFileContents(fileName);
  94. var og_objj_implementation = ObjectiveJ.Preprocessor.prototype.implementation;
  95. ObjectiveJ.Preprocessor.prototype.implementation = function (tokens)
  96. {
  97. var className = tokens.skip_whitespace();
  98. if (classNames.indexOf(className) === -1)
  99. classNames.push(className);
  100. tokens.skip_whitespace(YES);
  101. og_objj_implementation.apply(this, arguments);
  102. };
  103. ObjectiveJ.preprocess(fileNameContents);
  104. // reset the preprocessor's @implementation handler
  105. ObjectiveJ.Preprocessor.prototype.implementation = og_objj_implementation;
  106. // do automatic CPP runs on @imported files
  107. // to resolve #includes and conditional compilation
  108. CFHTTPRequest.prototype.responseText = function ()
  109. {
  110. if (this._URL.slice(-2) === ".j")
  111. {
  112. var tmpFile = FILE.canonical(FILE.join(FILE.dirname(this._URL.split(":")[1]), ".detect_missing_imports"));
  113. // hackishly give the file a window object
  114. FILE.write(tmpFile, "window = {};\n" + this._nativeRequest.responseText);
  115. var result = cPreprocessedFileContents(tmpFile);
  116. FILE.remove(tmpFile);
  117. }
  118. else
  119. result = this._nativeRequest.responseText;
  120. return result;
  121. };
  122. try
  123. {
  124. objj_importFile(FILE.canonical(fileName), YES);
  125. }
  126. catch (e)
  127. {
  128. if (e.name === "ReferenceError")
  129. stream.print("\0yellow(Detected missing @import in \"" + fileName + "\": " + e.message + "\0)");
  130. else
  131. throw e;
  132. }
  133. for(var j = 0, jj = classNames.length; j < jj; ++j)
  134. {
  135. var className = classNames[j],
  136. cls = objj_getClass(className);
  137. if (cls && cls.super_class)
  138. {
  139. // instance methods
  140. var method_dtable = cls.method_dtable;
  141. for (var methodName in method_dtable)
  142. {
  143. if (method_dtable.hasOwnProperty(methodName))
  144. {
  145. try
  146. {
  147. var instance = objj_msgSend(cls, "alloc");
  148. objj_msgSend(instance, methodName, method_dtable[methodName].types.slice(1));
  149. }
  150. catch (e)
  151. {
  152. if (e.name === "ReferenceError")
  153. {
  154. var hashKey = fileName + e.message;
  155. if (!errorMessageHashSet[hashKey])
  156. {
  157. stream.print("\0yellow(Detected missing @import in \"" + fileName + "\": " + e.message + " (Context: [" + className + " " + methodName + "])\0)");
  158. errorMessageHashSet[hashKey] = true;
  159. }
  160. }
  161. }
  162. }
  163. }
  164. // class methods
  165. method_dtable = cls.isa.method_dtable;
  166. for (var methodName in method_dtable)
  167. {
  168. if (method_dtable.hasOwnProperty(methodName))
  169. {
  170. try
  171. {
  172. objj_msgSend(cls, methodName);
  173. }
  174. catch (e)
  175. {
  176. if (e.name === "ReferenceError")
  177. {
  178. var hashKey = fileName + e.message;
  179. if (!errorMessageHashSet[hashKey])
  180. {
  181. stream.print("\0yellow(Detected missing @import in \"" + fileName + "\": " + e.message + " (Context: [" + className + " " + methodName + "])\0)");
  182. errorMessageHashSet[hashKey] = true;
  183. }
  184. }
  185. }
  186. }
  187. }
  188. }
  189. }
  190. };
  191. (new FileList("Foundation/**.j").include("AppKit/**.j")).forEach(function (aFile)
  192. {
  193. checkImportsForFile(aFile);
  194. });