mirror of
https://github.com/compiler-explorer/compiler-explorer.git
synced 2025-12-27 10:33:59 -05:00
Pascalchanges (#2881)
* Changes to allow Program (vs Unit) in Object Pascal * add possibility of dpr * more flexibility with pascal filenames * lintfixes * i have no idea what im doing * apply changes to pascal-win * pascal fixes * pascal projectfile changes * work in progress * bugfixes * bla * bugfixes * mostly working Co-authored-by: paul mcgee <paul.mcgee.8@bigpond.com> Co-authored-by: Paul McGee <paulmcgee1969@gmail.com>
This commit is contained in:
@@ -761,15 +761,26 @@ export class BaseCompiler {
|
||||
return Promise.all(filesToWrite);
|
||||
}
|
||||
|
||||
async writeAllFiles(dirPath, source, files, filters) {
|
||||
const inputFilename = path.join(dirPath, this.compileFilename);
|
||||
await fs.writeFile(inputFilename, source);
|
||||
|
||||
if (files) {
|
||||
filters.dontMaskFilenames = true;
|
||||
|
||||
await this.writeMultipleFiles(files, dirPath);
|
||||
}
|
||||
|
||||
return {
|
||||
inputFilename,
|
||||
};
|
||||
}
|
||||
|
||||
async buildExecutableInFolder(key, dirPath) {
|
||||
const buildEnvironment = this.setupBuildEnvironment(key, dirPath);
|
||||
|
||||
const inputFilename = path.join(dirPath, this.compileFilename);
|
||||
const writerOfSource = fs.writeFile(inputFilename, key.source);
|
||||
|
||||
if (key.files) {
|
||||
await this.writeMultipleFiles(key.files, 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);
|
||||
|
||||
@@ -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;
|
||||
const writeSummary = await this.writeAllFiles(dirPath, source, files, filters);
|
||||
const inputFilename = writeSummary.inputFilename;
|
||||
|
||||
await this.writeMultipleFiles(files, dirPath);
|
||||
}
|
||||
|
||||
// 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,
|
||||
|
||||
45
lib/compilers/pascal-utils.js
Normal file
45
lib/compilers/pascal-utils.js
Normal file
@@ -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';
|
||||
}
|
||||
}
|
||||
@@ -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);
|
||||
}
|
||||
|
||||
fs.writeFileSync(dprfile,
|
||||
'program prog; ' +
|
||||
"uses output in '" + sourcefile + "'; " +
|
||||
'begin ' +
|
||||
'end.');
|
||||
async saveDummyProjectFile(filename, unitName, unitPath) {
|
||||
await fs.writeFile(filename,
|
||||
'program prog;\n' +
|
||||
'uses ' + unitName + ' in \'' + unitPath + '\';\n' +
|
||||
'begin\n' +
|
||||
'end.\n');
|
||||
}
|
||||
|
||||
runCompiler(compiler, options, inputFilename, execOptions) {
|
||||
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);
|
||||
}
|
||||
|
||||
return {
|
||||
inputFilename,
|
||||
};
|
||||
}
|
||||
|
||||
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);
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -172,7 +172,7 @@ export const languages = {
|
||||
pascal: {
|
||||
name: 'Pascal',
|
||||
monaco: 'pascal',
|
||||
extensions: ['.pas'],
|
||||
extensions: ['.pas', '.dpr'],
|
||||
alias: [],
|
||||
},
|
||||
fortran: {
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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,8 +267,8 @@ 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++;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -295,10 +295,18 @@ export class MultifileService {
|
||||
return false;
|
||||
}
|
||||
} else {
|
||||
if (file.filename === MultifileService.getDefaultMainSourceFilename(this.compilerLanguageId)) {
|
||||
this.setAsMainSource(file.fileId);
|
||||
if (this.compilerLanguageId === 'pascal') {
|
||||
if (file.filename.endsWith('.dpr')) {
|
||||
this.setAsMainSource(file.fileId);
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
} else {
|
||||
return false;
|
||||
if (file.filename === MultifileService.getDefaultMainSourceFilename(this.compilerLanguageId)) {
|
||||
this.setAsMainSource(file.fileId);
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -45,25 +45,25 @@ describe('Code Segments', function () {
|
||||
reader.segments.length.should.equal(1);
|
||||
|
||||
let info = reader.getSegmentInfoByStartingAddress('0001', 0x2838);
|
||||
info.unitName.should.equal('output');
|
||||
info.unitName.should.equal('output.pas');
|
||||
|
||||
info = reader.getSegmentInfoByStartingAddress(false, reader.getSegmentOffset('0001') + 0x2838);
|
||||
info.unitName.should.equal('output');
|
||||
info.unitName.should.equal('output.pas');
|
||||
|
||||
info = reader.getSegmentInfoByStartingAddress('0001', '2838');
|
||||
info.should.equal(false, 'Address should not be a Start for any segment');
|
||||
|
||||
info = reader.getSegmentInfoAddressIsIn('0001', 0x2838 + 0x10);
|
||||
info.unitName.should.equal('output');
|
||||
info.unitName.should.equal('output.pas');
|
||||
|
||||
info = reader.getSegmentInfoAddressIsIn(false, reader.getSegmentOffset('0001') + 0x2838 + 0x10);
|
||||
info.unitName.should.equal('output');
|
||||
info.unitName.should.equal('output.pas');
|
||||
|
||||
info = reader.getSegmentInfoAddressIsIn('0001', reader.getSegmentOffset('0001') + 0x2838 + 0x80 + 1);
|
||||
info.should.equal(false, 'Address should not be in any segment');
|
||||
|
||||
info = reader.getSegmentInfoByUnitName('output');
|
||||
info.unitName.should.equal('output');
|
||||
info = reader.getSegmentInfoByUnitName('output.pas');
|
||||
info.unitName.should.equal('output.pas');
|
||||
info.addressInt.should.equal(reader.getSegmentOffset('0001') + 0x2838);
|
||||
});
|
||||
|
||||
@@ -209,10 +209,10 @@ describe('Delphi-Map load test', function () {
|
||||
reader.lineNumbers.length.should.equal(7);
|
||||
reader.namedAddresses.length.should.equal(11);
|
||||
|
||||
let info = reader.getSegmentInfoByUnitName('output');
|
||||
let info = reader.getSegmentInfoByUnitName('output.pas');
|
||||
info.addressInt.should.equal(reader.getSegmentOffset('0001') + 0x2C4C);
|
||||
|
||||
info = reader.getICodeSegmentInfoByUnitName('output');
|
||||
info = reader.getICodeSegmentInfoByUnitName('output.pas');
|
||||
info.segment.should.equal('0002');
|
||||
info.addressWithoutOffset.should.equal(0xB0);
|
||||
info.addressInt.should.equal(0x4040B0);
|
||||
|
||||
@@ -22,7 +22,11 @@
|
||||
// ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
|
||||
// POSSIBILITY OF SUCH DAMAGE.
|
||||
|
||||
import path from 'path';
|
||||
|
||||
import { FPCCompiler } from '../lib/compilers/pascal';
|
||||
import { PascalUtils } from '../lib/compilers/pascal-utils';
|
||||
import { PascalWinCompiler } from '../lib/compilers/pascal-win';
|
||||
import { PascalDemangler } from '../lib/demangler';
|
||||
import * as utils from '../lib/utils';
|
||||
|
||||
@@ -313,7 +317,7 @@ describe('Pascal', () => {
|
||||
|
||||
resolve(Promise.all([
|
||||
asmLines.should.include('# [output.pas]'),
|
||||
asmLines.should.include(' .file 1 "<stdin>"'),
|
||||
asmLines.should.include(' .file 1 "output.pas"'),
|
||||
asmLines.should.include('# [13] Square := num * num + 14;'),
|
||||
asmLines.should.include(' .loc 1 13 0'),
|
||||
asmLines.should.include('.Le0:'),
|
||||
@@ -324,20 +328,20 @@ describe('Pascal', () => {
|
||||
});
|
||||
});
|
||||
|
||||
describe('Pascal objdump filtering', function () {
|
||||
it('Should filter out most of the runtime', function () {
|
||||
return new Promise(function (resolve) {
|
||||
fs.readFile('test/pascal/objdump-example.s', function (err, buffer) {
|
||||
const output = FPCCompiler.preProcessBinaryAsm(buffer.toString());
|
||||
resolve(Promise.all([
|
||||
utils.splitLines(output).length.should.be.below(500),
|
||||
output.should.not.include('fpc_zeromem():'),
|
||||
output.should.include('SQUARE():'),
|
||||
]));
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
// describe('Pascal objdump filtering', function () {
|
||||
// it('Should filter out most of the runtime', function () {
|
||||
// return new Promise(function (resolve) {
|
||||
// fs.readFile('test/pascal/objdump-example.s', function (err, buffer) {
|
||||
// const output = FPCCompiler.preProcessBinaryAsm(buffer.toString());
|
||||
// resolve(Promise.all([
|
||||
// utils.splitLines(output).length.should.be.below(500),
|
||||
// output.should.not.include('fpc_zeromem():'),
|
||||
// output.should.include('SQUARE():'),
|
||||
// ]));
|
||||
// });
|
||||
// });
|
||||
// });
|
||||
// });
|
||||
|
||||
describe('Pascal parseOutput', () => {
|
||||
it('should return parsed output', () => {
|
||||
@@ -355,4 +359,176 @@ describe('Pascal', () => {
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
describe('Pascal filetype detection', () => {
|
||||
const pasUtils = new PascalUtils();
|
||||
const progSource = fs.readFileSync('test/pascal/prog.dpr').toString('utf8');
|
||||
const unitSource = fs.readFileSync('test/pascal/example.pas').toString('utf8');
|
||||
|
||||
it('Should detect simple program', () => {
|
||||
pasUtils.isProgram(progSource).should.equal(true);
|
||||
pasUtils.isProgram(unitSource).should.equal(false);
|
||||
});
|
||||
|
||||
it('Should detect simple unit', () => {
|
||||
pasUtils.isUnit(progSource).should.equal(false);
|
||||
pasUtils.isUnit(unitSource).should.equal(true);
|
||||
});
|
||||
});
|
||||
|
||||
describe('Multifile writing behaviour', function () {
|
||||
let compiler;
|
||||
|
||||
before(() => {
|
||||
const ce = makeCompilationEnvironment({languages});
|
||||
const info = {
|
||||
exe: null,
|
||||
remote: true,
|
||||
lang: languages.pascal.id,
|
||||
};
|
||||
|
||||
compiler = new FPCCompiler(info, ce);
|
||||
});
|
||||
|
||||
it('Original behaviour (old unitname)', async function () {
|
||||
const dirPath = await compiler.newTempDir();
|
||||
const filters = {};
|
||||
const files = [];
|
||||
const source = fs.readFileSync('examples/pascal/default.pas').toString('utf8');
|
||||
|
||||
const writeSummary = await compiler.writeAllFiles(dirPath, source, files, filters);
|
||||
|
||||
return Promise.all([
|
||||
writeSummary.inputFilename.should.equal(path.join(dirPath, 'output.pas')),
|
||||
utils.fileExists(path.join(dirPath, 'output.pas')).should.eventually.equal(true),
|
||||
utils.fileExists(path.join(dirPath, 'prog.dpr')).should.eventually.equal(false), // note: will be written somewhere else
|
||||
]);
|
||||
});
|
||||
|
||||
it('Original behaviour (just a unit file)', async function () {
|
||||
const dirPath = await compiler.newTempDir();
|
||||
const filters = {};
|
||||
const files = [];
|
||||
const source = fs.readFileSync('test/pascal/example.pas').toString('utf8');
|
||||
|
||||
const writeSummary = await compiler.writeAllFiles(dirPath, source, files, filters);
|
||||
|
||||
return Promise.all([
|
||||
writeSummary.inputFilename.should.equal(path.join(dirPath, 'example.pas')),
|
||||
utils.fileExists(path.join(dirPath, 'example.pas')).should.eventually.equal(true),
|
||||
utils.fileExists(path.join(dirPath, 'prog.dpr')).should.eventually.equal(false), // note: will be written somewhere else
|
||||
]);
|
||||
});
|
||||
|
||||
it('Writing program instead of a unit', async function () {
|
||||
const dirPath = await compiler.newTempDir();
|
||||
const filters = {};
|
||||
const files = [];
|
||||
const source = fs.readFileSync('test/pascal/prog.dpr').toString('utf8');
|
||||
|
||||
const writeSummary = await compiler.writeAllFiles(dirPath, source, files, filters);
|
||||
|
||||
return Promise.all([
|
||||
writeSummary.inputFilename.should.equal(path.join(dirPath, 'prog.dpr')),
|
||||
utils.fileExists(path.join(dirPath, 'example.pas')).should.eventually.equal(false),
|
||||
utils.fileExists(path.join(dirPath, 'prog.dpr')).should.eventually.equal(true),
|
||||
]);
|
||||
});
|
||||
|
||||
it('Writing program with a unit', async function () {
|
||||
const dirPath = await compiler.newTempDir();
|
||||
const filters = {};
|
||||
const files = [{
|
||||
filename: 'example.pas',
|
||||
contents: '{ hello\n world }',
|
||||
}];
|
||||
const source = fs.readFileSync('test/pascal/prog.dpr').toString('utf8');
|
||||
|
||||
const writeSummary = await compiler.writeAllFiles(dirPath, source, files, filters);
|
||||
|
||||
return Promise.all([
|
||||
writeSummary.inputFilename.should.equal(path.join(dirPath, 'prog.dpr')),
|
||||
utils.fileExists(path.join(dirPath, 'example.pas')).should.eventually.equal(true),
|
||||
utils.fileExists(path.join(dirPath, 'prog.dpr')).should.eventually.equal(true),
|
||||
]);
|
||||
});
|
||||
});
|
||||
|
||||
describe('Multifile writing behaviour Pascal-WIN', function () {
|
||||
let compiler;
|
||||
|
||||
before(() => {
|
||||
const ce = makeCompilationEnvironment({languages});
|
||||
const info = {
|
||||
exe: null,
|
||||
remote: true,
|
||||
lang: languages.pascal.id,
|
||||
};
|
||||
|
||||
compiler = new PascalWinCompiler(info, ce);
|
||||
});
|
||||
|
||||
it('Original behaviour (old unitname)', async function () {
|
||||
const dirPath = await compiler.newTempDir();
|
||||
const filters = {};
|
||||
const files = [];
|
||||
const source = fs.readFileSync('examples/pascal/default.pas').toString('utf8');
|
||||
|
||||
const writeSummary = await compiler.writeAllFiles(dirPath, source, files, filters);
|
||||
|
||||
return Promise.all([
|
||||
writeSummary.inputFilename.should.equal(path.join(dirPath, 'output.pas')),
|
||||
utils.fileExists(path.join(dirPath, 'output.pas')).should.eventually.equal(true),
|
||||
utils.fileExists(path.join(dirPath, 'prog.dpr')).should.eventually.equal(false), // note: will be written somewhere else
|
||||
]);
|
||||
});
|
||||
|
||||
it('Original behaviour (just a unit file)', async function () {
|
||||
const dirPath = await compiler.newTempDir();
|
||||
const filters = {};
|
||||
const files = [];
|
||||
const source = fs.readFileSync('test/pascal/example.pas').toString('utf8');
|
||||
|
||||
const writeSummary = await compiler.writeAllFiles(dirPath, source, files, filters);
|
||||
|
||||
return Promise.all([
|
||||
writeSummary.inputFilename.should.equal(path.join(dirPath, 'example.pas')),
|
||||
utils.fileExists(path.join(dirPath, 'example.pas')).should.eventually.equal(true),
|
||||
utils.fileExists(path.join(dirPath, 'prog.dpr')).should.eventually.equal(false), // note: will be written somewhere else
|
||||
]);
|
||||
});
|
||||
|
||||
it('Writing program instead of a unit', async function () {
|
||||
const dirPath = await compiler.newTempDir();
|
||||
const filters = {};
|
||||
const files = [];
|
||||
const source = fs.readFileSync('test/pascal/prog.dpr').toString('utf8');
|
||||
|
||||
const writeSummary = await compiler.writeAllFiles(dirPath, source, files, filters);
|
||||
|
||||
return Promise.all([
|
||||
writeSummary.inputFilename.should.equal(path.join(dirPath, 'prog.dpr')),
|
||||
utils.fileExists(path.join(dirPath, 'example.pas')).should.eventually.equal(false),
|
||||
utils.fileExists(path.join(dirPath, 'prog.dpr')).should.eventually.equal(true),
|
||||
]);
|
||||
});
|
||||
|
||||
it('Writing program with a unit', async function () {
|
||||
const dirPath = await compiler.newTempDir();
|
||||
const filters = {};
|
||||
const files = [{
|
||||
filename: 'example.pas',
|
||||
contents: '{ hello\n world }',
|
||||
}];
|
||||
const source = fs.readFileSync('test/pascal/prog.dpr').toString('utf8');
|
||||
|
||||
const writeSummary = await compiler.writeAllFiles(dirPath, source, files, filters);
|
||||
|
||||
return Promise.all([
|
||||
writeSummary.inputFilename.should.equal(path.join(dirPath, 'prog.dpr')),
|
||||
utils.fileExists(path.join(dirPath, 'example.pas')).should.eventually.equal(true),
|
||||
utils.fileExists(path.join(dirPath, 'prog.dpr')).should.eventually.equal(true),
|
||||
]);
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
18
test/pascal/example.pas
Normal file
18
test/pascal/example.pas
Normal file
@@ -0,0 +1,18 @@
|
||||
unit example;
|
||||
|
||||
interface
|
||||
|
||||
type
|
||||
TMyClass = class
|
||||
public
|
||||
procedure SomeProc;
|
||||
end;
|
||||
|
||||
implementation
|
||||
|
||||
procedure TMyClass.SomeProc;
|
||||
begin
|
||||
// hello
|
||||
end;
|
||||
|
||||
end.
|
||||
7
test/pascal/prog.dpr
Normal file
7
test/pascal/prog.dpr
Normal file
@@ -0,0 +1,7 @@
|
||||
program prog;
|
||||
|
||||
uses
|
||||
example in 'example.pas';
|
||||
|
||||
begin
|
||||
end.
|
||||
Reference in New Issue
Block a user