v8 147.3.0

Rust bindings to V8
Documentation
# Copyright 2025 The Chromium Authors
# Use of this source code is governed by a BSD-style license that can be
# found in the LICENSE file.

import collections
import pathlib
import re

from graph import Header
from graph import IncludeDir

_MODULE_START = re.compile(
    r'^(\s*)(?:explicit )?((?:framework )?)module ([^\s.{]+)', flags=re.I)
_HEADER = re.compile(
    r'\s+(exclude header|textual header|umbrella header|header|umbrella) "([^"]*)"'
)
_EXTERN_MODULE = re.compile(r'^(\s*)extern module ([^ ]*) "([^"]*)"')


def _parse_modulemap(include_dir: pathlib.Path, mm_path: pathlib.Path,
                     include_kind: IncludeDir, modules: dict[str, pathlib.Path],
                     headers: set[Header]):
  # The modulemap's paths are relative to the modulemap, but the include's
  # paths are relative to d.
  rel = str(mm_path.parent.relative_to(include_dir))
  rel_prefix = '' if rel == '.' else f'{rel}/'
  framework_layers = []

  def absolute(rel: str, check_exist=True):
    # We don't have / support three layers of nesting of frameworks.
    assert len(framework_layers) < 3
    if include_kind != IncludeDir.Framework:
      path = mm_path.parent / rel
    # Frameworks have very specific path requirements.
    elif len(framework_layers) == 1:
      path = include_dir / 'Headers' / rel
    elif len(framework_layers) == 2:
      path = include_dir / f'Frameworks/{framework_layers[1]}.framework/Headers' / rel
    if check_exist:
      assert path.is_file(), path
    return path

  def relative(rel: str):
    if include_kind == IncludeDir.Framework:
      # In the event of foo.framework/Frameworks/bar.framework/..., it's bar
      return f'{framework_layers[-1]}/{rel}'
    else:
      return rel_prefix + rel

  current_module = None
  with mm_path.open() as f:
    for line in f:
      match = _MODULE_START.match(line)
      if match is not None:
        indent, is_framework, mod = match.groups()
        if not indent:
          # It must be a root module
          current_module = mod
          modules[current_module] = mm_path
        if is_framework:
          # This is a bit hacky, but frameworks don't go deeper than two layers.
          if not indent:
            framework_layers = [mod]
          else:
            framework_layers = [framework_layers[0], mod]

      header = _HEADER.search(line)
      if header is not None:
        kind, name = header.groups()
        if kind in ['header', 'umbrella header', 'textual header']:
          headers.add(
              Header(
                  root_module=current_module,
                  include_dir=include_kind,
                  rel=relative(name),
                  abs=absolute(name),
                  textual=kind == 'textual header',
                  umbrella=kind == 'umbrella header',
              ))
        if kind == 'umbrella':
          if len(framework_layers) == 2:
            # In case of submodules, reroot at the submodule
            # a is an arbitrary filename that we discard with parents.
            mod_root = absolute('a', must_exist=False).parents[1]
          elif include_kind == IncludeDir.Framework:
            mod_root = include_dir
          else:
            mod_root = mm_path.parent
          for path in mod_root.glob(f"{name}/*.h"):
            # We need a way to calculate what the name *should* have been.
            rel = str(
                path.relative_to(mod_root)).removeprefix('Headers').lstrip('/')
            headers.add(
                Header(
                    root_module=current_module,
                    include_dir=include_kind,
                    rel=relative(rel),
                    abs=path,
                    textual=False,
                ))
      extern = _EXTERN_MODULE.match(line)
      if extern is not None:
        indent, extern_module_name, modulemap = extern.groups()
        # The same module can be defined in multiple files. If it is, we can use
        # the root module.modulemap's extern module foo "foo.modulemap" to
        # resolve which one is the canonical definition.
        if current_module is None:
          modules[extern_module_name] = include_dir / modulemap
        submap_headers = set()
        submap_modules = {}
        _parse_modulemap(
            include_dir,
            include_dir / modulemap,
            include_kind,
            modules=modules if current_module is None else submap_modules,
            headers=submap_headers)
        for k, v in submap_modules.items():
          if k not in submap_modules:
            submap_modules[k] = v

        # For module foo { extern module bar }, although the module is bar, the
        # compilation unit is foo
        if indent:
          for hdr in submap_headers:
            hdr.root_module = current_module
        headers.update(submap_headers)


def calculate_modules(
    include_kinds: list[tuple[pathlib.Path, IncludeDir]]
) -> tuple[dict[str, pathlib.Path], set[Header]]:
  """Calculates modules and the headers contained within.

  Args:
    include_kinds: A list of include dirs

  Returns:
    A mapping from module names to modulemaps, and headers defined by modulemaps
  """
  modules = {}
  headers = set()
  for d, kind in include_kinds:
    if kind == IncludeDir.Framework:
      # For the semantics of frameworks, see
      # https://clang.llvm.org/docs/Modules.html#module-declaration
      for modulemap in d.glob("**/Modules/module.modulemap"):
        if 'Versions' not in modulemap.parts:
          _parse_modulemap(modulemap.parents[1],
                           modulemap,
                           include_kind=kind,
                           modules=modules,
                           headers=headers)
    else:
      # One level deep is sufficient for the apple sysroot.
      # ** doesn't work because otherwise it includes the things referenced by
      # the root module.modulemap
      for modulemap in d.glob('*/module.modulemap'):
        _parse_modulemap(d,
                         modulemap,
                         include_kind=kind,
                         modules=modules,
                         headers=headers)

      # Intentionally place this after the previous parse_modulemap so that we
      # override the modules.
      if (d / 'module.modulemap').is_file():
        _parse_modulemap(d,
                         d / 'module.modulemap',
                         include_kind=kind,
                         modules=modules,
                         headers=headers)

  return modules, headers