From 861a375c6f85d76020b59dc44969c3c5288c87ff Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?F=C3=A1bio=20de=20Souza=20Villa=C3=A7a=20Medeiros?= Date: Fri, 13 Jan 2023 13:25:35 -0300 Subject: [PATCH] Add Line Mapping Support to Hook (#4580) --- lib/compilers/hook.ts | 31 ++++++++ test/compilers/hook-tests.js | 136 +++++++++++++++++++++++++++++++++ views/resources/logos/hook.png | Bin 9954 -> 11604 bytes 3 files changed, 167 insertions(+) create mode 100644 test/compilers/hook-tests.js diff --git a/lib/compilers/hook.ts b/lib/compilers/hook.ts index c30c1a981df..10443c18924 100644 --- a/lib/compilers/hook.ts +++ b/lib/compilers/hook.ts @@ -28,6 +28,8 @@ import {CompilationResult, ExecutionOptions} from '../../types/compilation/compi import {ParseFiltersAndOutputOptions} from '../../types/features/filters.interfaces'; import {BaseCompiler} from '../base-compiler'; +import {AsmResultSource, ParsedAsmResultLine} from '../../types/asmresult/asmresult.interfaces'; + export class HookCompiler extends BaseCompiler { static get key(): string { return 'hook'; @@ -52,4 +54,33 @@ export class HookCompiler extends BaseCompiler { options.push(outputFilename); return super.runCompiler(compiler, options, inputFilename, execOptions); } + + override processAsm(result) { + const commentRegex = /^\s*;(.*)/; + const instructionRegex = /^\s{2}(\d+)(.*)/; + const lines = result.asm.split('\n'); + const asm: ParsedAsmResultLine[] = []; + let lastLineNo: number | undefined; + for (const line of lines) { + if (commentRegex.test(line)) { + asm.push({text: line, source: {line: undefined, file: null}}); + lastLineNo = undefined; + continue; + } + const match = line.match(instructionRegex); + if (match) { + const lineNo = parseInt(match[1]); + asm.push({text: line, source: {line: lineNo, file: null}}); + lastLineNo = lineNo; + continue; + } + if (line) { + asm.push({text: line, source: {line: lastLineNo, file: null}}); + continue; + } + asm.push({text: line, source: {line: undefined, file: null}}); + lastLineNo = undefined; + } + return {asm: asm}; + } } diff --git a/test/compilers/hook-tests.js b/test/compilers/hook-tests.js new file mode 100644 index 00000000000..550c1b962aa --- /dev/null +++ b/test/compilers/hook-tests.js @@ -0,0 +1,136 @@ +// Copyright (c) 2021, Compiler Explorer Authors +// All rights reserved. +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions are met: +// +// * Redistributions of source code must retain the above copyright notice, +// this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright +// notice, this list of conditions and the following disclaimer in the +// documentation and/or other materials provided with the distribution. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" +// AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE +// ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE +// LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR +// CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF +// SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS +// INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN +// CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) +// ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE +// POSSIBILITY OF SUCH DAMAGE. + +import {HookCompiler} from '../../lib/compilers'; +import {chai, makeCompilationEnvironment} from '../utils'; + +const expect = chai.expect; + +describe('Hook compiler', () => { + it('should return correct key', () => { + HookCompiler.key.should.equal('hook'); + }); + + const info = {remote: true, lang: 'hook'}; + const languages = {hook: {id: 'hook'}}; + const hook = new HookCompiler(info, makeCompilationEnvironment({languages})); + + it('should return correct options for filter', () => { + hook.optionsForFilter().should.deep.equal(['--dump']); + }); + + it('should return correct output filename', () => { + const dirPath = '/tmp'; + hook.getOutputFilename(dirPath).should.equal('/tmp/example.out'); + }); + + it('should process and return correct bytecode result', () => { + const asm = + '; main in /app/example.hk at 0x56554a556550\n' + + '; 0 parameter(s), 0 non-local(s), 0 constant(s), 0 function(s)\n' + + ' 1 0 Int 2\n' + + ' 3 Int 2\n' + + ' 6 Multiply\n' + + ' 2 7 Load 2\n' + + ' 9 Return\n' + + ' 10 ReturnNil\n' + + '; 6 instruction(s)\n'; + const expected = { + asm: [ + { + source: { + file: null, + line: undefined, + }, + text: '; main in /app/example.hk at 0x56554a556550', + }, + { + source: { + file: null, + line: undefined, + }, + text: '; 0 parameter(s), 0 non-local(s), 0 constant(s), 0 function(s)', + }, + { + source: { + file: null, + line: 1, + }, + text: ' 1 0 Int 2', + }, + { + source: { + file: null, + line: 1, + }, + text: ' 3 Int 2', + }, + { + source: { + file: null, + line: 1, + }, + text: ' 6 Multiply', + }, + { + source: { + file: null, + line: 2, + }, + text: ' 2 7 Load 2', + }, + { + source: { + file: null, + line: 2, + }, + text: ' 9 Return', + }, + { + source: { + file: null, + line: 2, + }, + text: ' 10 ReturnNil', + }, + { + source: { + file: null, + line: undefined, + }, + text: '; 6 instruction(s)', + }, + { + source: { + file: null, + line: undefined, + }, + text: '', + }, + ], + }; + const result = hook.processAsm({asm: asm}); + result.should.deep.equal(expected); + }); +}); diff --git a/views/resources/logos/hook.png b/views/resources/logos/hook.png index 719b7955ca38dcd1b6ad7468eaa37ed88f54ddcf..478a3c29272513b5bc169ee79c40e8a72d5bb47d 100644 GIT binary patch literal 11604 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