diff options
author | RabsRincon <rubrinbla@gmail.com> | 2018-01-18 19:43:10 +0100 |
---|---|---|
committer | RabsRincon <rubrinbla@gmail.com> | 2018-01-18 19:43:10 +0100 |
commit | a54faefb4c8668d9c9f93c78bd6bc639d1f0c219 (patch) | |
tree | 6465bf3e1d8e270cc53e67c04277c5773b16f7d6 | |
parent | 7bd30b4c4b10f93f26db81fbbe492149b8d487eb (diff) | |
download | compiler-explorer-a54faefb4c8668d9c9f93c78bd6bc639d1f0c219.tar.gz compiler-explorer-a54faefb4c8668d9c9f93c78bd6bc639d1f0c219.zip |
ES6fy compilers inheritance
-rw-r--r-- | lib/base-compiler.js | 1089 | ||||
-rw-r--r-- | lib/compilers/WSL-CL.js | 75 | ||||
-rw-r--r-- | lib/compilers/Wine-CL.js | 75 | ||||
-rw-r--r-- | lib/compilers/argument-parsers.js | 65 | ||||
-rw-r--r-- | lib/compilers/assembly.js | 64 | ||||
-rw-r--r-- | lib/compilers/default.js | 14 | ||||
-rw-r--r-- | lib/compilers/fake-for-test.js | 38 | ||||
-rw-r--r-- | lib/compilers/gcc.js | 34 | ||||
-rw-r--r-- | lib/compilers/golang.js | 34 | ||||
-rw-r--r-- | lib/compilers/haskell.js | 41 | ||||
-rw-r--r-- | lib/compilers/ispc.js | 41 | ||||
-rw-r--r-- | lib/compilers/ldc.js | 31 | ||||
-rw-r--r-- | lib/compilers/pascal.js | 222 | ||||
-rw-r--r-- | lib/compilers/rust.js | 28 | ||||
-rw-r--r-- | lib/compilers/swift.js | 42 | ||||
-rw-r--r-- | lib/handlers/compile.js | 7 | ||||
-rw-r--r-- | test/compilers/argument-parsers-tests.js | 30 | ||||
-rw-r--r-- | test/handlers/compile-tests.js | 51 | ||||
-rw-r--r-- | test/win-path-tests.js | 9 |
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"); |