|  | // Copyright (c) 2022, 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 * as utils from '../utils'; | 
|  |  | 
|  | import {AsmParser} from './asm-parser'; | 
|  | import {AsmRegex} from './asmregex'; | 
|  |  | 
|  | export class DartAsmParser extends AsmParser { | 
|  | constructor() { | 
|  | super(); | 
|  |  | 
|  | this.lineRe = /^(file:)?(\/[^:]+):(?<line>\d+).*/; | 
|  | } | 
|  |  | 
|  | processBinaryAsm(asmResult, filters) { | 
|  | const startTime = process.hrtime.bigint(); | 
|  | const asm = []; | 
|  | const labelDefinitions = {}; | 
|  | const dontMaskFilenames = filters.dontMaskFilenames; | 
|  |  | 
|  | let asmLines = asmResult.split('\n'); | 
|  | const startingLineCount = asmLines.length; | 
|  | let source = null; | 
|  | let func = null; | 
|  | let mayRemovePreviousLabel = filters.libraryCode; | 
|  |  | 
|  | function maybeRemovePreviousLabel() { | 
|  | if (mayRemovePreviousLabel) { | 
|  | const previousLabelStart = labelDefinitions[func]; | 
|  | if (previousLabelStart) { | 
|  | asm.splice(previousLabelStart - 1); | 
|  | } | 
|  | } | 
|  | } | 
|  |  | 
|  | // Handle "error" documents. | 
|  | if (asmLines.length === 1 && asmLines[0][0] === '<') { | 
|  | return { | 
|  | asm: [{text: asmLines[0], source: null}], | 
|  | }; | 
|  | } | 
|  |  | 
|  | if (filters.preProcessBinaryAsmLines !== undefined) { | 
|  | asmLines = filters.preProcessBinaryAsmLines(asmLines); | 
|  | } | 
|  |  | 
|  | for (const line of asmLines) { | 
|  | const labelsInLine = []; | 
|  |  | 
|  | if (asm.length >= this.maxAsmLines && !mayRemovePreviousLabel) { | 
|  | if (asm.length === this.maxAsmLines) { | 
|  | asm.push({ | 
|  | text: '[truncated; too many lines]', | 
|  | source: null, | 
|  | labels: labelsInLine, | 
|  | }); | 
|  | } | 
|  | continue; | 
|  | } | 
|  | let match = line.match(this.lineRe); | 
|  | if (match) { | 
|  | if (dontMaskFilenames) { | 
|  | source = { | 
|  | file: utils.maskRootdir(match[1]), | 
|  | line: parseInt(match.groups.line), | 
|  | mainsource: true, | 
|  | }; | 
|  | } else { | 
|  | source = {file: null, line: parseInt(match.groups.line), mainsource: true}; | 
|  | } | 
|  | continue; | 
|  | } | 
|  |  | 
|  | match = line.match(this.labelRe); | 
|  | if (match) { | 
|  | maybeRemovePreviousLabel(); | 
|  | mayRemovePreviousLabel = filters.libraryCode; | 
|  | source = null; | 
|  | func = match[2]; | 
|  | if (this.isUserFunction(func)) { | 
|  | asm.push({ | 
|  | text: func + ':', | 
|  | source: null, | 
|  | labels: labelsInLine, | 
|  | }); | 
|  | labelDefinitions[func] = asm.length; | 
|  | } | 
|  | continue; | 
|  | } | 
|  |  | 
|  | if (func && line === `${func}():`) continue; | 
|  |  | 
|  | if (!func || !this.isUserFunction(func)) continue; | 
|  |  | 
|  | // note: normally the source.file will be null if it's code from example.ext | 
|  | //  but with filters.dontMaskFilenames it will be filled with the actual filename | 
|  | //  instead we can test source.mainsource in that situation | 
|  | const isMainsource = source && (source.file === null || source.mainsource); | 
|  | if (isMainsource) { | 
|  | mayRemovePreviousLabel = false; | 
|  | } | 
|  |  | 
|  | match = line.match(this.asmOpcodeRe); | 
|  | if (match) { | 
|  | const address = parseInt(match.groups.address, 16); | 
|  | const opcodes = (match.groups.opcodes || '').split(' ').filter(x => !!x); | 
|  | const disassembly = ' ' + AsmRegex.filterAsmLine(match.groups.disasm, filters); | 
|  | const destMatch = line.match(this.destRe); | 
|  | if (destMatch) { | 
|  | const labelName = destMatch[2]; | 
|  | const startCol = disassembly.indexOf(labelName) + 1; | 
|  | labelsInLine.push({ | 
|  | name: labelName, | 
|  | range: { | 
|  | startCol: startCol, | 
|  | endCol: startCol + labelName.length, | 
|  | }, | 
|  | }); | 
|  | } | 
|  | asm.push({ | 
|  | opcodes: opcodes, | 
|  | address: address, | 
|  | text: disassembly, | 
|  | source: source, | 
|  | labels: labelsInLine, | 
|  | }); | 
|  | } | 
|  | } | 
|  |  | 
|  | maybeRemovePreviousLabel(); | 
|  |  | 
|  | this.removeLabelsWithoutDefinition(asm, labelDefinitions); | 
|  |  | 
|  | const endTime = process.hrtime.bigint(); | 
|  |  | 
|  | return { | 
|  | asm: asm, | 
|  | labelDefinitions: labelDefinitions, | 
|  | parsingTime: ((endTime - startTime) / BigInt(1000000)).toString(), | 
|  | filteredCount: startingLineCount - asm.length, | 
|  | }; | 
|  | } | 
|  | } |