chromium/third_party/blink/renderer/bindings/scripts/web_idl/composition_parts.py

# Copyright 2019 The Chromium Authors
# Use of this source code is governed by a BSD-style license that can be
# found in the LICENSE file.

import posixpath

from .code_generator_info import CodeGeneratorInfo
from .code_generator_info import CodeGeneratorInfoMutable
from .exposure import Exposure
from .exposure import ExposureMutable
from .extended_attribute import ExtendedAttributes
from .extended_attribute import ExtendedAttributesMutable


class Identifier(str):
    pass


class WithIdentifier(object):
    """Implements |identifier| as a readonly attribute."""

    def __init__(self, identifier):
        if isinstance(identifier, WithIdentifier):
            identifier = identifier.identifier
        assert isinstance(identifier, Identifier)

        self._identifier = identifier

    @property
    def identifier(self):
        return self._identifier

    def change_identifier(self, new_identifier):
        assert isinstance(new_identifier, Identifier)
        self._identifier = new_identifier


class WithExtendedAttributes(object):
    """Implements |extended_attributes| as a readonly attribute."""

    def __init__(self, extended_attributes=None, readonly=False):
        if isinstance(extended_attributes, WithExtendedAttributes):
            extended_attributes = extended_attributes.extended_attributes
        elif extended_attributes is None:
            extended_attributes = ExtendedAttributes()
        assert isinstance(extended_attributes, ExtendedAttributes)

        if readonly:
            self._extended_attributes = ExtendedAttributes(extended_attributes)
        else:
            self._extended_attributes = ExtendedAttributesMutable(
                extended_attributes)

    @property
    def extended_attributes(self):
        return self._extended_attributes


class WithCodeGeneratorInfo(object):
    """Implements |code_generator_info| as a readonly attribute."""

    def __init__(self, code_generator_info=None, readonly=False):
        if isinstance(code_generator_info, WithCodeGeneratorInfo):
            code_generator_info = code_generator_info.code_generator_info
        elif code_generator_info is None:
            code_generator_info = CodeGeneratorInfoMutable()
        assert isinstance(code_generator_info, CodeGeneratorInfo)
        assert isinstance(readonly, bool)

        if readonly:
            self._code_generator_info = CodeGeneratorInfo(code_generator_info)
        else:
            self._code_generator_info = code_generator_info

    @property
    def code_generator_info(self):
        return self._code_generator_info


class WithExposure(object):
    """Implements |exposure| as a readonly attribute."""

    def __init__(self, exposure=None, readonly=False):
        if isinstance(exposure, WithExposure):
            exposure = exposure.exposure
        elif exposure is None:
            exposure = ExposureMutable()
        assert isinstance(exposure, Exposure)
        assert isinstance(readonly, bool)

        if readonly:
            self._exposure = Exposure(exposure)
        else:
            self._exposure = exposure

    @property
    def exposure(self):
        return self._exposure


class Component(str):
    """
    Represents a component that is a Blink-specific layering concept, such as
    'core' and 'modules'.
    """


class WithComponent(object):
    """
    Implements |components| as a readonly attribute.

    A single IDL definition such as 'interface' may consist from multiple IDL
    fragments like partial interfaces and mixins, which may exist across
    Blink components.  |components| is a list of Blink components of IDL
    fragments that are involved into this object.
    """

    def __init__(self, component, readonly=False):
        if isinstance(component, WithComponent):
            components = component._components
        elif isinstance(component, Component):
            components = [component]
        else:
            components = component
        assert (isinstance(components, (list, tuple)) and all(
            isinstance(component, Component) for component in components))
        assert isinstance(readonly, bool)

        if readonly:
            self._components = tuple(components)
        else:
            self._components = components

    @property
    def components(self):
        return self._components

    def add_components(self, components):
        assert isinstance(components, (list, tuple)) and all(
            isinstance(component, Component) for component in components)
        for component in components:
            if component not in self._components:
                self._components.append(component)


class Location(object):
    _blink_path_prefix = posixpath.sep + posixpath.join(
        'third_party', 'blink', 'renderer', '')

    def __init__(self, filepath=None, line_number=None, position=None):
        assert filepath is None or isinstance(filepath, str)
        assert line_number is None or isinstance(line_number, int)
        assert position is None or isinstance(position, int)

        # idl_parser produces paths based on the working directory, which may
        # not be the project root directory, e.g. "../../third_party/blink/...".
        # Canonicalize the paths heuristically.
        if filepath is not None:
            index = filepath.find(self._blink_path_prefix)
            if index >= 0:
                filepath = filepath[index + 1:]

        self._filepath = filepath
        self._line_number = line_number
        self._position = position  # Position number in a file

    def __str__(self):
        text = '{}'.format(self._filepath or '<<unknown path>>')
        if self._line_number:
            text += ':{}'.format(self._line_number)
        return text

    @property
    def filepath(self):
        return self._filepath

    @property
    def line_number(self):
        return self._line_number

    @property
    def position_in_file(self):
        return self._position


class DebugInfo(object):
    """Provides information useful for debugging."""

    def __init__(self, location=None):
        assert location is None or isinstance(location, Location)
        # The first entry is the primary location, e.g. location of non-partial
        # interface.  The rest is secondary locations, e.g. location of partial
        # interfaces and mixins.
        self._locations = [location] if location else []

    @property
    def location(self):
        """
        Returns the primary location, i.e. location of the main definition.
        """
        return self._locations[0] if self._locations else Location()

    @property
    def all_locations(self):
        """
        Returns a list of locations of all related IDL definitions, including
        partial definitions and mixins.
        """
        return tuple(self._locations)

    def add_locations(self, locations):
        assert isinstance(locations, (list, tuple)) and all(
            isinstance(location, Location) for location in locations)
        self._locations.extend(locations)


class WithDebugInfo(object):
    """Implements |debug_info| as a readonly attribute."""

    def __init__(self, debug_info=None):
        if isinstance(debug_info, WithDebugInfo):
            debug_info = debug_info.debug_info
        elif debug_info is None:
            debug_info = DebugInfo()
        assert isinstance(debug_info, DebugInfo)

        self._debug_info = debug_info

    @property
    def debug_info(self):
        return self._debug_info


class WithOwner(object):
    """Implements |owner| as a readonly attribute."""

    def __init__(self, owner):
        assert isinstance(owner, object) and owner is not None
        self._owner = owner

    @property
    def owner(self):
        return self._owner


class WithOwnerMixin(object):
    """Implements |owner_mixin| as a readonly attribute."""

    def __init__(self, owner_mixin=None):
        if isinstance(owner_mixin, WithOwnerMixin):
            owner_mixin = owner_mixin._owner_mixin
        from .reference import RefById
        assert owner_mixin is None or isinstance(owner_mixin, RefById)

        self._owner_mixin = owner_mixin

    @property
    def owner_mixin(self):
        """
        Returns the interface mixin object where this construct was originally
        defined.
        """
        return self._owner_mixin.target_object if self._owner_mixin else None

    def set_owner_mixin(self, mixin):
        from .reference import RefById
        assert isinstance(mixin, RefById)
        assert self._owner_mixin is None
        self._owner_mixin = mixin