diff options
Diffstat (limited to 'lib')
-rw-r--r-- | lib/base-compiler.js | 38 | ||||
-rw-r--r-- | lib/compilers/pascal-utils.js | 45 | ||||
-rw-r--r-- | lib/compilers/pascal-win.js | 67 | ||||
-rw-r--r-- | lib/compilers/pascal.js | 119 | ||||
-rw-r--r-- | lib/languages.js | 2 | ||||
-rw-r--r-- | lib/map-file-delphi.js | 12 | ||||
-rw-r--r-- | lib/pe32-support.js | 32 |
7 files changed, 252 insertions, 63 deletions
diff --git a/lib/base-compiler.js b/lib/base-compiler.js index d1bdf211d..c12dcab75 100644 --- a/lib/base-compiler.js +++ b/lib/base-compiler.js @@ -761,16 +761,27 @@ export class BaseCompiler { return Promise.all(filesToWrite); } - async buildExecutableInFolder(key, dirPath) { - const buildEnvironment = this.setupBuildEnvironment(key, dirPath); - + async writeAllFiles(dirPath, source, files, filters) { const inputFilename = path.join(dirPath, this.compileFilename); - const writerOfSource = fs.writeFile(inputFilename, key.source); + await fs.writeFile(inputFilename, source); + + if (files) { + filters.dontMaskFilenames = true; - if (key.files) { - await this.writeMultipleFiles(key.files, dirPath); + await this.writeMultipleFiles(files, dirPath); } + return { + inputFilename, + }; + } + + async buildExecutableInFolder(key, dirPath) { + const buildEnvironment = this.setupBuildEnvironment(key, dirPath); + + const writeSummary = await this.writeAllFiles(dirPath, key.source, key.files, key.filters); + const inputFilename = writeSummary.inputFilename; + const outputFilename = this.getExecutableFilename(dirPath, this.outputFilebase, key); const buildFilters = Object.assign({}, key.filters); @@ -785,7 +796,6 @@ export class BaseCompiler { const execOptions = this.getDefaultExecOptions(); execOptions.ldPath = this.getSharedLibraryPathsAsLdLibraryPaths(key.libraries); - await writerOfSource; const downloads = await buildEnvironment; const result = await this.buildExecutable(key.compiler.exe, compilerArguments, inputFilename, execOptions); @@ -829,7 +839,7 @@ export class BaseCompiler { const endTime = process.hrtime.bigint(); return Object.assign({}, buildResults, { code: 0, - inputFilename: path.join(dirPath, this.compileFilename), + inputFilename: path.join(dirPath, path.basename(buildResults.inputFilename)), dirPath: dirPath, executableFilename: this.getExecutableFilename(dirPath, this.outputFilebase, key), packageDownloadAndUnzipTime: ((endTime - startTime) / BigInt(1000000)).toString(), @@ -1329,17 +1339,11 @@ export class BaseCompiler { } const dirPath = await this.newTempDir(); - const inputFilename = path.join(dirPath, this.compileFilename); - await fs.writeFile(inputFilename, source); - - if (files) { - filters.dontMaskFilenames = true; - await this.writeMultipleFiles(files, dirPath); - } + const writeSummary = await this.writeAllFiles(dirPath, source, files, filters); + const inputFilename = writeSummary.inputFilename; - // TODO make const when I can - let [result, optOutput] = await this.doCompilation( + const [result, optOutput] = await this.doCompilation( inputFilename, dirPath, key, options, filters, backendOptions, libraries, tools); return await this.afterCompilation(result, doExecute, key, executeParameters, tools, backendOptions, diff --git a/lib/compilers/pascal-utils.js b/lib/compilers/pascal-utils.js new file mode 100644 index 000000000..d1f354bc1 --- /dev/null +++ b/lib/compilers/pascal-utils.js @@ -0,0 +1,45 @@ +// Copyright (c) 2021, Compiler Explorer Authors +// 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. + +export class PascalUtils { + isProgram(source) { + const re = /\s?program\s+([\w.-]*);/i; + return !!re.test(source); + } + + isUnit(source) { + const re = /\s?unit\s+([\w.-]*);/i; + return !!re.test(source); + } + + getUnitname(source) { + const re = /\s?unit\s+([\w.-]*);/i; + const match = source.match(re); + if (match) { + return match[1]; + } + + return 'example'; + } +} diff --git a/lib/compilers/pascal-win.js b/lib/compilers/pascal-win.js index eef29685e..85bdac61b 100644 --- a/lib/compilers/pascal-win.js +++ b/lib/compilers/pascal-win.js @@ -31,6 +31,8 @@ import { MapFileReaderDelphi } from '../map-file-delphi'; import { PELabelReconstructor } from '../pe32-support'; import * as utils from '../utils'; +import { PascalUtils } from './pascal-utils'; + export class PascalWinCompiler extends BaseCompiler { static get key() { return 'pascal-win'; } @@ -40,6 +42,12 @@ export class PascalWinCompiler extends BaseCompiler { this.mapFilename = false; this.compileFilename = 'output.pas'; + this.dprFilename = 'prog.dpr'; + this.pasUtils = new PascalUtils(); + } + + getSharedLibraryPathsAsArguments() { + return []; } exec(command, args, options) { @@ -56,6 +64,10 @@ export class PascalWinCompiler extends BaseCompiler { return super.exec(command, args, options); } + getExecutableFilename(dirPath) { + return path.join(dirPath, 'prog.exe'); + } + getOutputFilename(dirPath) { return path.join(dirPath, 'prog.exe'); } @@ -85,30 +97,59 @@ export class PascalWinCompiler extends BaseCompiler { }); } - saveDummyProjectFile(dprfile, sourcefile) { - if (dprfile.startsWith('Z:')) { - dprfile = dprfile.substr(2); + async saveDummyProjectFile(filename, unitName, unitPath) { + await fs.writeFile(filename, + 'program prog;\n' + + 'uses ' + unitName + ' in \'' + unitPath + '\';\n' + + 'begin\n' + + 'end.\n'); + } + + async writeAllFiles(dirPath, source, files, filters) { + let inputFilename; + if (this.pasUtils.isProgram(source)) { + inputFilename = path.join(dirPath, this.dprFilename); + } else { + const unitName = this.pasUtils.getUnitname(source); + if (unitName) { + inputFilename = path.join(dirPath, unitName + '.pas'); + } else { + inputFilename = path.join(dirPath, this.compileFilename); + } + } + + await fs.writeFile(inputFilename, source); + + if (files) { + filters.dontMaskFilenames = true; + + await this.writeMultipleFiles(files, dirPath); } - fs.writeFileSync(dprfile, - 'program prog; ' + - "uses output in '" + sourcefile + "'; " + - 'begin ' + - 'end.'); + return { + inputFilename, + }; } - runCompiler(compiler, options, inputFilename, execOptions) { + async runCompiler(compiler, options, inputFilename, execOptions) { if (!execOptions) { execOptions = this.getDefaultExecOptions(); } + let alreadyHasDPR = path.basename(inputFilename) === this.dprFilename; + const tempPath = path.dirname(inputFilename); - const projectFile = path.join(tempPath, 'prog.dpr'); + const projectFile = path.join(tempPath, this.dprFilename); this.mapFilename = path.join(tempPath, 'prog.map'); inputFilename = inputFilename.replace(/\//g, '\\'); - this.saveDummyProjectFile(projectFile, inputFilename); + + if (!alreadyHasDPR) { + const unitFilepath = path.basename(inputFilename); + const unitName = unitFilepath.replace(/.pas$/i, ''); + await this.saveDummyProjectFile(projectFile, unitName, unitFilepath); + } options.pop(); @@ -121,7 +162,8 @@ export class PascalWinCompiler extends BaseCompiler { '-V', '-B'); - options.push(projectFile.replace(/\//g, '\\')); + options.push(projectFile); + execOptions.customCwd = tempPath; return this.exec(compiler, options, execOptions).then((result) => { result.inputFilename = inputFilename; @@ -133,6 +175,7 @@ export class PascalWinCompiler extends BaseCompiler { optionsForFilter(filters) { filters.binary = true; + filters.dontMaskFilenames = true; filters.preProcessBinaryAsmLines = (asmLines) => { const mapFileReader = new MapFileReaderDelphi(this.mapFilename); const reconstructor = new PELabelReconstructor(asmLines, false, mapFileReader, false); diff --git a/lib/compilers/pascal.js b/lib/compilers/pascal.js index 605e741ab..136e49190 100644 --- a/lib/compilers/pascal.js +++ b/lib/compilers/pascal.js @@ -31,6 +31,7 @@ import { BaseCompiler } from '../base-compiler'; import * as utils from '../utils'; import { PascalParser } from './argument-parsers'; +import { PascalUtils } from './pascal-utils'; export class FPCCompiler extends BaseCompiler { static get key() { @@ -41,8 +42,10 @@ export class FPCCompiler extends BaseCompiler { super(info, env); this.compileFilename = 'output.pas'; + this.dprFilename = 'prog.dpr'; this.supportsOptOutput = false; this.nasmPath = this.compilerProps('nasmpath'); + this.pasUtils = new PascalUtils(); } getSharedLibraryPathsAsArguments() { @@ -78,6 +81,10 @@ export class FPCCompiler extends BaseCompiler { filters.preProcessLines = _.bind(this.preProcessLines, this); + if (filters.binary) { + filters.dontMaskFilenames = true; + } + return options; } @@ -90,7 +97,8 @@ export class FPCCompiler extends BaseCompiler { } static preProcessBinaryAsm(input) { - const relevantAsmStartsAt = input.indexOf('<OUTPUT'); + const systemInitOffset = input.indexOf('<SYSTEM_$$_init$>'); + const relevantAsmStartsAt = input.indexOf('...', systemInitOffset); if (relevantAsmStartsAt !== -1) { const lastLinefeedBeforeStart = input.lastIndexOf('\n', relevantAsmStartsAt); if (lastLinefeedBeforeStart !== -1) { @@ -106,22 +114,44 @@ export class FPCCompiler extends BaseCompiler { return input; } - getObjdumpOutputFilename(defaultOutputFilename) { - return this.getExecutableFilename(path.dirname(defaultOutputFilename)); - } - postProcessObjdumpOutput(output) { return FPCCompiler.preProcessBinaryAsm(output); } - async saveDummyProjectFile(filename) { - const unitName = path.basename(this.compileFilename, this.lang.extensions[0]); - + async saveDummyProjectFile(filename, unitName, unitPath) { await fs.writeFile(filename, - 'program prog; ' + - 'uses ' + unitName + ' in \'' + this.compileFilename + '\'; ' + - 'begin ' + - 'end.'); + 'program prog;\n' + + 'uses ' + unitName + ' in \'' + unitPath + '\';\n' + + 'begin\n' + + 'end.\n'); + } + + async writeAllFiles(dirPath, source, files, filters) { + let inputFilename; + if (this.pasUtils.isProgram(source)) { + inputFilename = path.join(dirPath, this.dprFilename); + } else { + const unitName = this.pasUtils.getUnitname(source); + if (unitName) { + inputFilename = path.join(dirPath, unitName + '.pas'); + } else { + inputFilename = path.join(dirPath, this.compileFilename); + } + } + + if (source !== '' || !files) { + await fs.writeFile(inputFilename, source); + } + + if (files) { + filters.dontMaskFilenames = true; + + await this.writeMultipleFiles(files, dirPath); + } + + return { + inputFilename, + }; } async runCompiler(compiler, options, inputFilename, execOptions) { @@ -129,14 +159,21 @@ export class FPCCompiler extends BaseCompiler { execOptions = this.getDefaultExecOptions(); } + let alreadyHasDPR = path.basename(inputFilename) === this.dprFilename; const dirPath = path.dirname(inputFilename); - const projectFile = path.join(dirPath, 'prog.dpr'); + + const projectFile = path.join(dirPath, this.dprFilename); execOptions.customCwd = dirPath; if (this.nasmPath) { execOptions.env = _.clone(process.env); execOptions.env.PATH = execOptions.env.PATH + ':' + this.nasmPath; } - await this.saveDummyProjectFile(projectFile); + + if (!alreadyHasDPR) { + const unitFilepath = path.basename(inputFilename); + const unitName = unitFilepath.replace(/.pas$/i, ''); + await this.saveDummyProjectFile(projectFile, unitName, unitFilepath); + } options.pop(); options.push('-FE' + dirPath, '-B', projectFile); @@ -152,17 +189,11 @@ export class FPCCompiler extends BaseCompiler { return result; } - execBinary(executable, maxSize, executeParameters, homeDir) { - executable = this.getExecutableFilename(path.dirname(executable)); - - return super.execBinary(executable, maxSize, executeParameters, homeDir); - } - getArgumentParser() { return PascalParser; } - getExtraAsmHint(asm) { + getExtraAsmHint(asm, currentFileId) { if (asm.startsWith('# [')) { const bracketEndPos = asm.indexOf(']', 3); let valueInBrackets = asm.substr(3, bracketEndPos - 3); @@ -171,12 +202,14 @@ export class FPCCompiler extends BaseCompiler { valueInBrackets = valueInBrackets.substr(0, colonPos - 1); } + if (valueInBrackets.startsWith('/')) { + valueInBrackets = valueInBrackets.substr(1); + } + if (!isNaN(valueInBrackets)) { - return ' .loc 1 ' + valueInBrackets + ' 0'; - } else if (valueInBrackets.includes(this.compileFilename)) { - return ' .file 1 "<stdin>"'; + return ` .loc ${currentFileId} ${valueInBrackets} 0`; } else { - return false; + return ` .file ${currentFileId} "${valueInBrackets}"`; } } else if (asm.startsWith('.Le')) { return ' .cfi_endproc'; @@ -185,11 +218,45 @@ export class FPCCompiler extends BaseCompiler { } } + tryGetFilenumber(asm, files) { + if (asm.startsWith('# [')) { + 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); + } + + if (valueInBrackets.startsWith('/')) { + valueInBrackets = valueInBrackets.substr(1); + } + + if (isNaN(valueInBrackets)) { + if (!files[valueInBrackets]) { + let maxFileId = _.max(files); + if (maxFileId === -Infinity) { + maxFileId = 0; + } + + files[valueInBrackets] = maxFileId + 1; + return maxFileId + 1; + } + } + } + + return false; + } + preProcessLines(asmLines) { let i = 0; + let files = {}; + let currentFileId = 1; while (i < asmLines.length) { - const extraHint = this.getExtraAsmHint(asmLines[i]); + let newFileId = this.tryGetFilenumber(asmLines[i], files); + if (newFileId) currentFileId = newFileId; + + const extraHint = this.getExtraAsmHint(asmLines[i], currentFileId); if (extraHint) { i++; asmLines.splice(i, 0, extraHint); diff --git a/lib/languages.js b/lib/languages.js index 098bf482a..3eacc276e 100644 --- a/lib/languages.js +++ b/lib/languages.js @@ -172,7 +172,7 @@ export const languages = { pascal: { name: 'Pascal', monaco: 'pascal', - extensions: ['.pas'], + extensions: ['.pas', '.dpr'], alias: [], }, fortran: { diff --git a/lib/map-file-delphi.js b/lib/map-file-delphi.js index f8d55a59e..d065cf301 100644 --- a/lib/map-file-delphi.js +++ b/lib/map-file-delphi.js @@ -72,6 +72,12 @@ export class MapFileReaderDelphi extends MapFileReader { codesegmentObject.segmentLength = parseInt(matches[3], 16); codesegmentObject.unitName = matches[4]; + if (codesegmentObject.unitName === 'prog') { + codesegmentObject.unitName = 'prog.dpr'; + } else { + codesegmentObject.unitName = codesegmentObject.unitName + '.pas'; + } + this.segments.push(codesegmentObject); } else { matches = line.match(this.regexDelphiICodeSegment); @@ -81,6 +87,12 @@ export class MapFileReaderDelphi extends MapFileReader { codesegmentObject.segmentLength = parseInt(matches[3], 16); codesegmentObject.unitName = matches[4]; + if (codesegmentObject.unitName === 'prog') { + codesegmentObject.unitName = 'prog.dpr'; + } else { + codesegmentObject.unitName = codesegmentObject.unitName + '.pas'; + } + this.isegments.push(codesegmentObject); } } diff --git a/lib/pe32-support.js b/lib/pe32-support.js index 37db398ed..8db9dc33c 100644 --- a/lib/pe32-support.js +++ b/lib/pe32-support.js @@ -48,10 +48,11 @@ export class PELabelReconstructor { * * @param {string} unitName */ - run(unitName) { + run(/*unitName*/) { this.mapFileReader.run(); - this.deleteEverythingBut(unitName); + //this.deleteEverythingBut(unitName); + this.deleteSystemUnits(); this.shortenInt3s(); this.collectJumpsAndCalls(); @@ -122,6 +123,25 @@ export class PELabelReconstructor { } } + deleteSystemUnits() { + const systemUnits = new Set(['SysInit.pas', 'System.pas', 'SysUtils.pas', 'Classes.pas']); + + let idx, info; + for (idx = 0; idx < this.mapFileReader.segments.length; idx++) { + info = this.mapFileReader.segments[idx]; + if (systemUnits.has(info.unitName)) { + this.deleteLinesBetweenAddresses(info.addressInt, info.addressInt + info.segmentLength); + } + } + + for (idx = 0; idx < this.mapFileReader.isegments.length; idx++) { + info = this.mapFileReader.isegments[idx]; + if (systemUnits.has(info.unitName)) { + this.deleteLinesBetweenAddresses(info.addressInt, info.addressInt + info.segmentLength); + } + } + } + /** * * @param {number} beginAddress @@ -201,8 +221,6 @@ export class PELabelReconstructor { * if an address doesn't have a mapped name, it is called <Laddress> */ insertLabels() { - const sourceFileId = this.mapFileReader.getSegmentIdByUnitName('output'); - let currentSegment = false; let lineIdx = 0; @@ -249,12 +267,12 @@ export class PELabelReconstructor { } const lineInfo = this.mapFileReader.getLineInfoByAddress(false, address); - if (lineInfo && currentSegment.unitName.startsWith('output')) { - this.asmLines.splice(lineIdx, 0, '/' + sourceFileId + ':' + lineInfo.lineNumber); + if (lineInfo && currentSegment.unitName) { + this.asmLines.splice(lineIdx, 0, '/app/' + currentSegment.unitName + ':' + lineInfo.lineNumber); lineIdx++; } } - + lineIdx++; } } |