1#!/usr/bin/env python3 2# -*- coding: utf-8 -*- 3# 4# Copyright (c) 2024 Huawei Device Co., Ltd. 5# Licensed under the Apache License, Version 2.0 (the "License"); 6# you may not use this file except in compliance with the License. 7# You may obtain a copy of the License at 8# 9# http://www.apache.org/licenses/LICENSE-2.0 10# 11# Unless required by applicable law or agreed to in writing, software 12# distributed under the License is distributed on an "AS IS" BASIS, 13# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 14# See the License for the specific language governing permissions and 15# limitations under the License. 16# 17 18import logging 19import subprocess # noqa: S404 20from dataclasses import dataclass 21from pathlib import Path 22 23from pytest import fixture 24 25from arkdb.runnable_module import ScriptFile 26 27LOG = logging.getLogger(__name__) 28 29 30@dataclass 31class Options: 32 app_path: Path = Path("bin", "ark_disasm") 33 verbose: bool = False 34 35 36class DisassemblerError(Exception): 37 pass 38 39 40class Disassembler: 41 def __init__(self, options: Options) -> None: 42 self.options = options 43 44 def run( 45 self, 46 panda_file: Path, 47 output_file: Path, 48 cwd: Path = Path(), 49 ): 50 args = [str(self.options.app_path)] 51 if self.options.verbose: 52 args.append(f"--verbose={self.options.verbose}") 53 args += [str(panda_file), str(output_file)] 54 LOG.info("Disassemble %s", args) 55 proc = subprocess.run( # noqa: S603 56 args=args, 57 cwd=cwd, 58 text=True, 59 capture_output=True, 60 ) 61 if proc.returncode == 1: 62 raise DisassemblerError(f"Stdout:\n{proc.stdout}\nStderr:\n{proc.stderr}") 63 proc.check_returncode() 64 return proc.stdout 65 66 67@fixture 68def ark_disassembler( 69 ark_disassembler_options: Options, 70) -> Disassembler: 71 return Disassembler(ark_disassembler_options) 72 73 74class ScriptFileDisassembler: 75 def __init__(self, tmp_path: Path, ark_disassembler: Disassembler): 76 self._tmp_path = tmp_path 77 self._ark_disassembler = ark_disassembler 78 79 def disassemble(self, file: ScriptFile) -> ScriptFile: 80 file.check_exists() 81 pa_file = file.panda_file.parent / f"{file.panda_file.stem}.pa" 82 self._ark_disassembler.run(file.panda_file, pa_file, cwd=self._tmp_path) 83 file.disasm_file = pa_file 84 return file 85 86 87@fixture 88def script_disassembler( 89 tmp_path: Path, 90 ark_disassembler: Disassembler, 91) -> ScriptFileDisassembler: 92 """ 93 Return :class:`StringCodeCompiler` instance that can compile ``ets``-file. 94 """ 95 return ScriptFileDisassembler(tmp_path, ark_disassembler) 96