diff --git a/Objective-J/Bootstrap.js b/Objective-J/Bootstrap.js index 2810c76fd..7f7be575f 100644 --- a/Objective-J/Bootstrap.js +++ b/Objective-J/Bootstrap.js @@ -66,6 +66,10 @@ if (typeof OBJJ_COMPILER_FLAGS !== 'undefined') case "InlineMsgSend": flags.inlineMsgSendFunctions = true; break; + + case "SourceMap": + flags.sourceMap = true; + break; } } FileExecutable.setCurrentCompilerFlags(flags); @@ -180,4 +184,4 @@ GLOBAL(objj_import) = function() { CPLog.warn("objj_import is deprecated, use objj_importFile instead"); objj_importFile.apply(this, arguments); -} +}; diff --git a/Objective-J/CFBundle.js b/Objective-J/CFBundle.js index 995b60904..296305b78 100644 --- a/Objective-J/CFBundle.js +++ b/Objective-J/CFBundle.js @@ -914,4 +914,4 @@ GLOBAL(CFCopyLocalizedStringWithDefaultValue) = function (key, tableName, bundle GLOBAL(CFBundleGetMainBundle) = function () { return CFBundle.mainBundle(); -} +}; diff --git a/Objective-J/CFHTTPRequest.js b/Objective-J/CFHTTPRequest.js index d07064696..30ac9895e 100755 --- a/Objective-J/CFHTTPRequest.js +++ b/Objective-J/CFHTTPRequest.js @@ -366,42 +366,6 @@ function FileRequest(/*CFURL*/ aURL, onsuccess, onfailure, onprogress) if (aURL.pathExtension() === "plist") request.overrideMimeType("text/xml"); -#if COMMONJS - if (aURL.pathExtension().toLowerCase() === "j") - { - var aFilePath = aURL.toString().substring(5), - OS = require("os"), - gccFlags = require("objective-j").FileExecutable.currentGccCompilerFlags(), - chunk, - fileContents = ""; - - try - { - var gcc = OS.popen("gcc -E -x c -P " + (gccFlags ? gccFlags : "") + " " + OS.enquote(aFilePath), { charset:"UTF-8" }); - while (chunk = gcc.stdout.read()) - fileContents += chunk; - } - finally - { - gcc.stdin.close(); - gcc.stdout.close(); - gcc.stderr.close(); - } - - if (fileContents.length > 0) - { - request._nativeRequest.responseText = fileContents; - onsuccess({request: request}); - } - else - { - onfailure({request: request}); - } - - return; - } -#endif - var loaded = 0, progressHandler = null; diff --git a/Objective-J/CFPropertyList.js b/Objective-J/CFPropertyList.js index 7dd16cc4c..00bf332c5 100644 --- a/Objective-J/CFPropertyList.js +++ b/Objective-J/CFPropertyList.js @@ -597,7 +597,7 @@ CFPropertyList.propertyListFromXML = function(/*String | XMLNode*/ aStringOrXMLN object = decodeHTMLComponent(FIRST_CHILD(XMLNode) ? TEXT_CONTENT(XMLNode) : ""); break; - + case PLIST_DATE: var timestamp = Date.parseISO8601(TEXT_CONTENT(XMLNode)); object = isNaN(timestamp) ? new Date() : new Date(timestamp); break; @@ -667,4 +667,4 @@ GLOBAL(CPPropertyListCreateFromData) = function(/*CFData*/ data, /*Format*/ aFor GLOBAL(CPPropertyListCreateData) = function(/*PropertyList*/ aPropertyList, /*Format*/ aFormat) { return CFPropertyList.dataFromPropertyList(aPropertyList, aFormat); -} +}; diff --git a/Objective-J/CommonJS/lib/objective-j.js b/Objective-J/CommonJS/lib/objective-j.js index 4d7feb707..b610953ca 100644 --- a/Objective-J/CommonJS/lib/objective-j.js +++ b/Objective-J/CommonJS/lib/objective-j.js @@ -91,7 +91,7 @@ exports.run = function(args) if (argv[0] === "--help" || argv[0] === "-h") { - print("Usage (objj): " + args[0] + " [options] [--] files..."); + print("Usage (objj): " + args[0].split("/").pop() + " [options] [--] files..."); print(" -v, --version print the current version of objj"); print(" -I, --objj-include-paths include a specific framework paths") print(" -h, --help print this help"); @@ -150,6 +150,35 @@ exports.run = function(args) flags.inlineMsgSendFunctions = true; ObjectiveJ.FileExecutable.setCurrentCompilerFlags(flags); break; + + case "-g": + case "--include-debug-symbols": + argv.shift(); + var flags = ObjectiveJ.FileExecutable.currentCompilerFlags(); + flags.includeMethodFunctionNames = true; + ObjectiveJ.FileExecutable.setCurrentCompilerFlags(flags); + break; + + case "-T": + case "--dont-include-type-signatures": + argv.shift(); + var flags = ObjectiveJ.FileExecutable.currentCompilerFlags(); + flags.includeIvarTypeSignatures = true; + flags.includeMethodArgumentTypeSignatures = true; + ObjectiveJ.FileExecutable.setCurrentCompilerFlags(flags); + break; + + case "-O2": + case "--inline-msg-send": + argv.shift(); + var flags = ObjectiveJ.FileExecutable.currentCompilerFlags(); + flags.inlineMsgSendFunctions = true; + ObjectiveJ.FileExecutable.setCurrentCompilerFlags(flags); + break; + + default: + print(args[0].split("/").pop() + " illegal option " + argv[0]); + OS.exit(1); } } } diff --git a/Objective-J/CommonJS/lib/objective-j/loader.js b/Objective-J/CommonJS/lib/objective-j/loader.js index 8d162d420..402b974df 100644 --- a/Objective-J/CommonJS/lib/objective-j/loader.js +++ b/Objective-J/CommonJS/lib/objective-j/loader.js @@ -2,22 +2,21 @@ function ObjectiveJLoader() { var loader = {}; var factories = {}; - + loader.reload = function(topId, path) { - if (!global.ObjectiveJ) + if (!global.ObjectiveJ) global.ObjectiveJ = require("objective-j"); - - //print("loading objective-j: " + topId + " (" + path + ")"); + factories[topId] = ObjectiveJ.make_narwhal_factory(path); factories[topId].path = path; } - + loader.load = function(topId, path) { if (!factories.hasOwnProperty(topId)) loader.reload(topId, path); return factories[topId]; } - + return loader; }; diff --git a/Objective-J/Debug.js b/Objective-J/Debug.js index d8583f634..3e29ccc69 100644 --- a/Objective-J/Debug.js +++ b/Objective-J/Debug.js @@ -275,4 +275,4 @@ GLOBAL(objj_debug_typecheck) = function(expectedType, object) actualType = typeof object; throw ("expected=" + expectedType + ", actual=" + actualType); -} +}; diff --git a/Objective-J/EventDispatcher.js b/Objective-J/EventDispatcher.js index 048ef3540..5c5eda619 100644 --- a/Objective-J/EventDispatcher.js +++ b/Objective-J/EventDispatcher.js @@ -81,4 +81,4 @@ EventDispatcher.prototype.dispatchEvent = function(/*Event*/ anEvent) if (manual) manual(anEvent); -} +}; diff --git a/Objective-J/Executable.js b/Objective-J/Executable.js index f138fb6c2..1e7844392 100644 --- a/Objective-J/Executable.js +++ b/Objective-J/Executable.js @@ -21,9 +21,10 @@ */ -var ExecutableUnloadedFileDependencies = 0, - ExecutableLoadingFileDependencies = 1, - ExecutableLoadedFileDependencies = 2, +var ExecutableUnloadedFileDependencies = 0, + ExecutableLoadingFileDependencies = 1, + ExecutableLoadedFileDependencies = 2, + ExecutableCantStartLoadYetFileDependencies = 3, AnonymousExecutableCount = 0; function Executable(/*String*/ aCode, /*Array*/ fileDependencies, /*CFURL|String*/ aURL, /*Function*/ aFunction, /*ObjJCompiler*/aCompiler, /*Dictionary*/ aFilenameTranslateDictionary) @@ -40,13 +41,21 @@ function Executable(/*String*/ aCode, /*Array*/ fileDependencies, /*CFURL|String this._fileDependencies = fileDependencies; this._filenameTranslateDictionary = aFilenameTranslateDictionary; - if (fileDependencies.length) + // This is a little hacky but if fileDependencies is null we can start loading file dependencies yet + if (!fileDependencies) + { + this._fileDependencyStatus = ExecutableCantStartLoadYetFileDependencies; + this._fileDependencyCallbacks = []; + } + else if (fileDependencies.length) { this._fileDependencyStatus = ExecutableUnloadedFileDependencies; this._fileDependencyCallbacks = []; } else + { this._fileDependencyStatus = ExecutableLoadedFileDependencies; + } if (this._function) return; @@ -168,7 +177,7 @@ Executable.prototype.execute = function() } this._compiler.popImport(); - this.setCode(this._compiler.compilePass2()); + this.setCode(this._compiler.compilePass2(), this._compiler.map()); if (FileExecutable.printWarningsAndErrors(this._compiler, exports.messageOutputFormatInXML)) throw "Compilation error"; @@ -197,7 +206,7 @@ Executable.prototype.code = function() DISPLAY_NAME(Executable.prototype.code); -Executable.prototype.setCode = function(code) +Executable.prototype.setCode = function(code, sourceMap) { this._code = code; @@ -219,7 +228,24 @@ Executable.prototype.setCode = function(code) //if (YES) { var absoluteString = this.URL().absoluteString(); - code += "/**/\n//# sourceURL=" + absoluteString; + code += "/**/\n//# sourceURL=" + absoluteString + "s"; + if (sourceMap) + { + // The new Function constructor will add a function header before the first line + // The compiler adds a new line as the first character to the code to get the spurce + // mapping correct. We have to remove it here + code = code.substring(2); + + var sourceMapBase64; + + if (typeof btoa === 'function') + sourceMapBase64 = btoa(UTF16ToUTF8(sourceMap)); + else if (typeof Buffer === 'function') + sourceMapBase64 = new Buffer(sourceMap).toString("base64"); + + if (sourceMapBase64) + code += "\n//# sourceMappingURL=data:application/json;charset=utf-8;base64," + sourceMapBase64; + } //} else { // // Firebug only does it for "eval()", not "new Function()". Ugh. Slower. // var functionText = "(function(){"+GET_CODE(aFragment)+"/**/\n})\n//# sourceURL="+GET_FILE(aFragment).path; @@ -244,6 +270,13 @@ Executable.prototype.fileDependencies = function() DISPLAY_NAME(Executable.prototype.fileDependencies); +Executable.prototype.setFileDependencies = function(newValue) +{ + this._fileDependencies = newValue; +} + +DISPLAY_NAME(Executable.prototype.setFileDependencies); + Executable.prototype.hasLoadedFileDependencies = function() { return this._fileDependencyStatus === ExecutableLoadedFileDependencies; @@ -278,6 +311,21 @@ Executable.prototype.loadFileDependencies = function(aCallback) DISPLAY_NAME(Executable.prototype.loadFileDependencies); +Executable.prototype.setExecutableUnloadedFileDependencies = function() +{ + if (this._fileDependencyStatus === ExecutableCantStartLoadYetFileDependencies) + this._fileDependencyStatus = ExecutableUnloadedFileDependencies; +} + +DISPLAY_NAME(Executable.prototype.setExecutableUnloadedFileDependencies); + +Executable.prototype.isExecutableCantStartLoadYetFileDependencies = function() +{ + return this._fileDependencyStatus === ExecutableCantStartLoadYetFileDependencies; +} + +DISPLAY_NAME(Executable.prototype.setExecutableUnloadedFileDependencies); + function loadFileDependenciesForExecutable(/*Executable*/ anExecutable) { fileDependencyExecutables.push(anExecutable); @@ -351,6 +399,7 @@ function fileExecutableDependencyLoadFinished() Executable.prototype.referenceURL = function() { if (this._referenceURL === undefined) + // Removed the filename (if any) from the path to get the directory this._referenceURL = new CFURL(".", this.URL()); return this._referenceURL; @@ -473,11 +522,12 @@ Executable.resetCachedFileExecutableSearchers = function() Executable.fileExecutableSearcherForURL = function(/*CFURL*/ referenceURL) { var referenceURLString = referenceURL.absoluteString(), - cachedFileExecutableSearcher = cachedFileExecutableSearchers[referenceURLString], - aFilenameTranslateDictionary = Executable.filenameTranslateDictionary ? Executable.filenameTranslateDictionary() : null; + cachedFileExecutableSearcher = cachedFileExecutableSearchers[referenceURLString]; if (!cachedFileExecutableSearcher) { + var aFilenameTranslateDictionary = Executable.filenameTranslateDictionary ? Executable.filenameTranslateDictionary() : null; + cachedFileExecutableSearcher = function(/*CFURL*/ aURL, /*BOOL*/ isQuoted, /*Function*/ success) { var cacheUID = (isQuoted && referenceURL || "") + aURL, @@ -503,7 +553,7 @@ Executable.fileExecutableSearcherForURL = function(/*CFURL*/ referenceURL) if (!aStaticResource) { var compilingFileUrl = exports.ObjJCompiler ? exports.ObjJCompiler.currentCompileFile : null; - throw new Error("Could not load file at " + aURL + (compilingFileUrl ? " when compiling " + compilingFileUrl : "")); + throw new Error("Could not load file at " + aURL + (compilingFileUrl ? " when compiling " + compilingFileUrl : "") + "\nwith includeURLs: " + StaticResource.includeURLs()); } cachedFileExecutableSearchResults[cacheUID] = aStaticResource; @@ -519,3 +569,110 @@ Executable.fileExecutableSearcherForURL = function(/*CFURL*/ referenceURL) } DISPLAY_NAME(Executable.fileExecutableSearcherForURL); + +/* + * Adaption to javascript by Malte Tancred 2012 from ConvertUTF.[ch] by Unicode, Inc. + * Speed improvements by Martin Carlberg 2016 + * + * Original copyright follows. + */ + +/* + * Copyright 2001-2004 Unicode, Inc. + * + * Disclaimer + * + * This source code is provided as is by Unicode, Inc. No claims are + * made as to fitness for any particular purpose. No warranties of any + * kind are expressed or implied. The recipient agrees to determine + * applicability of information provided. If this file has been + * purchased on magnetic or optical media from Unicode, Inc., the + * sole remedy for any claim will be exchange of defective media + * within 90 days of receipt. + * + * Limitations on Rights to Redistribute This Code + * + * Unicode, Inc. hereby grants the right to freely use the information + * supplied in this file in the creation of products supporting the + * Unicode Standard, and to make copies of this file in any form + * for internal or external distribution as long as this notice + * remains attached. + */ + +/* --------------------------------------------------------------------- + + Conversions between UTF32, UTF-16, and UTF-8. Source code file. + Author: Mark E. Davis, 1994. + Rev History: Rick McGowan, fixes & updates May 2001. + Sept 2001: fixed const & error conditions per + mods suggested by S. Parent & A. Lillich. + June 2002: Tim Dodd added detection and handling of incomplete + source sequences, enhanced error detection, added casts + to eliminate compiler warnings. + July 2003: slight mods to back out aggressive FFFE detection. + Jan 2004: updated switches in from-UTF8 conversions. + Oct 2004: updated to use UNI_MAX_LEGAL_UTF32 in UTF-32 conversions. + + See the header file "ConvertUTF.h" for complete documentation. + +------------------------------------------------------------------------ */ +var SURROGATE_HIGH_START = 0xD800; +var SURROGATE_HIGH_END = 0xDBFF; +var SURROGATE_LOW_START = 0xDC00; +var SURROGATE_LOW_END = 0xDFFF; +var REPLACEMENT_CHAR = 0xFFFD; +var FIRSTBYTEMARK = [0x00, 0xC0, 0xE0, 0xF0, 0xF8, 0xFC]; + +function UTF16ToUTF8(source) { + var target = ""; + var currentPos = 0; + for (var i = 0; i < source.length; i++) { + var c = source.charCodeAt(i); + if (c < 0x80) continue; + + if (i > currentPos) + target += source.substring(currentPos, i); + + if (c >= SURROGATE_HIGH_START && c <= SURROGATE_HIGH_END) { + i++; + if (i < source.length) { + var c2 = source.charCodeAt(i); + if (c2 >= SURROGATE_LOW_START && c2 <= SURROGATE_LOW_END) { + c = ((c - SURROGATE_HIGH_START) << 10) + (c2 - SURROGATE_LOW_START) + 0x10000; + } else { + // illegal second surrogate char + return null; + } + } else { + // missing second surrogate in pair + return null; + } + } else if (c >= SURROGATE_LOW_START && c <= SURROGATE_LOW_END) { + // stray surrogate + return null; + } + + currentPos = i + 1; + enc = []; + + var cc = c; + + if (cc >= 0x110000) { cc = 0x800; c = REPLACEMENT_CHAR; } + if (cc >= 0x10000) { enc.unshift(String.fromCharCode((c | 0x80) & 0xBF)); c >>= 6; } + if (cc >= 0x800) { enc.unshift(String.fromCharCode((c | 0x80) & 0xBF)); c >>= 6; } + if (cc >= 0x80) { enc.unshift(String.fromCharCode((c | 0x80) & 0xBF)); c >>= 6; } + + enc.unshift(String.fromCharCode( c | FIRSTBYTEMARK[enc.length])); + + target += enc.join(""); + } + + if (currentPos === 0) return source; + + if (i > currentPos) + target += source.substring(currentPos, i); + + return target; +} + +DISPLAY_NAME(UTF16ToUTF8); diff --git a/Objective-J/FileDependency.js b/Objective-J/FileDependency.js index c65861f7b..4c1e78167 100644 --- a/Objective-J/FileDependency.js +++ b/Objective-J/FileDependency.js @@ -49,4 +49,4 @@ FileDependency.prototype.toMarkedString = function() FileDependency.prototype.toString = function() { return (this.isLocal() ? "LOCAL: " : "STD: ") + this.URL(); -} +}; diff --git a/Objective-J/FileExecutable.js b/Objective-J/FileExecutable.js index d6e72e655..2fd4d88ac 100644 --- a/Objective-J/FileExecutable.js +++ b/Objective-J/FileExecutable.js @@ -41,11 +41,138 @@ function FileExecutable(/*CFURL|String*/ aURL, /*Dictionary*/ aFilenameTranslate executable = NULL, extension = aURL.pathExtension().toLowerCase(); + this._hasExecuted = NO; + if (fileContents.match(/^@STATIC;/)) executable = decompile(fileContents, aURL); else if ((extension === "j" || !extension) && !fileContents.match(/^{/)) { - var compiler = exports.ObjJCompiler.compileFileDependencies(fileContents, aURL, currentCompilerFlags || {}); + var compilerOptions = currentCompilerFlags || {}; + + this.cachedIncludeFileSearchResultsContent = {}; + this.cachedIncludeFileSearchResultsURL = {}; + compile(this, fileContents, aURL, compilerOptions, aFilenameTranslateDictionary); + return; + } + else + executable = new Executable(fileContents, [], aURL); + + Executable.apply(this, [executable.code(), executable.fileDependencies(), aURL, executable._function, executable._compiler, aFilenameTranslateDictionary]); +} + +exports.FileExecutable = FileExecutable; + +FileExecutable.prototype = new Executable(); + +var compile = function(self, fileContents, aURL, compilerOptions, aFilenameTranslateDictionary) +{ + var acornOptions = compilerOptions.acornOptions || (compilerOptions.acornOptions = {}); + + acornOptions.preprocessGetIncludeFile = function(filePath, isQuoted) { + var referenceURL = new CFURL(".", aURL), // Remove the filename from the url + includeURL = new CFURL(filePath); + + var cacheUID = (isQuoted && referenceURL || "") + includeURL, + cachedResult = self.cachedIncludeFileSearchResultsContent[cacheUID]; + + if (!cachedResult) { + var isAbsoluteURL = (includeURL instanceof CFURL) && includeURL.scheme(), + compileWhenCompleted = NO; + + function completed(/*StaticResource*/ aStaticResource) { + var includeString = aStaticResource && aStaticResource.contents(), + lastCharacter = includeString && includeString.charCodeAt(includeString.length - 1); + + if (includeString == null) throw new Error("Can't load file " + includeURL); + // Add a new line if the last character is not. If the last thing is a '#define' of other preprocess + // token it will not be handled correctly if we don't have a end of line at the end. + if (lastCharacter !== 10 && lastCharacter !== 13 && lastCharacter !== 8232 && lastCharacter !== 8233) { + includeString += '\n'; + } + + self.cachedIncludeFileSearchResultsContent[cacheUID] = includeString; + self.cachedIncludeFileSearchResultsURL[cacheUID] = aStaticResource.URL(); + + if (compileWhenCompleted) + compile(self, fileContents, aURL, compilerOptions, aFilenameTranslateDictionary); + } + + if (isQuoted || isAbsoluteURL) + { + if (!isAbsoluteURL) + includeURL = new CFURL(includeURL, new CFURL((aFilenameTranslateDictionary[aURL.lastPathComponent()] || "."), referenceURL)); + + StaticResource.resolveResourceAtURL(includeURL, NO, completed); + } + else + StaticResource.resolveResourceAtURLSearchingIncludeURLs(includeURL, completed); + + // Now we try to get the cached result again. If we get it then the completed function has already + // executed and we can return the include dictionary. + cachedResult = self.cachedIncludeFileSearchResultsContent[cacheUID]; + } + + if (cachedResult) { + return {include: cachedResult, sourceFile: self.cachedIncludeFileSearchResultsURL[cacheUID]}; + } else { + // When the file is not available (resolved) return null to tell the parser to throw an exception to exit + // Also tell the completed function to compile when finished. + compileWhenCompleted = YES + return null; + } + }; + + var includeFiles = currentCompilerFlags && currentCompilerFlags.includeFiles, + allPreIncludesResolved = true; + + acornOptions.preIncludeFiles = []; + + if (includeFiles) for (var i = 0, size = includeFiles.length; i < size; i++) + { + var includeFileUrl = makeAbsoluteURL(includeFiles[i]); + + try + { + // try to get all pre include files that acorn will parse before the file from 'aURL' + var aResource = StaticResource.resourceAtURL(makeAbsoluteURL(includeFileUrl)); + } + catch (e) + { + // Ok, the file is not available (resolved). Resolve all of the files and try again when available. + StaticResource.resolveResourcesAtURLs(includeFiles.map(function(u) {return makeAbsoluteURL(u)}), function() { + compile(self, fileContents, aURL, compilerOptions, aFilenameTranslateDictionary); + }); + + allPreIncludesResolved = false; + break; + } + + if (aResource) + { + if (aResource.isNotFound()) { + throw new Error("--include file not found " + includeUrl); + } + + var includeString = aResource.contents(); + var lastCharacter = includeString.charCodeAt(includeString.length - 1); + + // Add a new line if the last character is not. If the last thing is a '#define' of other preprocess + // token it will not be handled correctly if we don't have a end of line at the end. + if (lastCharacter !== 10 && lastCharacter !== 13 && lastCharacter !== 8232 && lastCharacter !== 8233) + includeString += '\n'; + acornOptions.preIncludeFiles.push({include: includeString, sourceFile: includeFileUrl.toString()}); + } + } + + if (allPreIncludesResolved) + { + var compiler = exports.ObjJCompiler.compileFileDependencies(fileContents, aURL, compilerOptions); + var warningsAndErrors = compiler.warningsAndErrors; + + // Kind of a hack but if we get a file not found error on a #include the get include function above should have asked for the resource + // so we should be able to just bail out and wait for the the next call to compile when the include file is loaded (resolved) + if (warningsAndErrors && warningsAndErrors.length === 1 && warningsAndErrors[0].message.indexOf("file not found") > -1) + return; if (FileExecutable.printWarningsAndErrors(compiler, exports.messageOutputFormatInXML)) throw "Compilation error"; @@ -53,19 +180,25 @@ function FileExecutable(/*CFURL|String*/ aURL, /*Dictionary*/ aFilenameTranslate var fileDependencies = compiler.dependencies.map(function (aFileDep) { return new FileDependency(new CFURL(aFileDep.url), aFileDep.isLocal); }); - executable = new Executable(compiler.jsBuffer ? compiler.jsBuffer.toString() : null, fileDependencies, compiler.URL, null, compiler); } - else - executable = new Executable(fileContents, [], aURL); - Executable.apply(this, [executable.code(), executable.fileDependencies(), aURL, executable._function, executable._compiler, aFilenameTranslateDictionary]); - - this._hasExecuted = NO; + if (self.isExecutableCantStartLoadYetFileDependencies()) + { + // Include files that was not loaded has cancelled the compiler so we are already an initialized Executable. + // Just set the status so we can start loading the file dependencies. + self.setFileDependencies(fileDependencies); + self.setExecutableUnloadedFileDependencies(); + self.loadFileDependencies(); + } + else if (self._fileDependencyStatus == null) + { + // Are we still a FileExecutable. Call 'super' init method to make us a initilized subclass of an Executable. + executable = new Executable(compiler && compiler.jsBuffer ? compiler.jsBuffer.toString() : null, fileDependencies, aURL, null, compiler); + Executable.apply(self, [executable.code(), executable.fileDependencies(), aURL, executable._function, executable._compiler, aFilenameTranslateDictionary]); + } } -exports.FileExecutable = FileExecutable; - -FileExecutable.prototype = new Executable(); +DISPLAY_NAME(compile); #ifdef COMMONJS FileExecutable.allFileExecutables = function() @@ -159,30 +292,7 @@ FileExecutable.setCurrentGccCompilerFlags = function(/*String*/ compilerFlags) currentGccCompilerFlags = compilerFlags; - var args = compilerFlags.split(" "), - count = args.length, - objjcFlags = {}; - - for (var index = 0; index < count; ++index) - { - var argument = args[index]; - - if (argument.indexOf("-g") === 0) - objjcFlags.includeMethodFunctionNames = true; - else if (argument.indexOf("-O") === 0) { - objjcFlags.inlineMsgSendFunctions = true; - // FIXME: currently we are sending in '-O2' when we want InlineMsgSend. Here we only check if it is '-O...'. - // Maybe we should have some other option for this - if (argument.length > 2) - objjcFlags.inlineMsgSendFunctions = true; - } - //else if (argument.indexOf("-G") === 0) - //objjcFlags |= ObjJAcornCompiler.Flags.Generate; - else if (argument.indexOf("-T") === 0) { - objjcFlags.includeIvarTypeSignatures = false; - objjcFlags.includeMethodArgumentTypeSignatures = false; - } - } + var objjcFlags = exports.ObjJCompiler.parseGccCompilerFlags(compilerFlags); FileExecutable.setCurrentCompilerFlags(objjcFlags); } diff --git a/Objective-J/Jakefile b/Objective-J/Jakefile index 5c5ef03ca..47840bf90 100644 --- a/Objective-J/Jakefile +++ b/Objective-J/Jakefile @@ -26,6 +26,13 @@ var FILE = require("file"), OS = require("os"), stream = require("narwhal/term").stream; + var walk = require("./acornwalk").acorn.walk; + +acorn = {walk: walk}; +acorn = require("./acorn").acorn; + + var compiler = require("./ObjJAcornCompiler").ObjJCompiler; + //$BUILD_CONFIGURATION_DIR = "../Build" $BROWSER_FILE = FILE.join("Browser", "Objective-J.js"); @@ -106,7 +113,7 @@ function environmentFlags() return environments.map(function(anEnvironment) { return "-D" + anEnvironment.toUpperCase(); - }).concat("-DENVIRONMENTS=" + OS.enquote(JSON.stringify(environments))); + }).concat("-DENVIRONMENTS=" + JSON.stringify(environments)); } var SHRINKSAFE = require("minify/shrinksafe"); @@ -114,26 +121,41 @@ function compressor(code) { return SHRINKSAFE.compress(code, { charset : "UTF-8", useServer : true }); } +var headerText = FILE.read("header.txt", { charset : "UTF-8" }); function gcc(inputFilePath, outputFilePath, flags, compress) { stream.print("Building... \0green(" + outputFilePath +"\0)"); - // GCC preprocess the file. - var cmd = ["gcc", "-E", "-x", "c", "-P"].concat(flags, inputFilePath).join(" "), - contents = FILE.read("header.txt", { charset : "UTF-8" }); + var source = FILE.read(inputFilePath, { charset : "UTF-8" }); + var compilerOptions = compiler.parseGccCompilerFlags(flags.join(" ")); + var acornOptions = compilerOptions.acornOptions || (compilerOptions.acornOptions = {}); - try - { - var gcc = OS.popen(cmd, { charset:"UTF-8" }); - contents += gcc.stdout.read(); + acornOptions.preprocessGetIncludeFile = function(filePath, isQuoted) { + var includeContent = FILE.read(filePath, { charset : "UTF-8" }); + //print ("Include content for file '" + filePath + "': " + includeContent); + //print ("Include file '" + filePath + "'"); + return {include: includeContent, sourceFile: filePath}; } - finally + + var c = compiler.compile(source, inputFilePath, compilerOptions); + var warnings = [], + anyErrors = false; + + for (var i = 0; i < c.warningsAndErrors.length; i++) { - gcc.stdin.close(); - gcc.stdout.close(); - gcc.stderr.close(); + var warning = c.warningsAndErrors[i], + message = c.prettifyMessage(warning); + + // Set anyErrors to 'true' if there are any errors in the list + anyErrors = anyErrors || warning.messageType === "ERROR"; + print(message); } + if (anyErrors) throw "Compilation Error"; + + var code = c.code(); + var contents = headerText + code; + if (FILE.extension(inputFilePath) === ".js" && compress) contents = compressor(contents); diff --git a/Objective-J/ObjJAcornCompiler.js b/Objective-J/ObjJAcornCompiler.js index b6b279970..7bc57f61d 100644 --- a/Objective-J/ObjJAcornCompiler.js +++ b/Objective-J/ObjJAcornCompiler.js @@ -16,14 +16,12 @@ (function(mod) { - //print("Compiler INIT! exports: " + typeof exports + ", module: " + typeof module + ", define: " + typeof define); - mod(exports.ObjJCompiler || (exports.ObjJCompiler = {}), exports.acorn, exports.acorn.walk/*, sourceMap*/); // Plain browser env + mod(exports.ObjJCompiler || (exports.ObjJCompiler = {}), exports.acorn || acorn, (exports.acorn || acorn).walk, typeof sourceMap != "undefined" ? sourceMap : null); // Plain browser env })(function(exports, acorn, walk, sourceMap) { "use strict"; exports.version = "0.3.7"; -//exports.acorn = acorn; var Scope = function(prev, base) { @@ -280,7 +278,7 @@ GlobalVariableMaybeWarning.prototype.isEqualTo = function(/* GlobalVariableMaybe return true; } -function StringBuffer(useSourceNode, file) +function StringBuffer(useSourceNode, file, sourceContent) { if (useSourceNode) { this.rootNode = new sourceMap.SourceNode(); @@ -289,8 +287,21 @@ function StringBuffer(useSourceNode, file) this.isEmpty = this.isEmptySourceNode; this.appendStringBuffer = this.appendStringBufferSourceNode; this.length = this.lengthSourceNode; - if (file) - this.file = file.toString(); + if (file) { + var fileString = file.toString(), + filename = fileString.substr(fileString.lastIndexOf('/') + 1), + sourceRoot = fileString.substr(0, fileString.lastIndexOf('/') + 1); + + this.filename = filename; + + if (sourceRoot.length > 0) + this.sourceRoot = sourceRoot; + if (sourceContent != null) + this.rootNode.setSourceContent(filename, sourceContent); + } + + if (sourceContent != null) + this.sourceContent = sourceContent; } else { this.atoms = []; this.concat = this.concatString; @@ -308,7 +319,7 @@ StringBuffer.prototype.toStringString = function() StringBuffer.prototype.toStringSourceNode = function() { - return this.rootNode.toStringWithSourceMap({file: this.file}); + return this.rootNode.toStringWithSourceMap({file: this.filename + "s", sourceRoot:this.sourceRoot}); } StringBuffer.prototype.concatString = function(aString) @@ -316,11 +327,11 @@ StringBuffer.prototype.concatString = function(aString) this.atoms.push(aString); } -StringBuffer.prototype.concatSourceNode = function(aString, node) +StringBuffer.prototype.concatSourceNode = function(aString, node, originalName) { if (node) { //console.log("Snippet: " + aString + ", line: " + node.loc.start.line + ", column: " + node.loc.start.column + ", source: " + node.loc.source); - this.rootNode.add(new sourceMap.SourceNode(node.loc.start.line, node.loc.start.column, node.loc.source, aString)); + this.rootNode.add(new sourceMap.SourceNode(node.loc.start.line, node.loc.start.column, node.loc.source, aString, originalName)); } else this.rootNode.add(aString); if (!this.notEmpty) @@ -624,10 +635,13 @@ var isInInstanceof = acorn.makePredicate("in instanceof"); // Turn on `sourceMap` generate a source map for the compiler file. sourceMap: false, + // Turn on `sourceMapIncludeSource` will include the source code in the source map. + sourceMapIncludeSource: false, + // The compiler can do different passes. // 1: Parse and walk AST tree to collect file dependencies. // 2: Parse and walk to generate code. - // Pass one is only for when the Objective-J load and runtime. + // Pass one is only for the Objective-J load and runtime. pass: 2, // Pass in class definitions. New class definitions in source file will be added here when compiling. @@ -678,6 +692,12 @@ var isInInstanceof = acorn.makePredicate("in instanceof"); // Turn off `inlineMsgSendFunctions` to use message send functions. Needed to use message send decorators. inlineMsgSendFunctions: true, + + // An array of macro objects and/or text definitions may be passed in. + // Definitions may be in one of two forms: + // macro + // macro=body + macros: null, }; // We copy the options to a new object as we don't want to mess up incoming options when we start compiling. @@ -710,7 +730,7 @@ var ObjJAcornCompiler = function(/*String*/ aString, /*CFURL*/ aURL, options) this.formatDescription = options.formatDescription; this.includeComments = options.includeComments; this.transformNamedFunctionDeclarationToAssignment = options.transformNamedFunctionDeclarationToAssignment; - this.jsBuffer = new StringBuffer(this.createSourceMap, aURL); + this.jsBuffer = new StringBuffer(this.createSourceMap, aURL, options.sourceMap && options.sourceMapIncludeSource ? this.source : null); this.imBuffer = null; this.cmBuffer = null; this.dependencies = []; @@ -727,7 +747,7 @@ var ObjJAcornCompiler = function(/*String*/ aString, /*CFURL*/ aURL, options) if (acornOptions) { - if (!acornOptions.sourceFile && this.URL) + if (this.URL) acornOptions.sourceFile = this.URL.substr(this.URL.lastIndexOf('/') + 1); if (options.sourceMap && !acornOptions.locations) acornOptions.locations = true; @@ -739,6 +759,14 @@ var ObjJAcornCompiler = function(/*String*/ aString, /*CFURL*/ aURL, options) acornOptions.locations = true; } + if (options.macros) + { + if (acornOptions.macros) + acornOptions.macros.concat(options.macros); + else + acornOptions.macros = options.macros; + } + try { this.tokens = acorn.parse(aString, options.acornOptions); (this.pass === 2 && (options.includeComments || options.formatDescription) ? compileWithFormat : compile)(this.tokens, new Scope(null ,{ compiler: this }), this.pass === 2 ? pass2 : pass1); @@ -795,14 +823,16 @@ exports.compileFileDependencies = function(/*String*/ aString, /*CFURL*/ aURL, o ObjJAcornCompiler.prototype.compilePass2 = function() { + var options = this.options; + exports.currentCompileFile = this.URL; - this.pass = this.options.pass = 2; - this.jsBuffer = new StringBuffer(this.createSourceMap, this.URL); + this.pass = options.pass = 2; + this.jsBuffer = new StringBuffer(this.createSourceMap, this.URL, options.sourceMap && options.sourceMapIncludeSource ? this.source : null); // To get the source mapping correct when the new Function construtor is used we add a // new line as first thing in the code. if (this.createSourceMap) - this.jsBuffer.concat("\n"); + this.jsBuffer.concat("\n\n"); this.warningsAndErrors = []; try { @@ -959,6 +989,76 @@ ObjJAcornCompiler.prototype.getTypeDef = function(/* String */ aTypeDefName) return null; } +/*! + Return a parsed option dictionary + */ +exports.parseGccCompilerFlags = function(/* String */ compilerFlags) +{ + var args = (compilerFlags || "").split(" "), + count = args.length, + objjcFlags = {}; + + for (var index = 0; index < count; ++index) + { + var argument = args[index]; + + if (argument.indexOf("-g") === 0) + objjcFlags.includeMethodFunctionNames = true; + else if (argument.indexOf("-O") === 0) { + objjcFlags.inlineMsgSendFunctions = true; + // FIXME: currently we are sending in '-O2' when we want InlineMsgSend. Here we only check if it is '-O...'. + // Maybe we should have some other option for this + if (argument.length > 2) + objjcFlags.inlineMsgSendFunctions = true; + } + //else if (argument.indexOf("-G") === 0) + //objjcFlags |= ObjJAcornCompiler.Flags.Generate; + else if (argument.indexOf("-T") === 0) { + objjcFlags.includeIvarTypeSignatures = false; + objjcFlags.includeMethodArgumentTypeSignatures = false; + } + else if (argument.indexOf("-S") === 0) { + objjcFlags.sourceMap = true; + objjcFlags.sourceMapIncludeSource = true; + } + else if (argument.indexOf("--include") === 0) { + var includeUrl = args[++index], + firstChar = includeUrl && includeUrl.charCodeAt(0); + + // Poor mans unquote + if (firstChar === 34 || firstChar === 39) // '"', "'" + includeUrl = includeUrl.substring(1, includeUrl.length - 1); + + (objjcFlags.includeFiles || (objjcFlags.includeFiles = [])).push(includeUrl); + } +/* else if (argument.indexOf("-I") === 0) { + var includeUrl = argument.substring(2), + firstChar = includeUrl && includeUrl.charCodeAt(0); + + (objjcFlags.includeFiles || (objjcFlags.includeFiles = [])).push(includeUrl); + } + else if (argument.indexOf("'-I") === 0) { + var includeUrl = argument.substring(3, argument.length - 1), + firstChar = includeUrl && includeUrl.charCodeAt(0); + + (objjcFlags.includeFiles || (objjcFlags.includeFiles = [])).push(includeUrl); + } + else if (argument.indexOf('"-I') === 0) { + var includeUrl = argument.substring(3, argument.length - 1), + firstChar = includeUrl && includeUrl.charCodeAt(0); + + (objjcFlags.includeFiles || (objjcFlags.includeFiles = [])).push(includeUrl); + }*/ + else if (argument.indexOf("-D") === 0) { + var macroDefinition = argument.substring(2); + + (objjcFlags.macros || (objjcFlags.macros = [])).push(macroDefinition); + } + } + + return objjcFlags; +} + ObjJAcornCompiler.methodDefsFromMethodList = function(/* Array */ methodList) { var methodSize = methodList.length, @@ -1006,10 +1106,10 @@ ObjJAcornCompiler.prototype.map = function() ObjJAcornCompiler.prototype.prettifyMessage = function(/* Message */ aMessage) { var line = aMessage.messageForLine, - message = "\n" + line; + message = "\n" + (line || ""); - message += (new Array(aMessage.messageOnColumn + 1)).join(" "); - message += (new Array(Math.min(1, line.length) + 1)).join("^") + "\n"; + message += (new Array((aMessage.messageOnColumn || 0) + 1)).join(" "); + if (line) message += (new Array(Math.min(1, line.length || 1) + 1)).join("^") + "\n"; message += aMessage.messageType + " line " + aMessage.messageOnLine + " in " + this.URL + ": " + aMessage.message; return message; @@ -1309,13 +1409,13 @@ BlockStatement: function(node, st, c, format) { } //Simulate a node for the last curly bracket - var endNode = node.loc && { loc: { start: { line : node.loc.end.line, column: node.loc.end.column-1}}, source: node.loc.source}; +// var endNode = node.loc && { loc: { start: { line : node.loc.end.line, column: node.loc.end.column}}, source: node.loc.source}; if (format) { buffer.concatFormat(format.beforeRightBrace); - buffer.concat("}", endNode); + buffer.concat("}", node); } else { buffer.concat(indentation.substring(indentationSize)); - buffer.concat("}", endNode); + buffer.concat("}", node); if (!skipIndentation && st.isDecl !== false) buffer.concat("\n"); st.indentBlockLevel--; @@ -1351,11 +1451,11 @@ IfStatement: function(node, st, c, format) { c(node.test, st, "Expression"); if (generate) { if (format) { - buffer.concat(")"); + buffer.concat(")", node); buffer.concatFormat(format.afterRightParenthesis); } else { // We don't want EmptyStatements to generate an extra parenthesis except when it is in a while, for, ... - buffer.concat(node.consequent.type === "EmptyStatement" ? ");\n" : ")\n"); + buffer.concat(node.consequent.type === "EmptyStatement" ? ");\n" : ")\n", node); } } indentation += indentStep; @@ -1367,13 +1467,13 @@ IfStatement: function(node, st, c, format) { if (generate) { if (format) { buffer.concatFormat(format.beforeElse); // Do we need this? - buffer.concat("else"); + buffer.concat("else", node); buffer.concatFormat(format.afterElse); } else { var emptyStatement = alternate.type === "EmptyStatement"; buffer.concat(indentation); // We don't want EmptyStatements to generate an extra parenthesis except when it is in a while, for, ... - buffer.concat(alternateNotIf ? emptyStatement ? "else;\n" : "else\n" : "else "); + buffer.concat(alternateNotIf ? emptyStatement ? "else;\n" : "else\n" : "else ", node); } } if (alternateNotIf) @@ -1392,10 +1492,10 @@ LabeledStatement: function(node, st, c, format) { if (!format) buffer.concat(indentation); c(node.label, st, "IdentifierName"); if (format) { - buffer.concat(":"); + buffer.concat(":", node); buffer.concatFormat(format.afterColon); } else { - buffer.concat(": "); + buffer.concat(": ", node); } } c(node.body, st, "Statement"); @@ -1456,10 +1556,10 @@ WithStatement: function(node, st, c, format) { c(node.object, st, "Expression"); if (generate) if (format) { - buffer.concat(")"); + buffer.concat(")", node); buffer.concatFormat(format.afterRightParenthesis); } else { - buffer.concat(")\n"); + buffer.concat(")\n", node); } indentation += indentStep; c(node.body, st, "Statement"); @@ -1968,7 +2068,7 @@ SequenceExpression: function(node, st, c, format) { buffer; if (generate) { buffer = compiler.jsBuffer; - buffer.concat("("); + buffer.concat("(", node); } for (var i = 0; i < node.expressions.length; ++i) { if (generate && i !== 0) @@ -2079,7 +2179,7 @@ AssignmentExpression: function(node, st, c, format) { if (!generate) buffer.concat(compiler.source.substring(compiler.lastPos, node.start)); // Output the dereference function, "(...)(z)" - buffer.concat("("); + buffer.concat("(", node); // What's being dereferenced could itself be an expression, such as when dereferencing a deref. if (!generate) compiler.lastPos = node.left.expr.start; c(node.left.expr, st, "Expression"); @@ -2269,7 +2369,7 @@ Identifier: function(node, st, c) { st.addMaybeWarning(message); } } - if (generate) compiler.jsBuffer.concat(identifier, node); + if (generate) compiler.jsBuffer.concat(identifier, node, "self"); }, // Use this when there should not be a look up to issue warnings or add 'self.' before ivars IdentifierName: function(node, st, c) { @@ -2316,7 +2416,7 @@ ArrayLiteral: function(node, st, c) { buffer.concat("@["); } else if (!elementLength) { if (compiler.options.inlineMsgSendFunctions) { - buffer.concat("(___r"); + buffer.concat("(___r", node); buffer.concat(++st.receiverLevel + ""); buffer.concat(" = (CPArray.isa.method_msgSend[\"alloc\"] || _objj_forward)(CPArray, \"alloc\"), ___r"); buffer.concat(st.receiverLevel + ""); @@ -2341,7 +2441,7 @@ ArrayLiteral: function(node, st, c) { st.maxReceiverLevel = st.receiverLevel; } else { if (compiler.options.inlineMsgSendFunctions) { - buffer.concat("(___r"); + buffer.concat("(___r", node); buffer.concat(++st.receiverLevel + ""); buffer.concat(" = (CPArray.isa.method_msgSend[\"alloc\"] || _objj_forward)(CPArray, \"alloc\"), ___r"); buffer.concat(st.receiverLevel + ""); @@ -2351,7 +2451,7 @@ ArrayLiteral: function(node, st, c) { buffer.concat(st.receiverLevel + ""); buffer.concat(", \"initWithObjects:count:\", ["); } else { - buffer.concat("(___r"); + buffer.concat("(___r", node); buffer.concat(++st.receiverLevel + ""); buffer.concat(" = CPArray.isa.objj_msgSend0(CPArray, \"alloc\"), ___r"); buffer.concat(st.receiverLevel + ""); @@ -2410,7 +2510,7 @@ DictionaryLiteral: function(node, st, c) { buffer.concat("}"); } else if (!keyLength) { if (compiler.options.inlineMsgSendFunctions) { - buffer.concat("(___r"); + buffer.concat("(___r", node); buffer.concat(++st.receiverLevel + ""); buffer.concat(" = (CPDictionary.isa.method_msgSend[\"alloc\"] || _objj_forward)(CPDictionary, \"alloc\"), ___r"); buffer.concat(st.receiverLevel + ""); @@ -2435,7 +2535,7 @@ DictionaryLiteral: function(node, st, c) { st.maxReceiverLevel = st.receiverLevel; } else { if (compiler.options.inlineMsgSendFunctions) { - buffer.concat("(___r"); + buffer.concat("(___r", node); buffer.concat(++st.receiverLevel + ""); buffer.concat(" = (CPDictionary.isa.method_msgSend[\"alloc\"] || _objj_forward)(CPDictionary, \"alloc\"), ___r"); buffer.concat(st.receiverLevel + ""); @@ -2445,7 +2545,7 @@ DictionaryLiteral: function(node, st, c) { buffer.concat(st.receiverLevel + ""); buffer.concat(", \"initWithObjects:forKeys:\", ["); } else { - buffer.concat("(___r"); + buffer.concat("(___r", node); buffer.concat(++st.receiverLevel + ""); buffer.concat(" = CPDictionary.isa.objj_msgSend0(CPDictionary, \"alloc\"), ___r"); buffer.concat(st.receiverLevel + ""); @@ -2515,10 +2615,11 @@ ClassDeclarationStatement: function(node, st, c, format) { classScope = new Scope(st), isInterfaceDeclaration = node.type === "InterfaceDeclarationStatement", protocols = node.protocols, - generateObjJ = compiler.options.generateObjJ; + options = compiler.options, + generateObjJ = options.generateObjJ; - compiler.imBuffer = new StringBuffer(compiler.createSourceMap, compiler.URL); - compiler.cmBuffer = new StringBuffer(compiler.createSourceMap), compiler.URL; + compiler.imBuffer = new StringBuffer(compiler.createSourceMap, compiler.URL, options.sourceMap && options.sourceMapIncludeSource ? compiler.source : null); + compiler.cmBuffer = new StringBuffer(compiler.createSourceMap, compiler.URL); compiler.classBodyBuffer = new StringBuffer(compiler.createSourceMap, compiler.URL); // TODO: Check if this is needed if (compiler.getTypeDef(className)) @@ -2660,7 +2761,7 @@ ClassDeclarationStatement: function(node, st, c, format) { else saveJSBuffer.concat(", "); - if (compiler.options.includeIvarTypeSignatures) + if (options.includeIvarTypeSignatures) saveJSBuffer.concat("new objj_ivar(\"" + ivarName + "\", \"" + ivarType + "\")", node); else saveJSBuffer.concat("new objj_ivar(\"" + ivarName + "\")", node); @@ -2708,7 +2809,8 @@ ClassDeclarationStatement: function(node, st, c, format) { // If we have accessors add get and set methods for them if (!generateObjJ && !isInterfaceDeclaration && hasAccessors) { - var getterSetterBuffer = new StringBuffer(compiler.createSourceMap, compiler.URL); + // We pass false to the string buffer as we don't need source map when we create the Objective-J code for the accessors + var getterSetterBuffer = new StringBuffer(false); // Add the class declaration to compile accessors correctly // Remove all protocols from class declaration @@ -2727,7 +2829,7 @@ ClassDeclarationStatement: function(node, st, c, format) { var property = (accessors.property && accessors.property.name) || ivarName, getterName = (accessors.getter && accessors.getter.name) || property, - getterCode = "- (" + (ivarType ? ivarType : "id") + ")" + getterName + "\n{\nreturn " + ivarName + ";\n}\n"; + getterCode = "- (" + (ivarType ? ivarType : "id") + ")" + getterName + "\n{\n return " + ivarName + ";\n}\n"; getterSetterBuffer.concat(getterCode); @@ -2743,10 +2845,10 @@ ClassDeclarationStatement: function(node, st, c, format) { setterName = (start ? "_" : "") + "set" + property.substr(start, 1).toUpperCase() + property.substring(start + 1) + ":"; } - var setterCode = "- (void)" + setterName + "(" + (ivarType ? ivarType : "id") + ")newValue\n{\n"; + var setterCode = "- (void)" + setterName + "(" + (ivarType ? ivarType : "id") + ")newValue\n{\n "; if (accessors.copy) - setterCode += "if (" + ivarName + " !== newValue)\n" + ivarName + " = [newValue copy];\n}\n"; + setterCode += "if (" + ivarName + " !== newValue)\n " + ivarName + " = [newValue copy];\n}\n"; else setterCode += ivarName + " = newValue;\n}\n"; @@ -2757,11 +2859,26 @@ ClassDeclarationStatement: function(node, st, c, format) { // Remove all @accessors or we will get a recursive loop in infinity var b = getterSetterBuffer.toString().replace(/@accessors(\(.*\))?/g, ""); - var imBuffer = exports.compileToIMBuffer(b, "Accessors", compiler.options); + var compilerOptions = setupOptions(options); + + compilerOptions.sourceMapIncludeSource = true; + var url = compiler.url; + var filename = url && compiler.URL.substr(compiler.URL.lastIndexOf('/') + 1); + var dotIndex = filename && filename.lastIndexOf("."); + var filenameNoExt = filename && (filename.substr(0, dotIndex === -1 ? filename.length : dotIndex)); + var filenameExt = filename && filename.substr(dotIndex === -1 ? filename.length : dotIndex); + var categoryname = node.categoryname && node.categoryname.id; + var imBuffer = exports.compileToIMBuffer(b, filenameNoExt + "_" + className + (categoryname ? "_" + categoryname : "") + "_Accessors" + (filenameExt || ""), compilerOptions); // Add the accessors methods first to instance method buffer. // This will allow manually added set and get methods to override the compiler generated - compiler.imBuffer.concat(imBuffer); + var generatedCode = imBuffer.toString(); + + if (compiler.createSourceMap) { + compiler.imBuffer.concat(sourceMap.SourceNode.fromStringWithSourceMap(generatedCode.code, sourceMap.SourceMapConsumer(generatedCode.map.toString()))); + } else { + compiler.imBuffer.concat(generatedCode); + } } // We will store the ivars into the classDef first after accessors are done so we don't get a duplicate ivars error when generating accessors @@ -2865,7 +2982,7 @@ ProtocolDeclarationStatement: function(node, st, c) { throw compiler.error_message("Duplicate protocol " + protocolName, node.protocolname); compiler.imBuffer = new StringBuffer(compiler.createSourceMap, compiler.URL); - compiler.cmBuffer = new StringBuffer(compiler.createSourceMap), compiler.URL; + compiler.cmBuffer = new StringBuffer(compiler.createSourceMap, compiler.URL); if (!generate) buffer.concat(compiler.source.substring(compiler.lastPos, node.start)); @@ -3227,17 +3344,17 @@ MessageSendExpression: function(node, st, c) { buffer.concat("[super "); } else { if (inlineMsgSend) { - buffer.concat("("); + buffer.concat("(", node); buffer.concat(st.currentMethodType() === "+" ? compiler.currentSuperMetaClass : compiler.currentSuperClass); - buffer.concat(".method_dtable[\""); + buffer.concat(".method_dtable[\"", node); buffer.concat(selector); - buffer.concat("\"] || _objj_forward)(self"); + buffer.concat("\"] || _objj_forward)(self", node); } else { - buffer.concat("objj_msgSendSuper"); + buffer.concat("objj_msgSendSuper", node); if (totalNoOfParameters < 4) { buffer.concat("" + totalNoOfParameters); } - buffer.concat("({ receiver:self, super_class:" + (st.currentMethodType() === "+" ? compiler.currentSuperMetaClass : compiler.currentSuperClass ) + " }"); + buffer.concat("({ receiver:self, super_class:" + (st.currentMethodType() === "+" ? compiler.currentSuperMetaClass : compiler.currentSuperClass ) + " }", node); } } } @@ -3261,36 +3378,35 @@ MessageSendExpression: function(node, st, c) { } if (receiverIsNotSelf) { - buffer.concat("("); + buffer.concat("(", node); c(nodeObject, st, "Expression"); - buffer.concat(" == null ? null : "); + buffer.concat(" == null ? null : ", node); } if (inlineMsgSend) - buffer.concat("("); + buffer.concat("(", node); c(nodeObject, st, "Expression"); } else { receiverIsNotSelf = true; if (!st.receiverLevel) st.receiverLevel = 0; - buffer.concat("((___r"); - buffer.concat(++st.receiverLevel + ""); - buffer.concat(" = "); + buffer.concat("((___r" + ++st.receiverLevel, node); + buffer.concat(" = ", node); c(nodeObject, st, "Expression"); - buffer.concat("), ___r"); - buffer.concat(st.receiverLevel + ""); - buffer.concat(" == null ? null : "); + buffer.concat(")", node); + buffer.concat(", ___r" + st.receiverLevel, node); + buffer.concat(" == null ? null : ", node); if (inlineMsgSend) - buffer.concat("("); - buffer.concat("___r"); - buffer.concat(st.receiverLevel + ""); + buffer.concat("(", node); + buffer.concat("___r" + st.receiverLevel, node); if (!(st.maxReceiverLevel >= st.receiverLevel)) st.maxReceiverLevel = st.receiverLevel; } if (inlineMsgSend) { - buffer.concat(".isa.method_msgSend[\""); - buffer.concat(selector); - buffer.concat("\"] || _objj_forward)"); - } else - buffer.concat(".isa.objj_msgSend"); + buffer.concat(".isa.method_msgSend[\"", node); + buffer.concat(selector, node); + buffer.concat("\"] || _objj_forward)", node); + } else { + buffer.concat(".isa.objj_msgSend", node); + } } else { buffer.concat(" "); // Add an extra space if it looks something like this: "return()". No space between return and expression. buffer.concat("objj_msgSend("); @@ -3325,28 +3441,27 @@ MessageSendExpression: function(node, st, c) { if (generate && !node.superObject) { if (!inlineMsgSend) { if (totalNoOfParameters < 4) { - buffer.concat("" + totalNoOfParameters); + buffer.concat("" + totalNoOfParameters, null); } } if (receiverIsIdentifier) { - buffer.concat("("); + buffer.concat("(", node); c(nodeObject, st, "Expression"); } else { - buffer.concat("(___r"); - buffer.concat(st.receiverLevel + ""); + buffer.concat("(___r" + st.receiverLevel, node); } } - buffer.concat(", \""); + buffer.concat(", \"", node); buffer.concat(selector); // FIXME: sel_getUid(selector + "") ? This FIXME is from the old preprocessor compiler - buffer.concat("\""); + buffer.concat("\"", node); if (nodeArguments) for (var i = 0; i < nodeArguments.length; i++) { var argument = nodeArguments[i]; - buffer.concat(", "); + buffer.concat(", ", node); if (!generate) compiler.lastPos = argument.start; c(argument, st, "Expression"); @@ -3360,7 +3475,7 @@ MessageSendExpression: function(node, st, c) { { var parameter = parameters[i]; - buffer.concat(", "); + buffer.concat(", ", node); if (!generate) compiler.lastPos = parameter.start; c(parameter, st, "Expression"); @@ -3372,12 +3487,12 @@ MessageSendExpression: function(node, st, c) { if (generate && !node.superObject) { if (receiverIsNotSelf) - buffer.concat(")"); + buffer.concat(")", node); if (!receiverIsIdentifier) st.receiverLevel--; } - buffer.concat(")"); + buffer.concat(")", node); } if (!generate) compiler.lastPos = node.end; @@ -3525,7 +3640,7 @@ TypeDefStatement: function(node, st, c) { if (!generate) buffer.concat(compiler.source.substring(compiler.lastPos, node.start)); - buffer.concat("{var the_typedef = objj_allocateTypeDef(\"" + typeDefName + "\");"); + buffer.concat("{var the_typedef = objj_allocateTypeDef(\"" + typeDefName + "\");", node); typeDef = new TypeDef(typeDefName); compiler.typeDefs[typeDefName] = typeDef; diff --git a/Objective-J/OldBrowserCompatibility.js b/Objective-J/OldBrowserCompatibility.js index cd3d30517..d4e43b937 100644 --- a/Objective-J/OldBrowserCompatibility.js +++ b/Objective-J/OldBrowserCompatibility.js @@ -115,3 +115,24 @@ if (!Array.prototype.indexOf) return -1; }; } + +// ECMAScript 6 has added this. It is copied from https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/String +if (!String.prototype.startsWith) { + String.prototype.startsWith = function(searchString, position){ + position = position || 0; + return this.substr(position, searchString.length) === searchString; + }; +} + +// ECMAScript 6 has added this. It is copied from https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/String +if (!String.prototype.endsWith) { + String.prototype.endsWith = function(searchString, position) { + var subjectString = this.toString(); + if (typeof position !== 'number' || !isFinite(position) || Math.floor(position) !== position || position > subjectString.length) { + position = subjectString.length; + } + position -= searchString.length; + var lastIndex = subjectString.indexOf(searchString, position); + return lastIndex !== -1 && lastIndex === position; + }; +}; \ No newline at end of file diff --git a/Objective-J/Preprocessor.js b/Objective-J/Preprocessor.js index 84489f1e0..00c6f3326 100644 --- a/Objective-J/Preprocessor.js +++ b/Objective-J/Preprocessor.js @@ -1011,4 +1011,4 @@ Preprocessor.prototype.error_message = function(errorMessage) return errorMessage + " "; -} +}; diff --git a/Objective-J/Runtime.js b/Objective-J/Runtime.js index 5fabc9c0d..bbc026574 100644 --- a/Objective-J/Runtime.js +++ b/Objective-J/Runtime.js @@ -646,11 +646,15 @@ GLOBAL(objj_allocateClassPair) = function(/*Class*/ superclass, /*String*/ aName classObject.name = aName; classObject.info = CLS_CLASS; classObject._UID = objj_generateObjectUID(); + // It needs initialize + classObject.init = true; metaClassObject.isa = rootClassObject.isa; metaClassObject.name = aName; metaClassObject.info = CLS_META; metaClassObject._UID = objj_generateObjectUID(); + // It needs initialize + metaClassObject.init = true; return classObject; } @@ -837,7 +841,16 @@ GLOBAL(objj_msgSend) = function(/*id*/ aReceiver, /*SEL*/ aSelector) var isa = aReceiver.isa; - CLASS_GET_METHOD_IMPLEMENTATION(var implementation, isa, aSelector); + // Here we should do the following line: CLASS_GET_METHOD_IMPLEMENTATION(var implementation, isa, aSelector); + // But set the 'init' attribute to 'true' when register the class pair and just check for that here is around 20% faster depending + // on environment. The '_class_initialize' function sets the 'init' attribute to 'false' when the class is initialized + + if (isa.init) + _class_initialize(isa); + + var method = isa.method_dtable[aSelector]; + + var implementation = method ? method.method_imp : _objj_forward; #ifdef MAXIMUM_RECURSION_CHECKS if (__objj_msgSend__StackDepth++ > MAXIMUM_RECURSION_DEPTH) @@ -851,6 +864,9 @@ GLOBAL(objj_msgSend) = function(/*id*/ aReceiver, /*SEL*/ aSelector) case 2: return implementation(aReceiver, aSelector); case 3: return implementation(aReceiver, aSelector, arguments[2]); case 4: return implementation(aReceiver, aSelector, arguments[2], arguments[3]); + case 5: return implementation(aReceiver, aSelector, arguments[2], arguments[3], arguments[4]); + case 6: return implementation(aReceiver, aSelector, arguments[2], arguments[3], arguments[4], arguments[5]); + case 7: return implementation(aReceiver, aSelector, arguments[2], arguments[3], arguments[4], arguments[5], arguments[6]); } return implementation.apply(aReceiver, arguments); diff --git a/Objective-J/StaticResource.js b/Objective-J/StaticResource.js index 292b6bcae..2a4d68c2b 100644 --- a/Objective-J/StaticResource.js +++ b/Objective-J/StaticResource.js @@ -197,6 +197,27 @@ StaticResource.prototype.resourceAtURL = function(/*CFURL|String*/ aURL, /*BOOL* return StaticResource.resourceAtURL(new CFURL(aURL, this.URL()), resolveAsDirectoriesIfNecessary); }; +/*! + * Returns an object with all the resources. Can not be directories. + */ +StaticResource.resolveResourcesAtURLs = function(/*Array of CFURL|String*/ URLs, /*Function*/ aCallback) +{ + var count = URLs.length, + allResources = {}; + + for (var i = 0, size = count; i < size; i++) + { + var url = URLs[i]; + + StaticResource.resolveResourceAtURL(url, NO, function(aResource) { + allResources[url] = aResource; + + if (--count === 0) + aCallback(allResources); + }); + } +} + StaticResource.resolveResourceAtURL = function(/*CFURL|String*/ aURL, /*BOOL*/ isDirectory, /*Function*/ aCallback, /*Dictionary*/ aFilenameTranslateDictionary) { aURL = makeAbsoluteURL(aURL).absoluteURL(); diff --git a/Objective-J/acorn.js b/Objective-J/acorn.js index feb02694c..f493aff8b 100644 --- a/Objective-J/acorn.js +++ b/Objective-J/acorn.js @@ -26,10 +26,6 @@ // [dammit]: acorn_loose.js // [walk]: util/walk.js -if (typeof exports != "undefined" && !exports.acorn) { - exports.acorn = {}; - exports.acorn.walk = {}; -} (function(exports, walk) { "use strict"; @@ -134,7 +130,14 @@ if (typeof exports != "undefined" && !exports.acorn) { // #if macro1 // #else // #endif + // etc... preprocess: true, + // Preprocess 'get include file' function. It should return an object with two attributes + // 'include': a string with the file to be included + // 'sourceFile': is optional and should be a string with the filename. It will + // be included in the locations property as 'source' + // Return null if file can't be found. The parser will raise an exception + preprocessGetIncludeFile: defaultGetIncludeFile, // Preprocess add macro function preprocessAddMacro: defaultAddMacro, // Preprocess get macro function @@ -150,7 +153,12 @@ if (typeof exports != "undefined" && !exports.acorn) { macros: null, // Turn off lineNoInErrorMessage to exclude line number in error messages // Needs to be on to run test cases - lineNoInErrorMessage: true + lineNoInErrorMessage: true, + // Array of files to parse before parsing the main file. Each item is an + // object containing the properties 'include' and 'sourceFile'. 'include' + // has the content from the file and 'sourceFile' has the file path. + // The preprocess options must be turn on for this. + preIncludeFiles: null }; function setOptions(opts) { @@ -166,11 +174,18 @@ if (typeof exports != "undefined" && !exports.acorn) { var macrosMakeBuiltin = function(name, macro, endPos) {return new Macro(name, macro, null, endPos - name.length)} var macrosBuiltinMacros = { - __OBJJ__: function() {return macrosMakeBuiltin("__OBJJ__", options.objj ? "1" : null, tokPos)}, - __BROWSER__: function() {return macrosMakeBuiltin("__BROWSER__", typeof(window) !== "undefined" ? "1" : null, tokPos)}, - __LINE__: function() {return macrosMakeBuiltin("__LINE__", String(options.locations ? tokCurLine : getLineInfo(input, tokPos).line), tokPos)}, + __OBJJ__: function() {return macrosMakeBuiltin("__OBJJ__", options.objj ? "1" : null, tokPos)} } + macrosBuiltinMacros["__" + "BROWSER" + "__"] = function() {return macrosMakeBuiltin("__BROWSER__", typeof(window) !== "undefined" ? "1" : null, tokPos)}; + macrosBuiltinMacros["__" + "LINE" + "__"] = function() {return macrosMakeBuiltin("__LINE__", String(options.locations ? tokCurLine : getLineInfo(input, tokPos).line), tokPos)}; + macrosBuiltinMacros["__" + "DATE" + "__"] = function() {var date, day; return macrosMakeBuiltin("__DATE__", (date = new Date(), day = String(date.getDate()), ["Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"][date.getMonth()] + (day.length > 1 ? " " : " ") + day + " " + date.getFullYear()), tokPos)}; + macrosBuiltinMacros["__" + "TIME" + "__"] = function() {var date; return macrosMakeBuiltin("__TIME__", (date = new Date(), ("0" + date.getHours()).slice(-2) + ":" + ("0" + date.getMinutes()).slice(-2) + ":" + ("0" + date.getSeconds()).slice(-2)), tokPos)}; + + function defaultGetIncludeFile(filename) { + return {include: "#define FOO(x) x\n", sourceFile: filename}; + } + function defaultAddMacro(macro) { macros[macro.identifier] = macro; macrosIsPredicate = null; @@ -292,7 +307,19 @@ if (typeof exports != "undefined" && !exports.acorn) { // tokMacroOffset is the offset to the current macro for the current token // tokPosMacroOffset is the offset to the current macro for the current tokPos - var tokFirstStart, tokStart, tokEnd, tokMacroOffset, tokPosMacroOffset, lastTokMacroOffset; + var tokFirstStart, firstTokEnd, tokStart, tokEnd, tokMacroOffset, tokPosMacroOffset, lastTokMacroOffset; + + // This is the end position for the last token in the current `input` buffer. Both regular and preprocess + // tokens count. It is '0' when entering a new buffer. It is used to determinate if a preprocess (#if) token + // is at the begining of a line + + var localLastEnd; + + // Is null except when a macro has ended and then contains the position of the last position in the tokens macro. + // `lastEndOfFile` contains this value for the last token. It is used to allow a semicolon to be inserted at the + // end of a macro. You can say an end of file is equivalent to a new line. + + var firstEndOfFile, lastEndOfFile; // When `options.locations` is true, these hold objects // containing the tokens start and end line/column pairs. @@ -337,7 +364,7 @@ if (typeof exports != "undefined" && !exports.acorn) { // Same as input but for the current token. If options.preprocess is used // this can differ due to macros. - var tokInput, preTokInput, tokFirstInput; + var tokInput, preTokInput, lastEndInput; // These store the position of the previous token, which is useful // when finishing a node and assigning its `end` position. @@ -369,6 +396,7 @@ if (typeof exports != "undefined" && !exports.acorn) { var preprocessParameterScope; var preTokParameterScope; var preprocessOverrideTokEndLoc; + var preprocessDontConcatenate; // Don't concatenate tokens when finishing preprocess tokens // True if we are concatenating two tokens. This is needed to handle when the second part is an empty macro // This is also used when stingifying tokens to get an empty macro @@ -492,6 +520,7 @@ if (typeof exports != "undefined" && !exports.acorn) { var _preWarning = {keyword: "warning"}; var _preprocessParamItem = {type: "preprocessParamItem"} var _preprocessSkipLine = {type: "skipLine"} + var _preInclude = {keyword: "include"}; // Map keyword names to token types. @@ -523,7 +552,7 @@ if (typeof exports != "undefined" && !exports.acorn) { var keywordTypesPreprocessor = {"define": _preDefine, "pragma": _prePragma, "ifdef": _preIfdef, "ifndef": _preIfndef, "undef": _preUndef, "if": _preIf, "endif": _preEndif, "else": _preElse, "elif": _preElseIf, - "defined": _preDefined, "warning": _preWarning, "error": _preError}; + "defined": _preDefined, "warning": _preWarning, "error": _preError, "include": _preInclude}; // Punctuation token types. Again, the `type` property is purely for debugging. @@ -646,7 +675,7 @@ if (typeof exports != "undefined" && !exports.acorn) { // The preprocessor keywords. - var isKeywordPreprocessor = makePredicate("define undef pragma if ifdef ifndef else elif endif defined error warning"); + var isKeywordPreprocessor = makePredicate("define undef pragma if ifdef ifndef else elif endif defined error warning include"); // ## Character categories @@ -752,6 +781,7 @@ if (typeof exports != "undefined" && !exports.acorn) { preprocessStack = []; preprocessStackLastItem = null; preprocessOnlyTransformArgumentsForLastToken = null; + preprocessDontConcatenate = false; preNotSkipping = true; preConcatenating = false; preIfLevel = []; @@ -764,15 +794,15 @@ if (typeof exports != "undefined" && !exports.acorn) { function finishToken(type, val, overrideTokEnd) { if (overrideTokEnd) { - tokEnd = overrideTokEnd; + firstTokEnd = tokEnd = overrideTokEnd; if (options.locations) tokEndLoc = preprocessOverrideTokLoc; } else { - tokEnd = tokPos; + firstTokEnd = tokEnd = tokPos; if (options.locations) tokEndLoc = new line_loc_t; } tokType = type; - skipSpace(); - if (options.preprocess && input.charCodeAt(tokPos) === 35 && input.charCodeAt(tokPos + 1) === 35) { // '##' + var ch = skipSpace(); + if (ch === 35 && options.preprocess && input.charCodeAt(tokPos + 1) === 35) { // '##' var val1 = val != null ? val : type.keyword || type.type; tokPos += 2; if (val1 != null) { @@ -874,21 +904,26 @@ if (typeof exports != "undefined" && !exports.acorn) { // will store all skipped comments in `tokComments`. If // `options.trackSpaces` is on, will store the last skipped spaces in // `tokSpaces`. + // Returns the char code of the first none whitespace or comment function skipSpace() { tokComments = null; tokSpaces = null; - onlySkipSpace(); + return onlySkipSpace(); } + // Returns the char code of the first none whitespace or comment + function onlySkipSpace(dontSkipEOL, dontSkipMacroBoundary, dontSkipComments) { var spaceStart = tokPos, - lastIsNewlinePos; + lastIsNewlinePos, + ch; for(;;) { - var ch = input.charCodeAt(tokPos); + ch = input.charCodeAt(tokPos); if (ch === 32) { // ' ' ++tokPos; - } else if (ch === 13 && !dontSkipEOL) { + } else if (ch === 13) { + if (dontSkipEOL) break; lastIsNewlinePos = tokPos; ++tokPos; var next = input.charCodeAt(tokPos); @@ -899,7 +934,8 @@ if (typeof exports != "undefined" && !exports.acorn) { ++tokCurLine; tokLineStart = tokPos; } - } else if (ch === 10 && !dontSkipEOL) { + } else if (ch === 10) { + if (dontSkipEOL) break; lastIsNewlinePos = tokPos; ++tokPos; if (options.locations) { @@ -908,7 +944,8 @@ if (typeof exports != "undefined" && !exports.acorn) { } } else if (ch === 9) { ++tokPos; - } else if (ch === 47 && !dontSkipComments) { // '/' + } else if (ch === 47) { // '/' + if (dontSkipComments) break; var next = input.charCodeAt(tokPos+1); if (next === 42) { // '*' if (options.trackSpaces) @@ -927,27 +964,33 @@ if (typeof exports != "undefined" && !exports.acorn) { if (options.preprocess) { if (dontSkipMacroBoundary) return true; if (!preprocessStack.length) break; + // If this is the first end of file after the token save to position to allow a semicolon to be inserted + // the end of file, if needed. + if (firstEndOfFile == null) firstEndOfFile = tokPos; // If we are at the end of the input inside a macro continue at last position var lastItem = preprocessStack.pop(); + var saveInputForPrint = input; + var saveSourceFileForPrint = sourceFile; tokPos = lastItem.end; input = lastItem.input; inputLen = lastItem.inputLen; tokCurLine = lastItem.currentLine; tokLineStart = lastItem.currentLineStart; - /*tokStart = *///tokFirstStart = lastItem.tokStart; - //lastEnd = lastItem.lastEnd; - //lastStart = lastItem.lastStart; preprocessOnlyTransformArgumentsForLastToken = lastItem.onlyTransformArgumentsForLastToken; preprocessParameterScope = lastItem.parameterScope; tokPosMacroOffset = lastItem.macroOffset; + sourceFile = lastItem.sourceFile; + firstTokEnd = lastItem.lastEnd; + // Set the last item var lastIndex = preprocessStack.length; preprocessStackLastItem = lastIndex ? preprocessStack[lastIndex - 1] : null; - onlySkipSpace(dontSkipEOL); + return onlySkipSpace(dontSkipEOL); } else { break; } - } else if (ch === 92 && options.preprocess) { // '\' + } else if (ch === 92) { // '\' + if (!options.preprocess) break; // Check if we have an escaped newline. We are using a relaxed treatment of escaped newlines like gcc. // We allow spaces, horizontal and vertical tabs, and form feeds between the backslash and the subsequent newline var pos = tokPos + 1; @@ -969,6 +1012,7 @@ if (typeof exports != "undefined" && !exports.acorn) { break; } } + return ch; } // ### Token reading @@ -1028,7 +1072,7 @@ if (typeof exports != "undefined" && !exports.acorn) { } function readToken_lt_gt(code, finisher) { // '<>' - if (tokType === _import && options.objj && code === 60) { // '<' + if (code === 60 && (tokType === _import || preTokType === _preInclude) && options.objj) { // '<' for (var start = tokPos + 1;;) { var ch = input.charCodeAt(++tokPos); if (ch === 62) // '>' @@ -1216,6 +1260,30 @@ if (typeof exports != "undefined" && !exports.acorn) { raise(start, "Error: " + String(preprocessEvalExpression(expr))); break; + case _preInclude: + if (!preNotSkipping) { + return finisher(_preInclude); + } + preprocessReadToken(); + if (preTokType === _string) + var localfilepath = true; + else if (preTokType ===_filename) + var localfilepath = false; + else + raise(preTokStart, "Expected \"FILENAME\" or : " + (preTokType.keyword || preTokType.type)); + + var theFileName = preTokVal; + var includeDict = options.preprocessGetIncludeFile(preTokVal, localfilepath) || raise(preTokStart, "'" + theFileName + "' file not found"); + var includeString = includeDict.include; + var includeMacro = new Macro(null, includeString, null, 0, false, null, false, null, includeDict.sourceFile); + preprocessFinishToken(_preprocess, null, null, true); // skipEOL + pushMacroToStack(includeMacro, includeMacro.macro, tokPosMacroOffset, null, null, tokPos, null, true); // isIncludeFile + skipSpace(); + readToken(null, null, true); // Stealth + return; + + break; + default: if (preprocessStackLastItem) { // If the current macro has parameters check if this word is one of them and should be stringifyed @@ -1240,14 +1308,18 @@ if (typeof exports != "undefined" && !exports.acorn) { else tokSpaces = ["\n"]; } - preprocessFinishToken(_preprocess, null, null, true); // skipEOL - return readToken(); + preprocessFinishToken(preTokType, null, null, true); // skipEOL + return next(true) // Stealth } function preprocessParseDefine() { preprocessIsParsingPreprocess = true; preprocessReadToken(); var macroIdentifierEnd = preTokEnd; + + // We don't want to concatenate tokens when creating macros + preprocessDontConcatenate = true; + var macroIdentifier = preprocessGetIdent(); // '(' Must follow directly after identifier to be a valid macro with parameters if (input.charCodeAt(macroIdentifierEnd) === 40) { // '(' @@ -1267,7 +1339,8 @@ if (typeof exports != "undefined" && !exports.acorn) { while(preTokType !== _eol && preTokType !== _eof) preprocessReadToken(); - var macroString = input.slice(start, preTokStart); + preprocessDontConcatenate = false; + var macroString = preTokInput.slice(start, preTokStart); macroString = macroString.replace(/\\/g, " "); // If variadic get the last parameter for the variadic parameter name options.preprocessAddMacro(new Macro(macroIdentifier, macroString, parameters, start, false, null, variadic && parameters[parameters.length - 1], positionOffset)); @@ -1436,8 +1509,8 @@ if (typeof exports != "undefined" && !exports.acorn) { } // Check if it is the first token on the line lineBreak.lastIndex = 0; - var match = lineBreak.exec(input.slice(lastEnd, tokPos)); - if (lastEnd !== 0 && lastEnd !== tokPos && !match) { + var match = lineBreak.exec(input.slice(localLastEnd, tokPos)); + if (lastEnd !== 0 && lastEnd !== tokPos && !match && ((preprocessStackLastItem && !preprocessStackLastItem.isIncludeFile) || tokPos !== 0)) { if (preprocessStackLastItem) { // Stringify next token return preprocessStringify(); @@ -1541,10 +1614,12 @@ if (typeof exports != "undefined" && !exports.acorn) { // Returns true if it stops at a line break. function preprocessSkipSpace(skipComments, skipEOL) { - onlySkipSpace(!skipEOL); - lineBreak.lastIndex = 0; - var match = lineBreak.exec(input.slice(tokPos, tokPos + 2)); - return match && match.index === 0; + var ch = onlySkipSpace(!skipEOL); + // Can't see that this line break test is used anymore + //lineBreak.lastIndex = 0; + //var match = lineBreak.exec(input.slice(tokPos, tokPos + 2)); + //return (match && match.index === 0); + return ch; } function preprocessSkipToElseOrEndif(skipElse) { @@ -1586,7 +1661,7 @@ if (typeof exports != "undefined" && !exports.acorn) { // preprocessToken is used to cancel preNotSkipping when calling from readToken_preprocess. // FIXME: Refactor to not use this parameter preprocessToken. It is kind of confusing and it should be possible to do in another way - function preprocessReadToken(skipComments, preprocessToken, processMacros) { + function preprocessReadToken(skipComments, preprocessToken, processMacros, onlyTransformMacroArguments) { preTokStart = tokPos; preTokInput = input; preTokParameterScope = preprocessParameterScope; @@ -1629,32 +1704,67 @@ if (typeof exports != "undefined" && !exports.acorn) { } } - function preprocessReadWord(processMacros) { + function preprocessReadWord(processMacros, onlyTransformMacroArguments) { var word = readWord1(); var type = _name; if (processMacros && options.preprocess) { - var readMacroWordReturn = readMacroWord(word, preprocessNext); + var readMacroWordReturn = readMacroWord(word, preprocessNext, onlyTransformMacroArguments); if (readMacroWordReturn === true) return true; } if (!containsEsc && isKeywordPreprocessor(word)) type = keywordTypesPreprocessor[word]; - preprocessFinishToken(type, word, readMacroWordReturn); // If readMacroWord returns anything except 'true' it is the real tokEndPos + preprocessFinishToken(type, word, readMacroWordReturn, false, processMacros); // If readMacroWord returns anything except 'true' it is the real tokEndPos } - function preprocessFinishToken(type, val, overrideTokEnd, skipEOL) { + function preprocessFinishToken(type, val, overrideTokEnd, skipEOL, processMacros) { preTokType = type; preTokVal = val; preTokEnd = overrideTokEnd || tokPos; + if (type !== _eol) firstTokEnd = preTokEnd; //tokRegexpAllowed = type.beforeExpr; - preprocessSkipSpace(false, skipEOL); // Dont skip comments + var ch = preprocessSkipSpace(false, skipEOL); // Dont skip comments + if (ch === 35 && options.preprocess && !preprocessDontConcatenate && input.charCodeAt(tokPos + 1) === 35) { // '##' + var val1 = val != null ? val : type.keyword || type.type; + tokPos += 2; + if (val1 != null) { + // Save current line and current line start. This is needed when option.locations is true + var positionOffset = options.locations && new PositionOffset(tokCurLine, tokLineStart); + // Save positions on first token to get start and end correct on node if cancatenated token is invalid + var saveTokInput = tokInput, saveTokEnd = preTokEnd, saveTokStart = preTokStart, start = preTokStart + tokMacroOffset, variadicName = preprocessStackLastItem && preprocessStackLastItem.macro && preprocessStackLastItem.macro.variadicName; + skipSpace(); + if (variadicName && variadicName === input.slice(tokPos, tokPos + variadicName.length)) var isVariadic = true; + preConcatenating = true; + preprocessReadToken(null, null, processMacros, 2); // 2 = Don't transform macros only arguments + preConcatenating = false; + var val2 = preTokVal != null ? preTokVal : preTokType.keyword || preTokType.type; + if (val2 != null) { + // Skip token if it is a ',' concatenated with an empty variadic parameter + if (isVariadic && val1 === "," && val2 === "") return preprocessReadToken(); + var concat = "" + val1 + val2, val2TokStart = preTokStart + tokPosMacroOffset; + // If the macro defines anything add it to the preprocess input stack + var concatMacro = new Macro(null, concat, null, start, false, null, false, positionOffset); + var r = readTokenFromMacro(concatMacro, tokPosMacroOffset, preprocessStackLastItem ? preprocessStackLastItem.parameterDict : null, null, tokPos, preprocessNext, null); + // Consumed the whole macro in one bite? If not the tokenizer can't create a single token from the two concatenated tokens + if (preprocessStackLastItem && preprocessStackLastItem.macro === concatMacro) { + // FIXME: Should change this to 'preTokType' and friends + preTokType = type; + preTokStart = saveTokStart; + preTokEnd = saveTokEnd; + tokInput = saveTokInput; + tokPosMacroOffset = val2TokStart - val1.length; // reset the macro offset to the second token to get start and end correct on node + if (!isVariadic) /*raise(tokStart,*/console.log("Warning: pasting formed '" + concat + "', an invalid preprocessing token"); + } else return r; + } + } + } } // FIXME: Find out if this is really used? function preprocessFinishTokenSkipComments(type, val) { preTokType = type; preTokVal = val; - preTokEnd = tokPos; + firstTokEnd = preTokEnd = tokPos; preprocessSkipSpace(true); // 'true' for skip comments } @@ -1662,10 +1772,11 @@ if (typeof exports != "undefined" && !exports.acorn) { function preprocessNext(stealth, onlyTransformArguments, forceRegexp, processMacros) { if (!stealth) { - preLastStart = tokStart; - preLastEnd = tokEnd; + preLastStart = preTokStart; + preLastEnd = preTokEnd; } - return preprocessReadToken(false, false, processMacros); + localLastEnd = firstTokEnd; + return preprocessReadToken(false, false, processMacros, onlyTransformArguments); } // Predicate that tests whether the next token is of the given @@ -1682,7 +1793,7 @@ if (typeof exports != "undefined" && !exports.acorn) { // raise with errorMessage or an unexpected token error. function preprocessExpect(type, errorMessage, processMacros) { - if (preTokType === type) preprocessReadToken(processMacros); + if (preTokType === type) preprocessNext(false, undefined, null, processMacros); else raise(preTokStart, errorMessage || "Unexpected token"); } @@ -1832,7 +1943,7 @@ if (typeof exports != "undefined" && !exports.acorn) { else tokPos = tokStart + 1; if (!stealth) { tokFirstStart = tokStart; - tokFirstInput = input; + //tokFirstInput = input; } tokInput = input; tokMacroOffset = tokPosMacroOffset; @@ -2094,7 +2205,7 @@ if (typeof exports != "undefined" && !exports.acorn) { // Lets look ahead to find out if we find a '##' for token concatenate // We don't want to prescan spaces across macro boundary as the macro stack will fall apart // So we do a special prescan if we have to cross a boundary all in the name of speed - if (onlySkipSpace(true, true)) { // don't skip EOL and don't skip macro boundary. + if (onlySkipSpace(true, true) === true) { // don't skip EOL and don't skip macro boundary. if (preprocessPrescanFor(35, 35)) // Prescan across boundary for '##' as we crossed a boundary onlyTransformArguments = 2; } else if (input.charCodeAt(tokPos) === 35 && input.charCodeAt(tokPos + 1) === 35) { // '##' @@ -2141,7 +2252,7 @@ if (typeof exports != "undefined" && !exports.acorn) { var pos = tokPos; var loc; if (options.locations) loc = new line_loc_t; - if ((onlySkipSpace(true, true) && preprocessPrescanFor(40)) || input.charCodeAt(tokPos) === 40) { // '(' + if ((onlySkipSpace(true, true) === true && preprocessPrescanFor(40)) || input.charCodeAt(tokPos) === 40) { // '(' nextIsParenL = true; } else { // We didn't find a '(' so don't transform to the macro. Return the real tokEndPos so we get correct token end values. @@ -2279,7 +2390,7 @@ if (typeof exports != "undefined" && !exports.acorn) { } } } - return scanInput.charCodeAt(scanPos) === first && (second == null || scanInput.charCodeAt(scanPos + 1) === second); + return scanInput && scanInput.charCodeAt(scanPos) === first && (second == null || scanInput.charCodeAt(scanPos + 1) === second); } // Push macro to stack and start read from it. @@ -2289,20 +2400,10 @@ if (typeof exports != "undefined" && !exports.acorn) { // If we are evaluation a macro expresion an empty macro definition means true or '1' if(!macroString && nextFinisher === preprocessNext) macroString = "1"; if (macroString) { - preprocessStackLastItem = {macro: macro, macroOffset: macroOffset, parameterDict: parameters, /*start: macroStart,*/ end:end, inputLen: inputLen, tokStart: tokStart, onlyTransformArgumentsForLastToken: preprocessOnlyTransformArgumentsForLastToken, currentLine: tokCurLine, currentLineStart: tokLineStart/*, lastStart: lastStart, lastEnd: lastEnd*/}; - if (parameterScope) preprocessStackLastItem.parameterScope = parameterScope; - preprocessStackLastItem.input = input; - preprocessStack.push(preprocessStackLastItem); - preprocessOnlyTransformArgumentsForLastToken = onlyTransformArguments; - input = macroString; - inputLen = macroString.length; - tokPosMacroOffset = macro.start; - tokPos = 0; - tokCurLine = 0; - tokLineStart = 0; + pushMacroToStack(macro, macroString, macroOffset, parameters, parameterScope, end, onlyTransformArguments); } else if (preConcatenating) { // If we are concatenating or stringifying and the macro is empty just make an empty string. - finishToken(_name, ""); + (nextFinisher === next ? finishToken : preprocessFinishToken)(_name, ""); return true; } // Now read the next token @@ -2311,6 +2412,27 @@ if (typeof exports != "undefined" && !exports.acorn) { return true; } + // Push macro to stack and reset tokPos etc. + // macroString is the string from the macro. It is usually 'macro.macro' but the caller can modify it if needed + // includeFile is true if the macro should be treated as a regular file. In other words don't stringify words after '#' + function pushMacroToStack(macro, macroString, macroOffset, parameters, parameterScope, end, onlyTransformArguments, isIncludeFile) { + preprocessStackLastItem = {macro: macro, macroOffset: macroOffset, parameterDict: parameters, /*start: macroStart,*/ end:end, lastEnd: localLastEnd, inputLen: inputLen, tokStart: tokStart, onlyTransformArgumentsForLastToken: preprocessOnlyTransformArgumentsForLastToken, currentLine: tokCurLine, currentLineStart: tokLineStart, sourceFile: sourceFile}; + if (parameterScope) preprocessStackLastItem.parameterScope = parameterScope; + if (isIncludeFile) preprocessStackLastItem.isIncludeFile = isIncludeFile; + preprocessStackLastItem.input = input; + preprocessStack.push(preprocessStackLastItem); + preprocessOnlyTransformArgumentsForLastToken = onlyTransformArguments; + input = macroString; + inputLen = macroString.length; + tokPosMacroOffset = macro.start; + tokPos = 0; + tokCurLine = 0; + tokLineStart = 0; + firstTokEnd = 0; + localLastEnd = 0; + if (macro.sourceFile) sourceFile = macro.sourceFile; + } + // ident is the identifier name for the macro // macro is the macro string // parameters is an array with the parameters for the macro @@ -2319,7 +2441,7 @@ if (typeof exports != "undefined" && !exports.acorn) { // parameterScope is the parameter scope // varadicName is the name of the varadic parameter if it is a varadic macro // locationOffset is the current line that the macro starts at and the position on the line - var Macro = exports.Macro = function Macro(ident, macro, parameters, start, isArgument, parameterScope, variadicName, locationOffset) { + var Macro = exports.Macro = function Macro(ident, macro, parameters, start, isArgument, parameterScope, variadicName, locationOffset, aSourceFile) { this.identifier = ident; if (macro != null) this.macro = macro; if (parameters) this.parameters = parameters; @@ -2328,6 +2450,7 @@ if (typeof exports != "undefined" && !exports.acorn) { if (parameterScope) this.parameterScope = parameterScope; if (variadicName) this.variadicName = variadicName; if (locationOffset) this.locationOffset = locationOffset; + if (aSourceFile) this.sourceFile = aSourceFile; } Macro.prototype.isParameterFunction = function() { @@ -2364,11 +2487,14 @@ if (typeof exports != "undefined" && !exports.acorn) { if (!stealth) { lastStart = tokStart; lastEnd = tokEnd; + lastEndInput = tokInput; + lastEndOfFile = firstEndOfFile; lastEndLoc = tokEndLoc; lastTokMacroOffset = tokMacroOffset; } - nodeMessageSendObjectExpression = null; - readToken(forceRegexp, onlyTransformArguments, stealth); + localLastEnd = firstTokEnd; + firstEndOfFile = nodeMessageSendObjectExpression = null; + return readToken(forceRegexp, onlyTransformArguments, stealth); } // Enter strict mode. Re-reads the next token to please pedantic @@ -2397,7 +2523,7 @@ if (typeof exports != "undefined" && !exports.acorn) { function node_loc_t() { this.start = tokStartLoc; this.end = null; - if (sourceFile !== null) this.source = sourceFile; + if (sourceFile != null) this.source = sourceFile; } function startNode() { @@ -2508,9 +2634,10 @@ if (typeof exports != "undefined" && !exports.acorn) { // Test whether a semicolon can be inserted at the current position. function canInsertSemicolon() { + //if (lastEnd !== localLastEnd) print("lastEnd: " + lastEnd + ", localLastEnd: " + localLastEnd); return !options.strictSemicolons && - (tokType === _eof || tokType === _braceR || newline.test(tokFirstInput.slice(lastEnd, tokFirstStart)) || - (nodeMessageSendObjectExpression && options.objj)); + (tokType === _eof || tokType === _braceR || newline.test(lastEndInput.slice(lastEnd, lastEndOfFile || tokFirstStart)) || + (nodeMessageSendObjectExpression && options.objj) || lastEndOfFile != null); } // Consume a semicolon, or, failing that, see if we are allowed to @@ -2550,9 +2677,22 @@ if (typeof exports != "undefined" && !exports.acorn) { // statements, and wraps them in a Program node. Optionally takes a // `program` argument. If present, the statements will be appended // to its body instead of creating a new node. + // If there are any pre include files they will be pushed onto the macro stack function parseTopLevel(program) { - lastStart = lastEnd = tokPos; + lastStart = localLastEnd = lastEnd = 0; + + if (options.preprocess) { + var preIncludeFiles = options.preIncludeFiles; + if (preIncludeFiles && preIncludeFiles.length) for (var i = preIncludeFiles.length - 1; i >= 0; i--) { + var preIncludeFile = preIncludeFiles[i]; + + var preIncludeMacro = new Macro(null, preIncludeFile.include, null, 0, false, null, false, null, preIncludeFile.sourceFile); + pushMacroToStack(preIncludeMacro, preIncludeMacro.macro, 0, null, null, tokPos, null, true); // isIncludeFile + skipSpace(); + } + } + if (options.locations) lastEndLoc = new line_loc_t; inFunction = strict = null; labels = []; @@ -3752,4 +3892,4 @@ if (typeof exports != "undefined" && !exports.acorn) { return finishNode(node, "ObjectiveJType"); } -})(exports.acorn, exports.acorn.walk); +})(exports.acorn || (exports.acorn = {}), exports.acorn.walk || (exports.acorn.walk = (typeof acorn !== 'undefined') && acorn.walk) || (exports.acorn.walk = {})); diff --git a/Objective-J/sprintf.js b/Objective-J/sprintf.js index 9a73e69ed..403d01b6c 100644 --- a/Objective-J/sprintf.js +++ b/Objective-J/sprintf.js @@ -198,4 +198,4 @@ function justify(sign, prefix, string, suffix, width, leftJustify, padZeros) function pad(n, ch) { return Array(MAX(0,n)+1).join(ch); -} +}; diff --git a/common.jake b/common.jake index 00f65302f..90cf52b02 100644 --- a/common.jake +++ b/common.jake @@ -256,7 +256,6 @@ function reforkWithPackages() if (additionalPackages().length > 0) { var cmd = serializedENV() + " " + system.args.map(OS.enquote).join(" "); - //print("REFORKING: " + cmd); OS.exit(OS.system(cmd)); } }