Files
cappuccino/Objective-J/CommonJS/lib/objective-j/compiler.js
T
Martin Carlberg 76f2d15b1b Fixed: Added the option to only inline message send function without also compress the output javascript file.
This allows us to inline the message send function in DEBUG mode. This is great when stepping into a method in the debugger as the message send function is not a separate function. Also the stack traces in the debugger is much more compact as the message send function is not there.
2017-12-08 14:02:11 +01:00

312 lines
9.8 KiB
JavaScript

var FILE = require("file"),
OS = require("os"),
ObjectiveJ = require("objective-j"),
JAKE = require("jake");
require("objective-j/rhino/regexp-rhino-patch");
var compressors = {
ss : { id : "minify/shrinksafe" }
//,yui : { id : "minify/yuicompressor" }
// ,cc : { id : "minify/closure-compiler" }
};
var compressorStats = {};
function compressor(code)
{
var winner, winnerName;
compressorStats['original'] = (compressorStats['original'] || 0) + code.length;
for (var name in compressors)
{
var aCompressor = require(compressors[name].id),
result = aCompressor.compress(code, { charset : "UTF-8", useServer : true });
compressorStats[name] = (compressorStats[name] || 0) + result.length;
if (!winner || result < winner.length) {
winner = result;
winnerName = name;
}
}
// print("winner="+winnerName+" compressorStats="+JSON.stringify(compressorStats));
return winner;
}
function compileWithResolvedFlags(aFilePath, objjcFlags, gccFlags, asPlainJavascript)
{
var shouldObjjPreprocess = true,
shouldCompress = objjcFlags.compress,
fileContents = "",
executable,
code;
if (!shouldObjjPreprocess)
{
try
{
var p = OS.popen("which gcc");
if (p.stdout.read().length === 0)
{
fileContents = FILE.read(aFilePath, { charset:"UTF-8" });
}
else
{
try
{
var gcc = OS.popen("gcc -E -x c -P " + (gccFlags ? gccFlags.join(" ") : "") + " " + OS.enquote(aFilePath), { charset:"UTF-8" }),
chunk = "";
while (chunk = gcc.stdout.read())
fileContents += chunk;
}
finally
{
gcc.stdin.close();
gcc.stdout.close();
gcc.stderr.close();
}
}
}
finally
{
p.stdin.close();
p.stdout.close();
p.stderr.close();
}
return fileContents;
}
// Preprocess contents into fragments.
// FIXME: should calculate relative path, etc.
try
{
var sources = new JAKE.FileList("**/*.j"),
translateFilenameToPath = {},
otherwayTranslateFilenameToPath = {};
// Create a filename to filename path dictionary. (For example: CPArray.j -> CPArray/CPArray.j)
sources.forEach(function(/*String*/ aFilename)
{
translateFilenameToPath[FILE.basename(aFilename)] = aFilename;
otherwayTranslateFilenameToPath[aFilename] = FILE.basename(aFilename);
}, this);
var translatedFilename = translateFilenameToPath[aFilePath] ? translateFilenameToPath[aFilePath] : aFilePath,
otherwayTranslatedFilename = otherwayTranslateFilenameToPath[aFilePath] ? otherwayTranslateFilenameToPath[aFilePath] : aFilePath,
theTranslatedFilename = otherwayTranslatedFilename ? otherwayTranslatedFilename : translatedFilename,
absolutePath = FILE.absolute(theTranslatedFilename),
basePath = absolutePath.substring(0, absolutePath.length - theTranslatedFilename.length);
// This ugly fix to make Cappuccino compile with rhino can be removed when the compiler is not loading classes into the runtime
var rhinoUglyFix = false;
if (system.engine === "rhino")
{
if (typeof document == "undefined") {
document = { createElement: function(x) { return { innerText: "", style: {}}}};
rhinoUglyFix = true;
}
if (typeof navigator == "undefined")
{
navigator = { "userAgent": "fakenavigator" };
rhinoUglyFix = true;
}
}
ObjectiveJ.FileExecutable.setCurrentCompilerFlags(objjcFlags);
ObjectiveJ.make_narwhal_factory(absolutePath, basePath, translateFilenameToPath)(require, {}, module, system, print);
executable = new ObjectiveJ.FileExecutable(FILE.basename(aFilePath));
if (rhinoUglyFix)
delete document;
}
catch (anException)
{
print(anException);
return;
}
if (shouldCompress)
{
code = executable.code();
code = compressor("function(){" + code + "}");
// more robust function wrapper stripping
code = code.replace(/^\s*function\s*\(\s*\)\s*{|}\s*;?\s*$/g, "");
executable.setCode(code);
}
return asPlainJavascript ? executable.code() : executable.toMarkedString();
}
function resolveFlags(args)
{
var filePaths = [],
outputFilePaths = [],
index = 0,
count = args.length,
gccFlags = [],
objjcFlags = {};
for (; index < count; ++index)
{
var argument = args[index];
if (argument === "-o")
{
if (++index < count)
outputFilePaths.push(args[index]);
}
else if (argument.indexOf("-D") === 0)
gccFlags.push(argument);
else if (argument.indexOf("-U") === 0)
gccFlags.push(argument);
else if (argument === "--include")
{
if (++index < count)
{
gccFlags.push(argument);
gccFlags.push(args[index]);
}
}
else if (argument.indexOf("-T") === 0)
{
objjcFlags.includeIvarTypeSignatures = false;
objjcFlags.includeMethodArgumentTypeSignatures = false;
}
else if (argument.indexOf("-g") === 0)
objjcFlags.includeMethodFunctionNames = true;
else if (argument.indexOf("-O") === 0) {
objjcFlags.compress = true;
// FIXME: currently we are sending in '-O2' when we want InlineMsgSend. Here we only check if we it is '-O...'
if (argument.length > 2)
objjcFlags.inlineMsgSendFunctions = true;
}
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);
}
return { filePaths:filePaths, outputFilePaths:outputFilePaths, objjcFlags:objjcFlags, gccFlags:gccFlags };
}
exports.compile = function(aFilePath, flags)
{
if (flags.split)
flags = flags.split(/\s+/);
var resolvedFlags = resolveFlags(flags);
return compileWithResolvedFlags(aFilePath, resolvedFlags.objjcFlags, resolvedFlags.gccFlags);
};
exports.main = function(args)
{
var shouldPrintOutput = false,
asPlainJavascript = false,
objjcFlags = {};
var argv = args.slice(1);
while(argv.length)
{
if (argv[0] === '--')
{
argv.shift();
break;
}
if (argv[0] === "-p" || argv[0] === "--print")
{
shouldPrintOutput = YES;
argv.shift();
continue;
}
if (argv[0] === "--unmarked")
{
asPlainJavascript = true;
argv.shift();
continue;
}
if (argv[0] === "-T" || argv[0] === "--includeTypeSignatures")
{
objjcFlags.includeIvarTypeSignatures = true;
objjcFlags.includeMethodArgumentTypeSignatures = true;
argv.shift();
continue;
}
if (argv[0] === "--help" || argv[0].substr(0, 1) == '-')
{
print("Usage (objjc 2.0): " + args[0] + " [options] [--] file...");
print(" -p, --print print the output directly to stdout");
print(" --unmarked don't tag the output with @STATIC header");
print("");
print(" -T, --dont-include-type-signatures include type signatures in the compiled output");
print(" -g, --include-debug-symbols include debug symbols in the compiled output");
print(" -T, --include-type-signatures include type signatures in the compiled output");
print(" -O, --compress compress the compiled output");
print(" -O2, --inline-msg-send inline objj_msgSend function in the compiled output");
print("");
print(" --help print this help");
return;
}
// Current argument doesn't begin with - so it's not an argument but the
// first filename.
// TODO Full GNU getopt parsing which doesn't stop on the first non-argument.
break;
}
var resolved = resolveFlags(argv),
outputFilePaths = resolved.outputFilePaths,
gccFlags = resolved.gccFlags,
resolvedObjjcFlags = resolved.objjcFlags;
// Merge resolved keys into objjcFlags
for (var key in resolvedObjjcFlags)
{
if (resolvedObjjcFlags.hasOwnProperty(key))
{
if (resolvedObjjcFlags[key])
objjcFlags[key] = resolvedObjjcFlags[key];
}
}
resolved.filePaths.forEach(function(filePath, index)
{
if (!shouldPrintOutput)
print("Statically Compiling " + filePath);
var output = compileWithResolvedFlags(filePath, objjcFlags, gccFlags, asPlainJavascript);
// Only write the output if the compilation didn't fail.
if (output !== undefined)
{
if (shouldPrintOutput)
print(output);
else
FILE.write(outputFilePaths[index], output, { charset: "UTF-8" });
}
});
};
if (require.main == module.id)
exports.main(system.args);