PageRenderTime 30ms CodeModel.GetById 15ms app.highlight 12ms RepoModel.GetById 1ms app.codeStats 0ms

/Tests/DetectMissingImports.js

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