diff --git a/src/lib/decode/gdbMi.js b/src/lib/decode/gdbMi.js index f75d460..026947e 100644 --- a/src/lib/decode/gdbMi.js +++ b/src/lib/decode/gdbMi.js @@ -248,12 +248,45 @@ function splitMiListItems(listContent) { * @returns {string} */ function unescapeMiString(value) { - return value - .replace(/\\\\/g, '\\') - .replace(/\\"/g, '"') - .replace(/\\n/g, '\n') - .replace(/\\r/g, '\r') - .replace(/\\t/g, '\t') + let result = '' + + for (let i = 0; i < value.length; i++) { + const char = value[i] + if (char !== '\\') { + result += char + continue + } + + const next = value[i + 1] + if (!next) { + result += '\\' + continue + } + i++ + + switch (next) { + case '\\': + result += '\\' + break + case '"': + result += '"' + break + case 'n': + result += '\n' + break + case 'r': + result += '\r' + break + case 't': + result += '\t' + break + default: + result += next + break + } + } + + return result } /** diff --git a/src/lib/decode/gdbMi.test.js b/src/lib/decode/gdbMi.test.js new file mode 100644 index 0000000..0c92ef2 --- /dev/null +++ b/src/lib/decode/gdbMi.test.js @@ -0,0 +1,28 @@ +// @ts-check + +import { describe, expect, it } from 'vitest' + +import { parseMiResultRecord, parseMiTupleList, stripMiList } from './gdbMi.js' + +describe('gdbMi', () => { + it('should keep backslashes in Windows paths when parsing MI tuples', () => { + const raw = + '^done,stack=[frame={level="0",fullname="C:\\\\Users\\\\xxx\\\\dev\\\\git\\\\boardlab\\\\test_workspace\\\\esp32backtracetest\\\\module2.cpp"}]' + const record = parseMiResultRecord(raw) + const frames = parseMiTupleList(stripMiList(record.stack), 'frame') + expect(frames).toStrictEqual([ + { + level: '0', + fullname: String.raw`C:\Users\xxx\dev\git\boardlab\test_workspace\esp32backtracetest\module2.cpp`, + }, + ]) + expect(frames[0].fullname.includes('\t')).toBe(false) + }) + + it('should decode escaped control chars in MI strings', () => { + const record = parseMiResultRecord( + '^done,msg="line1\\nline2\\t\\"quoted\\""' + ) + expect(record.msg).toBe('line1\nline2\t"quoted"') + }) +}) diff --git a/src/lib/decode/riscv.test.js b/src/lib/decode/riscv.test.js index c7347e3..0b75179 100644 --- a/src/lib/decode/riscv.test.js +++ b/src/lib/decode/riscv.test.js @@ -89,6 +89,8 @@ Backtrace stopped: previous frame inner to this frame (corrupt stack?) const miFramesOutput = '^done,stack=[frame={level="0",addr="0x4200007e",func="a::geta",file="/path/sketch.ino",fullname="/path/sketch.ino",line="11"},frame={level="1",addr="0x42000088",func="loop",file="/path/sketch.ino",line="21"},frame={level="2",addr="0x4c1c0042"}]' +const miFramesOutputWindows = + '^done,stack=[frame={level="0",addr="0x420000a0",func="functionC",fullname="C:\\\\Users\\\\xxx\\\\dev\\\\git\\\\boardlab\\\\test_workspace\\\\esp32backtracetest\\\\module2.cpp",line="9"}]' const miArgsOutput = '^done,stack-args=[frame={level="0",args=[{name="this",type="a *",value="0x0"}]},frame={level="1",args=[{name="pvParameters",value=""}]}]' @@ -593,6 +595,20 @@ Stack memory: ]) }) + it('should preserve Windows path separators when parsing MI frames', () => { + const frames = parseMiFrames(miFramesOutputWindows) + const parsed = frames.map(toParsedFrame) + expect(parsed).toStrictEqual([ + { + regAddr: '0x420000a0', + method: 'functionC', + file: String.raw`C:\Users\xxx\dev\git\boardlab\test_workspace\esp32backtracetest\module2.cpp`, + lineNumber: '9', + }, + ]) + expect(parsed[0].file.includes('\t')).toBe(false) + }) + it('should parse MI stack arguments', () => { const args = parseMiStackArgs(miArgsOutput, '1') expect(args).toStrictEqual([