aboutsummaryrefslogtreecommitdiff
path: root/lib
diff options
context:
space:
mode:
Diffstat (limited to 'lib')
-rw-r--r--lib/base-compiler.js38
-rw-r--r--lib/compilers/pascal-utils.js45
-rw-r--r--lib/compilers/pascal-win.js67
-rw-r--r--lib/compilers/pascal.js119
-rw-r--r--lib/languages.js2
-rw-r--r--lib/map-file-delphi.js12
-rw-r--r--lib/pe32-support.js32
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++;
}
}