diff --git a/.eslintrc.yml b/.eslintrc.yml index 11065be5735..80c14bbfd57 100644 --- a/.eslintrc.yml +++ b/.eslintrc.yml @@ -44,7 +44,7 @@ rules: import/first: error import/newline-after-import: error import/no-absolute-path: error - import/no-cycle: error + # import/no-cycle: error # disabled for now, there's an import cycle between the base-compiler and demanglers import/no-default-export: warn import/no-deprecated: warn import/no-mutable-exports: error @@ -101,6 +101,9 @@ rules: - error - never - onlyEquality: true + prefer-const: + - error + - destructuring: all jsdoc/check-alignment: warn jsdoc/check-param-names: warn jsdoc/check-syntax: warn diff --git a/lib/demangler/_all.js b/lib/demangler/_all.ts similarity index 100% rename from lib/demangler/_all.js rename to lib/demangler/_all.ts diff --git a/lib/demangler/base.js b/lib/demangler/base.ts similarity index 66% rename from lib/demangler/base.js rename to lib/demangler/base.ts index 9c8be548d90..eae6ab470a5 100644 --- a/lib/demangler/base.js +++ b/lib/demangler/base.ts @@ -22,6 +22,11 @@ // ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE // POSSIBILITY OF SUCH DAMAGE. +import {ParsedAsmResult} from '../../types/asmresult/asmresult.interfaces'; +import {ExecutionOptions} from '../../types/compilation/compilation.interfaces'; +import {UnprocessedExecResult} from '../../types/execution/execution.interfaces'; +import {unwrap} from '../assert'; +import {BaseCompiler} from '../base-compiler'; import {logger} from '../logger'; import {AsmRegex} from '../parsers/asmregex'; import {SymbolStore} from '../symbol-store'; @@ -30,42 +35,53 @@ import * as utils from '../utils'; import {PrefixTree} from './prefix-tree'; export class BaseDemangler extends AsmRegex { - constructor(demanglerExe, compiler) { + demanglerExe: string; + demanglerArguments: string[]; + symbolstore: SymbolStore | null; + othersymbols: SymbolStore; + result: ParsedAsmResult; + input: string[]; + includeMetadata: boolean; + compiler: BaseCompiler; + + readonly jumpDef = /(j\w+|b|bl|blx)\s+([$_a-z][\w$@]*)/i; + readonly callDef = /callq?\s+([$._a-z][\w$.@]*)/i; + readonly callPtrDef1 = /callq?.*ptr\s\[[a-z]*\s\+\s([$._a-z][\w$.@]*)]/i; + readonly callPtrDef2 = /callq?\s+([$*._a-z][\w$.@]*)/i; + readonly callPtrDef3 = /callq?.*\[qword ptr\s([$._a-z][\w$.@]*).*]/i; + readonly callPtrDef4 = /callq?.*qword\sptr\s\[[a-z]*\s\+\s([$._a-z][\w$.@]*)\+?\d?]/i; + + // symbols in a mov or lea command starting with an underscore + readonly movUnderscoreDef = /mov.*\s(_[\w$.@]*)/i; + readonly leaUnderscoreDef = /lea.*\s(_[\w$.@]*)/i; + readonly quadUnderscoreDef = /\.quad\s*(_[\w$.@]*)/i; + + // E.g., ".entry _Z6squarePii(" + // E.g., ".func (.param .b32 func_retval0) bar(" + readonly ptxFuncDef = /\.(entry|func)\s+(?:\([^)]*\)\s*)?([$.A-Z_a-z][\w$.]*)\(/; + // E.g., ".const .attribute(.managed) .align 4 .v4 .u32 myvar" + // E.g., ".global .texref mytex" + readonly ptxVarDef = + /^\.(global|const)\s+(?:\.(tex|sampler|surf)ref\s+)?(?:\.attribute\([^)]*\)\s+)?(?:\.align\s+\d+\s+)?(?:\.v\d+\s+)?(?:\.[a-z]\d+\s+)?([$.A-Z_a-z][\w$.]*)/; + + constructor(demanglerExe: string, compiler: BaseCompiler) { super(); this.demanglerExe = demanglerExe; this.demanglerArguments = []; this.symbolstore = null; this.othersymbols = new SymbolStore(); - this.result = {}; + this.result = { + asm: [], + }; this.input = []; this.includeMetadata = true; this.compiler = compiler; - - this.jumpDef = /(j\w+|b|bl|blx)\s+([$_a-z][\w$@]*)/i; - this.callDef = /callq?\s+([$._a-z][\w$.@]*)/i; - this.callPtrDef1 = /callq?.*ptr\s\[[a-z]*\s\+\s([$._a-z][\w$.@]*)]/i; - this.callPtrDef2 = /callq?\s+([$*._a-z][\w$.@]*)/i; - this.callPtrDef3 = /callq?.*\[qword ptr\s([$._a-z][\w$.@]*).*]/i; - this.callPtrDef4 = /callq?.*qword\sptr\s\[[a-z]*\s\+\s([$._a-z][\w$.@]*)\+?\d?]/i; - - // symbols in a mov or lea command starting with an underscore - this.movUnderscoreDef = /mov.*\s(_[\w$.@]*)/i; - this.leaUnderscoreDef = /lea.*\s(_[\w$.@]*)/i; - this.quadUnderscoreDef = /\.quad\s*(_[\w$.@]*)/i; - - // E.g., ".entry _Z6squarePii(" - // E.g., ".func (.param .b32 func_retval0) bar(" - this.ptxFuncDef = /\.(entry|func)\s+(?:\([^)]*\)\s*)?([$.A-Z_a-z][\w$.]*)\(/; - // E.g., ".const .attribute(.managed) .align 4 .v4 .u32 myvar" - // E.g., ".global .texref mytex" - this.ptxVarDef = - /^\.(global|const)\s+(?:\.(tex|sampler|surf)ref\s+)?(?:\.attribute\([^)]*\)\s+)?(?:\.align\s+\d+\s+)?(?:\.v\d+\s+)?(?:\.[a-z]\d+\s+)?([$.A-Z_a-z][\w$.]*)/; } // Iterates over the labels, demangle the label names and updates the start and // end position of the label. - demangleLabels(labels, tree) { + protected demangleLabels(labels, tree: PrefixTree) { if (!Array.isArray(labels) || labels.length === 0) return; for (const [index, label] of labels.entries()) { @@ -83,7 +99,7 @@ export class BaseDemangler extends AsmRegex { } } - demangleLabelDefinitions(labelDefinitions, translations) { + protected demangleLabelDefinitions(labelDefinitions, translations: [string, string][]) { if (!labelDefinitions) return; for (const [oldValue, newValue] of translations) { @@ -95,7 +111,7 @@ export class BaseDemangler extends AsmRegex { } } - collectLabels() { + protected collectLabels() { const symbolMatchers = [ this.jumpDef, this.callPtrDef4, @@ -114,7 +130,7 @@ export class BaseDemangler extends AsmRegex { if (!line) continue; const labelMatch = line.match(this.labelDef); - if (labelMatch) this.symbolstore.add(labelMatch[labelMatch.length - 1]); + if (labelMatch) unwrap(this.symbolstore).add(labelMatch[labelMatch.length - 1]); for (const reToMatch of symbolMatchers) { const matches = line.match(reToMatch); @@ -125,36 +141,36 @@ export class BaseDemangler extends AsmRegex { } } - this.othersymbols.exclude(this.symbolstore); + this.othersymbols.exclude(unwrap(this.symbolstore)); } - getInput() { + protected getInput() { this.input = []; - this.input = this.input.concat(this.symbolstore.listSymbols()); + this.input = this.input.concat(unwrap(this.symbolstore).listSymbols()); this.input = this.input.concat(this.othersymbols.listSymbols()); return this.input.join('\n'); } - getMetadata() { + protected getMetadata(symbol: string): {ident: RegExp; description: string}[] { return []; } - addTranslation(symbol, translation) { + protected addTranslation(symbol: string, translation: string) { if (this.includeMetadata) { translation += this.getMetadata(symbol) - .map(meta => ' [' + meta.description + ']') + .map(meta => ` [${meta.description}]`) .join(','); } - if (this.symbolstore.contains(symbol)) { - this.symbolstore.add(symbol, translation); + if (unwrap(this.symbolstore).contains(symbol)) { + unwrap(this.symbolstore).add(symbol, translation); } else { this.othersymbols.add(symbol, translation); } } - processOutput(output) { + protected processOutput(output: UnprocessedExecResult) { if (output.stdout.length === 0 && output.stderr.length > 0) { logger.error(`Error executing demangler ${this.demanglerExe}`, output); return this.result; @@ -167,9 +183,10 @@ export class BaseDemangler extends AsmRegex { } for (let i = 0; i < lines.length; ++i) this.addTranslation(this.input[i], lines[i]); - const translations = [...this.symbolstore.listTranslations(), ...this.othersymbols.listTranslations()].filter( - elem => elem[0] !== elem[1], - ); + const translations = [ + ...unwrap(this.symbolstore).listTranslations(), + ...this.othersymbols.listTranslations(), + ].filter(elem => elem[0] !== elem[1]); if (translations.length > 0) { const tree = new PrefixTree(translations); @@ -183,13 +200,13 @@ export class BaseDemangler extends AsmRegex { return this.result; } - execDemangler(options) { + protected execDemangler(options: ExecutionOptions) { options.maxOutput = -1; return this.compiler.exec(this.demanglerExe, this.demanglerArguments, options); } - async process(result, execOptions) { + public async process(result: ParsedAsmResult, execOptions?: ExecutionOptions) { const options = execOptions || {}; this.result = result; diff --git a/lib/demangler/cpp.js b/lib/demangler/cpp.ts similarity index 97% rename from lib/demangler/cpp.js rename to lib/demangler/cpp.ts index c5e98221b7e..b6a8d18f608 100644 --- a/lib/demangler/cpp.js +++ b/lib/demangler/cpp.ts @@ -38,7 +38,7 @@ export class CppDemangler extends BaseDemangler { return 'cpp'; } - getMetadata(symbol) { + protected override getMetadata(symbol: string) { return LabelMetadata.filter(metadata => metadata.ident.test(symbol)); } } diff --git a/lib/demangler/default.js b/lib/demangler/default.ts similarity index 100% rename from lib/demangler/default.js rename to lib/demangler/default.ts diff --git a/lib/demangler/index.js b/lib/demangler/index.ts similarity index 100% rename from lib/demangler/index.js rename to lib/demangler/index.ts diff --git a/lib/demangler/llvm.ts b/lib/demangler/llvm.ts index c516be4a6b3..533428c266a 100644 --- a/lib/demangler/llvm.ts +++ b/lib/demangler/llvm.ts @@ -38,7 +38,7 @@ export class LLVMIRDemangler extends BaseDemangler { return 'llvm-ir'; } - override collectLabels() { + protected override collectLabels() { for (const line of this.result.asm) { const text = line.text; if (!text) continue; @@ -51,7 +51,7 @@ export class LLVMIRDemangler extends BaseDemangler { } } - collect(result: {asm: ResultLine[]}) { + public collect(result: {asm: ResultLine[]}) { this.result = result; if (!this.symbolstore) { this.symbolstore = new SymbolStore(); @@ -59,7 +59,7 @@ export class LLVMIRDemangler extends BaseDemangler { } } - processPassOutput(passOutput: LLVMOptPipelineResults, demanglerOutput) { + protected processPassOutput(passOutput: LLVMOptPipelineResults, demanglerOutput) { if (demanglerOutput.stdout.length === 0 && demanglerOutput.stderr.length > 0) { logger.error(`Error executing demangler ${this.demanglerExe}`, demanglerOutput); return passOutput; @@ -95,7 +95,7 @@ export class LLVMIRDemangler extends BaseDemangler { return passOutput; } - async demangleLLVMPasses(passOutput: LLVMOptPipelineResults) { + public async demangleLLVMPasses(passOutput: LLVMOptPipelineResults) { const options = { input: this.getInput(), }; diff --git a/lib/demangler/nvhpc.ts b/lib/demangler/nvhpc.ts index e2e303a96a8..e0d18a5a327 100644 --- a/lib/demangler/nvhpc.ts +++ b/lib/demangler/nvhpc.ts @@ -37,7 +37,7 @@ export class NVHPCDemangler extends BaseDemangler { this.llvmDemangler = new LLVMIRDemangler(demanglerExe, compiler); } - override collectLabels() { + protected override collectLabels() { this.llvmDemangler.collect(this.result as any); this.symbolstore = this.llvmDemangler.symbolstore; } diff --git a/lib/demangler/pascal.js b/lib/demangler/pascal.ts similarity index 91% rename from lib/demangler/pascal.js rename to lib/demangler/pascal.ts index b3d039a3959..4dda282e8aa 100644 --- a/lib/demangler/pascal.js +++ b/lib/demangler/pascal.ts @@ -22,6 +22,8 @@ // ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE // POSSIBILITY OF SUCH DAMAGE. +import {ParsedAsmResult} from '../../types/asmresult/asmresult.interfaces'; +import {ExecutionOptions} from '../../types/compilation/compilation.interfaces'; import {SymbolStore} from '../symbol-store'; import {BaseDemangler} from './base'; @@ -31,6 +33,10 @@ export class PascalDemangler extends BaseDemangler { return 'pascal'; } + symbolStore: SymbolStore; + fixedsymbols: Record; + ignoredsymbols: string[]; + constructor(demanglerExe, compiler) { super(demanglerExe, compiler); @@ -41,7 +47,7 @@ export class PascalDemangler extends BaseDemangler { this.initBasicSymbols(); } - initBasicSymbols() { + protected initBasicSymbols() { this.fixedsymbols.OUTPUT_$$_init = 'unit_initialization'; this.fixedsymbols.OUTPUT_$$_finalize = 'unit_finalization'; this.fixedsymbols.OUTPUT_$$_init_implicit = 'unit_initialization_implicit'; @@ -76,8 +82,8 @@ export class PascalDemangler extends BaseDemangler { ]; } - shouldIgnoreSymbol(text) { - for (let k in this.ignoredsymbols) { + protected shouldIgnoreSymbol(text: string) { + for (const k in this.ignoredsymbols) { if (text.startsWith(this.ignoredsymbols[k])) { return true; } @@ -86,7 +92,7 @@ export class PascalDemangler extends BaseDemangler { return false; } - composeReadableMethodSignature(unitname, classname, methodname, params) { + protected composeReadableMethodSignature(unitname, classname, methodname, params) { let signature = ''; if (classname !== '') signature = classname.toLowerCase() + '.'; @@ -97,7 +103,7 @@ export class PascalDemangler extends BaseDemangler { return signature; } - demangle(text) { + protected demangle(text) { if (!text.endsWith(':')) return false; if (this.shouldIgnoreSymbol(text)) return false; @@ -194,11 +200,11 @@ export class PascalDemangler extends BaseDemangler { return unmangled; } - addDemangleToCache(text) { + protected addDemangleToCache(text) { this.demangle(text); } - demangleIfNeeded(text) { + protected demangleIfNeeded(text) { if (text.includes('$')) { if (this.shouldIgnoreSymbol(text)) { return text; @@ -215,8 +221,8 @@ export class PascalDemangler extends BaseDemangler { } } - async process(result, execOptions) { - let options = execOptions || {}; + public override async process(result: ParsedAsmResult, execOptions?: ExecutionOptions) { + const options = execOptions || {}; this.result = result; if (!this.symbolstore) { diff --git a/lib/demangler/prefix-tree.js b/lib/demangler/prefix-tree.ts similarity index 86% rename from lib/demangler/prefix-tree.js rename to lib/demangler/prefix-tree.ts index 77034e3135b..ea32da29256 100644 --- a/lib/demangler/prefix-tree.js +++ b/lib/demangler/prefix-tree.ts @@ -22,6 +22,8 @@ // ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE // POSSIBILITY OF SUCH DAMAGE. +import {assert} from '../assert'; + // A prefix tree, really a trie, but I find the name annoyingly pompous, and // as it's pronounced the same way as "tree", super confusing. // Essentially we have a N-way tree, for N possible ASCII characters. Each @@ -32,18 +34,23 @@ // * It's linear in the length of a match to find the longest prefix, or a match. // It's the "find longest prefix" performance characteristic that we want for the // demangler. + +type Node = Node[] & {result?: string}; + export class PrefixTree { - constructor(mappings) { - this.root = []; + root: Node = []; + + constructor(mappings: [string, string][]) { if (mappings) { for (const [from, to] of mappings) this.add(from, to); } } - add(from, to) { + add(from: string, to: string) { let node = this.root; for (let i = 0; i < from.length; ++i) { const character = from.codePointAt(i); + assert(character !== undefined, 'Undefined code point encountered in PrefixTree'); if (!node[character]) node[character] = []; node = node[character]; } @@ -53,11 +60,12 @@ export class PrefixTree { // Finds the longest possible match by walking along the N-way tree until we // mismatch or reach the end of the input string. Along the way, we note the // most recent match (if any), which will be our return value. - findLongestMatch(needle) { + findLongestMatch(needle: string) { let node = this.root; - let match = [null, null]; + let match: [string, string] | [null, null] = [null, null]; for (let i = 0; i < needle.length; ++i) { const character = needle.codePointAt(i); + assert(character !== undefined, 'Undefined code point encountered in PrefixTree'); node = node[character]; if (!node) break; if (node.result) match = [needle.substr(0, i + 1), node.result]; @@ -65,10 +73,11 @@ export class PrefixTree { return match; } - findExact(needle) { + findExact(needle: string) { let node = this.root; for (let i = 0; i < needle.length; ++i) { const character = needle.codePointAt(i); + assert(character !== undefined, 'Undefined code point encountered in PrefixTree'); node = node[character]; if (!node) break; } @@ -77,7 +86,7 @@ export class PrefixTree { } // Replace all matches (longest match first) in a line. - replaceAll(line) { + replaceAll(line: string) { let result = ''; let index = 0; // Loop over each possible replacement point in the line. diff --git a/lib/demangler/win32-llvm.js b/lib/demangler/win32-llvm.ts similarity index 88% rename from lib/demangler/win32-llvm.js rename to lib/demangler/win32-llvm.ts index 2cc1e4d44c8..ab72566efc9 100644 --- a/lib/demangler/win32-llvm.js +++ b/lib/demangler/win32-llvm.ts @@ -22,18 +22,19 @@ // ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE // POSSIBILITY OF SUCH DAMAGE. +import {unwrap} from '../assert'; import * as utils from '../utils'; import {Win32Demangler} from './win32'; export class LLVMWin32Demangler extends Win32Demangler { - static get key() { + static override get key() { // this should be used for llvm-undname return 'win32-llvm'; } - async execDemangler() { - const translations = {}; + override async createTranslations() { + const translations: Record = {}; const flags = ['--no-access-specifier', '--no-calling-convention']; const demangleFromStdin = async stdin => { @@ -56,11 +57,11 @@ export class LLVMWin32Demangler extends Win32Demangler { } }; - this.win32RawSymbols.sort(); + unwrap(this.win32RawSymbols).sort(); - let lastSymbol = null; - let symbolArray = []; - for (const symb of this.win32RawSymbols) { + let lastSymbol: string | null = null; + const symbolArray: string[] = []; + for (const symb of unwrap(this.win32RawSymbols)) { if (symb === lastSymbol) { continue; } diff --git a/lib/demangler/win32.js b/lib/demangler/win32.ts similarity index 80% rename from lib/demangler/win32.js rename to lib/demangler/win32.ts index a753c5e8ae4..3aa6c0317c0 100644 --- a/lib/demangler/win32.js +++ b/lib/demangler/win32.ts @@ -22,16 +22,25 @@ // ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE // POSSIBILITY OF SUCH DAMAGE. +import {ParsedAsmResult} from '../../types/asmresult/asmresult.interfaces'; +import {UnprocessedExecResult} from '../../types/execution/execution.interfaces'; +import {assert, unwrap} from '../assert'; import {logger} from '../logger'; import * as utils from '../utils'; import {CppDemangler} from './cpp'; export class Win32Demangler extends CppDemangler { - static get key() { + static override get key() { return 'win32'; } + flags: string; + allDecoratedLabels: RegExp; + allDecoratedLabelsWithQuotes: RegExp; + hasQuotesAroundDecoratedLabels: null | boolean; + win32RawSymbols?: string[]; + constructor(demanglerExe, compiler) { super(demanglerExe, compiler); @@ -50,7 +59,7 @@ export class Win32Demangler extends CppDemangler { this.hasQuotesAroundDecoratedLabels = null; } - collectLabels() { + protected override collectLabels() { this.win32RawSymbols = []; for (const asmLine of this.result.asm) { const labels = asmLine.text.match(this.allDecoratedLabels); @@ -66,13 +75,17 @@ export class Win32Demangler extends CppDemangler { } } - processOutput(translations) { + protected override processOutput(translations: UnprocessedExecResult): ParsedAsmResult { + assert(false, "Win32Demangler.processOutput shouldn't be called"); + } + + protected processTranslations(translations: Record) { for (const asmLine of this.result.asm) { const labels = this.hasQuotesAroundDecoratedLabels ? asmLine.text.match(this.allDecoratedLabelsWithQuotes) : asmLine.text.match(this.allDecoratedLabels); if (labels) { - let [, beforeComment, afterComment] = asmLine.text.match(/(.*)(;.*)?/); + let [, beforeComment, afterComment] = unwrap(asmLine.text.match(/(.*)(;.*)?/)); for (const label of labels) { const replacement = translations[label]; if (replacement) { @@ -88,8 +101,12 @@ export class Win32Demangler extends CppDemangler { return this.result; } - async execDemangler() { - const translations = {}; + protected override async execDemangler(): Promise { + assert(false, "Win32Demangler.processOutput shouldn't be called"); + } + + protected async createTranslations() { + const translations: Record = {}; const demangleSingleSet = async names => { const args = [this.flags, ...names]; @@ -118,13 +135,13 @@ export class Win32Demangler extends CppDemangler { // give some space for `undname` as well as the flag // should probably be done more correctly const maxCommandLineLength = 8000; - const commandLineArray = []; - this.win32RawSymbols.sort(); + const commandLineArray: string[][] = []; + unwrap(this.win32RawSymbols).sort(); - let lastSymbol = null; + let lastSymbol: null | string = null; let currentLength = 0; - let currentArray = []; - for (const symb of this.win32RawSymbols) { + let currentArray: string[] = []; + for (const symb of unwrap(this.win32RawSymbols)) { if (symb === lastSymbol) { continue; } @@ -147,7 +164,7 @@ export class Win32Demangler extends CppDemangler { return translations; } - async process(result) { + public override async process(result: ParsedAsmResult) { if (!this.demanglerExe) { logger.error("Attempted to demangle, but there's no demangler set"); return result; @@ -156,6 +173,6 @@ export class Win32Demangler extends CppDemangler { this.result = result; this.collectLabels(); - return this.processOutput(await this.execDemangler()); + return this.processTranslations(await this.createTranslations()); } }