This repository has been archived on 2024-09-30. You can view files and clone it, but cannot push or open issues/pull-requests.
hook-frida/venv/Lib/site-packages/frida_tools/compiler.py

118 lines
4.3 KiB
Python

import argparse
import os
import sys
from timeit import default_timer as timer
from typing import Any, Dict, List, Optional
import frida
from frida_tools.application import ConsoleApplication, await_ctrl_c
from frida_tools.cli_formatting import format_compiled, format_compiling, format_diagnostic, format_error
def main() -> None:
app = CompilerApplication()
app.run()
class CompilerApplication(ConsoleApplication):
def __init__(self) -> None:
super().__init__(await_ctrl_c)
def _usage(self) -> str:
return "%(prog)s [options] <module>"
def _add_options(self, parser: argparse.ArgumentParser) -> None:
parser.add_argument("module", help="TypeScript/JavaScript module to compile")
parser.add_argument("-o", "--output", help="write output to <file>")
parser.add_argument("-w", "--watch", help="watch for changes and recompile", action="store_true")
parser.add_argument("-S", "--no-source-maps", help="omit source-maps", action="store_true")
parser.add_argument("-c", "--compress", help="compress using terser", action="store_true")
parser.add_argument("-v", "--verbose", help="be verbose", action="store_true")
def _initialize(self, parser: argparse.ArgumentParser, options: argparse.Namespace, args: List[str]) -> None:
self._module = os.path.abspath(options.module)
self._output = options.output
self._mode = "watch" if options.watch else "build"
self._verbose = self._mode == "watch" or options.verbose
self._compiler_options = {
"project_root": os.getcwd(),
"source_maps": "omitted" if options.no_source_maps else "included",
"compression": "terser" if options.compress else "none",
}
compiler = frida.Compiler()
self._compiler = compiler
def on_compiler_finished() -> None:
self._reactor.schedule(lambda: self._on_compiler_finished())
def on_compiler_output(bundle: str) -> None:
self._reactor.schedule(lambda: self._on_compiler_output(bundle))
def on_compiler_diagnostics(diagnostics: List[Dict[str, Any]]) -> None:
self._reactor.schedule(lambda: self._on_compiler_diagnostics(diagnostics))
compiler.on("starting", self._on_compiler_starting)
compiler.on("finished", on_compiler_finished)
compiler.on("output", on_compiler_output)
compiler.on("diagnostics", on_compiler_diagnostics)
self._compilation_started: Optional[float] = None
def _needs_device(self) -> bool:
return False
def _start(self) -> None:
try:
if self._mode == "build":
self._compiler.build(self._module, **self._compiler_options)
self._exit(0)
else:
self._compiler.watch(self._module, **self._compiler_options)
except Exception as e:
error = e
self._reactor.schedule(lambda: self._on_fatal_error(error))
def _on_fatal_error(self, error: Exception) -> None:
self._print(format_error(error))
self._exit(1)
def _on_compiler_starting(self) -> None:
self._compilation_started = timer()
if self._verbose:
self._reactor.schedule(lambda: self._print_compiler_starting())
def _print_compiler_starting(self) -> None:
if self._mode == "watch":
sys.stdout.write("\x1Bc")
self._print(format_compiling(self._module, os.getcwd()))
def _on_compiler_finished(self) -> None:
if self._verbose:
time_finished = timer()
assert self._compilation_started is not None
self._print(format_compiled(self._module, os.getcwd(), self._compilation_started, time_finished))
def _on_compiler_output(self, bundle: str) -> None:
if self._output is not None:
try:
with open(self._output, "w", encoding="utf-8", newline="\n") as f:
f.write(bundle)
except Exception as e:
self._on_fatal_error(e)
else:
sys.stdout.write(bundle)
def _on_compiler_diagnostics(self, diagnostics: List[Dict[str, Any]]) -> None:
cwd = os.getcwd()
for diag in diagnostics:
self._print(format_diagnostic(diag, cwd))
if __name__ == "__main__":
try:
main()
except KeyboardInterrupt:
pass