# Copyright 2026 The Chromium Authors
# Use of this source code is governed by a BSD-style license that can be
# found in the LICENSE file.
"""A demangler relying on the in-tree tool llvm-cxxfilt."""

import os
import pathlib
import subprocess


class Demangler:
    """Demangles line by line.

    For simplicity, ignores stderr and can block forever if the external
    command does not return.
    """

    def __init__(self):
        src_path = pathlib.Path(__file__).resolve().parents[4]
        self.binary_path = str(
            src_path / 'third_party/llvm-build/Release+Asserts/bin/llvm-cxxfilt'
        )
        self.process = None

    def start(self):
        if not os.path.exists(self.binary_path):
            raise FileNotFoundError(f'Not found: {self.binary_path}')
        # TODO(crbug.com/473768497): Explore async demangling.
        self.process = subprocess.Popen(
            [self.binary_path],
            stdin=subprocess.PIPE,
            stdout=subprocess.PIPE,
            stderr=subprocess.DEVNULL,
            text=True,
            bufsize=1,
        )

    def demangle(self, mangled_name):
        if not self.process:
            raise RuntimeError('Process not started')

        try:
            self.process.stdin.write(mangled_name + '\n')
            self.process.stdin.flush()

            result = self.process.stdout.readline()
            return result.strip()
        except (BrokenPipeError, IOError):
            raise RuntimeError('Error: Process closed unexpectedly')

    def stop(self):
        if self.process:
            self.process.stdin.close()
            self.process.stdout.close()
            self.process.wait()
            self.process = None

    def __enter__(self):
        self.start()
        return self

    def __exit__(self, exc_type, exc_val, exc_tb):
        self.stop()
