aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorRabsRincon <rubrinbla@gmail.com>2018-01-18 19:43:10 +0100
committerRabsRincon <rubrinbla@gmail.com>2018-01-18 19:43:10 +0100
commita54faefb4c8668d9c9f93c78bd6bc639d1f0c219 (patch)
tree6465bf3e1d8e270cc53e67c04277c5773b16f7d6
parent7bd30b4c4b10f93f26db81fbbe492149b8d487eb (diff)
downloadcompiler-explorer-a54faefb4c8668d9c9f93c78bd6bc639d1f0c219.tar.gz
compiler-explorer-a54faefb4c8668d9c9f93c78bd6bc639d1f0c219.zip
ES6fy compilers inheritance
-rw-r--r--lib/base-compiler.js1089
-rw-r--r--lib/compilers/WSL-CL.js75
-rw-r--r--lib/compilers/Wine-CL.js75
-rw-r--r--lib/compilers/argument-parsers.js65
-rw-r--r--lib/compilers/assembly.js64
-rw-r--r--lib/compilers/default.js14
-rw-r--r--lib/compilers/fake-for-test.js38
-rw-r--r--lib/compilers/gcc.js34
-rw-r--r--lib/compilers/golang.js34
-rw-r--r--lib/compilers/haskell.js41
-rw-r--r--lib/compilers/ispc.js41
-rw-r--r--lib/compilers/ldc.js31
-rw-r--r--lib/compilers/pascal.js222
-rw-r--r--lib/compilers/rust.js28
-rw-r--r--lib/compilers/swift.js42
-rw-r--r--lib/handlers/compile.js7
-rw-r--r--test/compilers/argument-parsers-tests.js30
-rw-r--r--test/handlers/compile-tests.js51
-rw-r--r--test/win-path-tests.js9
19 files changed, 1072 insertions, 918 deletions
diff --git a/lib/base-compiler.js b/lib/base-compiler.js
index 33b0a9036..650bf99b6 100644
--- a/lib/base-compiler.js
+++ b/lib/base-compiler.js
@@ -22,8 +22,7 @@
// ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
// POSSIBILITY OF SUCH DAMAGE.
-const child_process = require('child_process'),
- temp = require('temp'),
+const temp = require('temp'),
path = require('path'),
fs = require('fs-extra'),
denodeify = require('denodeify'),
@@ -37,636 +36,638 @@ const child_process = require('child_process'),
cfg = require('./cfg'),
languages = require('./languages').list;
-function Compile(compiler, env) {
- this.compiler = compiler;
- this.lang = languages[compiler.lang];
- if (!this.lang) {
- throw new Error("Missing language info for " + compiler.lang);
- }
- this.compileFilename = 'example' + this.lang.extensions[0];
- this.env = env;
- this.compilerProps = _.partial(this.env.compilerPropsL, this.lang.id);
- this.asm = new asm.AsmParser(this.compilerPropsL);
- this.compiler.supportsIntel = !!this.compiler.intelAsm;
-
- if (!this.compiler.options) this.compiler.options = "";
- if (!this.compiler.optArg) this.compiler.optArg = "";
- if (!this.compiler.supportsOptOutput) this.compiler.supportsOptOutput = false;
-}
+class BaseCompiler {
+ constructor(compiler, env) {
+ this.compiler = compiler;
+ this.lang = languages[compiler.lang];
+ if (!this.lang) {
+ throw new Error("Missing language info for " + compiler.lang);
+ }
+ this.compileFilename = 'example' + this.lang.extensions[0];
+ this.env = env;
+ this.compilerProps = _.partial(this.env.compilerPropsL, this.lang.id);
+ this.compiler.supportsIntel = !!this.compiler.intelAsm;
+
+ if (!this.compiler.options) this.compiler.options = "";
+ if (!this.compiler.optArg) this.compiler.optArg = "";
+ if (!this.compiler.supportsOptOutput) this.compiler.supportsOptOutput = false;
+
+ this.writeFile = denodeify(fs.writeFile);
+ this.readFile = denodeify(fs.readFile);
+ this.stat = denodeify(fs.stat);
+ }
-Compile.prototype.newTempDir = function () {
- return new Promise(function (resolve, reject) {
- temp.mkdir({prefix: 'compiler-explorer-compiler', dir: process.env.tmpDir}, function (err, dirPath) {
- if (err)
- reject("Unable to open temp file: " + err);
- else
- resolve(dirPath);
- });
- });
-};
-
-Compile.prototype.writeFile = denodeify(fs.writeFile);
-Compile.prototype.readFile = denodeify(fs.readFile);
-Compile.prototype.stat = denodeify(fs.stat);
-
-Compile.prototype.optOutputRequested = function (options) {
- return options.some((x) => x === "-fsave-optimization-record");
-};
-
-Compile.prototype.getRemote = function () {
- if (this.compiler.exe === null && this.compiler.remote)
- return this.compiler.remote;
- return false;
-};
-
-Compile.prototype.exec = function (compiler, args, options) {
- // Here only so can be overridden by compiler implementations.
- return exec.execute(compiler, args, options);
-};
-
-Compile.prototype.getDefaultExecOptions = function () {
- return {
- timeoutMs: this.env.ceProps("compileTimeoutMs", 100),
- maxErrorOutput: this.env.ceProps("max-error-output", 5000),
- env: this.env.getEnv(this.compiler.needsMulti),
- wrapper: this.compilerProps("compiler-wrapper")
- };
-};
-
-Compile.prototype.runCompiler = function (compiler, options, inputFilename, execOptions) {
- if (!execOptions) {
- execOptions = this.getDefaultExecOptions();
- }
-
- return this.exec(compiler, options, execOptions).then(function (result) {
- result.inputFilename = inputFilename;
- result.stdout = utils.parseOutput(result.stdout, inputFilename);
- result.stderr = utils.parseOutput(result.stderr, inputFilename);
- return result;
- });
-};
-
-Compile.prototype.supportsObjdump = function () {
- return this.compiler.objdumper !== "";
-};
-
-Compile.prototype.objdump = function (outputFilename, result, maxSize, intelAsm, demangle) {
- let args = ["-d", outputFilename, "-l", "--insn-width=16"];
- if (demangle) args = args.concat("-C");
- if (intelAsm) args = args.concat(["-M", "intel"]);
- return this.exec(this.compiler.objdumper, args, {maxOutput: maxSize})
- .then(function (objResult) {
- result.asm = objResult.stdout;
- if (objResult.code !== 0) {
- result.asm = "<No output: objdump returned " + objResult.code + ">";
- }
- return result;
+ newTempDir() {
+ return new Promise(function (resolve, reject) {
+ temp.mkdir({prefix: 'compiler-explorer-compiler', dir: process.env.tmpDir}, function (err, dirPath) {
+ if (err)
+ reject("Unable to open temp file: " + err);
+ else
+ resolve(dirPath);
+ });
});
-};
-
-Compile.prototype.execBinary = function (executable, result, maxSize) {
- return exec.sandbox(executable, [], {
- maxOutput: maxSize,
- timeoutMs: 2000
- }) // TODO make config
- .then(function (execResult) {
- execResult.stdout = utils.parseOutput(execResult.stdout);
- execResult.stderr = utils.parseOutput(execResult.stderr);
- result.execResult = execResult;
- return result;
- }).catch(function (err) {
- // TODO: is this the best way? Perhaps failures in sandbox shouldn't reject
- // with "results", but instead should play on?
- result.execResult = {
- stdout: err.stdout ? utils.parseOutput(err.stdout) : [],
- stderr: err.stderr ? utils.parseOutput(err.stderr) : [],
- code: err.code !== undefined ? err.code : -1
- };
+ }
+
+ optOutputRequested(options) {
+ return options.some((x) => x === "-fsave-optimization-record");
+ }
+
+ getRemote() {
+ if (this.compiler.exe === null && this.compiler.remote)
+ return this.compiler.remote;
+ return false;
+ }
+
+ exec(compiler, args, options) {
+ // Here only so can be overridden by compiler implementations.
+ return exec.execute(compiler, args, options);
+ }
+
+ getDefaultExecOptions() {
+ return {
+ timeoutMs: this.env.ceProps("compileTimeoutMs", 100),
+ maxErrorOutput: this.env.ceProps("max-error-output", 5000),
+ env: this.env.getEnv(this.compiler.needsMulti),
+ wrapper: this.compilerProps("compiler-wrapper")
+ };
+ }
+
+ runCompiler(compiler, options, inputFilename, execOptions) {
+ if (!execOptions) {
+ execOptions = this.getDefaultExecOptions();
+ }
+
+ return this.exec(compiler, options, execOptions).then(function (result) {
+ result.inputFilename = inputFilename;
+ result.stdout = utils.parseOutput(result.stdout, inputFilename);
+ result.stderr = utils.parseOutput(result.stderr, inputFilename);
return result;
});
-};
+ }
-Compile.prototype.filename = function (fn) {
- return fn;
-};
+ supportsObjdump() {
+ return this.compiler.objdumper !== "";
+ }
-Compile.prototype.optionsForFilter = function (filters, outputFilename, userOptions) {
- let options = ['-g', '-o', this.filename(outputFilename)];
- if (this.compiler.intelAsm && filters.intel && !filters.binary) {
- options = options.concat(this.compiler.intelAsm.split(" "));
+ objdump(outputFilename, result, maxSize, intelAsm, demangle) {
+ let args = ["-d", outputFilename, "-l", "--insn-width=16"];
+ if (demangle) args = args.concat("-C");
+ if (intelAsm) args = args.concat(["-M", "intel"]);
+ return this.exec(this.compiler.objdumper, args, {maxOutput: maxSize})
+ .then(function (objResult) {
+ result.asm = objResult.stdout;
+ if (objResult.code !== 0) {
+ result.asm = "<No output: objdump returned " + objResult.code + ">";
+ }
+ return result;
+ });
}
- if (!filters.binary) options = options.concat('-S');
- return options;
-};
-Compile.prototype.prepareArguments = function (userOptions, filters, backendOptions, inputFilename, outputFilename) {
- let options = this.optionsForFilter(filters, outputFilename, userOptions);
- backendOptions = backendOptions || {};
+ execBinary(executable, result, maxSize) {
+ return exec.sandbox(executable, [], {
+ maxOutput: maxSize,
+ timeoutMs: 2000
+ }) // TODO make config
+ .then(function (execResult) {
+ execResult.stdout = utils.parseOutput(execResult.stdout);
+ execResult.stderr = utils.parseOutput(execResult.stderr);
+ result.execResult = execResult;
+ return result;
+ }).catch(function (err) {
+ // TODO: is this the best way? Perhaps failures in sandbox shouldn't reject
+ // with "results", but instead should play on?
+ result.execResult = {
+ stdout: err.stdout ? utils.parseOutput(err.stdout) : [],
+ stderr: err.stderr ? utils.parseOutput(err.stderr) : [],
+ code: err.code !== undefined ? err.code : -1
+ };
+ return result;
+ });
+ }
- if (this.compiler.options) {
- options = options.concat(this.compiler.options.split(" "));
+ filename(fn) {
+ return fn;
}
- if (this.compiler.supportsOptOutput && backendOptions.produceOptInfo) {
- options = options.concat(this.compiler.optArg);
+ optionsForFilter(filters, outputFilename, userOptions) {
+ let options = ['-g', '-o', this.filename(outputFilename)];
+ if (this.compiler.intelAsm && filters.intel && !filters.binary) {
+ options = options.concat(this.compiler.intelAsm.split(" "));
+ }
+ if (!filters.binary) options = options.concat('-S');
+ return options;
}
- userOptions = this.filterUserOptions(userOptions);
- return options.concat(userOptions || []).concat([this.filename(inputFilename)]);
-};
+ prepareArguments(userOptions, filters, backendOptions, inputFilename, outputFilename) {
+ let options = this.optionsForFilter(filters, outputFilename, userOptions);
+ backendOptions = backendOptions || {};
-Compile.prototype.filterUserOptions = function (userOptions) {
- return userOptions;
-};
+ if (this.compiler.options) {
+ options = options.concat(this.compiler.options.split(" "));
+ }
-Compile.prototype.generateAST = function (inputFilename, options) {
- // These options make Clang produce an AST dump
- let newOptions = _.filter(options, option => option !== '-fcolor-diagnostics')
- .concat(["-Xclang", "-ast-dump", "-fsyntax-only"]);
+ if (this.compiler.supportsOptOutput && backendOptions.produceOptInfo) {
+ options = options.concat(this.compiler.optArg);
+ }
+
+ userOptions = this.filterUserOptions(userOptions);
+ return options.concat(userOptions || []).concat([this.filename(inputFilename)]);
+ }
- let execOptions = this.getDefaultExecOptions();
- // A higher max output is needed for when the user includes headers
- execOptions.maxOutput = 1024 * 1024 * 1024;
- return this.runCompiler(this.compiler.exe, newOptions, this.filename(inputFilename), execOptions)
- .then(this.processAstOutput);
-};
+ filterUserOptions(userOptions) {
+ return userOptions;
+ }
-Compile.prototype.getOutputFilename = function (dirPath, outputFilebase) {
- return path.join(dirPath, outputFilebase + ".s"); // NB keep lower case as ldc compiler `tolower`s the output name
-};
+ generateAST(inputFilename, options) {
+ // These options make Clang produce an AST dump
+ let newOptions = _.filter(options, option => option !== '-fcolor-diagnostics')
+ .concat(["-Xclang", "-ast-dump", "-fsyntax-only"]);
-Compile.prototype.generateGccDump = function (inputFilename, options, gccDumpOptions) {
- // Maybe we should not force any RTL dump and let user hand-pick what he needs
- const addOpts = [];
- /* if not defined, consider it true */
+ let execOptions = this.getDefaultExecOptions();
+ // A higher max output is needed for when the user includes headers
+ execOptions.maxOutput = 1024 * 1024 * 1024;
- if (gccDumpOptions.treeDump !== false) {
- addOpts.push("-fdump-tree-all");
+ return this.runCompiler(this.compiler.exe, newOptions, this.filename(inputFilename), execOptions)
+ .then(this.processAstOutput);
}
- if (gccDumpOptions.rtlDump !== false) {
- addOpts.push("-fdump-rtl-all");
+
+ getOutputFilename(dirPath, outputFilebase) {
+ return path.join(dirPath, outputFilebase + ".s"); // NB keep lower case as ldc compiler `tolower`s the output name
}
- const newOptions = options.concat(addOpts);
+ generateGccDump(inputFilename, options, gccDumpOptions) {
+ // Maybe we should not force any RTL dump and let user hand-pick what he needs
+ const addOpts = [];
+ /* if not defined, consider it true */
- const execOptions = this.getDefaultExecOptions();
- // A higher max output is needed for when the user includes headers
- execOptions.maxOutput = 1024 * 1024 * 1024;
+ if (gccDumpOptions.treeDump !== false) {
+ addOpts.push("-fdump-tree-all");
+ }
+ if (gccDumpOptions.rtlDump !== false) {
+ addOpts.push("-fdump-rtl-all");
+ }
- return this.runCompiler(this.compiler.exe, newOptions, this.filename(inputFilename), execOptions)
- .then(result => this.processGccDumpOutput(gccDumpOptions, result));
-};
+ const newOptions = options.concat(addOpts);
-Compile.prototype.compile = function (source, options, backendOptions, filters) {
- const optionsError = this.checkOptions(options);
- if (optionsError) return Promise.reject(optionsError);
- const sourceError = this.checkSource(source);
- if (sourceError) return Promise.reject(sourceError);
+ const execOptions = this.getDefaultExecOptions();
+ // A higher max output is needed for when the user includes headers
+ execOptions.maxOutput = 1024 * 1024 * 1024;
- // Don't run binary for unsupported compilers, even if we're asked.
- if (filters.binary && !this.compiler.supportsBinary) {
- delete filters.binary;
+ return this.runCompiler(this.compiler.exe, newOptions, this.filename(inputFilename), execOptions)
+ .then(result => this.processGccDumpOutput(gccDumpOptions, result));
}
- const key = JSON.stringify({
- compiler: this.compiler,
- source: source,
- options: options,
- backendOptions: backendOptions,
- filters: filters
- });
+ compile(source, options, backendOptions, filters) {
+ const optionsError = this.checkOptions(options);
+ if (optionsError) return Promise.reject(optionsError);
+ const sourceError = this.checkSource(source);
+ if (sourceError) return Promise.reject(sourceError);
- const cached = this.env.cacheGet(key);
- if (cached) {
- return Promise.resolve(cached);
- }
+ // Don't run binary for unsupported compilers, even if we're asked.
+ if (filters.binary && !this.compiler.supportsBinary) {
+ delete filters.binary;
+ }
- if (filters.binary && !source.match(this.compilerProps("stubRe"))) {
- source += "\n" + this.compilerProps("stubText") + "\n";
- }
- return this.env.enqueue(() => {
- const tempFileAndDirPromise = this.newTempDir()
- .then(dirPath => {
- const inputFilename = path.join(dirPath, this.compileFilename);
- return this.writeFile(inputFilename, source).then(() => ({
- inputFilename: inputFilename,
- dirPath: dirPath
- }));
- });
+ const key = JSON.stringify({
+ compiler: this.compiler,
+ source: source,
+ options: options,
+ backendOptions: backendOptions,
+ filters: filters
+ });
- const compileToAsmPromise = tempFileAndDirPromise.then(info => {
- const inputFilename = info.inputFilename;
- const dirPath = info.dirPath;
- const outputFilebase = "output";
- const outputFilename = this.getOutputFilename(dirPath, outputFilebase);
+ const cached = this.env.cacheGet(key);
+ if (cached) {
+ return Promise.resolve(cached);
+ }
- options = _.compact(this.prepareArguments(options, filters, backendOptions, inputFilename, outputFilename));
+ if (filters.binary && !source.match(this.compilerProps("stubRe"))) {
+ source += "\n" + this.compilerProps("stubText") + "\n";
+ }
+ return this.env.enqueue(() => {
+ const tempFileAndDirPromise = this.newTempDir()
+ .then(dirPath => {
+ const inputFilename = path.join(dirPath, this.compileFilename);
+ return this.writeFile(inputFilename, source).then(() => ({
+ inputFilename: inputFilename,
+ dirPath: dirPath
+ }));
+ });
- const execOptions = this.getDefaultExecOptions();
+ const compileToAsmPromise = tempFileAndDirPromise.then(info => {
+ const inputFilename = info.inputFilename;
+ const dirPath = info.dirPath;
+ const outputFilebase = "output";
+ const outputFilename = this.getOutputFilename(dirPath, outputFilebase);
- const asmPromise = this.runCompiler(this.compiler.exe, options, this.filename(inputFilename), execOptions);
+ options = _.compact(this.prepareArguments(options, filters, backendOptions, inputFilename, outputFilename));
- let astPromise;
- if (backendOptions && backendOptions.produceAst) {
- if (this.couldSupportASTDump(options, this.compiler.version)) {
- astPromise = this.generateAST(inputFilename, options);
- }
- else {
- astPromise = Promise.resolve("AST output is only supported in Clang >= 3.3");
- }
- }
- else {
- astPromise = Promise.resolve("");
- }
+ const execOptions = this.getDefaultExecOptions();
- let gccDumpPromise;
- if (backendOptions && backendOptions.produceGccDump && backendOptions.produceGccDump.opened) {
- gccDumpPromise = this.generateGccDump(inputFilename, options, backendOptions.produceGccDump);
- }
- else {
- gccDumpPromise = Promise.resolve("");
- }
+ const asmPromise = this.runCompiler(this.compiler.exe, options, this.filename(inputFilename), execOptions);
- return Promise.all([asmPromise, astPromise, gccDumpPromise])
- .then(([asmResult, astResult, gccDumpResult]) => {
- asmResult.dirPath = dirPath;
- if (asmResult.code !== 0) {
- asmResult.asm = "<Compilation failed>";
- return asmResult;
- }
- asmResult.hasOptOutput = false;
- if (this.compiler.supportsOptOutput && this.optOutputRequested(options)) {
- const optPath = path.join(dirPath, outputFilebase + ".opt.yaml");
- if (fs.existsSync(optPath)) {
- asmResult.hasOptOutput = true;
- asmResult.optPath = optPath;
- }
+ let astPromise;
+ if (backendOptions && backendOptions.produceAst) {
+ if (this.couldSupportASTDump(options, this.compiler.version)) {
+ astPromise = this.generateAST(inputFilename, options);
}
- if (astResult) {
- asmResult.hasAstOutput = true;
- asmResult.astOutput = astResult;
+ else {
+ astPromise = Promise.resolve("AST output is only supported in Clang >= 3.3");
}
-
- if (this.compiler.supportsGccDump && gccDumpResult) {
- asmResult.hasGccDumpOutput = true;
- asmResult.gccDumpOutput = gccDumpResult;
- }
- return this.postProcess(asmResult, outputFilename, filters);
- });
- });
-
- return compileToAsmPromise
- .then(results => {
- //TODO(jared): this isn't ideal. Rethink
- let result;
- let optOutput;
- if (results.length) {
- result = results[0];
- optOutput = results[1];
- } else {
- result = results;
}
- if (result.dirPath) {
- fs.remove(result.dirPath);
- result.dirPath = undefined;
- }
- if (result.okToCache) {
- result.asm = this.asm.process(result.asm, filters);
- } else {
- result.asm = {text: result.asm};
+ else {
+ astPromise = Promise.resolve("");
}
- if (result.hasOptOutput) {
- delete result.optPath;
- result.optOutput = optOutput;
+
+ let gccDumpPromise;
+ if (backendOptions && backendOptions.produceGccDump && backendOptions.produceGccDump.opened) {
+ gccDumpPromise = this.generateGccDump(inputFilename, options, backendOptions.produceGccDump);
}
- return result;
- })
- .then(result => filters.demangle ? this.postProcessAsm(result) : result)
- .then(result => {
- if (this.compiler.supportsCfg && backendOptions && backendOptions.produceCfg) {
- result.cfg = cfg.generateStructure(this.compiler.version, result.asm);
+ else {
+ gccDumpPromise = Promise.resolve("");
}
- return result;
- })
- .then(result => {
- if (result.okToCache) this.env.cachePut(key, result);
- return result;
+
+ return Promise.all([asmPromise, astPromise, gccDumpPromise])
+ .then(([asmResult, astResult, gccDumpResult]) => {
+ asmResult.dirPath = dirPath;
+ if (asmResult.code !== 0) {
+ asmResult.asm = "<Compilation failed>";
+ return asmResult;
+ }
+ asmResult.hasOptOutput = false;
+ if (this.compiler.supportsOptOutput && this.optOutputRequested(options)) {
+ const optPath = path.join(dirPath, outputFilebase + ".opt.yaml");
+ if (fs.existsSync(optPath)) {
+ asmResult.hasOptOutput = true;
+ asmResult.optPath = optPath;
+ }
+ }
+ if (astResult) {
+ asmResult.hasAstOutput = true;
+ asmResult.astOutput = astResult;
+ }
+
+ if (this.compiler.supportsGccDump && gccDumpResult) {
+ asmResult.hasGccDumpOutput = true;
+ asmResult.gccDumpOutput = gccDumpResult;
+ }
+ return this.postProcess(asmResult, outputFilename, filters);
+ });
});
- });
-};
-
-Compile.prototype.postProcessAsm = function (result) {
- if (!result.okToCache) return result;
- const demangler = this.compiler.demangler;
- if (!demangler) return result;
- return this.exec(demangler, [], {input: _.pluck(result.asm, 'text').join("\n")})
- .then((demangleResult) => {
- const lines = utils.splitLines(demangleResult.stdout);
- for (let i = 0; i < result.asm.length; ++i)
- result.asm[i].text = lines[i];
- return result;
- });
-};
-
-Compile.prototype.processOptOutput = function (hasOptOutput, optPath) {
- let output = [];
- return new Promise(
- resolve => {
- fs.createReadStream(optPath, {encoding: "utf-8"})
- .pipe(new compilerOptInfo.LLVMOptTransformer())
- .on("data", opt => {
- if (opt.DebugLoc &&
- opt.DebugLoc.File &&
- opt.DebugLoc.File.indexOf(this.compileFilename) > -1) {
-
- output.push(opt);
+
+ return compileToAsmPromise
+ .then(results => {
+ //TODO(jared): this isn't ideal. Rethink
+ let result;
+ let optOutput;
+ if (results.length) {
+ result = results[0];
+ optOutput = results[1];
+ } else {
+ result = results;
}
- })
- .on("end", () => {
- if (this.compiler.demangler) {
- const result = JSON.stringify(output, null, 4);
- this.exec(this.compiler.demangler, ["-n", "-p"], {input: result})
- .then(demangleResult => resolve(JSON.parse(demangleResult.stdout)))
- .catch(exception => {
- logger.warn("Caught exception " + exception + " during opt demangle parsing");
- resolve(output);
- });
+ if (result.dirPath) {
+ fs.remove(result.dirPath);
+ result.dirPath = undefined;
+ }
+ if (result.okToCache) {
+ result.asm = this.asm.process(result.asm, filters);
} else {
- resolve(output);
+ result.asm = {text: result.asm};
+ }
+ if (result.hasOptOutput) {
+ delete result.optPath;
+ result.optOutput = optOutput;
+ }
+ return result;
+ })
+ .then(result => filters.demangle ? this.postProcessAsm(result, filters) : result)
+ .then(result => {
+ if (this.compiler.supportsCfg && backendOptions && backendOptions.produceCfg) {
+ result.cfg = cfg.generateStructure(this.compiler.version, result.asm);
}
+ return result;
+ })
+ .then(result => {
+ if (result.okToCache) this.env.cachePut(key, result);
+ return result;
});
});
-};
+ }
-Compile.prototype.couldSupportASTDump = function (options, version) {
- const versionRegex = /version (\d.\d+)/;
- const versionMatch = versionRegex.exec(version);
+ postProcessAsm(result) {
+ if (!result.okToCache) return result;
+ const demangler = this.compiler.demangler;
+ if (!demangler) return result;
+ return this.exec(demangler, [], {input: _.pluck(result.asm, 'text').join("\n")})
+ .then((demangleResult) => {
+ const lines = utils.splitLines(demangleResult.stdout);
+ for (let i = 0; i < result.asm.length; ++i)
+ result.asm[i].text = lines[i];
+ return result;
+ });
+ }
- if (versionMatch) {
- const versionNum = parseFloat(versionMatch[1]);
- return version.toLowerCase().indexOf("clang") > -1 && versionNum >= 3.3;
+ processOptOutput(hasOptOutput, optPath) {
+ let output = [];
+ return new Promise(
+ resolve => {
+ fs.createReadStream(optPath, {encoding: "utf-8"})
+ .pipe(new compilerOptInfo.LLVMOptTransformer())
+ .on("data", opt => {
+ if (opt.DebugLoc &&
+ opt.DebugLoc.File &&
+ opt.DebugLoc.File.indexOf(this.compileFilename) > -1) {
+
+ output.push(opt);
+ }
+ })
+ .on("end", () => {
+ if (this.compiler.demangler) {
+ const result = JSON.stringify(output, null, 4);
+ this.exec(this.compiler.demangler, ["-n", "-p"], {input: result})
+ .then(demangleResult => resolve(JSON.parse(demangleResult.stdout)))
+ .catch(exception => {
+ logger.warn("Caught exception " + exception + " during opt demangle parsing");
+ resolve(output);
+ });
+ } else {
+ resolve(output);
+ }
+ });
+ });
}
- return false;
-};
+ couldSupportASTDump(options, version) {
+ const versionRegex = /version (\d.\d+)/;
+ const versionMatch = versionRegex.exec(version);
-Compile.prototype.isCfgCompiler = function (compilerVersion) {
- return compilerVersion.includes("clang") || compilerVersion.indexOf("g++") === 0;
-};
+ if (versionMatch) {
+ const versionNum = parseFloat(versionMatch[1]);
+ return version.toLowerCase().indexOf("clang") > -1 && versionNum >= 3.3;
+ }
+
+ return false;
+ }
-Compile.prototype.processAstOutput = function (output) {
- output = output.stdout;
- output = output.map(function (x) {
- return x.text;
- });
+ isCfgCompiler(compilerVersion) {
+ return compilerVersion.includes("clang") || compilerVersion.indexOf("g++") === 0;
+ }
- // Top level decls start with |- or `-
- const topLevelRegex = /^(\||`)-/;
+ processAstOutput(output) {
+ output = output.stdout;
+ output = output.map(function (x) {
+ return x.text;
+ });
- // Refers to the user's source file rather than a system header
- const sourceRegex = /<source>/g;
+ // Top level decls start with |- or `-
+ const topLevelRegex = /^(\||`)-/;
- // Refers to whatever the most recent file specified was
- const lineRegex = /<line:/;
+ // Refers to the user's source file rather than a system header
+ const sourceRegex = /<source>/g;
- let mostRecentIsSource = false;
+ // Refers to whatever the most recent file specified was
+ const lineRegex = /<line:/;
- // Remove all AST nodes which aren't directly from the user's source code
- for (let i = 0; i < output.length; ++i) {
- if (output[i].match(topLevelRegex)) {
- if (output[i].match(lineRegex) && mostRecentIsSource) {
- //do nothing
- }
- // This is a system header or implicit definition,
- // remove everything up to the next top level decl
- else if (!output[i].match(sourceRegex)) {
- // Top level decls with invalid sloc as the file don't change the most recent file
- let slocRegex = /<<invalid sloc>>/;
- if (!output[i].match(slocRegex)) {
- mostRecentIsSource = false;
+ let mostRecentIsSource = false;
+
+ // Remove all AST nodes which aren't directly from the user's source code
+ for (let i = 0; i < output.length; ++i) {
+ if (output[i].match(topLevelRegex)) {
+ if (output[i].match(lineRegex) && mostRecentIsSource) {
+ //do nothing
}
+ // This is a system header or implicit definition,
+ // remove everything up to the next top level decl
+ else if (!output[i].match(sourceRegex)) {
+ // Top level decls with invalid sloc as the file don't change the most recent file
+ let slocRegex = /<<invalid sloc>>/;
+ if (!output[i].match(slocRegex)) {
+ mostRecentIsSource = false;
+ }
- let spliceMax = i + 1;
- while (output[spliceMax] && !output[spliceMax].match(topLevelRegex)) {
- spliceMax++;
+ let spliceMax = i + 1;
+ while (output[spliceMax] && !output[spliceMax].match(topLevelRegex)) {
+ spliceMax++;
+ }
+ output.splice(i, spliceMax - i);
+ --i;
+ }
+ else {
+ mostRecentIsSource = true;
}
- output.splice(i, spliceMax - i);
- --i;
- }
- else {
- mostRecentIsSource = true;
}
}
- }
-
- output = output.join('\n');
- // Filter out the symbol addresses
- const addressRegex = /^([^A-Za-z]*[A-Za-z]+) 0x[a-z0-9]+/mg;
- output = output.replace(addressRegex, '$1');
+ output = output.join('\n');
- // Filter out <invalid sloc> and <<invalid sloc>>
- let slocRegex = / ?<?<invalid sloc>>?/g;
- output = output.replace(slocRegex, '');
+ // Filter out the symbol addresses
+ const addressRegex = /^([^A-Za-z]*[A-Za-z]+) 0x[a-z0-9]+/mg;
+ output = output.replace(addressRegex, '$1');
- // Unify file references
- output = output.replace(sourceRegex, 'line');
+ // Filter out <invalid sloc> and <<invalid sloc>>
+ let slocRegex = / ?<?<invalid sloc>>?/g;
+ output = output.replace(slocRegex, '');
- return output;
-};
+ // Unify file references
+ output = output.replace(sourceRegex, 'line');
-Compile.prototype.processGccDumpOutput = function (opts, result) {
- const rootDir = path.dirname(result.inputFilename);
- const allFiles = fs.readdirSync(rootDir);
- const base = path.basename(result.inputFilename);
-
- if (opts.treeDump === false && opts.rtlDump === false) {
- return {
- all: [],
- selectedPass: "",
- currentPassOutput: 'Nothing selected for dump:\nselect at least one of Tree/RTL filter',
- syntaxHighlight: false
- };
+ return output;
}
- const allPasses = [];
+ processGccDumpOutput(opts, result) {
+ const rootDir = path.dirname(result.inputFilename);
+ const allFiles = fs.readdirSync(rootDir);
+ const base = path.basename(result.inputFilename);
+
+ if (opts.treeDump === false && opts.rtlDump === false) {
+ return {
+ all: [],
+ selectedPass: "",
+ currentPassOutput: 'Nothing selected for dump:\nselect at least one of Tree/RTL filter',
+ syntaxHighlight: false
+ };
+ }
+
+ const allPasses = [];
- for (let i in allFiles) {
- const pass_str_idx = allFiles[i].indexOf(base + '.');
- if (pass_str_idx !== -1) {
- allPasses.push(allFiles[i].substring(base.length + 1));
+ for (let i in allFiles) {
+ const pass_str_idx = allFiles[i].indexOf(base + '.');
+ if (pass_str_idx !== -1) {
+ allPasses.push(allFiles[i].substring(base.length + 1));
+ }
}
- }
- const output = {
- all: allPasses,
- selectedPass: opts.pass,
- currentPassOutput: '<No pass selected>',
- syntaxHighlight: false
- };
+ const output = {
+ all: allPasses,
+ selectedPass: opts.pass,
+ currentPassOutput: '<No pass selected>',
+ syntaxHighlight: false
+ };
- if (opts.pass) {
- const passDump = result.inputFilename + "." + opts.pass;
+ if (opts.pass) {
+ const passDump = result.inputFilename + "." + opts.pass;
- if (fs.existsSync(passDump) && fs.statSync(passDump).isFile()) {
- output.currentPassOutput = fs.readFileSync(passDump, 'utf-8');
- if (output.currentPassOutput.match('^\s*$')) {
- output.currentPassOutput = 'File for selected pass is empty.';
+ if (fs.existsSync(passDump) && fs.statSync(passDump).isFile()) {
+ output.currentPassOutput = fs.readFileSync(passDump, 'utf-8');
+ if (output.currentPassOutput.match('^\s*$')) {
+ output.currentPassOutput = 'File for selected pass is empty.';
+ } else {
+ output.syntaxHighlight = true;
+ }
} else {
- output.syntaxHighlight = true;
+ // most probably filter has changed and the request is outdated.
+ output.currentPassOutput = "Pass '" + output.selectedPass + "' was requested\n";
+ output.currentPassOutput += "but is not valid anymore with current filters.\n";
+ output.currentPassOutput += "Please select another pass or change filters.\n";
+
+ output.selectedPass = "";
}
+ }
+
+ return output;
+ }
+
+ postProcess(result, outputFilename, filters) {
+ const postProcess = _.compact(this.compiler.postProcess);
+ const maxSize = this.env.ceProps("max-asm-size", 8 * 1024 * 1024);
+ let optPromise, asmPromise, execPromise;
+ if (result.hasOptOutput) {
+ optPromise = this.processOptOutput(result.hasOptOutput, result.optPath);
} else {
- // most probably filter has changed and the request is outdated.
- output.currentPassOutput = "Pass '" + output.selectedPass + "' was requested\n";
- output.currentPassOutput += "but is not valid anymore with current filters.\n";
- output.currentPassOutput += "Please select another pass or change filters.\n";
+ optPromise = Promise.resolve("");
+ }
- output.selectedPass = "";
+ if (filters.binary && this.supportsObjdump()) {
+ asmPromise = this.objdump(outputFilename, result, maxSize, filters.intel, filters.demangle);
+ } else {
+ asmPromise = this.stat(outputFilename).then(stat => {
+ if (stat.size >= maxSize) {
+ result.asm = "<No output: generated assembly was too large (" + stat.size + " > " + maxSize + " bytes)>";
+ return result;
+ }
+ if (postProcess.length) {
+ const postCommand = 'cat "' + outputFilename + '" | ' + postProcess.join(" | ");
+ return this.exec("bash", ["-c", postCommand], {maxOutput: maxSize})
+ .then((postResult) => {
+ return this.handlePostProcessResult(result, postResult);
+ });
+ } else {
+ return this.readFile(outputFilename).then(function (contents) {
+ result.asm = contents.toString();
+ return Promise.resolve(result);
+ });
+ }
+ },
+ () => {
+ result.asm = "<No output file>";
+ return result;
+ }
+ );
+ }
+ if (filters.execute) {
+ const maxExecOutputSize = this.env.ceProps("max-executable-output-size", 32 * 1024);
+ execPromise = this.execBinary(outputFilename, result, maxExecOutputSize);
+ } else {
+ execPromise = Promise.resolve("");
}
+
+ return Promise.all([asmPromise, optPromise, execPromise]);
}
- return output;
-};
+ handlePostProcessResult(result, postResult) {
+ result.asm = postResult.stdout;
+ if (postResult.code !== 0) {
+ result.asm = "<Error during post processing: " + postResult.code + ">";
+ logger.error("Error during post-processing", result);
+ }
+ return result;
+ }
-Compile.prototype.postProcess = function (result, outputFilename, filters) {
- const postProcess = _.compact(this.compiler.postProcess);
- const maxSize = this.env.ceProps("max-asm-size", 8 * 1024 * 1024);
- let optPromise, asmPromise, execPromise;
- if (result.hasOptOutput) {
- optPromise = this.processOptOutput(result.hasOptOutput, result.optPath);
- } else {
- optPromise = Promise.resolve("");
+ checkOptions(options) {
+ const error = this.env.findBadOptions(options);
+ if (error.length > 0) return "Bad options: " + error.join(", ");
+ return null;
}
- if (filters.binary && this.supportsObjdump()) {
- asmPromise = this.objdump(outputFilename, result, maxSize, filters.intel, filters.demangle);
- } else {
- asmPromise = this.stat(outputFilename).then(stat => {
- if (stat.size >= maxSize) {
- result.asm = "<No output: generated assembly was too large (" + stat.size + " > " + maxSize + " bytes)>";
- return result;
- }
- if (postProcess.length) {
- const postCommand = 'cat "' + outputFilename + '" | ' + postProcess.join(" | ");
- return this.exec("bash", ["-c", postCommand], {maxOutput: maxSize})
- .then((postResult) => {
- return this.handlePostProcessResult(result, postResult);
- });
- } else {
- return this.readFile(outputFilename).then(function (contents) {
- result.asm = contents.toString();
- return Promise.resolve(result);
- });
- }
- },
- () => {
- result.asm = "<No output file>";
- return result;
+ // This check for arbitrary user-controlled preprocessor inclusions
+ // can be circumvented in more than one way. The goal here is to respond
+ // to simple attempts with a clear diagnostic; the service still needs to
+ // assume that malicious actors can make the compiler open arbitrary files.
+ checkSource(source) {
+ const re = /^\s*#\s*i(nclude|mport)(_next)?\s+["<](\/|.*\.\.)/;
+ const failed = [];
+ utils.splitLines(source).forEach(function (line, index) {
+ if (line.match(re)) {
+ failed.push("<stdin>:" + (index + 1) + ":1: no absolute or relative includes please");
}
- );
- }
- if (filters.execute) {
- const maxExecOutputSize = this.env.ceProps("max-executable-output-size", 32 * 1024);
- execPromise = this.execBinary(outputFilename, result, maxExecOutputSize);
- } else {
- execPromise = Promise.resolve("");
- }
-
- return Promise.all([asmPromise, optPromise, execPromise]);
-};
-
-Compile.prototype.handlePostProcessResult = function (result, postResult) {
- result.asm = postResult.stdout;
- if (postResult.code !== 0) {
- result.asm = "<Error during post processing: " + postResult.code + ">";
- logger.error("Error during post-processing", result);
- }
- return result;
-};
-
-Compile.prototype.checkOptions = function (options) {
- const error = this.env.findBadOptions(options);
- if (error.length > 0) return "Bad options: " + error.join(", ");
- return null;
-};
-
-// This check for arbitrary user-controlled preprocessor inclusions
-// can be circumvented in more than one way. The goal here is to respond
-// to simple attempts with a clear diagnostic; the service still needs to
-// assume that malicious actors can make the compiler open arbitrary files.
-Compile.prototype.checkSource = function (source) {
- const re = /^\s*#\s*i(nclude|mport)(_next)?\s+["<](\/|.*\.\.)/;
- const failed = [];
- utils.splitLines(source).forEach(function (line, index) {
- if (line.match(re)) {
- failed.push("<stdin>:" + (index + 1) + ":1: no absolute or relative includes please");
- }
- });
- if (failed.length > 0) return failed.join("\n");
- return null;
-};
-
-Compile.prototype.getArgumentParser = function () {
- let exe = this.compiler.exe.toLowerCase();
- if (exe.indexOf("clang") >= 0) { // check this first as "clang++" matches "g++"
- return argumentParsers.clang;
- } else if (exe.indexOf("g++") >= 0 || exe.indexOf("gcc") >= 0) {
- return argumentParsers.gcc;
- }
- //there is a lot of code around that makes this assumption.
- //probably not the best thing to do :D
- return argumentParsers.gcc;
-};
-
-Compile.prototype.initialise = function () {
- if (this.getRemote()) return Promise.resolve(this);
- const argumentParser = this.getArgumentParser();
- const compiler = this.compiler.exe;
- const versionRe = new RegExp(this.compiler.versionRe || '.*', 'i');
- return this.env.enqueue(() => {
- logger.info("Gathering version information on", compiler);
- const execOptions = this.getDefaultExecOptions();
- const versionFlag = this.compiler.versionFlag || '--version';
- execOptions.timeoutMs = 0; // No timeout for --version. A sort of workaround for slow EFS/NFS on the prod site
- return this.exec(compiler, [versionFlag], execOptions);
- }).then(result => {
- if (result.code !== 0) {
- logger.warn(`Compiler '${compiler}' - non-zero result ${result.code}`);
- }
- let version = "";
- _.each(utils.splitLines(result.stdout + result.stderr), line => {
- if (version) return;
- const match = line.match(versionRe);
- if (match) version = match[0];
});
- if (!version) {
- logger.error(`Unable to find compiler version for '${compiler}':`, result, 'with re', versionRe);
- return null;
- }
- logger.debug(`${compiler} is version '${version}'`);
- this.compiler.version = version;
- this.compiler.supportsCfg = this.isCfgCompiler(version);
- return argumentParser(this);
- }, err => {
- logger.error(`Unable to get version for compiler '${compiler}' - ${err}`);
+ if (failed.length > 0) return failed.join("\n");
return null;
- });
-};
-
-Compile.prototype.getInfo = function () {
- return this.compiler;
-};
-
-Compile.prototype.getDefaultFilters = function () {
- // TODO; propagate to UI?
- return {
- intel: true,
- commentOnly: true,
- directives: true,
- labels: true,
- optOutput: false
- };
-};
-
-module.exports = Compile;
+ }
+
+ getArgumentParser() {
+ let exe = this.compiler.exe.toLowerCase();
+ if (exe.indexOf("clang") >= 0) { // check this first as "clang++" matches "g++"
+ return argumentParsers.Clang;
+ } else if (exe.indexOf("g++") >= 0 || exe.indexOf("gcc") >= 0) {
+ return argumentParsers.GCC;
+ }
+ //there is a lot of code around that makes this assumption.
+ //probably not the best thing to do :D
+ return argumentParsers.GCC;
+ }
+
+ initialise() {
+ if (!this.asm) this.asm = new asm.AsmParser(this.compilerProps);
+ if (this.getRemote()) return Promise.resolve(this);
+ const compiler = this.compiler.exe;
+ const versionRe = new RegExp(this.compiler.versionRe || '.*', 'i');
+ return this.env.enqueue(() => {
+ logger.info("Gathering version information on", compiler);
+ const execOptions = this.getDefaultExecOptions();
+ const versionFlag = this.compiler.versionFlag || '--version';
+ execOptions.timeoutMs = 0; // No timeout for --version. A sort of workaround for slow EFS/NFS on the prod site
+ return this.exec(compiler, [versionFlag], execOptions);
+ }).then(result => {
+ if (result.code !== 0) {
+ logger.warn(`Compiler '${compiler}' - non-zero result ${result.code}`);
+ }
+ let version = "";
+ _.each(utils.splitLines(result.stdout + result.stderr), line => {
+ if (version) return;
+ const match = line.match(versionRe);
+ if (match) version = match[0];
+ });
+ if (!version) {
+ logger.error(`Unable to find compiler version for '${compiler}':`, result, 'with re', versionRe);
+ return null;
+ }
+ logger.debug(`${compiler} is version '${version}'`);
+ this.compiler.version = version;
+ this.compiler.supportsCfg = this.isCfgCompiler(version);
+ return this.getArgumentParser().parse(this);
+ }, err => {
+ logger.error(`Unable to get version for compiler '${compiler}' - ${err}`);
+ return null;
+ });
+ }
+
+ getInfo() {
+ return this.compiler;
+ }
+
+ getDefaultFilters() {
+ // TODO; propagate to UI?
+ return {
+ intel: true,
+ commentOnly: true,
+ directives: true,
+ labels: true,
+ optOutput: false
+ };
+ }
+}
+
+module.exports = BaseCompiler;
diff --git a/lib/compilers/WSL-CL.js b/lib/compilers/WSL-CL.js
index 1a3e37ec1..675c163ba 100644
--- a/lib/compilers/WSL-CL.js
+++ b/lib/compilers/WSL-CL.js
@@ -28,68 +28,73 @@
// Don't run under Wine (obviously)
// Translate compiler path from Unix mounted volume (/mnt/c/tmp) to Windows (c:/tmp)
-const Compile = require('../base-compiler'),
- asm = require('../asm-cl'),
+const BaseCompiler = require('../base-compiler'),
+ asmCl = require('../asm-cl'),
temp = require('temp'),
- RunCLDemangler = require('../cl-support').RunCLDemangler;
+ RunCLDemangler = require('../cl-support').RunCLDemangler,
+ argumentParsers = require("./argument-parsers");
-function compileCl(info, env) {
- var compile = new Compile(info, env);
- compile.asm = new asm.AsmParser(compile.compilerProps);
- info.supportsFiltersInBinary = true;
- if ((process.platform === "linux") || (process.platform === "darwin")) {
- const origExec = compile.exec;
- compile.exec = function (command, args, options) {
- return origExec(command, args, options);
- };
- compile.filename = function (fn) {
- // AP: Need to translate compiler paths from what the Node.js process sees
+class WSLCLCompiler extends BaseCompiler {
+ constructor(info, env) {
+ info.supportsFiltersInBinary = true;
+ super(info, env);
+ this.asm = new asmCl.AsmParser();
+
+ if (info.unitTestMode) {
+ this.initialise();
+ return this;
+ } else {
+ return this.initialise();
+ }
+ }
+
+ filename(fn) {
+ if (process.platform === "linux" || process.platform === "darwin") {
+ // AP: Need to translate compiler paths from what the Node.js process sees
// on a Unix mounted volume (/mnt/c/tmp) to what CL sees on Windows (c:/tmp)
// We know process.env.tmpDir is of format /mnt/X/dir where X is drive letter.
const driveLetter = process.env.winTmp.substring(5, 6);
const directoryPath = process.env.winTmp.substring(7);
const windowsStyle = driveLetter.concat(":/", directoryPath);
return fn.replace(process.env.winTmp, windowsStyle);
- };
+ } else {
+ return super.filename(fn);
+ }
}
+
// AP: Create CE temp directory in winTmp directory instead of the tmpDir directory.
// NPM temp package: https://www.npmjs.com/package/temp, see Affixes
- compile.newTempDir = function () {
- return new Promise(function (resolve, reject) {
- temp.mkdir({prefix: 'compiler-explorer-compiler', dir: process.env.winTmp}, function (err, dirPath) {
+ newTempDir () {
+ return new Promise((resolve, reject) => {
+ temp.mkdir({prefix: 'compiler-explorer-compiler', dir: process.env.winTmp}, (err, dirPath) => {
if (err)
- reject("Unable to open temp file: " + err);
+ reject(`Unable to open temp file: ${err}`);
else
resolve(dirPath);
});
});
- };
- compile.supportsObjdump = function () {
+ }
+
+ supportsObjdump() {
return false;
- };
+ }
- compile.getArgumentParser = () => (compiler) => compiler;
+ getArgumentParser() {
+ return argumentParsers.Base;
+ }
- compile.postProcessAsm = function(result) {
+ postProcessAsm(result) {
return RunCLDemangler(this, result);
- };
+ }
- compile.optionsForFilter = function (filters, outputFilename) {
+ optionsForFilter(filters, outputFilename) {
return [
'/FAsc',
'/c',
'/Fa' + this.filename(outputFilename),
'/Fo' + this.filename(outputFilename + '.obj')
];
- };
-
- if (info.unitTestMode) {
- compile.initialise();
-
- return compile;
- } else {
- return compile.initialise();
}
}
-module.exports = compileCl;
+module.exports = WSLCLCompiler;
diff --git a/lib/compilers/Wine-CL.js b/lib/compilers/Wine-CL.js
index c2cd4eaa3..1bce97e65 100644
--- a/lib/compilers/Wine-CL.js
+++ b/lib/compilers/Wine-CL.js
@@ -22,54 +22,59 @@
// ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
// POSSIBILITY OF SUCH DAMAGE.
-var Compile = require('../base-compiler');
-var asm = require('../asm-cl');
-var RunCLDemangler = require('../cl-support').RunCLDemangler;
+const BaseCompiler = require('../base-compiler'),
+ asmCl = require('../asm-cl'),
+ RunCLDemangler = require('../cl-support').RunCLDemangler,
+ argumentParsers = require("./argument-parsers");
-function compileCl(info, env) {
- var compile = new Compile(info, env);
- compile.asm = new asm.AsmParser(compile.compilerProps);
- info.supportsFiltersInBinary = true;
- if ((process.platform === "linux") || (process.platform === "darwin")) {
- var wine = env.ceProps("wine");
- var origExec = compile.exec;
- compile.exec = function (command, args, options) {
- if (command.toLowerCase().endsWith(".exe")) {
- args.unshift(command);
- command = wine;
- }
- return origExec(command, args, options);
- };
- compile.filename = function (fn) {
- return 'Z:' + fn;
- };
+
+class CLCompiler extends BaseCompiler {
+ constructor(info, env) {
+ info.supportsFiltersInBinary = true;
+ super(info, env);
+ this.asm = new asmCl.AsmParser();
+
+ if (info.unitTestMode) {
+ this.initialise();
+ return this;
+ } else {
+ return this.initialise();
+ }
+ }
+
+ exec(command, args, options) {
+ if ((process.platform === "linux" || process.platform === "darwin") && command.toLowerCase().endsWith(".exe")) {
+ args.unshift(command);
+ command = this.env.ceProps("wine");
+ }
+ return super.exec(command, args, options);
}
- compile.supportsObjdump = function () {
+
+ filename(fn) {
+ return process.platform === "linux" || process.platform === "darwin" ? 'Z:' + fn : super.filename(fn);
+ }
+
+ supportsObjdump() {
return false;
- };
+ }
- compile.getArgumentParser = () => (compiler) => compiler;
+ getArgumentParser() {
+ return argumentParsers.Base;
+ }
- compile.postProcessAsm = function(result) {
+ postProcessAsm(result) {
return RunCLDemangler(this, result);
- };
+ }
- compile.optionsForFilter = function (filters, outputFilename, userOptions) {
+ optionsForFilter(filters, outputFilename, userOptions) {
return [
'/FAsc',
'/c',
'/Fa' + this.filename(outputFilename),
'/Fo' + this.filename(outputFilename + '.obj')
];
- };
-
- if (info.unitTestMode) {
- compile.initialise();
-
- return compile;
- } else {
- return compile.initialise();
}
+
}
-module.exports = compileCl;
+module.exports = CLCompiler;
diff --git a/lib/compilers/argument-parsers.js b/lib/compilers/argument-parsers.js
index a0803a676..bf26271e5 100644
--- a/lib/compilers/argument-parsers.js
+++ b/lib/compilers/argument-parsers.js
@@ -26,9 +26,9 @@ const _ = require('underscore-node'),
logger = require('../logger').logger,
utils = require('../utils');
-const getOptions = function (compiler, helpArg) {
- return compiler.exec(compiler.compiler.exe, [helpArg])
- .then(result => {
+class BaseParser {
+ static getOptions(compiler, helpArg) {
+ return compiler.exec(compiler.compiler.exe, [helpArg]).then(result => {
const options = {};
if (result.code === 0) {
const optionFinder = /^\s*(--?[-a-zA-Z]+)/;
@@ -41,13 +41,18 @@ const getOptions = function (compiler, helpArg) {
}
return options;
});
-};
+ }
+ static parse(compiler) {
+ return compiler;
+ }
+}
-const gccParser = function (compiler) {
- return Promise.all([
- getOptions(compiler, "--target-help"),
- getOptions(compiler, "--help=common")])
- .then(results => {
+class GCCParser extends BaseParser {
+ static parse(compiler) {
+ return Promise.all([
+ GCCParser.getOptions(compiler, "--target-help"),
+ GCCParser.getOptions(compiler, "--help=common")
+ ]).then(results => {
const options = _.extend.apply(_.extend, results);
compiler.compiler.supportsGccDump = true;
logger.debug("gcc-like compiler options: ", _.keys(options).join(" "));
@@ -61,26 +66,28 @@ const gccParser = function (compiler) {
}
return compiler;
});
-};
-
-const clangParser = function (compiler) {
- return getOptions(compiler, "--help").then(options => {
- logger.debug("clang-like compiler options: ", _.keys(options).join(" "));
- if (options['-fsave-optimization-record']) {
- compiler.compiler.optArg = "-fsave-optimization-record";
- compiler.compiler.supportsOptOutput = true;
- }
- if (options['-fcolor-diagnostics']) {
- if (compiler.compiler.options) compiler.compiler.options += " ";
- compiler.compiler.options += "-fcolor-diagnostics";
- }
- return compiler;
- });
-};
+ }
+}
+class ClangParser extends BaseParser {
+ static parse(compiler) {
+ return ClangParser.getOptions(compiler, "--help").then(options => {
+ logger.debug("clang-like compiler options: ", _.keys(options).join(" "));
+ if (options['-fsave-optimization-record']) {
+ compiler.compiler.optArg = "-fsave-optimization-record";
+ compiler.compiler.supportsOptOutput = true;
+ }
+ if (options['-fcolor-diagnostics']) {
+ if (compiler.compiler.options) compiler.compiler.options += " ";
+ compiler.compiler.options += "-fcolor-diagnostics";
+ }
+ return compiler;
+ });
+ }
+}
module.exports = {
- getOptions: getOptions,
- clang: clangParser,
- gcc: gccParser
-}; \ No newline at end of file
+ Base: BaseParser,
+ Clang: ClangParser,
+ GCC: GCCParser
+};
diff --git a/lib/compilers/assembly.js b/lib/compilers/assembly.js
index e2f6d9d88..6fa2971d0 100644
--- a/lib/compilers/assembly.js
+++ b/lib/compilers/assembly.js
@@ -23,62 +23,71 @@
// POSSIBILITY OF SUCH DAMAGE.
"use strict";
-var Compile = require('../base-compiler'),
- logger = require('../logger').logger,
+const BaseCompiler = require('../base-compiler'),
AsmRaw = require('../asm-raw').AsmParser,
utils = require('../utils'),
fs = require("fs"),
- path = require("path");
+ path = require("path"),
+ argumentParsers = require("./argument-parsers");
-function compileAssembly(info, env) {
- var compiler = new Compile(info, env);
- compiler.asm = new AsmRaw();
+class AssemblyCompiler extends BaseCompiler {
+ constructor(info, env) {
+ super(info, env);
+ this.asm = new AsmRaw();
- compiler.getArgumentParser = () => (compiler) => compiler;
+ if (info.unitTestMode) {
+ this.initialise();
+ return this;
+ } else {
+ return this.initialise();
+ }
+ }
- compiler.optionsForFilter = function (filters, outputFilename, userOptions) {
- filters.binary = true;
+ getArgumentParser() {
+ return argumentParsers.Base;
+ }
+ optionsForFilter(filters, outputFilename, userOptions) {
+ filters.binary = true;
return [];
- };
+ }
- compiler.runCompiler = function (compiler, options, inputFilename, execOptions) {
+ runCompiler(compiler, options, inputFilename, execOptions) {
if (!execOptions) {
execOptions = this.getDefaultExecOptions();
}
execOptions.customCwd = path.dirname(inputFilename);
-
- return this.exec(compiler, options, execOptions).then(function (result) {
+
+ return this.exec(compiler, options, execOptions).then(result => {
result.inputFilename = inputFilename;
result.stdout = utils.parseOutput(result.stdout, inputFilename);
result.stderr = utils.parseOutput(result.stderr, inputFilename);
return result;
});
- };
+ }
- function getGeneratedOutputfilename(inputFilename) {
+ getGeneratedOutputfilename(inputFilename) {
const outputFolder = path.dirname(inputFilename);
return new Promise((resolve, reject) => {
fs.readdir(outputFolder, (err, files) => {
files.forEach(file => {
- if (file !== compiler.compileFilename) {
+ if (file !== this.compileFilename) {
resolve(path.join(outputFolder, file));
}
});
-
reject("No output file was generated");
});
});
}
- compiler.objdump = function (outputFilename, result, maxSize, intelAsm, demangle) {
- return getGeneratedOutputfilename(outputFilename).then((realOutputFilename) => {
+ objdump(outputFilename, result, maxSize, intelAsm, demangle) {
+ return this.getGeneratedOutputfilename(outputFilename).then((realOutputFilename) => {
let args = ["-d", realOutputFilename, "-l", "--insn-width=16"];
if (demangle) args = args.concat("-C");
if (intelAsm) args = args.concat(["-M", "intel"]);
return this.exec(this.compiler.objdumper, args, {maxOutput: maxSize})
- .then(function (objResult) {
+ .then(objResult => {
result.asm = objResult.stdout;
if (objResult.code !== 0) {
result.asm = "<No output: objdump returned " + objResult.code + ">";
@@ -86,17 +95,8 @@ function compileAssembly(info, env) {
return result;
});
});
- };
-
- compiler.getOutputFilename = function (dirPath, outputFilebase) {
- return path.join(dirPath, this.compileFilename);
- };
-
- if (info.unitTestMode) {
- compiler.initialise();
- return compiler;
- } else
- return compiler.initialise();
+ }
}
-module.exports = compileAssembly;
+
+module.exports = AssemblyCompiler;
diff --git a/lib/compilers/default.js b/lib/compilers/default.js
index bc65d81d6..f9ad44fff 100644
--- a/lib/compilers/default.js
+++ b/lib/compilers/default.js
@@ -22,9 +22,13 @@
// ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
// POSSIBILITY OF SUCH DAMAGE.
-const Compile = require('../base-compiler');
+const BaseCompier = require('../base-compiler');
-module.exports = function (info, env) {
- var comp = new Compile(info, env);
- return comp.initialise();
-}; \ No newline at end of file
+class DefaultCompiler extends BaseCompier {
+ constructor(info, env) {
+ super(info, env);
+ return this.initialise();
+ }
+}
+
+module.exports = DefaultCompiler;
diff --git a/lib/compilers/fake-for-test.js b/lib/compilers/fake-for-test.js
index e698a9366..4ac03c4b1 100644
--- a/lib/compilers/fake-for-test.js
+++ b/lib/compilers/fake-for-test.js
@@ -24,22 +24,36 @@
const _ = require('underscore-node');
-module.exports = (info, env) => {
- return {
- compiler: {
+class FakeCompiler {
+ constructor(info) {
+ this.compiler = {
id: info.id || 'fake-for-test',
- lang: info.lang || 'fake-lang'
- },
- getInfo: () => null,
- getDefaultFilters: () => [],
- getRemote: () => null,
- compile: (source, options, backendOptions, filters) => Promise.resolve(_.extend(info.fakeResult || {}, {
+ lang: info.lang || 'fake-lang',
+ options: info.options || ''
+ };
+ this.info = info;
+ }
+
+ getInfo() {
+ return null;
+ }
+ getDefaultFilters() {
+ return [];
+ }
+ getRemote() {
+ return null;
+ }
+ compile(source, options, backendOptions, filters) {
+ return Promise.resolve(_.extend(this.info.fakeResult || {}, {
input: {
source: source,
options: options,
backendOptions: backendOptions,
filters: filters
}
- }))
- };
-}; \ No newline at end of file
+ }));
+ }
+}
+
+
+module.exports = FakeCompiler; \ No newline at end of file
diff --git a/lib/compilers/gcc.js b/lib/compilers/gcc.js
index e69de29bb..012490789 100644
--- a/lib/compilers/gcc.js
+++ b/lib/compilers/gcc.js
@@ -0,0 +1,34 @@
+// Copyright (c) 2012-2018, Rubén Rincón
+// All rights reserved.
+//
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are met:
+//
+// * Redistributions of source code must retain the above copyright notice,
+// this list of conditions and the following disclaimer.
+// * Redistributions in binary form must reproduce the above copyright
+// notice, this list of conditions and the following disclaimer in the
+// documentation and/or other materials provided with the distribution.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+// AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+// ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
+// LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+// CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+// SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+// INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+// CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+// ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+// POSSIBILITY OF SUCH DAMAGE.
+
+const BaseCompiler = require('../base-compiler');
+
+class GCCCompiler extends BaseCompiler {
+ constructor(info, env) {
+ super(info, env);
+ return this.initialise();
+ }
+}
+
+module.exports = GCCCompiler;
diff --git a/lib/compilers/golang.js b/lib/compilers/golang.js
index ee6fd8096..4490b52a0 100644
--- a/lib/compilers/golang.js
+++ b/lib/compilers/golang.js
@@ -22,19 +22,21 @@
// ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
// POSSIBILITY OF SUCH DAMAGE.
-const Compile = require('../base-compiler'),
+const BaseCompiler = require('../base-compiler'),
_ = require('underscore-node');
-function compilenewgol(info, env) {
- const compiler = new Compile(info, env);
- compiler.originalGetDefaultExecOptions = compiler.getDefaultExecOptions;
+class GolangCompiler extends BaseCompiler {
+ constructor(info, env) {
+ super(info, env);
+ return this.initialise();
+ }
- function convertNewGoL(code) {
+ convertNewGoL(code) {
const re = /^\s+(0[xX]?[0-9A-Za-z]+)?\s?[0-9]+\s*\(([^:]+):([0-9]+)\)\s*([A-Z]+)(.*)/;
let prevLine = null;
let file = null;
let fileCount = 0;
- return _.compact(code.map(function (obj) {
+ return _.compact(code.map(obj => {
const line = obj.text;
const match = line.match(re);
if (match) {
@@ -54,30 +56,28 @@ function compilenewgol(info, env) {
})).join("\n");
}
- compiler.postProcess = function (result, outputFilename, filters) {
- result.asm = convertNewGoL(result.stdout);
+ postProcess(result, outputFilename, filters) {
+ result.asm = this.convertNewGoL(result.stdout);
result.stdout = [];
return Promise.resolve(result);
- };
+ }
- compiler.optionsForFilter = function (filters, outputFilename, userOptions) {
+ optionsForFilter(filters, outputFilename, userOptions) {
// If we're dealing with an older version...
if (this.compiler.id === '6g141') {
return ['tool', '6g', '-g', '-o', outputFilename, '-S'];
}
return ['tool', 'compile', '-o', outputFilename, '-S'];
- };
+ }
- compiler.getDefaultExecOptions = function () {
- const execOptions = this.originalGetDefaultExecOptions();
+ getDefaultExecOptions() {
+ const execOptions = super.getDefaultExecOptions();
const goroot = this.compilerProps("compiler." + this.compiler.id + ".goroot");
if (goroot) {
execOptions.env.GOROOT = goroot;
}
return execOptions;
- };
-
- return compiler.initialise();
+ }
}
-module.exports = compilenewgol;
+module.exports = GolangCompiler;
diff --git a/lib/compilers/haskell.js b/lib/compilers/haskell.js
index a4080192f..1399c62e1 100644
--- a/lib/compilers/haskell.js
+++ b/lib/compilers/haskell.js
@@ -1,11 +1,38 @@
-var Compile = require('../base-compiler');
+// Copyright (c) 2012-2018, Rubén Rincón
+// All rights reserved.
+//
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are met:
+//
+// * Redistributions of source code must retain the above copyright notice,
+// this list of conditions and the following disclaimer.
+// * Redistributions in binary form must reproduce the above copyright
+// notice, this list of conditions and the following disclaimer in the
+// documentation and/or other materials provided with the distribution.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+// AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+// ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
+// LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+// CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+// SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+// INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+// CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+// ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+// POSSIBILITY OF SUCH DAMAGE.
-function compileHaskell(info, env) {
- var compiler = new Compile(info, env);
- compiler.optionsForFilter = function (filters, outputFilename, userOptions) {
+const BaseCompiler = require('../base-compiler');
+
+class HaskellCompiler extends BaseCompiler {
+ constructor(info, env) {
+ super(info, env);
+ return this.initialise();
+ }
+
+ optionsForFilter(filters, outputFilename, userOptions) {
return ['-S', '-g', '-o', this.filename(outputFilename)];
- };
- return compiler.initialise();
+ }
}
-module.exports = compileHaskell;
+module.exports = HaskellCompiler;
diff --git a/lib/compilers/ispc.js b/lib/compilers/ispc.js
index 3bb0f124f..ec3a249e3 100644
--- a/lib/compilers/ispc.js
+++ b/lib/compilers/ispc.js
@@ -1,11 +1,38 @@
-var Compile = require('../base-compiler');
+// Copyright (c) 2012-2018, Matt Godbolt & Rubén Rincón
+// All rights reserved.
+//
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are met:
+//
+// * Redistributions of source code must retain the above copyright notice,
+// this list of conditions and the following disclaimer.
+// * Redistributions in binary form must reproduce the above copyright
+// notice, this list of conditions and the following disclaimer in the
+// documentation and/or other materials provided with the distribution.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+// AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+// ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
+// LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+// CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+// SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+// INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+// CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+// ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+// POSSIBILITY OF SUCH DAMAGE.
-function compileISPC(info, env) {
- var compiler = new Compile(info, env);
- compiler.optionsForFilter = function (filters, outputFilename, userOptions) {
+const BaseCompiler = require('../base-compiler');
+
+class ISPCCompiler extends BaseCompiler {
+ constructor(info, env) {
+ super(info, env);
+ return this.initialise();
+ }
+
+ optionsForFilter(filters, outputFilename, userOptions) {
return ['--target=sse2-i32x4', '--emit-asm', '-g', '-o', this.filename(outputFilename)];
- };
- return compiler.initialise();
+ }
}
-module.exports = compileISPC;
+module.exports = ISPCCompiler;
diff --git a/lib/compilers/ldc.js b/lib/compilers/ldc.js
index 1d2933f7a..5d540a8e8 100644
--- a/lib/compilers/ldc.js
+++ b/lib/compilers/ldc.js
@@ -22,25 +22,30 @@
// ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
// POSSIBILITY OF SUCH DAMAGE.
-const Compile = require('../base-compiler'),
+const BaseCompiler = require('../base-compiler'),
argumentParsers = require("./argument-parsers");
-function compileLdc(info, env) {
- const compiler = new Compile(info, env);
- compiler.compiler.supportsIntel = true;
- compiler.optionsForFilter = function (filters, outputFilename, userOptions) {
+class LDCCompiler extends BaseCompiler {
+ constructor(info, env) {
+ super(info, env);
+ this.compiler.supportsIntel = true;
+ return this.initialise();
+ }
+
+ optionsForFilter(filters, outputFilename, userOptions) {
let options = ['-g', '-of', this.filename(outputFilename)];
if (filters.intel && !filters.binary) options = options.concat('-x86-asm-syntax=intel');
if (!filters.binary) options = options.concat('-output-s');
return options;
- };
- compiler.getArgumentParser = function () {
- return argumentParsers.clang;
- };
- compiler.filterUserOptions = function (userOptions) {
+ }
+
+ getArgumentParser() {
+ return argumentParsers.Clang;
+ }
+
+ filterUserOptions(userOptions) {
return userOptions.filter(option => option !== '-run');
- };
- return compiler.initialise();
+ }
}
-module.exports = compileLdc;
+module.exports = LDCCompiler;
diff --git a/lib/compilers/pascal.js b/lib/compilers/pascal.js
index 3fa0d3821..cfe4065cf 100644
--- a/lib/compilers/pascal.js
+++ b/lib/compilers/pascal.js
@@ -23,66 +23,62 @@
// POSSIBILITY OF SUCH DAMAGE.
"use strict";
-var Compile = require('../base-compiler'),
+const BaseCompiler = require('../base-compiler'),
PascalDemangler = require('../pascal-support').demangler,
utils = require('../utils'),
fs = require("fs"),
- path = require("path");
-
-function compileFPC(info, env) {
- var demangler = new PascalDemangler();
- var compiler = new Compile(info, env);
- compiler.compileFilename = "output.pas";
- compiler.supportsOptOutput = false;
-
- var originalExecBinary = compiler.execBinary;
- var currentlyActiveFilters = {};
+ path = require("path"),
+ argumentParsers = require("./argument-parsers");
+
+class FPCCompiler extends BaseCompiler {
+ constructor(info, env) {
+ super(info, env);
+ this.demangler = new PascalDemangler();
+ this.compileFilename = 'output.pas';
+ this.supportsOptOutput = false;
+
+ if (info.unitTestMode) {
+ this.initialise();
+ return this;
+ } else {
+ return this.initialise();
+ }
+ }
- compiler.postProcessAsm = function (result) {
+ postProcessAsm(result, filters) {
if (!result.okToCache) return result;
- if (currentlyActiveFilters.binary) {
- preProcessAsm(result.asm);
+ if (filters.binary) {
+ for (let j = 0; j < result.asm.length; ++j) {
+ this.demangler.addDemangleToCache(result.asm[j].text);
+ }
}
- for (var j = 0; j < result.asm.length; ++j)
- result.asm[j].text = demangler.demangleIfNeeded(result.asm[j].text);
+ for (let j = 0; j < result.asm.length; ++j)
+ result.asm[j].text = this.demangler.demangleIfNeeded(result.asm[j].text);
return result;
- };
+ }
- compiler.optionsForFilter = function (filters, outputFilename, userOptions) {
- var options = ['-g'];
+ optionsForFilter(filters, outputFilename, userOptions) {
+ let options = ['-g'];
if (this.compiler.intelAsm && filters.intel && !filters.binary) {
options = options.concat(this.compiler.intelAsm.split(" "));
}
- currentlyActiveFilters = filters;
- filters.preProcessLines = preProcessLines;
-
return options;
- };
-
- compiler.getOutputFilename = function (dirPath, outputFilebase) {
- return path.join(dirPath, path.basename(this.compileFilename, this.lang.extensions[0]) + ".s");
- };
+ }
- var saveDummyProjectFile = function (filename) {
- const unitName = path.basename(compiler.compileFilename, compiler.lang.extensions[0]);
+ getOutputFilename(dirPath, outputFilebase) {
+ return path.join(dirPath, `${path.basename(this.compileFilename, this.lang.extensions[0])}.s`);
+ }
- fs.writeFileSync(filename,
- "program prog; " +
- "uses " + unitName + " in '" + compiler.compileFilename + "'; " +
- "begin " +
- "end.", function() {});
- };
-
- var preProcessBinaryAsm = function (input) {
- var relevantAsmStartsAt = input.indexOf("<OUTPUT");
- if (relevantAsmStartsAt != -1) {
- var lastLinefeedBeforeStart = input.lastIndexOf("\n", relevantAsmStartsAt);
- if (lastLinefeedBeforeStart != -1) {
+ static preProcessBinaryAsm(input) {
+ const relevantAsmStartsAt = input.indexOf("<OUTPUT");
+ if (relevantAsmStartsAt !== -1) {
+ const lastLinefeedBeforeStart = input.lastIndexOf("\n", relevantAsmStartsAt);
+ if (lastLinefeedBeforeStart !== -1) {
input =
input.substr(0, input.indexOf("00000000004")) + "\n" +
input.substr(lastLinefeedBeforeStart + 1);
@@ -92,15 +88,79 @@ function compileFPC(info, env) {
input.substr(relevantAsmStartsAt);
}
}
-
return input;
- };
+ }
+
+ objdump(outputFilename, result, maxSize, intelAsm, demangle) {
+ outputFilename = path.join(path.dirname(outputFilename), "prog");
+ let args = ["-d", outputFilename, "-l", "--insn-width=16"];
+ if (demangle) args = args.concat(["-C"]);
+ if (intelAsm) args = args.concat(["-M", "intel"]);
+ return this.exec(this.compiler.objdumper, args, {maxOutput: maxSize}).then(objResult => {
+ if (objResult.code !== 0) {
+ result.asm = "<No output: objdump returned " + objResult.code + ">";
+ } else {
+ result.asm = FPCCompiler.preProcessBinaryAsm(objResult.stdout);
+ }
+ return result;
+ });
+ }
+
+ saveDummyProjectFile(filename) {
+ const unitName = path.basename(this.compileFilename, this.lang.extensions[0]);
+
+ fs.writeFileSync(filename,
+ "program prog; " +
+ "uses " + unitName + " in '" + this.compileFilename + "'; " +
+ "begin " +
+ "end.", () => {});
+ }
+
+ runCompiler(compiler, options, inputFilename, execOptions) {
+ if (!execOptions) {
+ execOptions = this.getDefaultExecOptions();
+ }
+
+ const tempPath = path.dirname(inputFilename);
+ const projectFile = path.join(tempPath, "prog.dpr");
+
+ this.saveDummyProjectFile(projectFile);
+
+ options.pop();
+ options.push('-FE' + tempPath);
+ options.push('-B');
+ options.push(projectFile);
+
+ return this.exec(compiler, options, execOptions).then(result => {
+ result.inputFilename = inputFilename;
+ result.stdout = utils.parseOutput(result.stdout, inputFilename);
+ result.stderr = utils.parseOutput(result.stderr, inputFilename);
+ return result;
+ });
+ }
+
+ execBinary(executable, result, maxSize) {
+ executable = path.join(path.dirname(executable), "prog");
+
+ super.execBinary(executable, result, maxSize);
+ }
+
+ getArgumentParser() {
+ return argumentParsers.Base;
+ }
+}
+/*function compileFPC(info, env) {
+ const demangler = new PascalDemangler();
+ const compiler = new Compile(info, env);
+
+ const
- var getExtraAsmHint = function (asm) {
+
+ const getExtraAsmHint = asm => {
if (asm.startsWith("# [")) {
- var bracketEndPos = asm.indexOf("]", 3);
- var valueInBrackets = asm.substr(3, bracketEndPos - 3);
- var colonPos = valueInBrackets.indexOf(":");
+ const bracketEndPos = asm.indexOf("]", 3);
+ let valueInBrackets = asm.substr(3, bracketEndPos - 3);
+ const colonPos = valueInBrackets.indexOf(":");
if (colonPos != -1) {
valueInBrackets = valueInBrackets.substr(0, colonPos - 1);
}
@@ -119,11 +179,11 @@ function compileFPC(info, env) {
}
};
- var preProcessLines = function(asmLines) {
- var i = 0;
+ var preProcessLines = asmLines => {
+ let i = 0;
while (i < asmLines.length) {
- var extraHint = getExtraAsmHint(asmLines[i]);
+ const extraHint = getExtraAsmHint(asmLines[i]);
if (extraHint) {
i++;
asmLines.splice(i, 0, extraHint);
@@ -137,64 +197,6 @@ function compileFPC(info, env) {
return asmLines;
};
- var preProcessAsm = function(asm) {
- for (var j = 0; j < asm.length; ++j) demangler.addDemangleToCache(asm[j].text);
- };
-
- compiler.objdump = function (outputFilename, result, maxSize, intelAsm, demangle) {
- outputFilename = path.join(path.dirname(outputFilename), "prog");
-
- var args = ["-d", outputFilename, "-l", "--insn-width=16"];
- if (demangle) args = args.concat(["-C"]);
- if (intelAsm) args = args.concat(["-M", "intel"]);
- return this.exec(this.compiler.objdumper, args, {maxOutput: maxSize})
- .then(function (objResult) {
- if (objResult.code !== 0) {
- result.asm = "<No output: objdump returned " + objResult.code + ">";
- } else {
- result.asm = preProcessBinaryAsm(objResult.stdout);
- }
-
- return result;
- });
- };
-
- compiler.runCompiler = function (compiler, options, inputFilename, execOptions) {
- if (!execOptions) {
- execOptions = this.getDefaultExecOptions();
- }
-
- var tempPath = path.dirname(inputFilename);
- var projectFile = path.join(tempPath, "prog.dpr");
-
- saveDummyProjectFile(projectFile);
-
- options.pop();
- options.push('-FE' + tempPath);
- options.push('-B');
- options.push(projectFile);
-
- return this.exec(compiler, options, execOptions).then(function (result) {
- result.inputFilename = inputFilename;
- result.stdout = utils.parseOutput(result.stdout, inputFilename);
- result.stderr = utils.parseOutput(result.stderr, inputFilename);
- return result;
- });
- };
-
- compiler.execBinary = function (executable, result, maxSize) {
- executable = path.join(path.dirname(executable), "prog");
-
- originalExecBinary(executable, result, maxSize);
- };
-
- compiler.getArgumentParser = () => (compiler) => compiler;
-
- if (info.unitTestMode) {
- compiler.initialise();
- return compiler;
- } else
- return compiler.initialise();
-}
+}*/
-module.exports = compileFPC;
+module.exports = FPCCompiler;
diff --git a/lib/compilers/rust.js b/lib/compilers/rust.js
index 8b11d2dfe..c7f16bc1f 100644
--- a/lib/compilers/rust.js
+++ b/lib/compilers/rust.js
@@ -22,18 +22,21 @@
// ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
// POSSIBILITY OF SUCH DAMAGE.
-var Compile = require('../base-compiler'),
+const BaseCompiler = require('../base-compiler'),
_ = require('underscore-node');
-function compileRust(info, env) {
- var compiler = new Compile(info, env);
- compiler.compiler.supportsIntel = true;
- compiler.optionsForFilter = function (filters, outputFilename, userOptions) {
- var options = ['-C', 'debuginfo=1', '-o', this.filename(outputFilename)];
-
- var userRequestedEmit = _.any(userOptions, function(opt) {
- return opt.indexOf("--emit") > -1;
- });
+class RustCompiler extends BaseCompiler {
+ constructor(info, env) {
+ super(info, env);
+ this.compiler.supportsIntel = true;
+
+ return this.initialise();
+ }
+
+ optionsForFilter(filters, outputFilename, userOptions) {
+ let options = ['-C', 'debuginfo=1', '-o', this.filename(outputFilename)];
+
+ let userRequestedEmit = _.any(userOptions, opt => opt.indexOf("--emit") > -1);
//TODO: Binary not supported (?)
if (!filters.binary) {
if(!userRequestedEmit) {
@@ -43,8 +46,7 @@ function compileRust(info, env) {
}
options = options.concat(['--crate-type', 'rlib']);
return options;
- };
- return compiler.initialise();
+ }
}
-module.exports = compileRust;
+module.exports = RustCompiler;
diff --git a/lib/compilers/swift.js b/lib/compilers/swift.js
index 884828852..832608eba 100644
--- a/lib/compilers/swift.js
+++ b/lib/compilers/swift.js
@@ -1,19 +1,45 @@
-const Compile = require('../base-compiler'),
+// Copyright (c) 2012-2018, Matt Godbolt & Rubén Rincón
+// All rights reserved.
+//
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are met:
+//
+// * Redistributions of source code must retain the above copyright notice,
+// this list of conditions and the following disclaimer.
+// * Redistributions in binary form must reproduce the above copyright
+// notice, this list of conditions and the following disclaimer in the
+// documentation and/or other materials provided with the distribution.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+// AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+// ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
+// LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+// CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+// SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+// INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+// CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+// ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+// POSSIBILITY OF SUCH DAMAGE.
+
+const BaseCompiler = require('../base-compiler'),
logger = require('../logger').logger;
-function compileSwift(info, env) {
- const compiler = new Compile(info, env);
+class SwiftCompiler extends BaseCompiler {
+ constructor(info, env) {
+ super(info, env);
+ return this.initialise();
+ }
- compiler.handlePostProcessResult = function (result, postResult) {
+ handlePostProcessResult(result, postResult) {
result.asm = postResult.stdout;
- // Seems swift-demangle like to exit with error 1
+ // Seems swift-demangle likes to exit with error 1
if (postResult.code !== 0 && !result.asm) {
result.asm = "<Error during post processing: " + postResult.code + ">";
logger.error("Error during post-processing", result);
}
return result;
- };
- return compiler.initialise();
+ }
}
-module.exports = compileSwift;
+module.exports = SwiftCompiler;
diff --git a/lib/handlers/compile.js b/lib/handlers/compile.js
index 38db079ac..9becfb46d 100644
--- a/lib/handlers/compile.js
+++ b/lib/handlers/compile.js
@@ -83,7 +83,7 @@ class CompileHandler {
logger.debug(compiler.id + " is unchanged");
return cached;
}
- return this.factories[type](compiler, this.compilerEnv)
+ return new this.factories[type](compiler, this.compilerEnv)
.then(compiler => {
if (compiler) compiler.mtime = res.mtime;
return compiler;
@@ -94,7 +94,7 @@ class CompileHandler {
return null;
});
} else {
- return this.factories[type](compiler, this.compilerEnv);
+ return new this.factories[type](compiler, this.compilerEnv);
}
}
@@ -213,8 +213,7 @@ class CompileHandler {
}
compiler.compile(source, options, backendOptions, filters)
- .then(
- result => {
+ .then(result => {
if (req.accepts(['text', 'json']) === 'json') {
res.set('Content-Type', 'application/json');
res.end(JSON.stringify(result));
diff --git a/test/compilers/argument-parsers-tests.js b/test/compilers/argument-parsers-tests.js
index 1660baca1..bc1e5bfb3 100644
--- a/test/compilers/argument-parsers-tests.js
+++ b/test/compilers/argument-parsers-tests.js
@@ -23,7 +23,7 @@
// POSSIBILITY OF SUCH DAMAGE.
const chai = require('chai'),
- Compile = require('../../lib/base-compiler'),
+ FakeCompiler = require('../../lib/compilers/fake-for-test'),
CompilationEnvironment = require('../../lib/compilation-env').CompilationEnvironment,
chaiAsPromised = require("chai-as-promised"),
parsers = require('../../lib/compilers/argument-parsers');
@@ -34,38 +34,36 @@ chai.should();
function makeCompiler(stdout, stderr, code) {
if (code === undefined) code = 0;
const env = new CompilationEnvironment((key, def) => def);
- const compiler = new Compile({'lang': 'c++', 'remote': true}, env);
- compiler.exec = () => {
- return Promise.resolve({code: code, stdout: stdout || "", stderr: stderr || ""});
- };
+ const compiler = new FakeCompiler({lang: 'c++', remote: true}, env);
+ compiler.exec = () => Promise.resolve({code: code, stdout: stdout || "", stderr: stderr || ""});
return compiler;
}
describe('option parser', () => {
it('should handle empty options', () => {
- return parsers.getOptions(makeCompiler()).should.eventually.deep.equals({});
+ return parsers.Base.getOptions(makeCompiler()).should.eventually.deep.equals({});
});
it('should parse single-dash options', () => {
- return parsers.getOptions(makeCompiler("-foo\n")).should.eventually.deep.equals({'-foo': true});
+ return parsers.Base.getOptions(makeCompiler("-foo\n")).should.eventually.deep.equals({'-foo': true});
});
it('should parse double-dash options', () => {
- return parsers.getOptions(makeCompiler("--foo\n")).should.eventually.deep.equals({'--foo': true});
+ return parsers.Base.getOptions(makeCompiler("--foo\n")).should.eventually.deep.equals({'--foo': true});
});
it('should parse stderr options', () => {
- return parsers.getOptions(makeCompiler("", "--bar=monkey\n")).should.eventually.deep.equals({'--bar': true});
+ return parsers.Base.getOptions(makeCompiler("", "--bar=monkey\n")).should.eventually.deep.equals({'--bar': true});
});
it('handles non-option text', () => {
- return parsers.getOptions(makeCompiler("-foo=123\nthis is a fish\n-badger=123")).should.eventually.deep.equals(
+ return parsers.Base.getOptions(makeCompiler("-foo=123\nthis is a fish\n-badger=123")).should.eventually.deep.equals(
{'-foo': true, '-badger': true});
});
it('should ignore if errors occur', () => {
- return parsers.getOptions(makeCompiler("--foo\n", "--bar\n", 1)).should.eventually.deep.equals({});
+ return parsers.Base.getOptions(makeCompiler("--foo\n", "--bar\n", 1)).should.eventually.deep.equals({});
});
});
describe('gcc parser', () => {
it('should handle empty options', () => {
- return parsers.gcc(makeCompiler()).should.eventually.satisfy(result => {
+ return new parsers.GCC(makeCompiler()).should.eventually.satisfy(result => {
return Promise.all([
result.compiler.supportsGccDump.should.equals(true),
result.compiler.options.should.equals('')
@@ -73,7 +71,7 @@ describe('gcc parser', () => {
});
});
it('should handle options', () => {
- return parsers.gcc(makeCompiler("-masm=intel\n-fdiagnostics-color=[blah]"))
+ return new parsers.GCC(makeCompiler("-masm=intel\n-fdiagnostics-color=[blah]"))
.should.eventually.satisfy(result => {
return Promise.all([
result.compiler.supportsGccDump.should.equals(true),
@@ -84,7 +82,7 @@ describe('gcc parser', () => {
});
});
it('should handle undefined options', () => {
- return parsers.gcc(makeCompiler("-fdiagnostics-color=[blah]")).should.eventually.satisfy(result => {
+ return new parsers.GCC(makeCompiler("-fdiagnostics-color=[blah]")).should.eventually.satisfy(result => {
return Promise.all([
result.compiler.supportsGccDump.should.equals(true),
result.compiler.options.should.equals('-fdiagnostics-color=always')
@@ -95,14 +93,14 @@ describe('gcc parser', () => {
describe('clang parser', () => {
it('should handle empty options', () => {
- return parsers.clang(makeCompiler()).should.eventually.satisfy(result => {
+ return new parsers.Clang(makeCompiler()).should.eventually.satisfy(result => {
return Promise.all([
result.compiler.options.should.equals('')
]);
});
});
it('should handle options', () => {
- return parsers.clang(makeCompiler("-fsave-optimization-record\n-fcolor-diagnostics"))
+ return new parsers.Clang(makeCompiler("-fsave-optimization-record\n-fcolor-diagnostics"))
.should.eventually.satisfy(result => {
return Promise.all([
result.compiler.supportsOptOutput.should.equals(true),
diff --git a/test/handlers/compile-tests.js b/test/handlers/compile-tests.js
index 2d689d8bd..a322c1c09 100644
--- a/test/handlers/compile-tests.js
+++ b/test/handlers/compile-tests.js
@@ -74,7 +74,7 @@ describe('Compiler tests', () => {
res.text.should.contain("Something from stderr");
res.text.should.contain("ASMASMASM");
})
- .catch(function (err) {
+ .catch(err => {
throw err;
});
});
@@ -101,25 +101,24 @@ describe('Compiler tests', () => {
stdout: [{text: "Something from stdout"}],
stderr: [{text: "Something from stderr"}],
asm: [{text: "ASMASMASM"}]
- })
- .then(res => {
- res.should.have.status(200);
- res.should.be.json;
- res.body.should.deep.equals({
- asm: [{text: "ASMASMASM"}],
- code: 0,
- input: {
- filters: [],
- options: [],
- source: "I am a program"
- },
- stderr: [{text: "Something from stderr"}],
- stdout: [{text: "Something from stdout"}]
- });
- })
- .catch(function (err) {
- throw err;
+ }).then(res => {
+ res.should.have.status(200);
+ res.should.be.json;
+ res.body.should.deep.equals({
+ asm: [{text: "ASMASMASM"}],
+ code: 0,
+ input: {
+ filters: [],
+ options: [],
+ source: "I am a program"
+ },
+ stderr: [{text: "Something from stderr"}],
+ stdout: [{text: "Something from stdout"}]
});
+ })
+ .catch(err => {
+ throw err;
+ });
});
it('parses options and filters', () => {
@@ -158,7 +157,7 @@ describe('Compiler tests', () => {
res.body.input.options.should.deep.equals([]);
res.body.input.filters.should.deep.equals({a: true, b: true, c: true});
})
- .catch(function (err) {
+ .catch(err => {
throw err;
});
});
@@ -171,7 +170,7 @@ describe('Compiler tests', () => {
res.body.input.options.should.deep.equals([]);
res.body.input.filters.should.deep.equals({a: true, e: true, f: true});
})
- .catch(function (err) {
+ .catch(err => {
throw err;
});
});
@@ -183,7 +182,7 @@ describe('Compiler tests', () => {
res.body.input.options.should.deep.equals([]);
res.body.input.filters.should.deep.equals({a: true});
})
- .catch(function (err) {
+ .catch(err => {
throw err;
});
});
@@ -195,7 +194,7 @@ describe('Compiler tests', () => {
res.body.input.options.should.deep.equals([]);
res.body.input.filters.should.deep.equals({a: true, g: true});
})
- .catch(function (err) {
+ .catch(err => {
throw err;
});
});
@@ -243,7 +242,7 @@ describe('Compiler tests', () => {
res.should.be.json;
res.body.asm.should.deep.equals([{text: "LANG B"}]);
})
- .catch(function (err) {
+ .catch(err => {
throw err;
});
});
@@ -255,7 +254,7 @@ describe('Compiler tests', () => {
res.should.be.json;
res.body.asm.should.deep.equals([{text: "LANG A"}]);
})
- .catch(function (err) {
+ .catch(err => {
throw err;
});
});
@@ -267,7 +266,7 @@ describe('Compiler tests', () => {
res.should.be.json;
res.body.asm.should.deep.equals([{text: "LANG B but A"}]);
})
- .catch(function (err) {
+ .catch(err => {
throw err;
});
});
diff --git a/test/win-path-tests.js b/test/win-path-tests.js
index 1b4cb7178..a10034667 100644
--- a/test/win-path-tests.js
+++ b/test/win-path-tests.js
@@ -23,13 +23,13 @@
// POSSIBILITY OF SUCH DAMAGE.
const chai = require('chai');
-const should = chai.should();
-const assert = chai.assert;
+
const WslCL = require('../lib/compilers/WSL-CL');
const WineCL = require('../lib/compilers/Wine-CL');
-const logger = require('../lib/logger').logger;
const {CompilationEnvironment} = require('../lib/compilation-env');
+chai.should();
+
describe('Paths', () => {
it('Linux -> Wine path', () => {
const info = {
@@ -41,8 +41,7 @@ describe('Paths', () => {
const envprops = (key, deflt) => deflt;
const env = new CompilationEnvironment(envprops);
- env.compilerProps = () => {
- };
+ env.compilerProps = undefined;
const compiler = new WineCL(info, env);
compiler.filename("/tmp/123456/output.s").should.equal("Z:/tmp/123456/output.s");