From 024c0a3186f27f16143ddb31c00dcf9af6d82958 Mon Sep 17 00:00:00 2001 From: Martin Carlberg Date: Tue, 12 Apr 2016 10:13:16 +0200 Subject: [PATCH 01/11] Fixed: Handle source maps if source-map.js is included from the index.html file. MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit To create source maps the compiler flag ”SourceMap” has to be added in the OBJJ_COMPILER_FLAGS array in the index.html file. --- Objective-J/Bootstrap.js | 4 +++ Objective-J/Executable.js | 14 ++++++++--- Objective-J/ObjJAcornCompiler.js | 43 ++++++++++++++++++-------------- 3 files changed, 39 insertions(+), 22 deletions(-) diff --git a/Objective-J/Bootstrap.js b/Objective-J/Bootstrap.js index 2810c76fd..f3f5675c7 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); diff --git a/Objective-J/Executable.js b/Objective-J/Executable.js index f138fb6c2..63e56fe98 100644 --- a/Objective-J/Executable.js +++ b/Objective-J/Executable.js @@ -168,7 +168,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 +197,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 +219,15 @@ 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); + code += "\n//# sourceMappingURL=data:application/json;base64," + (typeof btoa === 'function' ? btoa(sourceMap) : new Buffer(sourceMap).toString("base64")) + } //} 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; diff --git a/Objective-J/ObjJAcornCompiler.js b/Objective-J/ObjJAcornCompiler.js index b6b279970..0345808f7 100644 --- a/Objective-J/ObjJAcornCompiler.js +++ b/Objective-J/ObjJAcornCompiler.js @@ -17,13 +17,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, exports.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) { @@ -308,7 +307,7 @@ StringBuffer.prototype.toStringString = function() StringBuffer.prototype.toStringSourceNode = function() { - return this.rootNode.toStringWithSourceMap({file: this.file}); + return this.rootNode.toStringWithSourceMap({file: this.file.substr(this.file.lastIndexOf('/') + 1) + "s", sourceRoot:this.file.substr(0, this.file.lastIndexOf('/') + 1)}); } StringBuffer.prototype.concatString = function(aString) @@ -1968,7 +1967,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 +2078,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"); @@ -2316,7 +2315,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 +2340,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 +2350,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 +2409,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 +2434,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 +2444,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 + ""); @@ -2518,7 +2517,7 @@ ClassDeclarationStatement: function(node, st, c, format) { generateObjJ = compiler.options.generateObjJ; compiler.imBuffer = new StringBuffer(compiler.createSourceMap, compiler.URL); - compiler.cmBuffer = new StringBuffer(compiler.createSourceMap), compiler.URL; + 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)) @@ -2756,12 +2755,18 @@ ClassDeclarationStatement: function(node, st, c, format) { getterSetterBuffer.concat("\n@end"); // Remove all @accessors or we will get a recursive loop in infinity - var b = getterSetterBuffer.toString().replace(/@accessors(\(.*\))?/g, ""); + var b = getterSetterBuffer.toString(); + + if (compiler.createSourceMap) + b = b.code; + + b = b.replace(/@accessors(\(.*\))?/g, ""); + var imBuffer = exports.compileToIMBuffer(b, "Accessors", compiler.options); // 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); + compiler.imBuffer.concat(compiler.createSourceMap ? imBuffer.toString().code : imBuffer.toString()); } // 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 @@ -3227,13 +3232,13 @@ 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(selector); buffer.concat("\"] || _objj_forward)(self"); } else { - buffer.concat("objj_msgSendSuper"); + buffer.concat("objj_msgSendSuper", node); if (totalNoOfParameters < 4) { buffer.concat("" + totalNoOfParameters); } @@ -3261,12 +3266,12 @@ MessageSendExpression: function(node, st, c) { } if (receiverIsNotSelf) { - buffer.concat("("); + buffer.concat("(", node); c(nodeObject, st, "Expression"); buffer.concat(" == null ? null : "); } if (inlineMsgSend) - buffer.concat("("); + buffer.concat("(", receiverIsNotSelf ? null : node); c(nodeObject, st, "Expression"); } else { receiverIsNotSelf = true; @@ -3525,7 +3530,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; From 6456809a727c6c11a77327654f7aff2f80d3ff60 Mon Sep 17 00:00:00 2001 From: Martin Carlberg Date: Mon, 26 Jun 2017 15:45:02 +0200 Subject: [PATCH 02/11] Fixed: Use semicolon on last line in #included files. The new preprocessor might think that the statement will continue on the next line. MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Due to Javascript don’t need to have semicolon at end of statement there are times when the parser thinks the line continues on the next line. For example we have a file.js: var f = function(x) { return x + x; } Now in another file we do: #include file.js (function(exports) { a = 42; })(exports.b) There will be two different behaviors if the ’file.js’ has a semicolon at the end of the last line. This commit make sure we have the right behavior. --- Objective-J/Bootstrap.js | 2 +- Objective-J/CFBundle.js | 2 +- Objective-J/CFPropertyList.js | 4 ++-- Objective-J/Debug.js | 2 +- Objective-J/EventDispatcher.js | 2 +- Objective-J/FileDependency.js | 2 +- Objective-J/Preprocessor.js | 2 +- Objective-J/sprintf.js | 2 +- 8 files changed, 9 insertions(+), 9 deletions(-) diff --git a/Objective-J/Bootstrap.js b/Objective-J/Bootstrap.js index f3f5675c7..7f7be575f 100644 --- a/Objective-J/Bootstrap.js +++ b/Objective-J/Bootstrap.js @@ -184,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/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/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/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/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/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); -} +}; From b4263aa3ecf41a857111d08c56fcfcb8b58cba46 Mon Sep 17 00:00:00 2001 From: Martin Carlberg Date: Mon, 26 Jun 2017 15:50:08 +0200 Subject: [PATCH 03/11] =?UTF-8?q?New:=20Make=20sure=20we=20can=20use=20Str?= =?UTF-8?q?ing=20function=20=E2=80=99startsWith=E2=80=99=20and=20=E2=80=99?= =?UTF-8?q?endsWith=E2=80=99=20even=20if=20an=20older=20browser=20or=20Jav?= =?UTF-8?q?ascript=20engine=20is=20used.?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- Objective-J/OldBrowserCompatibility.js | 21 +++++++++++++++++++++ 1 file changed, 21 insertions(+) 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 From a8ec5cf5e9f808d04b3ced7e226aa7cd986caef1 Mon Sep 17 00:00:00 2001 From: Martin Carlberg Date: Mon, 26 Jun 2017 16:03:20 +0200 Subject: [PATCH 04/11] New: Support to create source maps for the frameworks and applications --- Objective-J/Executable.js | 118 ++++++++++++++++++++- Objective-J/ObjJAcornCompiler.js | 176 ++++++++++++++++++------------- 2 files changed, 222 insertions(+), 72 deletions(-) diff --git a/Objective-J/Executable.js b/Objective-J/Executable.js index 63e56fe98..6f6286cbc 100644 --- a/Objective-J/Executable.js +++ b/Objective-J/Executable.js @@ -226,7 +226,16 @@ Executable.prototype.setCode = function(code, sourceMap) // 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); - code += "\n//# sourceMappingURL=data:application/json;base64," + (typeof btoa === 'function' ? btoa(sourceMap) : new Buffer(sourceMap).toString("base64")) + + 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. @@ -527,3 +536,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/ObjJAcornCompiler.js b/Objective-J/ObjJAcornCompiler.js index 0345808f7..5bf2179ae 100644 --- a/Objective-J/ObjJAcornCompiler.js +++ b/Objective-J/ObjJAcornCompiler.js @@ -16,8 +16,7 @@ (function(mod) { - //print("Compiler INIT! exports: " + typeof exports + ", module: " + typeof module + ", define: " + typeof define); - mod(exports.ObjJCompiler || (exports.ObjJCompiler = {}), exports.acorn, exports.acorn.walk, typeof sourceMap != "undefined" ? sourceMap : null); // 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"; @@ -279,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(); @@ -288,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; @@ -307,7 +319,7 @@ StringBuffer.prototype.toStringString = function() StringBuffer.prototype.toStringSourceNode = function() { - return this.rootNode.toStringWithSourceMap({file: this.file.substr(this.file.lastIndexOf('/') + 1) + "s", sourceRoot:this.file.substr(0, this.file.lastIndexOf('/') + 1)}); + return this.rootNode.toStringWithSourceMap({file: this.filename + "s", sourceRoot:this.sourceRoot}); } StringBuffer.prototype.concatString = function(aString) @@ -315,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) @@ -623,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. @@ -709,7 +724,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 = []; @@ -726,7 +741,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; @@ -738,6 +753,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); @@ -794,14 +817,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 { @@ -1308,13 +1333,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--; @@ -1350,11 +1375,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; @@ -1366,13 +1391,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) @@ -1391,10 +1416,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"); @@ -1455,10 +1480,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"); @@ -2268,7 +2293,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) { @@ -2514,9 +2539,10 @@ 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.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 @@ -2659,7 +2685,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); @@ -2707,7 +2733,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 @@ -2726,7 +2753,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); @@ -2742,10 +2769,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"; @@ -2755,18 +2782,27 @@ ClassDeclarationStatement: function(node, st, c, format) { getterSetterBuffer.concat("\n@end"); // Remove all @accessors or we will get a recursive loop in infinity - var b = getterSetterBuffer.toString(); + var b = getterSetterBuffer.toString().replace(/@accessors(\(.*\))?/g, ""); + var compilerOptions = setupOptions(options); - if (compiler.createSourceMap) - b = b.code; - - b = b.replace(/@accessors(\(.*\))?/g, ""); - - var imBuffer = exports.compileToIMBuffer(b, "Accessors", compiler.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(compiler.createSourceMap ? imBuffer.toString().code : imBuffer.toString()); + 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 @@ -2870,7 +2906,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)); @@ -3234,15 +3270,15 @@ MessageSendExpression: function(node, st, c) { if (inlineMsgSend) { 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", 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); } } } @@ -3268,34 +3304,33 @@ MessageSendExpression: function(node, st, c) { if (receiverIsNotSelf) { buffer.concat("(", node); c(nodeObject, st, "Expression"); - buffer.concat(" == null ? null : "); + buffer.concat(" == null ? null : ", node); } if (inlineMsgSend) - buffer.concat("(", receiverIsNotSelf ? null : node); + 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("); @@ -3330,28 +3365,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"); @@ -3365,7 +3399,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"); @@ -3377,12 +3411,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; From 994ac3fc8e807c01559c068115469d6486b76739 Mon Sep 17 00:00:00 2001 From: Martin Carlberg Date: Mon, 26 Jun 2017 16:04:01 +0200 Subject: [PATCH 05/11] Fixed: Support for more options to the objj tool --- Objective-J/CommonJS/lib/objective-j.js | 31 ++++++++++++++++++++++++- 1 file changed, 30 insertions(+), 1 deletion(-) 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); } } } From 195b109a3691b1d6a63f7f1af5f5465bafc0678a Mon Sep 17 00:00:00 2001 From: Martin Carlberg Date: Mon, 26 Jun 2017 16:19:56 +0200 Subject: [PATCH 06/11] =?UTF-8?q?New:=20Added=20=E2=80=99#include=E2=80=99?= =?UTF-8?q?=20to=20the=20preprocessor,=20compiler=20and=20Cappuccino=20loa?= =?UTF-8?q?d=20system.=20Also=20a=20lot=20of=20bug=20fixes=20in=20the=20pr?= =?UTF-8?q?eprocessor?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- Objective-J/CFHTTPRequest.js | 36 ---- Objective-J/Executable.js | 46 ++++- Objective-J/FileExecutable.js | 178 +++++++++++++++---- Objective-J/Jakefile | 46 +++-- Objective-J/ObjJAcornCompiler.js | 76 +++++++++ Objective-J/StaticResource.js | 21 +++ Objective-J/acorn.js | 284 +++++++++++++++++++++++-------- 7 files changed, 526 insertions(+), 161 deletions(-) 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/Executable.js b/Objective-J/Executable.js index 6f6286cbc..6f65764cd 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; @@ -261,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; @@ -295,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); @@ -490,11 +521,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, @@ -520,7 +552,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; diff --git a/Objective-J/FileExecutable.js b/Objective-J/FileExecutable.js index d6e72e655..ba110eacf 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; + + 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); + + 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); + } + + // 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 5bf2179ae..cb7fa83ff 100644 --- a/Objective-J/ObjJAcornCompiler.js +++ b/Objective-J/ObjJAcornCompiler.js @@ -692,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. @@ -983,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, 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 = {})); From 1fa8c88f6b2736839c114e7dcefac8598dca340d Mon Sep 17 00:00:00 2001 From: Martin Carlberg Date: Mon, 26 Jun 2017 16:23:59 +0200 Subject: [PATCH 07/11] =?UTF-8?q?Fixed:=20Speed=20up=20of=20objj=5FmsgSend?= =?UTF-8?q?=20function.=20Don=E2=80=99t=20need=20to=20use=20complex=20chec?= =?UTF-8?q?k=20if=20class=20is=20initialized.?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- Objective-J/Runtime.js | 18 +++++++++++++++++- 1 file changed, 17 insertions(+), 1 deletion(-) 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); From c0233c803da76e80b69591b481d06ee2cfb7eaa4 Mon Sep 17 00:00:00 2001 From: Martin Carlberg Date: Mon, 26 Jun 2017 16:24:51 +0200 Subject: [PATCH 08/11] Fixed: Removed old print statements for debugging --- Objective-J/CommonJS/lib/objective-j/loader.js | 11 +++++------ common.jake | 1 - 2 files changed, 5 insertions(+), 7 deletions(-) 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/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)); } } From dd1dfdcfeb5794faee06d33fe8a6fc7dcde5e224 Mon Sep 17 00:00:00 2001 From: Martin Carlberg Date: Mon, 26 Jun 2017 16:25:44 +0200 Subject: [PATCH 09/11] Fixed: Added a comment on tricky code line --- Objective-J/Executable.js | 1 + 1 file changed, 1 insertion(+) diff --git a/Objective-J/Executable.js b/Objective-J/Executable.js index 6f65764cd..1e7844392 100644 --- a/Objective-J/Executable.js +++ b/Objective-J/Executable.js @@ -399,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; From d46b51fa9a469eb1be941bdc7e8fe237d77e840a Mon Sep 17 00:00:00 2001 From: Martin Carlberg Date: Mon, 26 Jun 2017 17:58:13 +0200 Subject: [PATCH 10/11] Fixed: In Rhino a function must be declared before it is used even if it is in the same scope. --- Objective-J/FileExecutable.js | 20 ++++++++++---------- 1 file changed, 10 insertions(+), 10 deletions(-) diff --git a/Objective-J/FileExecutable.js b/Objective-J/FileExecutable.js index ba110eacf..2fd4d88ac 100644 --- a/Objective-J/FileExecutable.js +++ b/Objective-J/FileExecutable.js @@ -79,16 +79,6 @@ var compile = function(self, fileContents, aURL, compilerOptions, aFilenameTrans var isAbsoluteURL = (includeURL instanceof CFURL) && includeURL.scheme(), compileWhenCompleted = NO; - 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); - function completed(/*StaticResource*/ aStaticResource) { var includeString = aStaticResource && aStaticResource.contents(), lastCharacter = includeString && includeString.charCodeAt(includeString.length - 1); @@ -107,6 +97,16 @@ var compile = function(self, fileContents, aURL, compilerOptions, aFilenameTrans 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]; From 033e8583dbe97baa7acdb566759d3edb43b3b8df Mon Sep 17 00:00:00 2001 From: Martin Carlberg Date: Mon, 26 Jun 2017 17:59:08 +0200 Subject: [PATCH 11/11] Fixed: A more robust prettify message function --- Objective-J/ObjJAcornCompiler.js | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/Objective-J/ObjJAcornCompiler.js b/Objective-J/ObjJAcornCompiler.js index cb7fa83ff..7bc57f61d 100644 --- a/Objective-J/ObjJAcornCompiler.js +++ b/Objective-J/ObjJAcornCompiler.js @@ -1106,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;