Merge pull request #2561 from mrcarlberg/compile_with_sourcemap

Full support for source map when debugging
This commit is contained in:
Martin Carlberg authored and GitHub committed 2017-12-11 10:52:05 +01:00
commit c4cbb631a9
17 files changed
+3242 -59

No files matched your search

+1 -1
View File
@@ -37,7 +37,7 @@ appKitTask = framework ("AppKit", function(appKitTask)
appKitTask.setCompilerFlags("-O2 " + INCLUDES);
}
else
appKitTask.setCompilerFlags("-DDEBUG -g " + INCLUDES);
appKitTask.setCompilerFlags("-DDEBUG -g -S --inline-msg-send " + INCLUDES);
});
$BUILD_CJS_CAPPUCCINO_APPKIT = FILE.join($BUILD_CJS_CAPPUCCINO_FRAMEWORKS, "AppKit");
+1 -1
View File
@@ -53,7 +53,7 @@ foundationTask = framework ("Foundation", function(foundationTask)
if ($CONFIGURATION === "Release")
foundationTask.setCompilerFlags("-O2 " + INCLUDES);
else
foundationTask.setCompilerFlags("-DDEBUG -g " + INCLUDES);
foundationTask.setCompilerFlags("-DDEBUG -g -S --inline-msg-send " + INCLUDES);
});
$BUILD_CJS_FOUNDATION = FILE.join($BUILD_CJS_CAPPUCCINO_FRAMEWORKS, "Foundation");
+1
View File
@@ -772,6 +772,7 @@ var STATIC_MAGIC_NUMBER = "@STATIC",
MARKER_TEXT = "t",
MARKER_IMPORT_STD = 'I',
MARKER_IMPORT_LOCAL = 'i';
MARKER_SOURCE_MAP = 'S';
function decompileStaticFile(/*Bundle*/ aBundle, /*String*/ aString, /*String*/ aPath)
{
@@ -193,6 +193,11 @@ function resolveFlags(args)
else if (argument.indexOf("-G") === 0)
objjcFlags.generate = true;
else if (argument.indexOf("--inline-msg-send") === 0)
{
// This option is if you only want to inline message send functions and not compress
objjcFlags.inlineMsgSendFunctions = true;
}
else
filePaths.push(argument);
}
+25 -11
View File
@@ -27,7 +27,7 @@ var ExecutableUnloadedFileDependencies = 0,
ExecutableCantStartLoadYetFileDependencies = 3,
AnonymousExecutableCount = 0;
function Executable(/*String*/ aCode, /*Array*/ fileDependencies, /*CFURL|String*/ aURL, /*Function*/ aFunction, /*ObjJCompiler*/aCompiler, /*Dictionary*/ aFilenameTranslateDictionary)
function Executable(/*String*/ aCode, /*Array*/ fileDependencies, /*CFURL|String*/ aURL, /*Function*/ aFunction, /*ObjJCompiler*/aCompiler, /*Dictionary*/ aFilenameTranslateDictionary, /* Base64 String */ sourceMap)
{
if (arguments.length === 0)
return this;
@@ -41,6 +41,9 @@ function Executable(/*String*/ aCode, /*Array*/ fileDependencies, /*CFURL|String
this._fileDependencies = fileDependencies;
this._filenameTranslateDictionary = aFilenameTranslateDictionary;
if (sourceMap)
this._base64EncodedSourceMap = sourceMap;
// This is a little hacky but if fileDependencies is null we can start loading file dependencies yet
if (!fileDependencies)
{
@@ -148,6 +151,12 @@ Executable.prototype.toMarkedString = function()
for (; index < count; ++index)
markedString += dependencies[index].toMarkedString();
var sourceMap = this._base64EncodedSourceMap;
if (sourceMap) {
markedString += MARKER_SOURCE_MAP + ";" + sourceMap.length + ";" + sourceMap;
}
var code = this.code();
return markedString + MARKER_TEXT + ";" + code.length + ";" + code;
@@ -179,7 +188,7 @@ Executable.prototype.execute = function()
this.setCode(this._compiler.compilePass2(), this._compiler.map());
if (FileExecutable.printWarningsAndErrors(this._compiler, exports.messageOutputFormatInXML))
if (FileExecutable.printWarningsAndErrors(this._compiler, exports.messageOutputFormatInXML))
throw "Compilation error";
this._compiler = null;
@@ -211,6 +220,7 @@ Executable.prototype.setCode = function(code, sourceMap)
this._code = code;
var parameters = this.functionParameters().join(",");
var sourceMapBase64;
#if COMMONJS
if (typeof system !== "undefined" && system.engine === "rhino")
@@ -222,6 +232,9 @@ Executable.prototype.setCode = function(code, sourceMap)
{
#endif
#if DEBUG
// Check if base64 source map is available
sourceMapBase64 = this._base64EncodedSourceMap;
// "//# sourceURL=" at the end lets us name our eval'd files for debuggers, etc.
// * WebKit: http://pmuellr.blogspot.com/2009/06/debugger-friendly.html
// * Firebug: http://blog.getfirebug.com/2009/08/11/give-your-eval-a-name-with-sourceurl/
@@ -229,22 +242,23 @@ Executable.prototype.setCode = function(code, sourceMap)
var absoluteString = this.URL().absoluteString();
code += "/**/\n//# sourceURL=" + absoluteString + "s";
if (sourceMap)
{
// The new Function constructor will add a function header before the first line
// The compiler adds a new line as the first character to the code to get the spurce
// mapping correct. We have to remove it here
code = code.substring(2);
var sourceMapBase64;
if (typeof btoa === 'function')
sourceMapBase64 = btoa(UTF16ToUTF8(sourceMap));
else if (typeof Buffer === 'function')
sourceMapBase64 = new Buffer(sourceMap).toString("base64");
}
if (sourceMapBase64)
code += "\n//# sourceMappingURL=data:application/json;charset=utf-8;base64," + sourceMapBase64;
if (sourceMapBase64) {
// The new Function constructor will add a function header before the first line
// The compiler adds two newlines as the first character to the code to get the source
// mapping correct. We have to remove it here. As Javascript engines adds diffentent
// amount of lines at the top we need to calculate how many.
code = code.substring(exports.ObjJCompiler.numberOfLinesAtTopOfFunction());
this._base64EncodedSourceMap = sourceMapBase64;
code += "\n//# sourceMappingURL=data:application/json;charset=utf-8;base64," + sourceMapBase64;
}
//} else {
// // Firebug only does it for "eval()", not "new Function()". Ugh. Slower.
+7 -3
View File
@@ -248,7 +248,8 @@ function decompile(/*String*/ aString, /*CFURL*/ aURL)
*/
var marker = NULL,
code = "",
dependencies = [];
dependencies = [],
sourceMap;
while (marker = stream.getMarker())
{
@@ -262,14 +263,17 @@ function decompile(/*String*/ aString, /*CFURL*/ aURL)
else if (marker === MARKER_IMPORT_LOCAL)
dependencies.push(new FileDependency(new CFURL(text), YES));
else if (marker === MARKER_SOURCE_MAP)
sourceMap = text;
}
var fn = FileExecutable._lookupCachedFunction(aURL);
if (fn)
return new Executable(code, dependencies, aURL, fn);
return new Executable(code, dependencies, aURL, fn, null, null, sourceMap);
return new Executable(code, dependencies, aURL);
return new Executable(code, dependencies, aURL, null, null, null, sourceMap);
}
var FunctionCache = { };
+1
View File
@@ -46,6 +46,7 @@
#include "CFBundle.js"
#include "StaticResource.js"
#include "Preprocessor.js"
#include "source-map.js"
#include "acorn.js"
#include "acornwalk.js"
#include "ObjJAcornCompiler.js"
+64 -11
View File
@@ -16,7 +16,7 @@
(function(mod)
{
mod(exports.ObjJCompiler || (exports.ObjJCompiler = {}), exports.acorn || acorn, (exports.acorn || acorn).walk, typeof sourceMap != "undefined" ? sourceMap : null); // Plain browser env
mod(exports.ObjJCompiler || (exports.ObjJCompiler = {}), exports.acorn || acorn, (exports.acorn || acorn).walk, typeof exports.sourceMap != "undefined" ? exports.sourceMap : (typeof module != "undefined" && typeof module.exports === "object" ? module.exports : null)); // Plain browser env
})(function(exports, acorn, walk, sourceMap)
{
"use strict";
@@ -821,6 +821,28 @@ exports.compileFileDependencies = function(/*String*/ aString, /*CFURL*/ aURL, o
return new ObjJAcornCompiler(aString, aURL, options);
}
/*!
This function is used to calculate the number of lines that is added when a 'new Function(...) call is used.
This is used to make sure source maps are correct
Currently Safari is adding one line and Chrome and Firefox is adding two lines.
We calculate this by creating a function and counts the number of new lines at the top of the function
The result is cached so we only need to make the calculation once.
*/
exports.numberOfLinesAtTopOfFunction = function() {
var f = new Function("x", "return x;");
var fString = f.toString();
var index = fString.indexOf("return x;");
var firstPart = fString.substring(0, index);
var numberOfLines = (firstPart.match(/\n/g) || []).length;
ObjJAcornCompiler.numberOfLinesAtTopOfFunction = function() {
return numberOfLines;
}
return numberOfLines;
}
ObjJAcornCompiler.prototype.compilePass2 = function()
{
var options = this.options;
@@ -1005,7 +1027,7 @@ exports.parseGccCompilerFlags = function(/* String */ compilerFlags)
if (argument.indexOf("-g") === 0)
objjcFlags.includeMethodFunctionNames = true;
else if (argument.indexOf("-O") === 0) {
objjcFlags.inlineMsgSendFunctions = true;
objjcFlags.compress = true; // This is not used in the compiler option dictionary but we add it here as it is also done if compiling from command line.
// 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)
@@ -1031,6 +1053,11 @@ exports.parseGccCompilerFlags = function(/* String */ compilerFlags)
(objjcFlags.includeFiles || (objjcFlags.includeFiles = [])).push(includeUrl);
}
else if (argument.indexOf("--inline-msg-send") === 0)
{
// This option is if you only want to inline message send functions
objjcFlags.inlineMsgSendFunctions = true;
}
/* else if (argument.indexOf("-I") === 0) {
var includeUrl = argument.substring(2),
firstChar = includeUrl && includeUrl.charCodeAt(0);
@@ -1427,7 +1454,7 @@ ExpressionStatement: function(node, st, c, format) {
generate = compiler.generate && !format;
if (generate) compiler.jsBuffer.concat(indentation);
c(node.expression, st, "Expression");
if (generate) compiler.jsBuffer.concat(";\n");
if (generate) compiler.jsBuffer.concat(";\n", node);
},
IfStatement: function(node, st, c, format) {
var compiler = st.compiler,
@@ -1994,7 +2021,7 @@ VariableDeclaration: function(node, st, c, format) {
}
}
}
if (generate && !format && !st.isFor) buffer.concat(";\n"); // Don't add ';' if this is a for statement but do it if this is a statement
if (generate && !format && !st.isFor) buffer.concat(";\n", node); // Don't add ';' if this is a for statement but do it if this is a statement
},
ThisExpression: function(node, st, c) {
var compiler = st.compiler;
@@ -2149,7 +2176,7 @@ BinaryExpression: function(node, st, c, format) {
if (generate) {
var buffer = compiler.jsBuffer;
buffer.concatFormat(format ? format.beforeOperator : " ");
buffer.concat(node.operator);
buffer.concat(node.operator, node);
buffer.concatFormat(format ? format.afterOperator : " ");
}
(generate && nodePrecedence(node, node.right, true) ? surroundExpression(c) : c)(node.right, st, "Expression");
@@ -2315,7 +2342,7 @@ MemberExpression: function(node, st, c) {
if (generate)
compiler.jsBuffer.concat(computed ? "[" : ".", node);
st.secondMemberExpression = !computed;
// No parentheses when it is computed, '[' amd ']' are the same thing.
// No parentheses when it is computed, '[' and ']' are the same thing.
(generate && !computed && nodePrecedence(node, node.property) ? surroundExpression(c) : c)(node.property, st, "Expression");
st.secondMemberExpression = false;
if (generate && computed)
@@ -2369,7 +2396,7 @@ Identifier: function(node, st, c) {
st.addMaybeWarning(message);
}
}
if (generate) compiler.jsBuffer.concat(identifier, node, "self");
if (generate) compiler.jsBuffer.concat(identifier, node, identifier === "self" ? "self" : null);
},
// Use this when there should not be a look up to issue warnings or add 'self.' before ivars
IdentifierName: function(node, st, c) {
@@ -3315,7 +3342,8 @@ MessageSendExpression: function(node, st, c) {
firstSelector = selectors[0],
selector = firstSelector ? firstSelector.name : "", // There is always at least one selector
parameters = node.parameters,
generateObjJ = compiler.options.generateObjJ;
options = compiler.options,
generateObjJ = options.generateObjJ;
// Put together the selector. Maybe this should be done in the parser...
for (var i = 0; i < argumentsLength; i++) {
@@ -3438,10 +3466,12 @@ MessageSendExpression: function(node, st, c) {
}
buffer.concat("]");
} else {
var selectorJSPath;
if (generate && !node.superObject) {
if (!inlineMsgSend) {
if (totalNoOfParameters < 4) {
buffer.concat("" + totalNoOfParameters, null);
buffer.concat("" + totalNoOfParameters, node);
}
}
@@ -3451,12 +3481,35 @@ MessageSendExpression: function(node, st, c) {
} else {
buffer.concat("(___r" + st.receiverLevel, node);
}
// Only do this if source map is enabled and we have an identifier
if (options.sourceMap && nodeObject.type === "Identifier") {
// Get target expression for sourcemap to allow hovering selector to show method function. Create new buffer to write in.
compiler.jsBuffer = new StringBuffer();
c(nodeObject, st, "Expression");
var aTarget = compiler.jsBuffer.toString();
selectorJSPath = aTarget + ".isa.method_dtable[\"" + selector + "\"]"
// Restored buffer so everything will continue as usually.
compiler.jsBuffer = buffer;
}
}
buffer.concat(", \"", node);
buffer.concat(selector); // FIXME: sel_getUid(selector + "") ? This FIXME is from the old preprocessor compiler
buffer.concat(", ", node);
if (selectorJSPath) {
buffer.concat("(", node);
for (var i = 0; i < selectors.length; i++) {
var nextSelector = selectors[i];
if (nextSelector) {
buffer.concat(selectorJSPath, nextSelector);
buffer.concat(", ", node);
}
}
}
buffer.concat("\"", node);
buffer.concat(selector, node); // FIXME: sel_getUid(selector + "") ? This FIXME is from the old preprocessor compiler
buffer.concat(selectorJSPath ? "\")" : "\"", node);
if (nodeArguments) for (var i = 0; i < nodeArguments.length; i++)
{
var argument = nodeArguments[i];
+21 -25
View File
@@ -738,13 +738,13 @@
if (macroCurrentLine) this.line += macroCurrentLine;
var macroCurrentLineStart = locationOffset.column;
// Only add column offset if we are on the first line
if (macroCurrentLineStart) this.column += tokPosMacroOffset - (tokCurLine === 0 ? macroCurrentLineStart : 0);
if (macroCurrentLineStart) this.column += tokPosMacroOffset - (tokCurLine === 1 ? macroCurrentLineStart : 0);
}
}
}
function PositionOffset(line, column) {
this.line = line;
this.line = line - 1; // Line start on one so we have to convert it to an offset
this.column = column;
if (preprocessStackLastItem) {
var macro = preprocessStackLastItem.macro;
@@ -893,10 +893,6 @@
last = ch;
ch = input.charCodeAt(++tokPos);
}
if (options.locations) {
++tokCurLine;
tokLineStart = tokPos;
}
}
// Called at the start of the parse and after every token. Skips
@@ -1320,6 +1316,8 @@
// We don't want to concatenate tokens when creating macros
preprocessDontConcatenate = true;
// Get position offset now as ´tokCurLine´ and ´tokLineStart´ points to next token.
var positionOffset = options.locations && new PositionOffset(tokCurLine, tokLineStart);
var macroIdentifier = preprocessGetIdent();
// '(' Must follow directly after identifier to be a valid macro with parameters
if (input.charCodeAt(macroIdentifierEnd) === 40) { // '('
@@ -1332,10 +1330,12 @@
if (!first) preprocessExpect(_comma, "Expected ',' between macro parameters"); else first = false;
parameters.push(preprocessEat(_dotdotdot) ? variadic = true && "__VA_ARGS__" : preprocessGetIdent());
if (preprocessEat(_dotdotdot)) variadic = true;
// Get a new position offset as macro has parameters. This is needed if line has escaped (backslash) newline
positionOffset = options.locations && new PositionOffset(tokCurLine, tokLineStart);
}
}
var start = preTokStart;
var positionOffset = options.locations && new PositionOffset(tokCurLine, tokLineStart);
while(preTokType !== _eol && preTokType !== _eof)
preprocessReadToken();
@@ -1532,18 +1532,13 @@
if (allowEndOfLineToken) {
var r;
if (code === 13) {
r = finishOp(_eol, input.charCodeAt(tokPos+1) === 10 ? 2 : 1, finisher);
} else if (code === 10 || code === 8232 || code === 8233) {
r = finishOp(_eol, 1, finisher);
} else {
return false;
if (code === 13 || code === 10 || code === 8232 || code === 8233) {
if (options.locations) {
++tokCurLine;
tokLineStart = tokPos;
}
return finishOp(_eol, (code === 13 && input.charCodeAt(tokPos+1) === 10) ? 2 : 1, finisher);
}
if (options.locations) {
++tokCurLine;
tokLineStart = tokPos;
}
return r;
}
return false;
@@ -1613,8 +1608,8 @@
// Skip whitespaces sometimes without line breaks
// Returns true if it stops at a line break.
function preprocessSkipSpace(skipComments, skipEOL) {
var ch = onlySkipSpace(!skipEOL);
function preprocessSkipSpace(dontSkipComments, skipEOL) {
var ch = onlySkipSpace(!skipEOL, false, dontSkipComments);
// Can't see that this line break test is used anymore
//lineBreak.lastIndex = 0;
//var match = lineBreak.exec(input.slice(tokPos, tokPos + 2));
@@ -1670,7 +1665,9 @@
if (!preprocessToken && !preNotSkipping && code !== 35) { // '#'
// If we are skipping take the whole line if the token does not start with '#' (preprocess tokens)
preprocesSkipRestOfLine();
return preprocessFinishToken(_preprocessSkipLine, input.slice(preTokStart, tokPos++));
preprocessFinishToken(_preprocessSkipLine, input.slice(preTokStart, tokPos));
preprocessSkipSpace(true, true); // Don't skip comments and skip EOL
return;
} else if (preprocessMacroParameterListMode && code !== 41 && code !== 44) { // ')', ','
var parenLevel = 0;
// If we are parsing a macro parameter list parentheses within each argument must balance
@@ -1723,7 +1720,7 @@
preTokEnd = overrideTokEnd || tokPos;
if (type !== _eol) firstTokEnd = preTokEnd;
//tokRegexpAllowed = type.beforeExpr;
var ch = preprocessSkipSpace(false, skipEOL); // Dont skip comments
var ch = preprocessSkipSpace(false, skipEOL); // Skip comments
if (ch === 35 && options.preprocess && !preprocessDontConcatenate && input.charCodeAt(tokPos + 1) === 35) { // '##'
var val1 = val != null ? val : type.keyword || type.type;
tokPos += 2;
@@ -1765,7 +1762,7 @@
preTokType = type;
preTokVal = val;
firstTokEnd = preTokEnd = tokPos;
preprocessSkipSpace(true); // 'true' for skip comments
preprocessSkipSpace(true); // 'true' for don't skip comments
}
// Continue to the next token.
@@ -2426,7 +2423,7 @@
inputLen = macroString.length;
tokPosMacroOffset = macro.start;
tokPos = 0;
tokCurLine = 0;
tokCurLine = 1;
tokLineStart = 0;
firstTokEnd = 0;
localLastEnd = 0;
@@ -2634,7 +2631,6 @@
// 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(lastEndInput.slice(lastEnd, lastEndOfFile || tokFirstStart)) ||
(nodeMessageSendObjectExpression && options.objj) || lastEndOfFile != null);
File diff suppressed because it is too large. Load diff
@@ -38,7 +38,7 @@ app (projectName, function(task)
task.setInfoPlistPath("Info.plist");
if (configuration === "Debug")
task.setCompilerFlags("-DDEBUG -g");
task.setCompilerFlags("-DDEBUG -g -S --inline-msg-send");
else
task.setCompilerFlags("-O2");
});
@@ -44,7 +44,7 @@
// more information on decorators.
//
// Uncomment or comment on the line below to change the flags
OBJJ_COMPILER_FLAGS = ["IncludeDebugSymbols", "IncludeTypeSignatures"/*, "InlineMsgSend"*/];
OBJJ_COMPILER_FLAGS = ["IncludeDebugSymbols", "IncludeTypeSignatures", "SourceMap", "InlineMsgSend"];
var progressBar = null;
@@ -36,7 +36,7 @@
// This will affect only Objective-J code that is compiled when loading the application. It will not affect precompiled
// code like the Cappuccino frameworks.
// Uncomment or comment on the line below to change the flags
OBJJ_COMPILER_FLAGS = [/*"IncludeDebugSymbols"*/, "IncludeTypeSignatures", "InlineMsgSend"];
OBJJ_COMPILER_FLAGS = [/*"IncludeDebugSymbols"*/, "IncludeTypeSignatures"/*, "SourceMap"*/, "InlineMsgSend"];
var progressBar = null;
@@ -110,7 +110,7 @@ var frameworkTask = framework (productName, function(frameworkTask)
//frameworkTask.setEnvironments([browserEnvironment]);
if (configuration === "Debug")
frameworkTask.setCompilerFlags("-DDEBUG -g");
frameworkTask.setCompilerFlags("-DDEBUG -g -S --inline-msg-send");
else
frameworkTask.setCompilerFlags("-O2");
});
@@ -38,7 +38,7 @@ app (projectName, function(task)
task.setInfoPlistPath("Info.plist");
if (configuration === "Debug")
task.setCompilerFlags("-DDEBUG -g");
task.setCompilerFlags("-DDEBUG -g -S --inline-msg-send");
else
task.setCompilerFlags("-O2");
});
@@ -44,7 +44,7 @@
// more information on decorators.
//
// Uncomment or comment on the line below to change the flags
OBJJ_COMPILER_FLAGS = ["IncludeDebugSymbols", "IncludeTypeSignatures"/*, "InlineMsgSend"*/];
OBJJ_COMPILER_FLAGS = ["IncludeDebugSymbols", "IncludeTypeSignatures", "SourceMap", "InlineMsgSend"];
var progressBar = null;
@@ -36,7 +36,7 @@
// This will affect only Objective-J code that is compiled when loading the application. It will not affect precompiled
// code like the Cappuccino frameworks.
// Uncomment or comment on the line below to change the flags
OBJJ_COMPILER_FLAGS = [/*"IncludeDebugSymbols"*/, "IncludeTypeSignatures", "InlineMsgSend"];
OBJJ_COMPILER_FLAGS = [/*"IncludeDebugSymbols"*/, "IncludeTypeSignatures"/*, "SourceMap"*/, "InlineMsgSend"];
var progressBar = null;