mirror of
https://github.com/compiler-explorer/compiler-explorer.git
synced 2025-12-27 10:33:59 -05:00
Common utilities and type work (#5200)
This PR refactors some common utilities out of lib/ and into shared/ and eliminates some use of underscore.js, as well as general type improvements done along the way.
This commit is contained in:
65
app.ts
65
app.ts
@@ -45,7 +45,6 @@ import urljoin from 'url-join';
|
||||
|
||||
import * as aws from './lib/aws.js';
|
||||
import * as normalizer from './lib/clientstate-normalizer.js';
|
||||
import {ElementType} from './lib/common-utils.js';
|
||||
import {CompilationEnvironment} from './lib/compilation-env.js';
|
||||
import {CompilationQueue} from './lib/compilation-queue.js';
|
||||
import {CompilerFinder} from './lib/compiler-finder.js';
|
||||
@@ -68,17 +67,24 @@ import {sources} from './lib/sources/index.js';
|
||||
import {loadSponsorsFromString} from './lib/sponsors.js';
|
||||
import {getStorageTypeByKey} from './lib/storage/index.js';
|
||||
import * as utils from './lib/utils.js';
|
||||
import {ElementType} from './shared/common-utils.js';
|
||||
import type {Language, LanguageKey} from './types/languages.interfaces.js';
|
||||
|
||||
// Used by assert.ts
|
||||
global.ce_base_directory = new URL('.', import.meta.url);
|
||||
|
||||
(nopt as any).invalidHandler = (key, val, types) => {
|
||||
logger.error(
|
||||
`Command line argument type error for "--${key}=${val}", expected ${types.map(t => typeof t).join(' | ')}`,
|
||||
);
|
||||
};
|
||||
|
||||
// Parse arguments from command line 'node ./app.js args...'
|
||||
const opts = nopt({
|
||||
env: [String, Array],
|
||||
rootDir: [String],
|
||||
host: [String],
|
||||
port: [String, Number],
|
||||
port: [Number],
|
||||
propDebug: [Boolean],
|
||||
debug: [Boolean],
|
||||
dist: [Boolean],
|
||||
@@ -104,7 +110,33 @@ const opts = nopt({
|
||||
version: [Boolean],
|
||||
webpackContent: [String],
|
||||
noLocal: [Boolean],
|
||||
});
|
||||
}) as Partial<{
|
||||
env: string[];
|
||||
rootDir: string;
|
||||
host: string;
|
||||
port: number;
|
||||
propDebug: boolean;
|
||||
debug: boolean;
|
||||
dist: boolean;
|
||||
archivedVersions: string;
|
||||
noRemoteFetch: boolean;
|
||||
tmpDir: string;
|
||||
wsl: boolean;
|
||||
language: string;
|
||||
noCache: boolean;
|
||||
ensureNoIdClash: boolean;
|
||||
logHost: string;
|
||||
logPort: number;
|
||||
hostnameForLogging: string;
|
||||
suppressConsoleLog: boolean;
|
||||
metricsPort: number;
|
||||
loki: string;
|
||||
discoveryonly: string;
|
||||
prediscovered: string;
|
||||
version: boolean;
|
||||
webpackContent: string;
|
||||
noLocal: boolean;
|
||||
}>;
|
||||
|
||||
if (opts.debug) logger.level = 'debug';
|
||||
|
||||
@@ -158,8 +190,22 @@ const releaseBuildNumber = (() => {
|
||||
return '';
|
||||
})();
|
||||
|
||||
export type AppDefaultArguments = {
|
||||
rootDir: string;
|
||||
env: string[];
|
||||
hostname?: string;
|
||||
port: number;
|
||||
gitReleaseName: string;
|
||||
releaseBuildNumber: string;
|
||||
wantedLanguages: string | null;
|
||||
doCache: boolean;
|
||||
fetchCompilersFromRemote: boolean;
|
||||
ensureNoCompilerClash: boolean | undefined;
|
||||
suppressConsoleLog: boolean;
|
||||
};
|
||||
|
||||
// Set default values for omitted arguments
|
||||
const defArgs = {
|
||||
const defArgs: AppDefaultArguments = {
|
||||
rootDir: opts.rootDir || './etc',
|
||||
env: opts.env || ['dev'],
|
||||
hostname: opts.host,
|
||||
@@ -221,7 +267,7 @@ props.initialize(configDir, propHierarchy);
|
||||
// Instantiate a function to access records concerning "compiler-explorer"
|
||||
// in hidden object props.properties
|
||||
const ceProps = props.propsFor('compiler-explorer');
|
||||
defArgs.wantedLanguages = ceProps('restrictToLanguages', defArgs.wantedLanguages);
|
||||
defArgs.wantedLanguages = ceProps<string>('restrictToLanguages', defArgs.wantedLanguages);
|
||||
|
||||
const languages = (() => {
|
||||
if (defArgs.wantedLanguages) {
|
||||
@@ -229,7 +275,7 @@ const languages = (() => {
|
||||
const passedLangs = defArgs.wantedLanguages.split(',');
|
||||
for (const wantedLang of passedLangs) {
|
||||
for (const lang of Object.values(allLanguages)) {
|
||||
if (lang.id === wantedLang || lang.name === wantedLang || lang.alias === wantedLang) {
|
||||
if (lang.id === wantedLang || lang.name === wantedLang || lang.alias.includes(wantedLang)) {
|
||||
filteredLangs[lang.id] = lang;
|
||||
}
|
||||
}
|
||||
@@ -439,7 +485,12 @@ function startListening(server: express.Express) {
|
||||
logger.info(` Listening on http://${defArgs.hostname || 'localhost'}:${_port}/`);
|
||||
logger.info(` Startup duration: ${startupDurationMs}ms`);
|
||||
logger.info('=======================================');
|
||||
server.listen(_port, defArgs.hostname);
|
||||
// silly express typing, passing undefined is fine but
|
||||
if (defArgs.hostname) {
|
||||
server.listen(_port, defArgs.hostname);
|
||||
} else {
|
||||
server.listen(_port);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -4,11 +4,15 @@
|
||||
|
||||
These are using Javascript and/or using external websites to facilitate emulation after creating a suitable binary.
|
||||
|
||||
- [NES](https://github.com/compiler-explorer/jsnes-ceweb) (https://static.ce-cdn.net/jsnes-ceweb/index.html) - for images built with LLVM MOS NES or CC65 (`--target nes`)
|
||||
- [NES](https://github.com/compiler-explorer/jsnes-ceweb) (https://static.ce-cdn.net/jsnes-ceweb/index.html) - for
|
||||
images built with LLVM MOS NES or CC65 (`--target nes`)
|
||||
- [JSBeeb](https://github.com/mattgodbolt/jsbeeb) (https://bbc.godbolt.org) - for binaries built with BeebAsm
|
||||
- [Speccy](https://github.com/compiler-explorer/jsspeccy3) (https://static.ce-cdn.net/jsspeccy/index.html) - for `.tap` files built with Z88DK (target `+zx`)
|
||||
- [Miracle](https://github.com/mattgodbolt/Miracle) (https://xania.org/miracle/miracle.html) - for `.sms` files built with Z88DK (target `+sms`)
|
||||
- [Viciious](https://github.com/compiler-explorer/viciious) (https://static.ce-cdn.net/viciious/viciious.html) - for `.prg` files built with LLVM MOS C64 or CC65 (`--target c64`)
|
||||
- [Speccy](https://github.com/compiler-explorer/jsspeccy3) (https://static.ce-cdn.net/jsspeccy/index.html) - for `.tap`
|
||||
files built with Z88DK (target `+zx`)
|
||||
- [Miracle](https://github.com/mattgodbolt/Miracle) (https://xania.org/miracle/miracle.html) - for `.sms` files built
|
||||
with Z88DK (target `+sms`)
|
||||
- [Viciious](https://github.com/compiler-explorer/viciious) (https://static.ce-cdn.net/viciious/viciious.html) - for
|
||||
`.prg` files built with LLVM MOS C64 or CC65 (`--target c64`)
|
||||
|
||||
## Examples
|
||||
|
||||
|
||||
@@ -25,8 +25,8 @@
|
||||
import * as fs from 'fs';
|
||||
import path from 'path';
|
||||
|
||||
import {parse} from './stacktrace.js';
|
||||
import {isString} from './common-utils.js';
|
||||
import {parse} from '../shared/stacktrace.js';
|
||||
import {isString} from '../shared/common-utils.js';
|
||||
|
||||
const filePrefix = 'file://';
|
||||
|
||||
|
||||
@@ -37,6 +37,7 @@ import {
|
||||
CompilationCacheKey,
|
||||
CompilationInfo,
|
||||
CompilationResult,
|
||||
CompileChildLibraries,
|
||||
CustomInputForTool,
|
||||
ExecutionOptions,
|
||||
bypassCompilationCache,
|
||||
@@ -105,6 +106,8 @@ import {
|
||||
} from '../types/compilation/compiler-overrides.interfaces.js';
|
||||
import {LLVMIrBackendOptions} from '../types/compilation/ir.interfaces.js';
|
||||
import {ParsedAsmResultLine} from '../types/asmresult/asmresult.interfaces.js';
|
||||
import {unique} from '../shared/common-utils.js';
|
||||
import {ClientOptionsType, OptionsHandlerLibrary, VersionInfo} from './options-handler.js';
|
||||
|
||||
const compilationTimeHistogram = new PromClient.Histogram({
|
||||
name: 'ce_base_compiler_compilation_duration_seconds',
|
||||
@@ -261,8 +264,8 @@ export class BaseCompiler implements ICompiler {
|
||||
this.packager = new Packager();
|
||||
}
|
||||
|
||||
copyAndFilterLibraries(allLibraries, filter) {
|
||||
const filterLibAndVersion = _.map(filter, lib => {
|
||||
copyAndFilterLibraries(allLibraries: Record<string, OptionsHandlerLibrary>, filter: string[]) {
|
||||
const filterLibAndVersion = filter.map(lib => {
|
||||
const match = lib.match(/([\w-]*)\.([\w-]*)/i);
|
||||
if (match) {
|
||||
return {
|
||||
@@ -296,7 +299,7 @@ export class BaseCompiler implements ICompiler {
|
||||
}
|
||||
|
||||
return true;
|
||||
});
|
||||
}) as Record<string, VersionInfo>;
|
||||
|
||||
copiedLibraries[libid] = libcopy;
|
||||
});
|
||||
@@ -304,7 +307,7 @@ export class BaseCompiler implements ICompiler {
|
||||
return copiedLibraries;
|
||||
}
|
||||
|
||||
getSupportedLibraries(supportedLibrariesArr, allLibs) {
|
||||
getSupportedLibraries(supportedLibrariesArr: string[], allLibs: Record<string, OptionsHandlerLibrary>) {
|
||||
if (supportedLibrariesArr.length > 0) {
|
||||
return this.copyAndFilterLibraries(allLibs, supportedLibrariesArr);
|
||||
}
|
||||
@@ -748,15 +751,15 @@ export class BaseCompiler implements ICompiler {
|
||||
};
|
||||
}
|
||||
|
||||
getSortedStaticLibraries(libraries) {
|
||||
getSortedStaticLibraries(libraries: CompileChildLibraries[]) {
|
||||
const dictionary = {};
|
||||
const links = _.uniq(
|
||||
_.flatten(
|
||||
_.map(libraries, selectedLib => {
|
||||
const links = unique(
|
||||
libraries
|
||||
.map(selectedLib => {
|
||||
const foundVersion = this.findLibVersion(selectedLib);
|
||||
if (!foundVersion) return false;
|
||||
|
||||
return _.map(foundVersion.staticliblink, lib => {
|
||||
return foundVersion.staticliblink.map(lib => {
|
||||
if (lib) {
|
||||
dictionary[lib] = foundVersion;
|
||||
return [lib, foundVersion.dependencies];
|
||||
@@ -764,106 +767,105 @@ export class BaseCompiler implements ICompiler {
|
||||
return false;
|
||||
}
|
||||
});
|
||||
}),
|
||||
),
|
||||
})
|
||||
.flat(3),
|
||||
);
|
||||
|
||||
const sortedlinks: string[] = [];
|
||||
|
||||
_.each(links, libToInsertName => {
|
||||
const libToInsertObj = dictionary[libToInsertName];
|
||||
for (const libToInsertName of links) {
|
||||
if (libToInsertName) {
|
||||
const libToInsertObj = dictionary[libToInsertName];
|
||||
|
||||
let idxToInsert = sortedlinks.length;
|
||||
for (const [idx, libCompareName] of sortedlinks.entries()) {
|
||||
const libCompareObj: LibraryVersion = dictionary[libCompareName];
|
||||
let idxToInsert = sortedlinks.length;
|
||||
for (const [idx, libCompareName] of sortedlinks.entries()) {
|
||||
const libCompareObj: LibraryVersion = dictionary[libCompareName];
|
||||
|
||||
if (
|
||||
libToInsertObj &&
|
||||
libCompareObj &&
|
||||
_.intersection(libToInsertObj.dependencies, libCompareObj.staticliblink).length > 0
|
||||
) {
|
||||
idxToInsert = idx;
|
||||
break;
|
||||
} else if (libToInsertObj && libToInsertObj.dependencies.includes(libCompareName)) {
|
||||
idxToInsert = idx;
|
||||
break;
|
||||
} else if (libCompareObj && libCompareObj.dependencies.includes(libToInsertName)) {
|
||||
continue;
|
||||
} else if (
|
||||
libToInsertObj &&
|
||||
libToInsertObj.staticliblink.includes(libToInsertName) &&
|
||||
libToInsertObj.staticliblink.includes(libCompareName)
|
||||
) {
|
||||
if (
|
||||
libToInsertObj.staticliblink.indexOf(libToInsertName) >
|
||||
libToInsertObj.staticliblink.indexOf(libCompareName)
|
||||
libToInsertObj &&
|
||||
libCompareObj &&
|
||||
_.intersection(libToInsertObj.dependencies, libCompareObj.staticliblink).length > 0
|
||||
) {
|
||||
continue;
|
||||
} else {
|
||||
idxToInsert = idx;
|
||||
}
|
||||
break;
|
||||
} else if (
|
||||
libCompareObj &&
|
||||
libCompareObj.staticliblink.includes(libToInsertName) &&
|
||||
libCompareObj.staticliblink.includes(libCompareName)
|
||||
) {
|
||||
if (
|
||||
libCompareObj.staticliblink.indexOf(libToInsertName) >
|
||||
libCompareObj.staticliblink.indexOf(libCompareName)
|
||||
break;
|
||||
} else if (libToInsertObj && libToInsertObj.dependencies.includes(libCompareName)) {
|
||||
idxToInsert = idx;
|
||||
break;
|
||||
} else if (libCompareObj && libCompareObj.dependencies.includes(libToInsertName)) {
|
||||
continue;
|
||||
} else if (
|
||||
libToInsertObj &&
|
||||
libToInsertObj.staticliblink.includes(libToInsertName) &&
|
||||
libToInsertObj.staticliblink.includes(libCompareName)
|
||||
) {
|
||||
continue;
|
||||
} else {
|
||||
idxToInsert = idx;
|
||||
if (
|
||||
libToInsertObj.staticliblink.indexOf(libToInsertName) >
|
||||
libToInsertObj.staticliblink.indexOf(libCompareName)
|
||||
) {
|
||||
continue;
|
||||
} else {
|
||||
idxToInsert = idx;
|
||||
}
|
||||
break;
|
||||
} else if (
|
||||
libCompareObj &&
|
||||
libCompareObj.staticliblink.includes(libToInsertName) &&
|
||||
libCompareObj.staticliblink.includes(libCompareName)
|
||||
) {
|
||||
if (
|
||||
libCompareObj.staticliblink.indexOf(libToInsertName) >
|
||||
libCompareObj.staticliblink.indexOf(libCompareName)
|
||||
) {
|
||||
continue;
|
||||
} else {
|
||||
idxToInsert = idx;
|
||||
}
|
||||
break;
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
if (idxToInsert < sortedlinks.length) {
|
||||
sortedlinks.splice(idxToInsert, 0, libToInsertName);
|
||||
} else {
|
||||
sortedlinks.push(libToInsertName);
|
||||
}
|
||||
}
|
||||
|
||||
if (idxToInsert < sortedlinks.length) {
|
||||
sortedlinks.splice(idxToInsert, 0, libToInsertName);
|
||||
} else {
|
||||
sortedlinks.push(libToInsertName);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
return sortedlinks;
|
||||
}
|
||||
|
||||
getStaticLibraryLinks(libraries) {
|
||||
getStaticLibraryLinks(libraries: CompileChildLibraries[]) {
|
||||
const linkFlag = this.compiler.linkFlag || '-l';
|
||||
|
||||
return _.map(this.getSortedStaticLibraries(libraries), lib => {
|
||||
if (lib) {
|
||||
return linkFlag + lib;
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
}) as string[];
|
||||
return this.getSortedStaticLibraries(libraries)
|
||||
.filter(lib => lib)
|
||||
.map(lib => linkFlag + lib);
|
||||
}
|
||||
|
||||
getSharedLibraryLinks(libraries: any[]): string[] {
|
||||
getSharedLibraryLinks(libraries: CompileChildLibraries[]): string[] {
|
||||
const linkFlag = this.compiler.linkFlag || '-l';
|
||||
|
||||
return _.flatten(
|
||||
_.map(libraries, selectedLib => {
|
||||
return libraries
|
||||
.map(selectedLib => {
|
||||
const foundVersion = this.findLibVersion(selectedLib);
|
||||
if (!foundVersion) return false;
|
||||
|
||||
return _.map(foundVersion.liblink, lib => {
|
||||
return foundVersion.liblink.map(lib => {
|
||||
if (lib) {
|
||||
return linkFlag + lib;
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
});
|
||||
}),
|
||||
) as string[];
|
||||
})
|
||||
.flat()
|
||||
.filter(link => link) as string[];
|
||||
}
|
||||
|
||||
getSharedLibraryPaths(libraries) {
|
||||
return _.flatten(
|
||||
_.map(libraries, selectedLib => {
|
||||
getSharedLibraryPaths(libraries: CompileChildLibraries[]) {
|
||||
return libraries
|
||||
.map(selectedLib => {
|
||||
const foundVersion = this.findLibVersion(selectedLib);
|
||||
if (!foundVersion) return false;
|
||||
|
||||
@@ -872,11 +874,15 @@ export class BaseCompiler implements ICompiler {
|
||||
paths.push(`/app/${selectedLib.id}/lib`);
|
||||
}
|
||||
return paths;
|
||||
}),
|
||||
) as string[];
|
||||
})
|
||||
.flat();
|
||||
}
|
||||
|
||||
protected getSharedLibraryPathsAsArguments(libraries, libDownloadPath?: string, toolchainPath?: string) {
|
||||
protected getSharedLibraryPathsAsArguments(
|
||||
libraries: CompileChildLibraries[],
|
||||
libDownloadPath?: string,
|
||||
toolchainPath?: string,
|
||||
) {
|
||||
const pathFlag = this.compiler.rpathFlag || '-Wl,-rpath,';
|
||||
const libPathFlag = this.compiler.libpathFlag || '-L';
|
||||
|
||||
@@ -1058,7 +1064,7 @@ export class BaseCompiler implements ICompiler {
|
||||
backendOptions: Record<string, any>,
|
||||
inputFilename: string,
|
||||
outputFilename: string,
|
||||
libraries,
|
||||
libraries: CompileChildLibraries[],
|
||||
overrides: ConfiguredOverrides,
|
||||
) {
|
||||
let options = this.optionsForFilter(filters, outputFilename, userOptions);
|
||||
@@ -1086,9 +1092,9 @@ export class BaseCompiler implements ICompiler {
|
||||
let staticLibLinks: string[] = [];
|
||||
|
||||
if (filters.binary) {
|
||||
libLinks = this.getSharedLibraryLinks(libraries) || [];
|
||||
libLinks = (this.getSharedLibraryLinks(libraries).filter(l => l) as string[]) || [];
|
||||
libPaths = this.getSharedLibraryPathsAsArguments(libraries, undefined, toolchainPath);
|
||||
staticLibLinks = this.getStaticLibraryLinks(libraries) || [];
|
||||
staticLibLinks = (this.getStaticLibraryLinks(libraries).filter(l => l) as string[]) || [];
|
||||
}
|
||||
|
||||
userOptions = this.filterUserOptions(userOptions) || [];
|
||||
@@ -2041,7 +2047,16 @@ export class BaseCompiler implements ICompiler {
|
||||
}
|
||||
}
|
||||
|
||||
async doCompilation(inputFilename, dirPath, key, options, filters, backendOptions, libraries, tools) {
|
||||
async doCompilation(
|
||||
inputFilename,
|
||||
dirPath,
|
||||
key,
|
||||
options,
|
||||
filters,
|
||||
backendOptions,
|
||||
libraries: CompileChildLibraries[],
|
||||
tools,
|
||||
) {
|
||||
const inputFilenameSafe = this.filename(inputFilename);
|
||||
|
||||
const outputFilename = this.getOutputFilename(dirPath, this.outputFilebase, key);
|
||||
@@ -2565,7 +2580,7 @@ export class BaseCompiler implements ICompiler {
|
||||
bypassCache: BypassCache,
|
||||
tools,
|
||||
executionParameters,
|
||||
libraries,
|
||||
libraries: CompileChildLibraries[],
|
||||
files,
|
||||
) {
|
||||
const optionsError = this.checkOptions(options);
|
||||
@@ -3093,8 +3108,13 @@ but nothing was dumped. Possible causes are:
|
||||
}
|
||||
}
|
||||
|
||||
initialiseLibraries(clientOptions) {
|
||||
this.supportedLibraries = this.getSupportedLibraries(this.compiler.libsArr, clientOptions.libs[this.lang.id]);
|
||||
initialiseLibraries(clientOptions: ClientOptionsType) {
|
||||
// TODO: Awful cast here because of OptionsHandlerLibrary vs Library. These might really be the same types and
|
||||
// OptionsHandlerLibrary should maybe be yeeted.
|
||||
this.supportedLibraries = this.getSupportedLibraries(
|
||||
this.compiler.libsArr,
|
||||
clientOptions.libs[this.lang.id],
|
||||
) as any as Record<string, Library>;
|
||||
}
|
||||
|
||||
async getTargetsAsOverrideValues(): Promise<CompilerOverrideOption[]> {
|
||||
@@ -3178,7 +3198,7 @@ but nothing was dumped. Possible causes are:
|
||||
return this.env.getPossibleToolchains();
|
||||
}
|
||||
|
||||
async initialise(mtime: Date, clientOptions, isPrediscovered = false) {
|
||||
async initialise(mtime: Date, clientOptions: ClientOptionsType, isPrediscovered = false) {
|
||||
this.mtime = mtime;
|
||||
|
||||
if (this.buildenvsetup) {
|
||||
|
||||
@@ -38,12 +38,13 @@ import {unwrap, assert} from './assert.js';
|
||||
import {InstanceFetcher} from './aws.js';
|
||||
import {CompileHandler} from './handlers/compile.js';
|
||||
import {logger} from './logger.js';
|
||||
import {ClientOptionsHandler, OptionHandlerArguments} from './options-handler.js';
|
||||
import {ClientOptionsHandler} from './options-handler.js';
|
||||
import {CompilerProps} from './properties.js';
|
||||
import type {PropertyGetter} from './properties.interfaces.js';
|
||||
import {basic_comparator, remove} from './common-utils.js';
|
||||
import {basic_comparator, remove} from '../shared/common-utils.js';
|
||||
import {getPossibleGccToolchainsFromCompilerInfo} from './toolchain-utils.js';
|
||||
import {InstructionSet, InstructionSetsList} from '../types/instructionsets.js';
|
||||
import {AppDefaultArguments} from '../app.js';
|
||||
|
||||
const sleep = promisify(setTimeout);
|
||||
|
||||
@@ -54,7 +55,7 @@ export class CompilerFinder {
|
||||
compilerProps: CompilerProps['get'];
|
||||
ceProps: PropertyGetter;
|
||||
awsProps: PropertyGetter;
|
||||
args: OptionHandlerArguments;
|
||||
args: AppDefaultArguments;
|
||||
compileHandler: CompileHandler;
|
||||
languages: Record<string, Language>;
|
||||
awsPoller: InstanceFetcher | null = null;
|
||||
@@ -64,7 +65,7 @@ export class CompilerFinder {
|
||||
compileHandler: CompileHandler,
|
||||
compilerProps: CompilerProps,
|
||||
awsProps: PropertyGetter,
|
||||
args: OptionHandlerArguments,
|
||||
args: AppDefaultArguments,
|
||||
optionsHandler: ClientOptionsHandler,
|
||||
) {
|
||||
this.compilerProps = compilerProps.get.bind(compilerProps);
|
||||
|
||||
@@ -108,24 +108,24 @@ export class NvccCompiler extends BaseCompiler {
|
||||
filters.binary
|
||||
? this.objdump(outputFilename, {}, maxSize, filters.intel, filters.demangle, false, false, filters)
|
||||
: (async () => {
|
||||
if (result.asmSize === undefined) {
|
||||
result.asm = '<No output file>';
|
||||
return result;
|
||||
}
|
||||
if (result.asmSize >= maxSize) {
|
||||
result.asm =
|
||||
'<No output: generated assembly was too large' +
|
||||
` (${result.asmSize} > ${maxSize} bytes)>`;
|
||||
return result;
|
||||
}
|
||||
if (postProcess.length > 0) {
|
||||
return await this.execPostProcess(result, postProcess, outputFilename, maxSize);
|
||||
} else {
|
||||
const contents = await fs.readFile(outputFilename, {encoding: 'utf8'});
|
||||
result.asm = contents.toString();
|
||||
return result;
|
||||
}
|
||||
})()
|
||||
if (result.asmSize === undefined) {
|
||||
result.asm = '<No output file>';
|
||||
return result;
|
||||
}
|
||||
if (result.asmSize >= maxSize) {
|
||||
result.asm =
|
||||
'<No output: generated assembly was too large' +
|
||||
` (${result.asmSize} > ${maxSize} bytes)>`;
|
||||
return result;
|
||||
}
|
||||
if (postProcess.length > 0) {
|
||||
return await this.execPostProcess(result, postProcess, outputFilename, maxSize);
|
||||
} else {
|
||||
const contents = await fs.readFile(outputFilename, {encoding: 'utf8'});
|
||||
result.asm = contents.toString();
|
||||
return result;
|
||||
}
|
||||
})()
|
||||
).then(asm => {
|
||||
result.asm = typeof asm === 'string' ? asm : asm.asm;
|
||||
return result;
|
||||
|
||||
@@ -30,7 +30,7 @@ import {CompilerInfo} from '../../types/compiler.interfaces.js';
|
||||
import {Language, LanguageKey} from '../../types/languages.interfaces.js';
|
||||
import {assert, unwrap} from '../assert.js';
|
||||
import {ClientStateNormalizer} from '../clientstate-normalizer.js';
|
||||
import {isString, unique} from '../common-utils.js';
|
||||
import {isString, unique} from '../../shared/common-utils.js';
|
||||
import {logger} from '../logger.js';
|
||||
import {ClientOptionsHandler} from '../options-handler.js';
|
||||
import {PropertyGetter} from '../properties.interfaces.js';
|
||||
@@ -241,9 +241,10 @@ export class ApiHandler {
|
||||
return Object.keys(libsForLanguageObj).map(key => {
|
||||
const language = libsForLanguageObj[key];
|
||||
const versionArr = Object.keys(language.versions).map(key => {
|
||||
const versionObj = Object.assign({}, language.versions[key]);
|
||||
versionObj.id = key;
|
||||
return versionObj;
|
||||
return {
|
||||
...language.versions[key],
|
||||
id: key,
|
||||
};
|
||||
});
|
||||
|
||||
return {
|
||||
|
||||
@@ -48,11 +48,12 @@ import {
|
||||
CompileRequestTextBody,
|
||||
ExecutionRequestParams,
|
||||
} from './compile.interfaces.js';
|
||||
import {remove} from '../common-utils.js';
|
||||
import {remove} from '../../shared/common-utils.js';
|
||||
import {CompilerOverrideOptions} from '../../types/compilation/compiler-overrides.interfaces.js';
|
||||
import {BypassCache, CompileChildLibraries, ExecutionParams} from '../../types/compilation/compilation.interfaces.js';
|
||||
import {SentryCapture} from '../sentry.js';
|
||||
import {ResultLine} from '../../types/resultline/resultline.interfaces.js';
|
||||
import {ClientOptionsType} from '../options-handler.js';
|
||||
|
||||
temp.track();
|
||||
|
||||
@@ -101,7 +102,7 @@ export class CompileHandler {
|
||||
private readonly textBanner: string;
|
||||
private readonly proxy: Server;
|
||||
private readonly awsProps: PropertyGetter;
|
||||
private clientOptions: Record<string, any> | null = null;
|
||||
private clientOptions: ClientOptionsType | null = null;
|
||||
private readonly compileCounter: Counter<string> = new PromClient.Counter({
|
||||
name: 'ce_compilations_total',
|
||||
help: 'Number of compilations',
|
||||
@@ -245,7 +246,7 @@ export class CompileHandler {
|
||||
}
|
||||
}
|
||||
|
||||
async setCompilers(compilers: PreliminaryCompilerInfo[], clientOptions: Record<string, any>) {
|
||||
async setCompilers(compilers: PreliminaryCompilerInfo[], clientOptions: ClientOptionsType) {
|
||||
// Be careful not to update this.compilersById until we can replace it entirely.
|
||||
const compilersById = {};
|
||||
try {
|
||||
|
||||
@@ -28,7 +28,7 @@ import express from 'express';
|
||||
import {assert} from '../assert.js';
|
||||
import {ClientState} from '../clientstate.js';
|
||||
import {ClientStateNormalizer} from '../clientstate-normalizer.js';
|
||||
import {isString} from '../common-utils.js';
|
||||
import {isString} from '../../shared/common-utils.js';
|
||||
import {logger} from '../logger.js';
|
||||
import {ClientOptionsHandler} from '../options-handler.js';
|
||||
import {StorageBase} from '../storage/index.js';
|
||||
|
||||
@@ -27,7 +27,7 @@ import express from 'express';
|
||||
import {assert, unwrap} from '../assert.js';
|
||||
import {ClientState} from '../clientstate.js';
|
||||
import {ClientStateGoldenifier, ClientStateNormalizer} from '../clientstate-normalizer.js';
|
||||
import {isString} from '../common-utils.js';
|
||||
import {isString} from '../../shared/common-utils.js';
|
||||
import {logger} from '../logger.js';
|
||||
import {StorageBase} from '../storage/index.js';
|
||||
import * as utils from '../utils.js';
|
||||
|
||||
@@ -25,7 +25,6 @@
|
||||
import path from 'path';
|
||||
|
||||
import fs from 'fs-extra';
|
||||
import _ from 'underscore';
|
||||
|
||||
import type {Language, LanguageKey} from '../types/languages.interfaces.js';
|
||||
|
||||
@@ -684,19 +683,21 @@ const definitions: Record<LanguageKey, LanguageDefinition> = {
|
||||
},
|
||||
};
|
||||
|
||||
export const languages: Record<LanguageKey, Language> = _.mapObject(definitions, (lang, key) => {
|
||||
let example: string;
|
||||
try {
|
||||
example = fs.readFileSync(path.join('examples', key, 'default' + lang.extensions[0]), 'utf8');
|
||||
} catch (error) {
|
||||
example = 'Oops, something went wrong and we could not get the default code for this language.';
|
||||
}
|
||||
export const languages = Object.fromEntries(
|
||||
Object.entries(definitions).map(([key, lang]) => {
|
||||
let example: string;
|
||||
try {
|
||||
example = fs.readFileSync(path.join('examples', key, 'default' + lang.extensions[0]), 'utf8');
|
||||
} catch (error) {
|
||||
example = 'Oops, something went wrong and we could not get the default code for this language.';
|
||||
}
|
||||
|
||||
const def: Language = {
|
||||
...lang,
|
||||
id: key as LanguageKey,
|
||||
supportsExecute: false,
|
||||
example,
|
||||
};
|
||||
return def;
|
||||
});
|
||||
const def: Language = {
|
||||
...lang,
|
||||
id: key as LanguageKey,
|
||||
supportsExecute: false,
|
||||
example,
|
||||
};
|
||||
return [key, def];
|
||||
}),
|
||||
) as Record<LanguageKey, Language>;
|
||||
|
||||
@@ -22,14 +22,13 @@
|
||||
// ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
|
||||
// POSSIBILITY OF SUCH DAMAGE.
|
||||
|
||||
import _ from 'underscore';
|
||||
|
||||
import type {IRResultLine} from '../types/asmresult/asmresult.interfaces.js';
|
||||
|
||||
import * as utils from './utils.js';
|
||||
import {LLVMIrBackendOptions} from '../types/compilation/ir.interfaces.js';
|
||||
import {LLVMIRDemangler} from './demangler/llvm.js';
|
||||
import {ParseFiltersAndOutputOptions} from '../types/features/filters.interfaces.js';
|
||||
import {isString} from '../shared/common-utils.js';
|
||||
|
||||
type MetaNode = {
|
||||
metaId: string;
|
||||
@@ -246,7 +245,7 @@ export class LlvmIrParser {
|
||||
}
|
||||
|
||||
async processFromFilters(ir, filters: ParseFiltersAndOutputOptions) {
|
||||
if (_.isString(ir)) {
|
||||
if (isString(ir)) {
|
||||
return await this.processIr(ir, {
|
||||
filterDebugInfo: !!filters.debugCalls,
|
||||
filterIRMetadata: !!filters.directives,
|
||||
|
||||
@@ -32,7 +32,7 @@ import PromClient from 'prom-client';
|
||||
* @param hostname - The TCP host to attach the listener.
|
||||
* @returns void
|
||||
*/
|
||||
export function setupMetricsServer(serverPort: number, hostname: string): void {
|
||||
export function setupMetricsServer(serverPort: number, hostname: string | undefined): void {
|
||||
PromClient.collectDefaultMetrics();
|
||||
const metricsServer = express();
|
||||
|
||||
@@ -45,5 +45,10 @@ export function setupMetricsServer(serverPort: number, hostname: string): void {
|
||||
.catch(err => res.status(500).send(err));
|
||||
});
|
||||
|
||||
metricsServer.listen(serverPort, hostname);
|
||||
// silly express typing, passing undefined is fine but
|
||||
if (hostname) {
|
||||
metricsServer.listen(serverPort, hostname);
|
||||
} else {
|
||||
metricsServer.listen(serverPort);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -38,24 +38,37 @@ import type {PropertyGetter, PropertyValue} from './properties.interfaces.js';
|
||||
import {Source} from './sources/index.js';
|
||||
import {BaseTool, getToolTypeByKey} from './tooling/index.js';
|
||||
import {asSafeVer, getHash, splitArguments, splitIntoArray} from './utils.js';
|
||||
import {AppDefaultArguments} from '../app.js';
|
||||
|
||||
// TODO: There is surely a better name for this type. Used both here and in the compiler finder.
|
||||
export type OptionHandlerArguments = {
|
||||
rootDir: string;
|
||||
env: string[];
|
||||
hostname: string[];
|
||||
port: number;
|
||||
gitReleaseName: string;
|
||||
releaseBuildNumber: string;
|
||||
wantedLanguages: string | null;
|
||||
doCache: boolean;
|
||||
fetchCompilersFromRemote: boolean;
|
||||
ensureNoCompilerClash: boolean;
|
||||
suppressConsoleLog: boolean;
|
||||
// TODO: Figure out if same as libraries.interfaces.ts?
|
||||
export type VersionInfo = {
|
||||
version: string;
|
||||
staticliblink: string[];
|
||||
alias: string[];
|
||||
dependencies: string[];
|
||||
path: string[];
|
||||
libpath: string[];
|
||||
liblink: string[];
|
||||
lookupversion?: PropertyValue;
|
||||
options: string[];
|
||||
hidden: boolean;
|
||||
packagedheaders?: boolean;
|
||||
};
|
||||
export type OptionsHandlerLibrary = {
|
||||
name: string;
|
||||
url: string;
|
||||
description: string;
|
||||
staticliblink: string[];
|
||||
liblink: string[];
|
||||
dependencies: string[];
|
||||
versions: Record<string, VersionInfo>;
|
||||
examples: string[];
|
||||
options: string[];
|
||||
packagedheaders?: boolean;
|
||||
};
|
||||
|
||||
// TODO: Is this the same as Options in static/options.interfaces.ts?
|
||||
type OptionsType = {
|
||||
export type ClientOptionsType = {
|
||||
googleAnalyticsAccount: string;
|
||||
googleAnalyticsEnabled: boolean;
|
||||
sharingEnabled: boolean;
|
||||
@@ -66,7 +79,7 @@ type OptionsType = {
|
||||
urlShortenService: string;
|
||||
defaultSource: string;
|
||||
compilers: never[];
|
||||
libs: Record<any, any>;
|
||||
libs: Record<string, Record<string, OptionsHandlerLibrary>>;
|
||||
remoteLibs: Record<any, any>;
|
||||
tools: Record<any, any>;
|
||||
defaultLibs: Record<LanguageKey, string>;
|
||||
@@ -119,7 +132,7 @@ export class ClientOptionsHandler {
|
||||
supportsLibraryCodeFilterPerLanguage: Record<LanguageKey, boolean>;
|
||||
supportsLibraryCodeFilter: boolean;
|
||||
remoteLibs: Record<any, any>;
|
||||
options: OptionsType;
|
||||
options: ClientOptionsType;
|
||||
optionsJSON: string;
|
||||
optionsHash: string;
|
||||
/***
|
||||
@@ -130,7 +143,7 @@ export class ClientOptionsHandler {
|
||||
* @param {CompilerProps} compilerProps
|
||||
* @param {Object} defArgs - Compiler Explorer arguments
|
||||
*/
|
||||
constructor(fileSources: Source[], compilerProps: CompilerProps, defArgs: OptionHandlerArguments) {
|
||||
constructor(fileSources: Source[], compilerProps: CompilerProps, defArgs: AppDefaultArguments) {
|
||||
this.compilerProps = compilerProps.get.bind(compilerProps);
|
||||
this.ceProps = compilerProps.ceProps;
|
||||
const ceProps = compilerProps.ceProps;
|
||||
@@ -262,33 +275,8 @@ export class ClientOptionsHandler {
|
||||
}
|
||||
|
||||
parseLibraries(baseLibs: Record<string, string>) {
|
||||
type VersionInfo = {
|
||||
version: string;
|
||||
staticliblink: string[];
|
||||
alias: string[];
|
||||
dependencies: string[];
|
||||
path: string[];
|
||||
libpath: string[];
|
||||
liblink: string[];
|
||||
lookupversion?: PropertyValue;
|
||||
options: string[];
|
||||
hidden: boolean;
|
||||
packagedheaders?: boolean;
|
||||
};
|
||||
type Library = {
|
||||
name: string;
|
||||
url: string;
|
||||
description: string;
|
||||
staticliblink: string[];
|
||||
liblink: string[];
|
||||
dependencies: string[];
|
||||
versions: Record<string, VersionInfo>;
|
||||
examples: string[];
|
||||
options: string[];
|
||||
packagedheaders?: boolean;
|
||||
};
|
||||
// Record language -> {Record lib name -> lib}
|
||||
const libraries: Record<string, Record<string, Library>> = {};
|
||||
const libraries: Record<string, Record<string, OptionsHandlerLibrary>> = {};
|
||||
for (const [lang, forLang] of Object.entries(baseLibs)) {
|
||||
if (lang && forLang) {
|
||||
libraries[lang] = {};
|
||||
|
||||
@@ -32,7 +32,7 @@ import {
|
||||
} from '../../types/asmresult/asmresult.interfaces.js';
|
||||
import {ParseFiltersAndOutputOptions} from '../../types/features/filters.interfaces.js';
|
||||
import {assert} from '../assert.js';
|
||||
import {isString} from '../common-utils.js';
|
||||
import {isString} from '../../shared/common-utils.js';
|
||||
import {PropertyGetter} from '../properties.interfaces.js';
|
||||
import * as utils from '../utils.js';
|
||||
|
||||
|
||||
@@ -32,6 +32,7 @@ import type {LanguageKey} from '../types/languages.interfaces.js';
|
||||
import {logger} from './logger.js';
|
||||
import type {PropertyGetter, PropertyValue, Widen} from './properties.interfaces.js';
|
||||
import {toProperty} from './utils.js';
|
||||
import {isString} from '../shared/common-utils.js';
|
||||
|
||||
let properties: Record<string, Record<string, PropertyValue>> = {};
|
||||
|
||||
@@ -94,7 +95,7 @@ export function parseProperties(blob: string, name) {
|
||||
return props;
|
||||
}
|
||||
|
||||
export function initialize(directory, hier) {
|
||||
export function initialize(directory: string, hier) {
|
||||
if (hier === null) throw new Error('Must supply a hierarchy array');
|
||||
hierarchy = _.map(hier, x => x.toLowerCase());
|
||||
logger.info(`Reading properties from ${directory} with hierarchy ${hierarchy}`);
|
||||
@@ -258,7 +259,7 @@ export class CompilerProps {
|
||||
if (_.isEmpty(langs)) {
|
||||
return map_fn(this.ceProps(key, defaultValue));
|
||||
}
|
||||
if (_.isString(langs)) {
|
||||
if (isString(langs)) {
|
||||
if (this.propsByLangId[langs]) {
|
||||
return map_fn(this.$getInternal(langs, key, defaultValue), this.languages[langs]);
|
||||
} else {
|
||||
|
||||
@@ -24,7 +24,7 @@
|
||||
|
||||
import {logger} from './logger.js';
|
||||
import {PropertyGetter} from './properties.interfaces.js';
|
||||
import {parse} from './stacktrace.js';
|
||||
import {parse} from '../shared/stacktrace.js';
|
||||
|
||||
import * as Sentry from '@sentry/node';
|
||||
|
||||
|
||||
7
package-lock.json
generated
7
package-lock.json
generated
@@ -97,6 +97,7 @@
|
||||
"@types/js-cookie": "^3.0.2",
|
||||
"@types/mocha": "^10.0.1",
|
||||
"@types/node-targz": "^0.2.0",
|
||||
"@types/nopt": "^3.0.29",
|
||||
"@types/qs": "^6.9.7",
|
||||
"@types/request": "^2.48.8",
|
||||
"@types/shell-quote": "^1.7.1",
|
||||
@@ -4420,6 +4421,12 @@
|
||||
"@types/tar-fs": "*"
|
||||
}
|
||||
},
|
||||
"node_modules/@types/nopt": {
|
||||
"version": "3.0.29",
|
||||
"resolved": "https://registry.npmjs.org/@types/nopt/-/nopt-3.0.29.tgz",
|
||||
"integrity": "sha512-PAO73Sc7+IiTIuPY1r/l+TgdIK4lugz5QxPaQ25EsjBBuZAw8OOtNEEGXvGciYwWa+JBE5wNQ8mR6nJE+H2csQ==",
|
||||
"dev": true
|
||||
},
|
||||
"node_modules/@types/normalize-package-data": {
|
||||
"version": "2.4.1",
|
||||
"resolved": "https://registry.npmjs.org/@types/normalize-package-data/-/normalize-package-data-2.4.1.tgz",
|
||||
|
||||
@@ -106,6 +106,7 @@
|
||||
"@types/js-cookie": "^3.0.2",
|
||||
"@types/mocha": "^10.0.1",
|
||||
"@types/node-targz": "^0.2.0",
|
||||
"@types/nopt": "^3.0.29",
|
||||
"@types/qs": "^6.9.7",
|
||||
"@types/request": "^2.48.8",
|
||||
"@types/shell-quote": "^1.7.1",
|
||||
|
||||
94
shared/.eslint-ce-lib.yml
Normal file
94
shared/.eslint-ce-lib.yml
Normal file
@@ -0,0 +1,94 @@
|
||||
---
|
||||
plugins:
|
||||
- jsdoc
|
||||
- sonarjs
|
||||
- unicorn
|
||||
- prettier
|
||||
extends:
|
||||
- ../.eslint-license-header.yml
|
||||
- eslint:recommended
|
||||
env:
|
||||
browser: true
|
||||
node: true
|
||||
es6: false
|
||||
rules:
|
||||
comma-dangle:
|
||||
- error
|
||||
- arrays: always-multiline
|
||||
objects: always-multiline
|
||||
imports: always-multiline
|
||||
exports: always-multiline
|
||||
functions: always-multiline
|
||||
eol-last:
|
||||
- error
|
||||
- always
|
||||
eqeqeq:
|
||||
- error
|
||||
- smart
|
||||
indent:
|
||||
- off
|
||||
#- 4
|
||||
#- SwitchCase: 1
|
||||
max-len:
|
||||
- error
|
||||
- 120
|
||||
- ignoreRegExpLiterals: true
|
||||
ignoreComments: true
|
||||
# TODO: Disabled for now
|
||||
#max-statements:
|
||||
# - error
|
||||
# - 50
|
||||
no-console: error
|
||||
no-control-regex: 0
|
||||
no-useless-call: error
|
||||
no-useless-computed-key: error
|
||||
no-useless-concat: error
|
||||
no-useless-escape: error
|
||||
no-useless-rename: error
|
||||
no-useless-return: error
|
||||
no-empty:
|
||||
- error
|
||||
- allowEmptyCatch: true
|
||||
quote-props:
|
||||
- error
|
||||
- as-needed
|
||||
quotes:
|
||||
- error
|
||||
- single
|
||||
- allowTemplateLiterals: true
|
||||
avoidEscape: true
|
||||
semi:
|
||||
- error
|
||||
- always
|
||||
space-before-function-paren:
|
||||
- error
|
||||
- anonymous: always
|
||||
asyncArrow: always
|
||||
named: never
|
||||
yoda:
|
||||
- error
|
||||
- never
|
||||
- onlyEquality: true
|
||||
prefer-const:
|
||||
- error
|
||||
- destructuring: all
|
||||
jsdoc/check-alignment: warn
|
||||
jsdoc/check-param-names: warn
|
||||
jsdoc/check-syntax: warn
|
||||
jsdoc/check-tag-names: off
|
||||
jsdoc/check-types: warn
|
||||
jsdoc/empty-tags: warn
|
||||
jsdoc/require-hyphen-before-param-description: warn
|
||||
jsdoc/valid-types: warn
|
||||
sonarjs/no-collection-size-mischeck: error
|
||||
sonarjs/no-redundant-boolean: error
|
||||
sonarjs/no-unused-collection: error
|
||||
sonarjs/prefer-immediate-return: error
|
||||
sonarjs/prefer-object-literal: error
|
||||
sonarjs/prefer-single-boolean-return: error
|
||||
unicorn/filename-case: error
|
||||
parserOptions:
|
||||
ecmaVersion: 6
|
||||
globals:
|
||||
define: false
|
||||
__webpack_public_path__: true
|
||||
76
shared/.eslintrc.cjs
Normal file
76
shared/.eslintrc.cjs
Normal file
@@ -0,0 +1,76 @@
|
||||
// Copyright (c) 2023, 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.
|
||||
|
||||
module.exports = {
|
||||
root: true,
|
||||
plugins: ['promise', 'requirejs', 'unused-imports'],
|
||||
extends: ['./.eslint-ce-lib.yml'],
|
||||
rules: {
|
||||
'promise/catch-or-return': 'off',
|
||||
'promise/no-new-statics': 'error',
|
||||
'promise/no-return-wrap': 'error',
|
||||
'promise/param-names': 'error',
|
||||
'promise/valid-params': 'error',
|
||||
},
|
||||
overrides: [
|
||||
{
|
||||
files: ['*.ts'],
|
||||
plugins: ['import', '@typescript-eslint'],
|
||||
extends: [
|
||||
'./.eslint-ce-lib.yml',
|
||||
'plugin:@typescript-eslint/eslint-recommended',
|
||||
'plugin:@typescript-eslint/recommended',
|
||||
'plugin:import/recommended',
|
||||
'plugin:import/typescript',
|
||||
],
|
||||
env: {
|
||||
browser: true,
|
||||
es6: true,
|
||||
node: false,
|
||||
},
|
||||
parser: '@typescript-eslint/parser',
|
||||
parserOptions: {
|
||||
sourceType: 'module',
|
||||
ecmaVersion: 'latest',
|
||||
tsconfigRootDir: __dirname,
|
||||
project: '../tsconfig.json',
|
||||
},
|
||||
rules: {
|
||||
'import/no-unresolved': 'off',
|
||||
'node/no-missing-imports': 'off',
|
||||
'unused-imports/no-unused-imports': 'error',
|
||||
'@typescript-eslint/await-thenable': 'error',
|
||||
'@typescript-eslint/no-empty-function': 'off',
|
||||
'@typescript-eslint/no-unused-vars': 'off',
|
||||
'@typescript-eslint/no-var-requires': 'error',
|
||||
'@typescript-eslint/no-explicit-any': 'off', // Too much js code still exists
|
||||
'@typescript-eslint/ban-ts-comment': 'error',
|
||||
// TODO: Disabled for now
|
||||
//'@typescript-eslint/no-unnecessary-condition': 'error',
|
||||
//'@typescript-eslint/no-unnecessary-type-assertion': 'error',
|
||||
//'@typescript-eslint/prefer-includes': 'error',
|
||||
},
|
||||
},
|
||||
],
|
||||
};
|
||||
@@ -63,3 +63,16 @@ export function basic_comparator<T>(a: T, b: T) {
|
||||
|
||||
// https://stackoverflow.com/questions/41253310/typescript-retrieve-element-type-information-from-array-type
|
||||
export type ElementType<ArrayType extends readonly unknown[]> = ArrayType extends readonly (infer T)[] ? T : never;
|
||||
|
||||
const EscapeMap = {
|
||||
'&': '&',
|
||||
'<': '<',
|
||||
'>': '>',
|
||||
'"': '"',
|
||||
"'": ''',
|
||||
'`': '`',
|
||||
};
|
||||
const EscapeRE = new RegExp(`(?:${Object.keys(EscapeMap).join('|')})`, 'g');
|
||||
export function escapeHTML(text: string) {
|
||||
return text.replace(EscapeRE, str => EscapeMap[str]);
|
||||
}
|
||||
@@ -30,7 +30,7 @@
|
||||
import _ from 'underscore';
|
||||
import {AnsiToHtmlOptions, ColorCodes} from './ansi-to-html.interfaces.js';
|
||||
import {assert, unwrap} from './assert.js';
|
||||
import {isString} from '../lib/common-utils.js';
|
||||
import {isString} from '../shared/common-utils.js';
|
||||
|
||||
const defaults: AnsiToHtmlOptions = {
|
||||
fg: '#FFF',
|
||||
|
||||
@@ -22,8 +22,8 @@
|
||||
// ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
|
||||
// POSSIBILITY OF SUCH DAMAGE.
|
||||
|
||||
import {isString} from '../lib/common-utils.js';
|
||||
import {parse} from '../lib/stacktrace.js';
|
||||
import {isString} from '../shared/common-utils.js';
|
||||
import {parse} from '../shared/stacktrace.js';
|
||||
|
||||
// This file defines three assert utilities:
|
||||
// assert(condition, message?, extra_info...?): asserts condition
|
||||
|
||||
@@ -63,7 +63,7 @@ import {Language, LanguageKey} from '../types/languages.interfaces.js';
|
||||
import {CompilerExplorerOptions} from './global.js';
|
||||
import {ComponentConfig, EmptyCompilerState, StateWithId, StateWithLanguage} from './components.interfaces.js';
|
||||
|
||||
import * as utils from '../lib/common-utils.js';
|
||||
import * as utils from '../shared/common-utils.js';
|
||||
import {Printerinator} from './print-view.js';
|
||||
|
||||
const logos = require.context('../views/resources/logos', false, /\.(png|svg)$/);
|
||||
|
||||
@@ -47,6 +47,7 @@ import TomSelect from 'tom-select';
|
||||
import {assert, unwrap} from '../assert.js';
|
||||
import {CompilationResult} from '../compilation/compilation.interfaces.js';
|
||||
import {CompilerInfo} from '../compiler.interfaces.js';
|
||||
import {escapeHTML} from '../../shared/common-utils.js';
|
||||
|
||||
const ColorTable = {
|
||||
red: '#FE5D5D',
|
||||
@@ -65,16 +66,6 @@ const MINZOOM = 0.1;
|
||||
|
||||
const EST_COMPRESSION_RATIO = 0.022;
|
||||
|
||||
// https://stackoverflow.com/questions/6234773/can-i-escape-html-special-chars-in-javascript
|
||||
function escapeSVG(text: string) {
|
||||
return text
|
||||
.replace(/&/g, '&')
|
||||
.replace(/</g, '<')
|
||||
.replace(/>/g, '>')
|
||||
.replace(/"/g, '"')
|
||||
.replace(/'/g, ''');
|
||||
}
|
||||
|
||||
function attrs(attributes: Record<string, string | number | null>) {
|
||||
return Object.entries(attributes)
|
||||
.map(([k, v]) => `${k}="${v}"`)
|
||||
@@ -636,7 +627,7 @@ export class Cfg extends Pane<CfgState> {
|
||||
y: block.coordinates.y + top + span_box.height / 2 + parseInt(block_style.paddingTop),
|
||||
class: 'code',
|
||||
fill: span_style.color,
|
||||
})}>${escapeSVG(text)}</text>`;
|
||||
})}>${escapeHTML(text)}</text>`;
|
||||
}
|
||||
}
|
||||
doc += '</svg>';
|
||||
|
||||
@@ -79,6 +79,7 @@ import {CompilerShared} from '../compiler-shared.js';
|
||||
import {SentryCapture} from '../sentry.js';
|
||||
import {LLVMIrBackendOptions} from '../compilation/ir.interfaces.js';
|
||||
import {InstructionSet} from '../instructionsets.js';
|
||||
import {escapeHTML} from '../../shared/common-utils.js';
|
||||
|
||||
const toolIcons = require.context('../../views/resources/logos', false, /\.(png|svg)$/);
|
||||
|
||||
@@ -2812,7 +2813,7 @@ export class Compiler extends MonacoPane<monaco.editor.IStandaloneCodeEditor, Co
|
||||
argumentButton.html(
|
||||
"<div class='argmenuitem'>" +
|
||||
"<span class='argtitle'>" +
|
||||
_.escape(key + '') +
|
||||
escapeHTML(key + '') +
|
||||
'</span>' +
|
||||
"<span class='argdescription'>" +
|
||||
arg.description +
|
||||
@@ -3392,7 +3393,7 @@ export class Compiler extends MonacoPane<monaco.editor.IStandaloneCodeEditor, Co
|
||||
warnings.map(w => `<div class="compiler-arg-warning">${w}</div>`).join('\n') +
|
||||
'\n' +
|
||||
(warnings.length > 0 ? infoLine : '') +
|
||||
_.escape(content || 'No options in use') +
|
||||
escapeHTML(content || 'No options in use') +
|
||||
`\n<div class="compiler-arg-warning-shake-setting"></div>`,
|
||||
html: true,
|
||||
template:
|
||||
@@ -3421,14 +3422,14 @@ export class Compiler extends MonacoPane<monaco.editor.IStandaloneCodeEditor, Co
|
||||
// `notification` contains HTML from a config file, so is 'safe'.
|
||||
// `version` comes from compiler output, so isn't, and is escaped.
|
||||
const bodyContent = $('<div>');
|
||||
const versionContent = $('<div>').html(_.escape(version?.version ?? ''));
|
||||
const versionContent = $('<div>').html(escapeHTML(version?.version ?? ''));
|
||||
bodyContent.append(versionContent);
|
||||
if (version?.fullVersion && version.fullVersion.trim() !== version.version.trim()) {
|
||||
const hiddenSection = $('<div>');
|
||||
const lines = version.fullVersion
|
||||
.split('\n')
|
||||
.map(line => {
|
||||
return _.escape(line);
|
||||
return escapeHTML(line);
|
||||
})
|
||||
.join('<br/>');
|
||||
const hiddenVersionText = $('<div>').html(lines).hide();
|
||||
@@ -3811,7 +3812,7 @@ export class Compiler extends MonacoPane<monaco.editor.IStandaloneCodeEditor, Co
|
||||
}
|
||||
|
||||
override getExtraPrintData() {
|
||||
return `<p>Flags: <code>${_.escape(unwrapString(this.optionsField.val()))}</code></p>`;
|
||||
return `<p>Flags: <code>${escapeHTML(unwrapString(this.optionsField.val()))}</code></p>`;
|
||||
}
|
||||
|
||||
override resize() {
|
||||
|
||||
@@ -43,7 +43,7 @@ import {CompilerInfo} from '../../types/compiler.interfaces.js';
|
||||
import {CompilationResult} from '../../types/compilation/compilation.interfaces.js';
|
||||
import {Lib} from '../widgets/libs-widget.interfaces.js';
|
||||
import {SourceAndFiles} from '../download-service.js';
|
||||
import {unique} from '../../lib/common-utils.js';
|
||||
import {escapeHTML, unique} from '../../shared/common-utils.js';
|
||||
import {unwrapString} from '../assert.js';
|
||||
|
||||
type ConformanceStatus = {
|
||||
@@ -215,7 +215,7 @@ export class Conformance extends Pane<ConformanceViewState> {
|
||||
compilerText = ' ' + this.compilerPickers.length + '/' + this.maxCompilations;
|
||||
}
|
||||
const name = this.paneName ? this.paneName + compilerText : this.getPaneName() + compilerText;
|
||||
this.container.setTitle(_.escape(name));
|
||||
this.container.setTitle(escapeHTML(name));
|
||||
}
|
||||
|
||||
addCompilerPicker(config?: AddCompilerPickerConfig): void {
|
||||
|
||||
@@ -54,6 +54,7 @@ import {Decoration, Motd} from '../motd.interfaces.js';
|
||||
import type {escape_html} from 'tom-select/dist/types/utils';
|
||||
import {Compiler} from './compiler.js';
|
||||
import {assert, unwrap} from '../assert.js';
|
||||
import {escapeHTML, isString} from '../../shared/common-utils.js';
|
||||
|
||||
const loadSave = new loadSaveLib.LoadSave();
|
||||
const languages = options.languages;
|
||||
@@ -1090,7 +1091,7 @@ export class Editor extends MonacoPane<monaco.editor.IStandaloneCodeEditor, Edit
|
||||
}
|
||||
}
|
||||
// navigator.language[s] is supposed to return strings, but hey, you never know
|
||||
if (lang !== result && _.isString(lang)) {
|
||||
if (lang !== result && isString(lang)) {
|
||||
const primaryLanguageSubtagIdx = lang.indexOf('-');
|
||||
result = lang.substring(0, primaryLanguageSubtagIdx).toLowerCase();
|
||||
}
|
||||
@@ -1913,7 +1914,7 @@ export class Editor extends MonacoPane<monaco.editor.IStandaloneCodeEditor, Edit
|
||||
if (name.endsWith('CMakeLists.txt')) {
|
||||
this.changeLanguage('cmake');
|
||||
}
|
||||
this.container.setTitle(_.escape(customName));
|
||||
this.container.setTitle(escapeHTML(customName));
|
||||
}
|
||||
|
||||
// Called every time we change language, so we get the relevant code
|
||||
|
||||
@@ -59,6 +59,7 @@ import {SourceAndFiles} from '../download-service.js';
|
||||
import {ICompilerShared} from '../compiler-shared.interfaces.js';
|
||||
import {CompilerShared} from '../compiler-shared.js';
|
||||
import {LangInfo} from './compiler-request.interfaces.js';
|
||||
import {escapeHTML} from '../../shared/common-utils.js';
|
||||
|
||||
const languages = options.languages;
|
||||
|
||||
@@ -1122,7 +1123,7 @@ export class Executor extends Pane<ExecutorState> {
|
||||
|
||||
override updateTitle(): void {
|
||||
const name = this.paneName ? this.paneName : this.getPaneName();
|
||||
this.container.setTitle(_.escape(name));
|
||||
this.container.setTitle(escapeHTML(name));
|
||||
}
|
||||
|
||||
updateCompilerName() {
|
||||
@@ -1158,11 +1159,11 @@ export class Executor extends Pane<ExecutorState> {
|
||||
// `notification` contains HTML from a config file, so is 'safe'.
|
||||
// `version` comes from compiler output, so isn't, and is escaped.
|
||||
const bodyContent = $('<div>');
|
||||
const versionContent = $('<div>').html(_.escape(version?.version ?? ''));
|
||||
const versionContent = $('<div>').html(escapeHTML(version?.version ?? ''));
|
||||
bodyContent.append(versionContent);
|
||||
if (version?.fullVersion) {
|
||||
const hiddenSection = $('<div>');
|
||||
const hiddenVersionText = $('<div>').html(_.escape(version.fullVersion)).hide();
|
||||
const hiddenVersionText = $('<div>').html(escapeHTML(version.fullVersion)).hide();
|
||||
const clickToExpandContent = $('<a>')
|
||||
.attr('href', 'javascript:;')
|
||||
.text('Toggle full version output')
|
||||
|
||||
@@ -47,6 +47,7 @@ import {
|
||||
import {unwrap} from '../assert.js';
|
||||
import {CompilationResult} from '../compilation/compilation.interfaces.js';
|
||||
import {CompilerInfo} from '../compiler.interfaces.js';
|
||||
import {escapeHTML} from '../../shared/common-utils.js';
|
||||
|
||||
const MIN_SIDEBAR_WIDTH = 100;
|
||||
|
||||
@@ -320,7 +321,7 @@ export class LLVMOptPipeline extends MonacoPane<monaco.editor.IStandaloneDiffEdi
|
||||
className += ' firstMachinePass';
|
||||
isFirstMachinePass = false;
|
||||
}
|
||||
this.passesList.append(`<div data-i="${i}" class="pass ${className}">${_.escape(pass.name)}</div>`);
|
||||
this.passesList.append(`<div data-i="${i}" class="pass ${className}">${escapeHTML(pass.name)}</div>`);
|
||||
}
|
||||
const passDivs = this.passesList.find('.pass');
|
||||
passDivs.on('click', e => {
|
||||
|
||||
@@ -36,6 +36,7 @@ import {OutputState} from './output.interfaces.js';
|
||||
import {FontScale} from '../widgets/fontscale.js';
|
||||
import {CompilationResult} from '../../types/compilation/compilation.interfaces.js';
|
||||
import {CompilerInfo} from '../../types/compiler.interfaces.js';
|
||||
import {escapeHTML} from '../../shared/common-utils.js';
|
||||
|
||||
function makeAnsiToHtml(color?) {
|
||||
return new AnsiToHtml.Filter({
|
||||
@@ -338,7 +339,7 @@ export class Output extends Pane<OutputState> {
|
||||
this.eventHub.emit(
|
||||
'printdata',
|
||||
// eslint-disable-next-line no-useless-concat
|
||||
`<h1>Output Pane: ${_.escape(this.getPaneName())}</h1>` + `<code>${this.contentRoot.html()}</code>`,
|
||||
`<h1>Output Pane: ${escapeHTML(this.getPaneName())}</h1>` + `<code>${this.contentRoot.html()}</code>`,
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -38,6 +38,7 @@ import {Hub} from '../hub.js';
|
||||
import {unwrap} from '../assert.js';
|
||||
import {CompilerInfo} from '../compiler.interfaces.js';
|
||||
import {CompilationResult} from '../compilation/compilation.interfaces.js';
|
||||
import {escapeHTML} from '../../shared/common-utils.js';
|
||||
|
||||
/**
|
||||
* Basic container for a tool pane in Compiler Explorer.
|
||||
@@ -237,7 +238,7 @@ export abstract class Pane<S> {
|
||||
|
||||
/** Update the pane's title, called when the pane name or compiler info changes */
|
||||
protected updateTitle() {
|
||||
this.container.setTitle(_.escape(this.getPaneName()));
|
||||
this.container.setTitle(escapeHTML(this.getPaneName()));
|
||||
}
|
||||
|
||||
/** Close the pane if the compiler this pane was attached to closes */
|
||||
@@ -389,7 +390,7 @@ export abstract class MonacoPane<E extends monaco.editor.IEditor, S> extends Pan
|
||||
const extra = this.getExtraPrintData();
|
||||
this.eventHub.emit(
|
||||
'printdata',
|
||||
`<h1>${this.getPrintName()}: ${_.escape(this.getPaneName())}</h1>` +
|
||||
`<h1>${this.getPrintName()}: ${escapeHTML(this.getPaneName())}</h1>` +
|
||||
(extra ?? '') +
|
||||
`<code>${lines.join('<br/>\n')}</code>`,
|
||||
);
|
||||
|
||||
@@ -40,6 +40,7 @@ import {saveAs} from 'file-saver';
|
||||
import {Container} from 'golden-layout';
|
||||
import _ from 'underscore';
|
||||
import {assert, unwrap, unwrapString} from '../assert.js';
|
||||
import {escapeHTML} from '../../shared/common-utils.js';
|
||||
|
||||
const languages = options.languages;
|
||||
|
||||
@@ -166,7 +167,7 @@ export class Tree {
|
||||
}
|
||||
|
||||
private getCustomOutputFilename(): string {
|
||||
return _.escape(unwrapString(this.customOutputFilenameInput.val()));
|
||||
return escapeHTML(unwrapString(this.customOutputFilenameInput.val()));
|
||||
}
|
||||
|
||||
public currentState(): TreeState {
|
||||
@@ -366,7 +367,7 @@ export class Tree {
|
||||
if (file) {
|
||||
this.alertSystem.ask(
|
||||
'Delete file',
|
||||
`Are you sure you want to delete ${file.filename ? _.escape(file.filename) : 'this file'}?`,
|
||||
`Are you sure you want to delete ${file.filename ? escapeHTML(file.filename) : 'this file'}?`,
|
||||
{
|
||||
yes: () => {
|
||||
this.removeFile(fileId);
|
||||
@@ -593,7 +594,7 @@ export class Tree {
|
||||
private async askForOverwriteAndDo(filename): Promise<void> {
|
||||
return new Promise((resolve, reject) => {
|
||||
if (this.multifileService.fileExists(filename)) {
|
||||
this.alertSystem.ask('Overwrite file', `${_.escape(filename)} already exists, overwrite this file?`, {
|
||||
this.alertSystem.ask('Overwrite file', `${escapeHTML(filename)} already exists, overwrite this file?`, {
|
||||
yes: () => {
|
||||
this.removeFileByFilename(filename);
|
||||
resolve();
|
||||
@@ -738,7 +739,7 @@ export class Tree {
|
||||
|
||||
private updateTitle() {
|
||||
const name = this.paneName ? this.paneName : this.getPaneName();
|
||||
this.container.setTitle(_.escape(name));
|
||||
this.container.setTitle(escapeHTML(name));
|
||||
}
|
||||
|
||||
private close() {
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
|
||||
import {assert, unwrap} from './assert.js';
|
||||
|
||||
import {isString} from '../lib/common-utils.js';
|
||||
import {isString} from '../shared/common-utils.js';
|
||||
|
||||
//////////////////////////////////////////////////
|
||||
//
|
||||
|
||||
@@ -22,7 +22,7 @@
|
||||
// ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
|
||||
// POSSIBILITY OF SUCH DAMAGE.
|
||||
|
||||
import {parse} from '../lib/stacktrace.js';
|
||||
import {parse} from '../shared/stacktrace.js';
|
||||
|
||||
import {options} from './options.js';
|
||||
|
||||
|
||||
@@ -30,7 +30,7 @@ import {themes, Themes} from './themes.js';
|
||||
import {AppTheme, ColourScheme, ColourSchemeInfo} from './colour.js';
|
||||
import {Hub} from './hub.js';
|
||||
import {EventHub} from './event-hub.js';
|
||||
import {keys, isString} from '../lib/common-utils.js';
|
||||
import {keys, isString} from '../shared/common-utils.js';
|
||||
import {assert, unwrapString} from './assert.js';
|
||||
|
||||
import {LanguageKey} from '../types/languages.interfaces.js';
|
||||
|
||||
@@ -147,7 +147,7 @@ li.tweet {
|
||||
#printview {
|
||||
display: none;
|
||||
code {
|
||||
font-family: Consolas, "Liberation Mono", Courier, monospace, Consolas, "Courier New", monospace;
|
||||
font-family: Consolas, 'Liberation Mono', Courier, monospace, Consolas, 'Courier New', monospace;
|
||||
}
|
||||
.pagebreak {
|
||||
page-break-before: always;
|
||||
@@ -1244,7 +1244,8 @@ html[data-theme='pink'] {
|
||||
}
|
||||
}
|
||||
|
||||
#site-templates-list, #site-user-templates-list {
|
||||
#site-templates-list,
|
||||
#site-user-templates-list {
|
||||
list-style-type: none;
|
||||
margin: 0;
|
||||
padding: 0;
|
||||
@@ -1258,7 +1259,8 @@ html[data-theme='pink'] {
|
||||
.title {
|
||||
flex: 1 1 auto;
|
||||
}
|
||||
.title, .delete {
|
||||
.title,
|
||||
.delete {
|
||||
background: rgba(0, 0, 0, 0.2);
|
||||
padding: 3px 5px;
|
||||
&:hover {
|
||||
|
||||
@@ -26,7 +26,7 @@ import $ from 'jquery';
|
||||
import {editor} from 'monaco-editor';
|
||||
import {SiteSettings} from './settings.js';
|
||||
import GoldenLayout from 'golden-layout';
|
||||
import {isString} from '../lib/common-utils.js';
|
||||
import {isString} from '../shared/common-utils.js';
|
||||
|
||||
export type Themes = 'default' | 'dark' | 'darkplus' | 'pink' | 'system';
|
||||
|
||||
|
||||
@@ -23,12 +23,11 @@
|
||||
// POSSIBILITY OF SUCH DAMAGE.
|
||||
|
||||
import $ from 'jquery';
|
||||
import _ from 'underscore';
|
||||
|
||||
import * as sifter from '@orchidjs/sifter';
|
||||
|
||||
import {CompilerInfo} from '../../types/compiler.interfaces';
|
||||
import {intersection, remove, unique} from '../../lib/common-utils';
|
||||
import {escapeHTML, intersection, remove, unique} from '../../shared/common-utils';
|
||||
import {unwrap, unwrapString} from '../assert';
|
||||
import {CompilerPicker} from './compiler-picker';
|
||||
import {CompilerService} from '../compiler-service';
|
||||
@@ -86,7 +85,7 @@ export class CompilerPickerPopup {
|
||||
this.architectures.append(
|
||||
...unique(instruction_sets)
|
||||
.sort()
|
||||
.map(isa => `<span class="architecture" data-value=${_.escape(isa)}>${_.escape(isa)}</span>`),
|
||||
.map(isa => `<span class="architecture" data-value=${escapeHTML(isa)}>${escapeHTML(isa)}</span>`),
|
||||
);
|
||||
// get available compiler types
|
||||
const compilerTypes = compilers.map(compiler => compiler.compilerCategories ?? ['other']).flat();
|
||||
@@ -94,7 +93,7 @@ export class CompilerPickerPopup {
|
||||
this.compilerTypes.append(
|
||||
...unique(compilerTypes)
|
||||
.sort()
|
||||
.map(type => `<span class="compiler-type" data-value=${_.escape(type)}>${_.escape(type)}</span>`),
|
||||
.map(type => `<span class="compiler-type" data-value=${escapeHTML(type)}>${escapeHTML(type)}</span>`),
|
||||
);
|
||||
|
||||
// search box
|
||||
@@ -179,7 +178,7 @@ export class CompilerPickerPopup {
|
||||
// This is just a good measure to take. If a compiler is ever added that does have special characters in
|
||||
// its name it could interfere with the highlighting (e.g. if your text search is for "<" that won't
|
||||
// highlight). I'm going to defer handling that to a future PR though.
|
||||
const name = _.escape(compiler.name);
|
||||
const name = escapeHTML(compiler.name);
|
||||
const compiler_elem = $(
|
||||
`
|
||||
<div class="compiler d-flex" data-value="${compiler.id}">
|
||||
@@ -212,7 +211,7 @@ export class CompilerPickerPopup {
|
||||
`
|
||||
<div class="group-wrapper">
|
||||
<div class="group">
|
||||
<div class="label">${_.escape(group.label)}</div>
|
||||
<div class="label">${escapeHTML(group.label)}</div>
|
||||
</div>
|
||||
</div>
|
||||
`,
|
||||
|
||||
@@ -30,6 +30,7 @@ import {ga} from '../analytics.js';
|
||||
import * as local from '../local.js';
|
||||
import {Language} from '../../types/languages.interfaces.js';
|
||||
import {unwrap, unwrapString} from '../assert.js';
|
||||
import {escapeHTML} from '../../shared/common-utils.js';
|
||||
|
||||
const history = require('../history');
|
||||
|
||||
@@ -145,8 +146,8 @@ export class LoadSave {
|
||||
},
|
||||
delete: () => {
|
||||
this.alertSystem.ask(
|
||||
`Delete ${_.escape(name)}?`,
|
||||
`Do you want to delete '${_.escape(name)}'?`,
|
||||
`Delete ${escapeHTML(name)}?`,
|
||||
`Do you want to delete '${escapeHTML(name)}'?`,
|
||||
{
|
||||
yes: () => {
|
||||
LoadSave.removeLocalFile(name);
|
||||
@@ -157,8 +158,8 @@ export class LoadSave {
|
||||
},
|
||||
overwrite: () => {
|
||||
this.alertSystem.ask(
|
||||
`Overwrite ${_.escape(name)}?`,
|
||||
`Do you want to overwrite '${_.escape(name)}'?`,
|
||||
`Overwrite ${escapeHTML(name)}?`,
|
||||
`Do you want to overwrite '${escapeHTML(name)}'?`,
|
||||
{
|
||||
yes: () => {
|
||||
LoadSave.setLocalFile(name, this.editorText);
|
||||
@@ -244,7 +245,7 @@ export class LoadSave {
|
||||
this.modal?.modal('hide');
|
||||
this.alertSystem.ask(
|
||||
'Replace current?',
|
||||
`Do you want to replace the existing saved file '${_.escape(name)}'?`,
|
||||
`Do you want to replace the existing saved file '${escapeHTML(name)}'?`,
|
||||
{yes: doneCallback},
|
||||
);
|
||||
} else {
|
||||
|
||||
@@ -23,7 +23,6 @@
|
||||
// POSSIBILITY OF SUCH DAMAGE.
|
||||
|
||||
import $ from 'jquery';
|
||||
import _ from 'underscore';
|
||||
|
||||
import {SiteTemplatesType, UserSiteTemplate} from '../../types/features/site-templates.interfaces.js';
|
||||
import {assert, unwrap, unwrapString} from '../assert.js';
|
||||
@@ -32,6 +31,7 @@ import * as local from '../local.js';
|
||||
import * as url from '../url.js';
|
||||
import GoldenLayout from 'golden-layout';
|
||||
import {Alert} from './alert.js';
|
||||
import {escapeHTML} from '../../shared/common-utils.js';
|
||||
|
||||
class SiteTemplatesWidget {
|
||||
private readonly modal: JQuery;
|
||||
@@ -112,7 +112,7 @@ class SiteTemplatesWidget {
|
||||
} else {
|
||||
for (const [id, {title, data}] of Object.entries(userTemplates)) {
|
||||
const li = $(`<li></li>`);
|
||||
$(`<div class="title">${_.escape(title)}</div>`)
|
||||
$(`<div class="title">${escapeHTML(title)}</div>`)
|
||||
.attr('data-data', data)
|
||||
.appendTo(li);
|
||||
$(`<div class="delete" data-id="${id}"><i class="fa-solid fa-trash"></i></div>`).appendTo(li);
|
||||
@@ -136,7 +136,7 @@ class SiteTemplatesWidget {
|
||||
// Note: Trusting the server-provided data attribute
|
||||
siteTemplatesList.append(
|
||||
`<li>` +
|
||||
`<div class="title" data-data="${data}" data-name="${name.replace(/[^a-z]/gi, '')}">${_.escape(
|
||||
`<div class="title" data-data="${data}" data-name="${name.replace(/[^a-z]/gi, '')}">${escapeHTML(
|
||||
name,
|
||||
)}</div>` +
|
||||
`</li>`,
|
||||
|
||||
@@ -27,8 +27,8 @@ import {Settings} from '../settings.js';
|
||||
import {Chart, ChartData, defaults} from 'chart.js';
|
||||
import 'chart.js/auto';
|
||||
import {CompilationResult} from '../../types/compilation/compilation.interfaces.js';
|
||||
import _ from 'underscore';
|
||||
import {unwrap} from '../assert.js';
|
||||
import {isString} from '../../shared/common-utils.js';
|
||||
|
||||
type Data = ChartData<'bar', number[], string> & {steps: number};
|
||||
|
||||
@@ -116,7 +116,7 @@ function initializeChartDataFromResult(compileResult: CompilationResult, totalTi
|
||||
pushTimingInfo(data, 'Process execution result', compileResult.processExecutionResultTime);
|
||||
}
|
||||
|
||||
if (compileResult.hasLLVMOptPipelineOutput && !_.isString(compileResult.llvmOptPipelineOutput)) {
|
||||
if (compileResult.hasLLVMOptPipelineOutput && !isString(compileResult.llvmOptPipelineOutput)) {
|
||||
if (compileResult.llvmOptPipelineOutput?.clangTime !== undefined) {
|
||||
pushTimingInfo(data, 'Llvm opt pipeline clang time', compileResult.llvmOptPipelineOutput.clangTime);
|
||||
}
|
||||
|
||||
34
test/common-utils-tests.ts
Normal file
34
test/common-utils-tests.ts
Normal file
@@ -0,0 +1,34 @@
|
||||
// Copyright (c) 2023, 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.
|
||||
|
||||
import {escapeHTML} from '../shared/common-utils.js';
|
||||
|
||||
describe('HTML Escape Test Cases', () => {
|
||||
it('should prevent basic injection', () => {
|
||||
escapeHTML("<script>alert('hi');</script>").should.equal(`<script>alert('hi');</script>`);
|
||||
});
|
||||
it('should prevent tag injection', () => {
|
||||
escapeHTML('\'"`>').should.equal(`'"`>`);
|
||||
});
|
||||
});
|
||||
Reference in New Issue
Block a user