• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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
19from pathlib import Path
20from typing import Any, Iterable, Protocol
21
22import rich.syntax
23
24from arkdb.logs import RichLogger
25
26
27def syntax(code: str, **kwargs) -> rich.syntax.Syntax:
28    kwargs = {
29        "line_numbers": True,
30        "start_line": 0,
31        **kwargs,
32    }
33    return rich.syntax.Syntax(code, lexer="ts", **kwargs)
34
35
36class RunnableModule(Protocol):
37    # Read-only fields
38    @property
39    def entry_abc(self) -> Path:
40        pass
41
42    @property
43    def boot_abc(self) -> list[Path]:
44        pass
45
46    def check_exists(self):
47        pass
48
49
50class ScriptFile(RunnableModule):
51    def __init__(self, source_file: Path, panda_file: Path, ast: dict[str, Any] | None = None) -> None:
52        if not source_file.exists():
53            raise FileNotFoundError(source_file)
54        self.source_file = source_file
55        self.panda_file = panda_file
56        self._ast: dict[str, Any] | None = ast
57        self._disasm_file: Path | None = None
58
59    @property
60    def ast(self) -> dict[str, Any]:
61        if self._ast is None:
62            raise RuntimeError()
63        return self._ast
64
65    @ast.setter
66    def ast(self, new_ast: dict[str, Any]):
67        self._ast = new_ast
68
69    @property
70    def disasm_file(self) -> Path:
71        if not (self._disasm_file and self._disasm_file.exists()):
72            raise FileNotFoundError(self._disasm_file)
73        return self._disasm_file
74
75    @disasm_file.setter
76    def disasm_file(self, file: Path):
77        self._disasm_file = file
78
79    @property
80    def entry_abc(self):
81        return self.panda_file
82
83    @property
84    def boot_abc(self):
85        return []
86
87    def read_text(self) -> str:
88        if not self.source_file.exists():
89            raise FileNotFoundError(self.source_file)
90        return self.source_file.read_text()
91
92    def check_exists(self):
93        if not self.source_file.exists():
94            raise FileNotFoundError(self.source_file)
95        if not self.panda_file.exists():
96            raise FileNotFoundError(self.panda_file)
97
98    def syntax(self, **kwargs) -> rich.syntax.Syntax:
99        return syntax(self.read_text(), **kwargs)
100
101    def log(self, log: RichLogger, level: int = logging.INFO, **kwargs) -> None:
102        log.log(
103            level,
104            "%s",
105            self.source_file,
106            rich=self.syntax(),
107            stacklevel=2,
108        )
109
110
111class ArkTsModule(RunnableModule):
112    def __init__(self, entry_file: ScriptFile, boot_files: Iterable[ScriptFile] | None = None):
113        self.entry_file = entry_file
114        self.boot_files = list(boot_files) if boot_files else []
115
116    @property
117    def entry_abc(self):
118        return self.entry_file.panda_file
119
120    @property
121    def boot_abc(self):
122        return [f.panda_file for f in self.boot_files]
123
124    def check_exists(self):
125        self.entry_file.check_exists()
126        for f in self.boot_files:
127            f.check_exists()
128