chromium/third_party/blink/renderer/bindings/scripts/bind_gen/dictionary.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 web_idl

from . import name_style
from .blink_v8_bridge import blink_class_name
from .blink_v8_bridge import blink_type_info
from .blink_v8_bridge import make_default_value_expr
from .blink_v8_bridge import native_value_tag
from .code_node import EmptyNode
from .code_node import FormatNode
from .code_node import ListNode
from .code_node import SequenceNode
from .code_node import SymbolNode
from .code_node import SymbolScopeNode
from .code_node import TextNode
from .code_node_cxx import CxxClassDefNode
from .code_node_cxx import CxxFuncDefNode
from .code_node_cxx import CxxLikelyIfNode
from .code_node_cxx import CxxNamespaceNode
from .code_node_cxx import CxxUnlikelyIfNode
from .codegen_accumulator import CodeGenAccumulator
from .codegen_context import CodeGenContext
from .codegen_expr import expr_from_exposure
from .codegen_format import format_template as _format
from .codegen_utils import collect_forward_decls_and_include_headers
from .codegen_utils import component_export
from .codegen_utils import component_export_header
from .codegen_utils import enclose_with_header_guard
from .codegen_utils import make_copyright_header
from .codegen_utils import make_forward_declarations
from .codegen_utils import make_header_include_directives
from .codegen_utils import write_code_node_to_file
from .mako_renderer import MakoRenderer
from .package_initializer import package_initializer
from .path_manager import PathManager
from .task_queue import TaskQueue


class _DictionaryMember(object):
    """
    _DictionaryMember represents the properties that the code generator
    directly needs while web_idl.DictionaryMember represents properties of IDL
    dictionary member independent from ECMAScript binding.  _DictionaryMember
    is specific to not only ECMAScript binding but also Blink implementation
    of IDL dictionary.
    """

    # Map from Blink member type to presence expression.
    _MEMBER_TYPE_TO_PRESENCE_EXPR = {
        "ScriptPromiseUntyped": "!{}.IsEmpty()",
        "ScriptValue": "!{}.IsEmpty()",
    }

    def __init__(self, dict_member):
        assert isinstance(dict_member, web_idl.DictionaryMember)

        self._identifier = dict_member.identifier
        self._base_name = (
            dict_member.code_generator_info.property_implemented_as
            or dict_member.identifier)

        self._api_has = name_style.api_func("has", self._base_name)
        self._api_get = name_style.api_func(self._base_name)
        self._api_get_or = name_style.api_func("get", self._base_name, "or")
        self._api_set = name_style.api_func("set", self._base_name)
        # C++ data member that shows the presence of the dictionary member.
        self._presence_var = name_style.member_var("has", self._base_name)
        # C++ data member that holds the value of the dictionary member.
        self._value_var = name_style.member_var("member", self._base_name)

        # Migration adapters
        self._api_has_non_null = name_style.api_func("has", self._base_name,
                                                     "non_null")
        self._api_get_non_null = name_style.api_func(self._base_name,
                                                     "non_null")

        self._idl_type = dict_member.idl_type
        self._type_info = blink_type_info(self._idl_type)
        self._is_required = dict_member.is_required
        if dict_member.default_value:
            self._default_expr = make_default_value_expr(
                self._idl_type, dict_member.default_value)
        else:
            self._default_expr = None

        self._exposure = dict_member.exposure
        self._extended_attributes = dict_member.extended_attributes

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

    @property
    def api_has(self):
        return self._api_has

    @property
    def api_get(self):
        return self._api_get

    @property
    def api_get_or(self):
        return self._api_get_or

    @property
    def api_set(self):
        return self._api_set

    @property
    def api_has_non_null(self):
        return self._api_has_non_null

    @property
    def api_get_non_null(self):
        return self._api_get_non_null

    @property
    def presence_var(self):
        return self._presence_var

    @property
    def value_var(self):
        return self._value_var

    @property
    def idl_type(self):
        return self._idl_type

    @property
    def type_info(self):
        return self._type_info

    @property
    def is_required(self):
        return self._is_required

    @property
    def presence_expr(self):
        if self.is_always_present:
            return "true"
        expr = self._MEMBER_TYPE_TO_PRESENCE_EXPR.get(self.type_info.member_t)
        if expr:
            return expr.format(self.value_var)
        return self.presence_var

    @property
    def does_use_presence_var(self):
        return not (
            self.is_always_present
            or self.type_info.member_t in self._MEMBER_TYPE_TO_PRESENCE_EXPR)

    @property
    def is_always_present(self):
        return bool(self.is_required or self._default_expr)

    @property
    def initializer_expr(self):
        return (self._default_expr and self._default_expr.initializer_expr
                or None)

    @property
    def initializer_deps(self):
        return (self._default_expr and self._default_expr.initializer_deps
                or None)

    @property
    def initializer_on_constructor(self):
        # In order to avoid cyclic header inclusion of IDL dictionaries, put
        # the initializer in *.cc if the type is a dictionary.
        if (self.initializer_expr and (self.initializer_deps  #
                                       or self.idl_type.unwrap().is_dictionary
                                       or self.idl_type.unwrap().is_union)):
            return self.initializer_expr
        return None

    @property
    def initializer_on_member_decl(self):
        # In order to avoid cyclic header inclusion of IDL dictionaries, put
        # the initializer in *.cc if the type is a dictionary.
        if (self.initializer_expr
                and not (self.initializer_deps
                         or self.idl_type.unwrap().is_dictionary
                         or self.idl_type.unwrap().is_union)):
            return self.initializer_expr
        idl_type = self.idl_type.unwrap(typedef=True)
        if idl_type.is_enumeration:
            # Since the IDL enumeration class is not default constructible,
            # construct the IDL enumeration with 0th enum value.  Note that
            # this is necessary only for compilation, and the value must never
            # be used due to the guard by `api_has` (`presence_expr`).
            return "static_cast<{}::Enum>(0)".format(self.type_info.value_t)
        return None

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

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


def bind_local_vars(code_node, cg_context):
    assert isinstance(code_node, SymbolScopeNode)
    assert isinstance(cg_context, CodeGenContext)

    S = SymbolNode

    local_vars = []

    local_vars.extend([
        S("class_like_name", ("const char* const ${class_like_name} = "
                              "\"${class_like.identifier}\";")),
        S("current_context", ("v8::Local<v8::Context> ${current_context} = "
                              "${isolate}->GetCurrentContext();")),
        S("is_cross_origin_isolated",
          ("const bool ${is_cross_origin_isolated} = "
           "${execution_context}"
           "->CrossOriginIsolatedCapabilityOrDisabledWebSecurity();")),
        S("is_in_injection_mitigated_context",
          ("const bool ${is_in_injection_mitigated_context} = "
           "${execution_context}->IsInjectionMitigatedContext();")),
        S("is_in_isolated_context",
          ("const bool ${is_in_isolated_context} = "
           "${execution_context}->IsIsolatedContext();")),
        S("is_in_secure_context",
          ("const bool ${is_in_secure_context} = "
           "${execution_context}->IsSecureContext();")),
        S("v8_own_member_names", ("const auto& ${v8_own_member_names} = "
                                  "GetV8OwnMemberNames(${isolate});")),
    ])

    # execution_context
    node = S("execution_context",
             ("ExecutionContext* ${execution_context} = "
              "ExecutionContext::From(${current_context});"))
    node.accumulate(
        CodeGenAccumulator.require_include_headers([
            "third_party/blink/renderer/core/execution_context/execution_context.h"
        ]))
    local_vars.append(node)

    code_node.register_code_symbols(local_vars)


def _constructor_needs_v8_isolate(dictionary):
    assert isinstance(dictionary, web_idl.Dictionary)

    return any(
        make_default_value_expr(member.idl_type,
                                member.default_value).initializer_deps
        for member in dictionary.members if member.default_value)


def make_factory_methods(cg_context):
    assert isinstance(cg_context, CodeGenContext)

    T = TextNode

    decls = ListNode()
    defs = ListNode()

    dictionary = cg_context.dictionary

    if not _constructor_needs_v8_isolate(dictionary):
        func_def = CxxFuncDefNode(name="Create",
                                  arg_decls=[],
                                  return_type="${class_name}*",
                                  static=True)
        decls.append(func_def)
        func_def.set_base_template_vars(cg_context.template_bindings())
        func_def.body.append(
            TextNode("return MakeGarbageCollected<${class_name}>();"))

    func_def = CxxFuncDefNode(name="Create",
                              arg_decls=["v8::Isolate* isolate"],
                              return_type="${class_name}*",
                              static=True)
    decls.append(func_def)
    func_def.set_base_template_vars(cg_context.template_bindings())
    func_def.body.add_template_vars({"isolate": "isolate"})
    func_def.body.append(
        TextNode("return MakeGarbageCollected<${class_name}>(${isolate});"))

    func_def = CxxFuncDefNode(name="Create",
                              arg_decls=[
                                  "v8::Isolate* isolate",
                                  "v8::Local<v8::Value> v8_value",
                                  "ExceptionState& exception_state",
                              ],
                              return_type="${class_name}*",
                              class_name=cg_context.class_name)
    decls.append(func_def.make_decl(static=True))

    defs.append(func_def)
    func_def.set_base_template_vars(cg_context.template_bindings())
    body = func_def.body
    body.add_template_vars({
        "isolate": "isolate",
        "v8_value": "v8_value",
        "exception_state": "exception_state",
    })
    bind_local_vars(body, cg_context)

    body.append(
        T("${class_name}* dictionary = "
          "MakeGarbageCollected<${class_name}>(${isolate});"))
    if not dictionary.has_required_member:
        body.append(
            CxxLikelyIfNode(cond="${v8_value}->IsNullOrUndefined()",
                            attribute=None,
                            body=T("return dictionary;")))
    # [PermissiveDictionaryConversion]
    if "PermissiveDictionaryConversion" in dictionary.extended_attributes:
        body.append(
            CxxUnlikelyIfNode(cond="!${v8_value}->IsObject()",
                              attribute=None,
                              body=[
                                  T("// [PermissiveDictionaryConversion]"),
                                  T("return dictionary;"),
                              ]))
    else:
        body.append(
            CxxUnlikelyIfNode(cond="!${v8_value}->IsObject()",
                              attribute=None,
                              body=[
                                  T("${exception_state}.ThrowTypeError("
                                    "ExceptionMessages::ValueNotOfType("
                                    "${class_like_name}));"),
                                  T("return nullptr;"),
                              ]))
    body.extend([
        T("dictionary->FillMembersFromV8Object("
          "${isolate}, ${v8_value}.As<v8::Object>(), ${exception_state});"),
        CxxUnlikelyIfNode(cond="${exception_state}.HadException()",
                          attribute="[[unlikely]]",
                          body=T("return nullptr;")),
        T("return dictionary;"),
    ])

    return decls, defs


def make_constructors(cg_context):
    assert isinstance(cg_context, CodeGenContext)

    decls = ListNode()
    defs = ListNode()

    member_initializer_list = [
        "{}({})".format(member.value_var, member.initializer_on_constructor)
        for member in cg_context.dictionary_own_members
        if member.initializer_on_constructor
    ]

    if not _constructor_needs_v8_isolate(cg_context.dictionary):
        func_def = CxxFuncDefNode(
            name=cg_context.class_name,
            arg_decls=[],
            return_type="",
            class_name=cg_context.class_name,
            member_initializer_list=member_initializer_list)
        func_def.set_base_template_vars(cg_context.template_bindings())
        decls.append(func_def.make_decl(explicit=True))
        defs.append(func_def)
        defs.append(EmptyNode())

    if cg_context.dictionary.inherited:
        member_initializer_list = ["${base_class_name}(isolate)"
                                   ] + member_initializer_list
    func_def = CxxFuncDefNode(name=cg_context.class_name,
                              arg_decls=["v8::Isolate* isolate"],
                              return_type="",
                              class_name=cg_context.class_name,
                              member_initializer_list=member_initializer_list)
    func_def.set_base_template_vars(cg_context.template_bindings())
    func_def.add_template_vars({"isolate": "isolate"})
    decls.append(func_def.make_decl(explicit=True))
    defs.append(func_def)

    return decls, defs


def make_accessor_functions(cg_context):
    assert isinstance(cg_context, CodeGenContext)

    T = TextNode
    F = FormatNode

    def make_check_assigned_value(member):
        idl_type = member.idl_type.unwrap(typedef=True)
        if idl_type.is_object:
            return F("DCHECK({}.IsObject());", member.value_var)
        if (member.type_info.is_gc_type and not idl_type.is_nullable):
            return F("DCHECK({});", member.value_var)
        return None

    def make_api_has(member):
        func_def = CxxFuncDefNode(name=member.api_has,
                                  arg_decls=[],
                                  return_type="bool",
                                  const=True)
        func_def.set_base_template_vars(cg_context.template_bindings())
        func_def.body.append(F("return {};", member.presence_expr))
        return func_def, None

    def make_api_get(member):
        func_def = CxxFuncDefNode(name=member.api_get,
                                  arg_decls=[],
                                  return_type=member.type_info.member_ref_t,
                                  const=True)
        func_def.set_base_template_vars(cg_context.template_bindings())
        if not member.is_always_present:
            func_def.body.append(F("DCHECK({}());", member.api_has))
        func_def.body.append(
            F("return {};",
              member.type_info.member_var_to_ref_expr(member.value_var)))
        return func_def, None

    def make_api_get_or(member):
        func_def = CxxFuncDefNode(name=member.api_get_or,
                                  arg_decls=[
                                      "{} fallback_value".format(
                                          member.type_info.member_ref_t)
                                  ],
                                  return_type=member.type_info.value_t,
                                  const=True)
        func_def.set_base_template_vars(cg_context.template_bindings())
        func_def.body.extend([
            CxxUnlikelyIfNode(cond="!{}()".format(member.api_has),
                              attribute=None,
                              body=T("return fallback_value;")),
            F("return {};",
              member.type_info.member_var_to_ref_expr(member.value_var)),
        ])
        return func_def, None

    def make_api_get_or_copy_and_move(member):
        copy_func_def = CxxFuncDefNode(name=member.api_get_or,
                                       arg_decls=[
                                           "{} fallback_value".format(
                                               member.type_info.member_ref_t)
                                       ],
                                       return_type=member.type_info.value_t,
                                       class_name=cg_context.class_name,
                                       const=True)
        copy_func_def.set_base_template_vars(cg_context.template_bindings())
        copy_func_def.body.extend([
            CxxUnlikelyIfNode(cond="!{}()".format(member.api_has),
                              attribute=None,
                              body=T("return fallback_value;")),
            F("return {};",
              member.type_info.member_var_to_ref_expr(member.value_var)),
        ])

        move_func_def = CxxFuncDefNode(
            name=member.api_get_or,
            arg_decls=["{}&& fallback_value".format(member.type_info.value_t)],
            return_type=member.type_info.value_t,
            class_name=cg_context.class_name,
            const=True)
        move_func_def.set_base_template_vars(cg_context.template_bindings())
        move_func_def.body.extend([
            CxxUnlikelyIfNode(cond="!{}()".format(member.api_has),
                              attribute=None,
                              body=T("return std::move(fallback_value);")),
            F("return {};",
              member.type_info.member_var_to_ref_expr(member.value_var)),
        ])

        decls = ListNode(
            [copy_func_def.make_decl(),
             move_func_def.make_decl()])
        defs = ListNode([copy_func_def, EmptyNode(), move_func_def])
        return decls, defs

    def make_api_get_or_string(member):
        # getMemberOr(const char*) in addition to
        # getMemberOr(const String&) to avoid creation of a temporary String
        # object.
        if not member.idl_type.unwrap(typedef=True).is_string:
            return None, None
        func_def = CxxFuncDefNode(name=member.api_get_or,
                                  arg_decls=["const char* fallback_value"],
                                  return_type=member.type_info.value_t,
                                  const=True)
        func_def.set_base_template_vars(cg_context.template_bindings())
        func_def.body.extend([
            CxxUnlikelyIfNode(cond="!{}()".format(member.api_has),
                              attribute=None,
                              body=T("return fallback_value;")),
            F("return {};",
              member.type_info.member_var_to_ref_expr(member.value_var)),
        ])
        return func_def, None

    def make_api_set(member, type_info=None):
        if type_info is None:
            type_info = member.type_info
        func_def = CxxFuncDefNode(
            name=member.api_set,
            arg_decls=["{} value".format(type_info.member_ref_t)],
            return_type="void")
        func_def.set_base_template_vars(cg_context.template_bindings())
        func_def.body.append(F("{} = value;", member.value_var))
        if member.does_use_presence_var:
            func_def.body.append(F("{} = true;", member.presence_var))
        func_def.body.append(make_check_assigned_value(member))
        return func_def, None

    def make_api_set_copy_and_move(member, type_info=None):
        if type_info is None:
            type_info = member.type_info
        copy_func_def = CxxFuncDefNode(
            name=member.api_set,
            arg_decls=["{} value".format(type_info.member_ref_t)],
            return_type="void",
            class_name=cg_context.class_name)
        copy_func_def.set_base_template_vars(cg_context.template_bindings())
        copy_func_def.body.append(F("{} = value;", member.value_var))
        if member.does_use_presence_var:
            copy_func_def.body.append(F("{} = true;", member.presence_var))
        copy_func_def.body.append(make_check_assigned_value(member))

        move_func_def = CxxFuncDefNode(
            name=member.api_set,
            arg_decls=["{}&& value".format(type_info.value_t)],
            return_type="void",
            class_name=cg_context.class_name)
        move_func_def.set_base_template_vars(cg_context.template_bindings())
        move_func_def.body.append(F("{} = std::move(value);",
                                    member.value_var))
        if member.does_use_presence_var:
            move_func_def.body.append(F("{} = true;", member.presence_var))
        move_func_def.body.append(make_check_assigned_value(member))

        decls = ListNode(
            [copy_func_def.make_decl(),
             move_func_def.make_decl()])
        defs = ListNode([copy_func_def, EmptyNode(), move_func_def])
        return decls, defs

    def make_api_set_non_nullable(member):
        # setMember(InnerType) in addition to
        # setMember(std::optional<InnerType>) for convenience.
        if not (member.idl_type.does_include_nullable_type
                and not member.type_info.has_null_value):
            return None, None
        return make_api_set(member, blink_type_info(member.idl_type.unwrap()))

    def make_api_set_copy_and_move_non_nullable(member):
        # setMember(InnerType) in addition to
        # setMember(std::optional<InnerType>) for convenience.
        if not (member.idl_type.does_include_nullable_type
                and not member.type_info.has_null_value):
            return None, None
        return make_api_set_copy_and_move(
            member, blink_type_info(member.idl_type.unwrap()))

    def make_api_set_enum(member):
        # setMember(V8Enum::Enum) in addition to
        # setMember(V8Enum) for convenience.
        if not member.idl_type.unwrap().is_enumeration:
            return None, None
        type_info = blink_type_info(member.idl_type.unwrap())
        func_def = CxxFuncDefNode(
            name=member.api_set,
            arg_decls=["{}::Enum value".format(type_info.value_t)],
            return_type="void")
        func_def.set_base_template_vars(cg_context.template_bindings())
        func_def.body.append(
            F("{} = {}(value);", member.value_var, type_info.value_t))
        if member.does_use_presence_var:
            func_def.body.append(F("{} = true;", member.presence_var))
        func_def.body.append(make_check_assigned_value(member))
        return func_def, None

    decls = ListNode()
    defs = ListNode()

    def add(func_decl, func_def):
        decls.append(func_decl)
        defs.append(func_def)
        defs.append(EmptyNode())

    for member in cg_context.dictionary_own_members:
        # Predicate
        add(*make_api_has(member))

        # Getter
        add(*make_api_get(member))
        if member.is_always_present:
            pass
        elif member.type_info.is_move_effective:
            add(*make_api_get_or_copy_and_move(member))
        else:
            add(*make_api_get_or(member))
            add(*make_api_get_or_string(member))

        # Setter
        if member.type_info.is_move_effective:
            add(*make_api_set_copy_and_move(member))
            add(*make_api_set_copy_and_move_non_nullable(member))
        else:
            add(*make_api_set(member))
            add(*make_api_set_non_nullable(member))
            add(*make_api_set_enum(member))

        decls.append(EmptyNode())

    return decls, defs


def make_backward_compatible_accessors(cg_context):
    assert isinstance(cg_context, CodeGenContext)

    # TODO(crbug.com/1070871): Remove the accessors introduced just to be
    # backward compatible.

    F = FormatNode

    decls = ListNode()

    def make_api_has_non_null(member):
        func_def = CxxFuncDefNode(name=member.api_has_non_null,
                                  arg_decls=[],
                                  return_type="bool",
                                  const=True)
        func_def.set_base_template_vars(cg_context.template_bindings())
        func_def.body.append(
            F("return {}() && {}().has_value();", member.api_has,
              member.api_get))
        return func_def

    def make_api_get_non_null(member):
        func_def = CxxFuncDefNode(name=member.api_get_non_null,
                                  arg_decls=[],
                                  return_type=blink_type_info(
                                      member.idl_type.unwrap()).member_ref_t,
                                  const=True)
        func_def.set_base_template_vars(cg_context.template_bindings())
        func_def.body.extend([
            F("DCHECK({}());", member.api_has_non_null),
            F("return {}().value();", member.api_get),
        ])
        return func_def

    def make_api_set_enum_string(member):
        type_info = blink_type_info(member.idl_type.unwrap())
        func_def = CxxFuncDefNode(name=member.api_set,
                                  arg_decls=["const String& value"],
                                  return_type="void")
        func_def.set_base_template_vars(cg_context.template_bindings())
        func_def.body.append(
            F("{} = {}::Create(value).value();", member.value_var,
              type_info.value_t))
        if member.does_use_presence_var:
            func_def.body.append(F("{} = true;", member.presence_var))
        return func_def

    for member in cg_context.dictionary_own_members:
        if member.idl_type.unwrap().is_enumeration:
            decls.append(make_api_set_enum_string(member))

        if (not member.idl_type.unwrap(nullable=False).is_nullable
                or member.type_info.has_null_value):
            continue
        # The Blink type is std::optional<T>.
        decls.append(make_api_has_non_null(member))
        decls.append(make_api_get_non_null(member))

    if decls:
        decls.insert(0, TextNode("// Obsolete accessor functions"))

    return decls, None


def make_trace_function(cg_context):
    assert isinstance(cg_context, CodeGenContext)

    func_def = CxxFuncDefNode(name="Trace",
                              arg_decls=["Visitor* visitor"],
                              return_type="void",
                              class_name=cg_context.class_name,
                              const=True)
    func_def.set_base_template_vars(cg_context.template_bindings())
    body = func_def.body

    for member in cg_context.dictionary_own_members:
        body.append(
            TextNode("TraceIfNeeded<{}>::Trace(visitor, {});".format(
                member.type_info.member_t, member.value_var)))
    body.append(TextNode("${base_class_name}::Trace(visitor);"))

    return func_def.make_decl(override=True), func_def


def make_property_count_const(cg_context):
    assert isinstance(cg_context, CodeGenContext)

    T = TextNode

    base_property_count = ("${base_class_name}::kTotalPropertyCount"
                           if cg_context.dictionary.inherited else "0")

    return ListNode([
        T("static constexpr size_t kBasePropertyCount = {};".format(
            base_property_count)),
        T("static constexpr size_t kOwnPropertyCount = {};".format(
            len(cg_context.dictionary.own_members))),
        T("static constexpr size_t kTotalPropertyCount "
          "= kBasePropertyCount + kOwnPropertyCount;")
    ])


def make_properties_array(cg_context):
    assert isinstance(cg_context, CodeGenContext)

    T = TextNode

    if not cg_context.dictionary.own_members:
        return ListNode({})

    properties = ListNode([
        T("\"{}\",".format(member.identifier))
        for member in cg_context.dictionary.own_members
    ])
    return ListNode([
        T("const std::string_view kOwnPropertyNames[] = {"),
        properties,
        T("};"),
        EmptyNode(),
    ])


def make_fill_template_properties_function(cg_context):
    assert isinstance(cg_context, CodeGenContext)

    T = TextNode

    func_def = CxxFuncDefNode(name="FillTemplateProperties",
                              arg_decls=[
                                  "WTF::Vector<std::string_view>& properties",
                              ],
                              return_type="void",
                              class_name=cg_context.class_name,
                              const=True)

    func_def.set_base_template_vars(cg_context.template_bindings())
    body = func_def.body

    if cg_context.dictionary.inherited:
        body.extend([
            T("${base_class_name}::FillTemplateProperties(properties);"),
            T("DCHECK_EQ(properties.size(), kBasePropertyCount);"),
            EmptyNode(),
        ])

    if cg_context.dictionary.own_members:
        body.extend([
            T("static_assert(std::size(kOwnPropertyNames) "
              "== kOwnPropertyCount);"),
            T("properties.AppendRange(std::cbegin(kOwnPropertyNames),"
              " std::cend(kOwnPropertyNames));"),
            T("DCHECK_EQ(properties.size(), kTotalPropertyCount);")
        ])

    return func_def.make_decl(override=True), func_def


def make_fill_values_function(cg_context):
    assert isinstance(cg_context, CodeGenContext)

    S = SymbolNode
    T = TextNode
    F = FormatNode

    func_def = CxxFuncDefNode(
        name="FillValues",
        arg_decls=[
            "ScriptState* script_state",
            "v8::Local<v8::DictionaryTemplate> dict_template",
        ],
        return_type="v8::Local<v8::Object>",
        class_name=cg_context.class_name,
        const=True)

    func_def.set_base_template_vars(cg_context.template_bindings())
    if cg_context.dictionary.members:
        func_def.body.extend([
            T("v8::MaybeLocal<v8::Value> values[kTotalPropertyCount];"),
            T("FillValuesImpl(script_state, values);"),
            T("return dict_template->NewInstance("
              "script_state->GetContext(), values);")
        ])
    else:
        func_def.body.extend([
            T("return dict_template->NewInstance("
              "script_state->GetContext(), {});")
        ])

    return func_def.make_decl(override=True), func_def


def make_fill_values_impl_function(cg_context):
    assert isinstance(cg_context, CodeGenContext)

    S = SymbolNode
    T = TextNode
    F = FormatNode

    func_def = CxxFuncDefNode(
        name="FillValuesImpl",
        arg_decls=[
            "ScriptState* script_state",
            "base::span<v8::MaybeLocal<v8::Value>> values"
        ],
        return_type="void",
        class_name=cg_context.class_name,
        const=True)

    func_def.set_base_template_vars(cg_context.template_bindings())
    body = func_def.body
    body.add_template_vars({"script_state": "script_state"})
    bind_local_vars(body, cg_context)

    body.register_code_symbols([
        S("isolate",
          "v8::Isolate* ${isolate} = ${script_state}->GetIsolate();"),
    ])

    if cg_context.dictionary.inherited:
        body.extend([
            F("${base_class_name}::FillValuesImpl("
              "script_state, values.first(kBasePropertyCount));"),
            T("values = values.subspan(kBasePropertyCount);"),
            EmptyNode()
        ])

    body.append(T("CHECK_EQ(kOwnPropertyCount, values.size());"))
    for index, member in enumerate(cg_context.dictionary_own_members):
        convert_property = F(
            "values[{index}] = "
            "ToV8Traits<{native_value_tag}>::ToV8(script_state, {blink_value});",
            native_value_tag=native_value_tag(member.idl_type),
            blink_value=member.type_info.member_var_to_ref_expr(
                member.value_var),
            index=index)

        node = CxxLikelyIfNode(cond="{}()".format(member.api_has),
                               attribute=None,
                               body=[
                                   convert_property,
                                   F("DCHECK(!values[{index}].IsEmpty());",
                                     index=index)
                               ])

        exposure_conditional = expr_from_exposure(member.exposure)
        if not exposure_conditional.is_always_true:
            node = CxxLikelyIfNode(cond=exposure_conditional,
                                   attribute=None,
                                   body=node)
            node.accumulate(
                CodeGenAccumulator.require_include_headers([
                    "third_party/blink/renderer/platform/runtime_enabled_features.h"
                ]))

        body.append(node)

    return func_def.make_decl(), func_def


def make_template_key_function(cg_context):
    assert isinstance(cg_context, CodeGenContext)

    T = TextNode

    func_def = CxxFuncDefNode(name="TemplateKey",
                              arg_decls=[],
                              return_type="const void*",
                              class_name=cg_context.class_name,
                              const=True)

    func_def.body.extend(
        [T("static const void *s_key = &s_key;"),
         T("return s_key;")])

    return func_def.make_decl(override=True), func_def


def make_v8_to_blink_function(cg_context):
    assert isinstance(cg_context, CodeGenContext)

    S = SymbolNode
    T = TextNode
    F = FormatNode

    func_def = CxxFuncDefNode(name="FillMembersFromV8Object",
                              arg_decls=[
                                  "v8::Isolate* isolate",
                                  "v8::Local<v8::Object> v8_dictionary",
                                  "ExceptionState& exception_state",
                              ],
                              return_type="void",
                              class_name=cg_context.class_name)
    func_def.set_base_template_vars(cg_context.template_bindings())
    body = func_def.body
    body.add_template_vars({
        "isolate": "isolate",
        "v8_dictionary": "v8_dictionary",
        "exception_state": "exception_state",
    })
    body.register_code_symbols([
        S("exception_context_scope",
          ("ExceptionState::ContextScope ${exception_context_scope}("
           "ExceptionContext("
           "v8::ExceptionContext::kAttributeGet, "
           "${class_like_name}, \"\"), "
           "${exception_state});")),
        S("fallback_presence_var", "bool ${fallback_presence_var};"),
        S("has_deprecated", "bool ${has_deprecated};"),
        S("is_optional", "constexpr bool ${is_optional} = false;"),
        S("is_required", "constexpr bool ${is_required} = true;"),
    ])
    bind_local_vars(body, cg_context)

    body.append(
        T("TryRethrowScope rethrow_scope(${isolate}, ${exception_state});"))
    if cg_context.dictionary.inherited:
        body.extend([
            T("${base_class_name}::FillMembersFromV8Object"
              "(${isolate}, ${v8_dictionary}, ${exception_state});"),
            CxxUnlikelyIfNode(cond="${exception_state}.HadException()",
                              attribute="[[unlikely]]",
                              body=T("return;")),
            EmptyNode(),
        ])

    for index, member in enumerate(cg_context.dictionary_own_members):
        cond = _format(
            "!bindings::GetDictionaryMemberFromV8Object"
            "<{native_value_tag}, {is_required}>("
            "${isolate}, ${current_context}, "
            "${v8_dictionary}, "
            "${v8_own_member_names}[{index}].Get(${isolate}), "
            "{presence_var}, {value_var}, "
            "${exception_state})",
            native_value_tag=native_value_tag(member.idl_type),
            is_required=("${is_required}"
                         if member.is_required else "${is_optional}"),
            index=index,
            presence_var=(member.presence_var if member.does_use_presence_var
                          else "${fallback_presence_var}"),
            value_var=member.value_var)
        node = SequenceNode([
            F(("${exception_context_scope}"
               ".ChangePropertyNameAsOptimizationHack(\"{member_name}\");"),
              member_name=member.identifier),
            CxxUnlikelyIfNode(cond=cond, attribute=None, body=T("return;")),
        ])

        # [DeprecateAs]
        deprecate_as = member.extended_attributes.value_of("DeprecateAs")
        if deprecate_as:
            node.extend([
                T("// [DeprecateAs]"),
                CxxUnlikelyIfNode(cond=_format(
                    "!${v8_dictionary}->Has("
                    "${current_context}, "
                    "${v8_own_member_names}[{index}].Get(${isolate}))"
                    ".To(&${has_deprecated})",
                    index=index),
                                  attribute=None,
                                  body=T("return;")),
                CxxUnlikelyIfNode(cond="${has_deprecated}",
                                  attribute=None,
                                  body=F(("Deprecation::CountDeprecation("
                                          "${execution_context}, "
                                          "WebFeature::k{deprecate_as});"),
                                         deprecate_as=deprecate_as)),
            ])
            node.accumulate(
                CodeGenAccumulator.require_include_headers([
                    "third_party/blink/renderer/core/frame/deprecation/deprecation.h"
                ]))

        conditional = expr_from_exposure(member.exposure)
        if not conditional.is_always_true:
            node = CxxLikelyIfNode(cond=conditional, attribute=None, body=node)
            node.accumulate(
                CodeGenAccumulator.require_include_headers([
                    "third_party/blink/renderer/platform/runtime_enabled_features.h"
                ]))

        body.append(node)

    return func_def.make_decl(), func_def


def make_v8_own_member_names_function(cg_context):
    assert isinstance(cg_context, CodeGenContext)

    func_def = CxxFuncDefNode(
        name="GetV8OwnMemberNames",
        arg_decls=["v8::Isolate* isolate"],
        return_type="const base::span<const v8::Eternal<v8::Name>>",
        class_name=cg_context.class_name)
    func_def.set_base_template_vars(cg_context.template_bindings())
    body = func_def.body
    func_def.body.add_template_vars({"isolate": "isolate"})
    func_decl = func_def.make_decl(static=True)

    if not cg_context.dictionary.own_members:
        body.append(TextNode("return {};"))
        return func_decl, func_def

    body.extend([
        TextNode("return V8PerIsolateData::From(${isolate})"
                 "->FindOrCreateEternalNameCache"
                 "(kOwnPropertyNames, kOwnPropertyNames);"),
    ])

    return func_decl, func_def


def make_member_vars_def(cg_context):
    assert isinstance(cg_context, CodeGenContext)

    F = FormatNode

    presence_vars = ListNode([
        F("bool {} = false;", member.presence_var)
        for member in cg_context.dictionary_own_members
        if member.does_use_presence_var
    ])

    value_vars = ListNode([
        F("{} {}{};", member.type_info.member_t, member.value_var,
          ("{{{}}}".format(member.initializer_on_member_decl)
           if member.initializer_on_member_decl else ""))
        for member in cg_context.dictionary_own_members
    ])

    return ListNode([
        presence_vars,
        EmptyNode(),
        value_vars,
    ])


def generate_dictionary(dictionary_identifier):
    assert isinstance(dictionary_identifier, web_idl.Identifier)

    web_idl_database = package_initializer().web_idl_database()
    dictionary = web_idl_database.find(dictionary_identifier)

    path_manager = PathManager(dictionary)
    assert path_manager.api_component == path_manager.impl_component, (
        "Partial dictionaries across components are not supported.")
    api_component = path_manager.api_component
    for_testing = dictionary.code_generator_info.for_testing

    # Class names
    class_name = blink_class_name(dictionary)
    if dictionary.inherited:
        base_class_name = blink_class_name(dictionary.inherited)
    else:
        base_class_name = "bindings::DictionaryBase"

    cg_context = CodeGenContext(dictionary=dictionary,
                                dictionary_own_members=tuple(
                                    map(_DictionaryMember,
                                        dictionary.own_members)),
                                class_name=class_name,
                                base_class_name=base_class_name)

    # Filepaths
    header_path = path_manager.api_path(ext="h")
    source_path = path_manager.api_path(ext="cc")

    # Root nodes
    header_node = ListNode(tail="\n")
    header_node.set_accumulator(CodeGenAccumulator())
    header_node.set_renderer(MakoRenderer())
    source_node = ListNode(tail="\n")
    source_node.set_accumulator(CodeGenAccumulator())
    source_node.set_renderer(MakoRenderer())

    # Namespaces
    header_blink_ns = CxxNamespaceNode(name_style.namespace("blink"))
    source_blink_ns = CxxNamespaceNode(name_style.namespace("blink"))
    source_anon_ns = CxxNamespaceNode("")

    # Class definition
    class_def = CxxClassDefNode(cg_context.class_name,
                                base_class_names=[cg_context.base_class_name],
                                export=component_export(
                                    api_component, for_testing))
    class_def.set_base_template_vars(cg_context.template_bindings())

    # Implementation parts
    factory_decls, factory_defs = make_factory_methods(cg_context)
    ctor_decls, ctor_defs = make_constructors(cg_context)
    accessor_decls, accessor_defs = make_accessor_functions(cg_context)
    backward_compatible_accessor_decls, backward_compatible_accessor_defs = (
        make_backward_compatible_accessors(cg_context))
    trace_func_decls, trace_func_defs = make_trace_function(cg_context)

    # blink_to_v8_decls, blink_to_v8_defs = make_blink_to_v8_function(cg_context)

    template_key_decl, template_key_def = make_template_key_function(
        cg_context)
    fill_template_properties_decl, fill_template_properties_def = (
        make_fill_template_properties_function(cg_context))
    fill_values_decl, fill_values_def = make_fill_values_function(cg_context)
    fill_values_impl_decl, fill_values_impl_def = (
        make_fill_values_impl_function(cg_context))

    v8_to_blink_decls, v8_to_blink_defs = make_v8_to_blink_function(cg_context)
    v8_names_decls, v8_names_defs = make_v8_own_member_names_function(
        cg_context)
    member_vars_def = make_member_vars_def(cg_context)

    # Header part (copyright, include directives, and forward declarations)
    header_node.extend([
        make_copyright_header(),
        EmptyNode(),
        enclose_with_header_guard(
            ListNode([
                make_header_include_directives(header_node.accumulator),
                EmptyNode(),
                header_blink_ns,
            ]), name_style.header_guard(header_path)),
    ])
    header_blink_ns.body.extend([
        make_forward_declarations(header_node.accumulator),
        EmptyNode(),
    ])
    source_node.extend([
        make_copyright_header(),
        EmptyNode(),
        TextNode("#include \"{}\"".format(header_path)),
        EmptyNode(),
        make_header_include_directives(source_node.accumulator),
        EmptyNode(),
        source_blink_ns,
    ])
    source_blink_ns.body.extend([
        make_forward_declarations(source_node.accumulator),
        EmptyNode(),
        source_anon_ns,
        EmptyNode(),
    ])

    # Assemble the parts.
    header_node.accumulator.add_class_decls(["ExceptionState"])
    header_node.accumulator.add_include_headers([
        (PathManager(dictionary.inherited).api_path(
            ext="h") if dictionary.inherited else
         "third_party/blink/renderer/platform/bindings/dictionary_base.h"),
        "base/containers/span.h",
        component_export_header(api_component, for_testing),
    ])
    source_node.accumulator.add_include_headers([
        "third_party/blink/renderer/bindings/core/v8/generated_code_helper.h",
        "third_party/blink/renderer/bindings/core/v8/native_value_traits_impl.h",
        "third_party/blink/renderer/bindings/core/v8/to_v8_traits.h",
        "third_party/blink/renderer/platform/bindings/exception_messages.h",
        "third_party/blink/renderer/platform/bindings/exception_state.h",
        "third_party/blink/renderer/platform/bindings/v8_per_isolate_data.h",
    ])
    (
        header_forward_decls,
        header_include_headers,
        header_stdcpp_include_headers,
        source_forward_decls,
        source_include_headers,
    ) = collect_forward_decls_and_include_headers(
        list(map(lambda member: member.idl_type, dictionary.own_members)))
    header_node.accumulator.add_class_decls(header_forward_decls)
    header_node.accumulator.add_include_headers(header_include_headers)
    header_node.accumulator.add_stdcpp_include_headers(
        header_stdcpp_include_headers)
    source_node.accumulator.add_class_decls(source_forward_decls)
    source_node.accumulator.add_include_headers(source_include_headers)

    header_blink_ns.body.append(class_def)
    header_blink_ns.body.append(EmptyNode())

    class_def.public_section.append(factory_decls)
    class_def.public_section.append(EmptyNode())
    source_blink_ns.body.append(factory_defs)
    source_blink_ns.body.append(EmptyNode())

    class_def.public_section.append(ctor_decls)
    class_def.public_section.append(EmptyNode())
    source_blink_ns.body.append(ctor_defs)
    source_blink_ns.body.append(EmptyNode())

    class_def.public_section.append(accessor_decls)
    class_def.public_section.append(EmptyNode())
    source_blink_ns.body.append(accessor_defs)
    source_blink_ns.body.append(EmptyNode())

    class_def.public_section.append(backward_compatible_accessor_decls)
    class_def.public_section.append(EmptyNode())
    source_blink_ns.body.append(backward_compatible_accessor_defs)
    source_blink_ns.body.append(EmptyNode())

    class_def.public_section.append(trace_func_decls)
    class_def.public_section.append(EmptyNode())
    source_blink_ns.body.append(trace_func_defs)
    source_blink_ns.body.append(EmptyNode())

    class_def.protected_section.append(make_property_count_const(cg_context))

    source_anon_ns.body.append(make_properties_array(cg_context))

    class_def.protected_section.append(fill_template_properties_decl)
    class_def.protected_section.append(fill_values_impl_decl)
    class_def.protected_section.append(EmptyNode())
    class_def.private_section.append(template_key_decl)
    class_def.private_section.append(fill_values_decl)
    class_def.protected_section.append(EmptyNode())
    source_blink_ns.body.append(fill_template_properties_def)
    source_blink_ns.body.append(EmptyNode())
    source_blink_ns.body.append(fill_values_impl_def)
    source_blink_ns.body.append(EmptyNode())
    source_blink_ns.body.append(template_key_def)
    source_blink_ns.body.append(EmptyNode())
    source_blink_ns.body.append(fill_values_def)
    source_blink_ns.body.append(EmptyNode())

    class_def.protected_section.append(v8_to_blink_decls)
    class_def.protected_section.append(EmptyNode())
    source_blink_ns.body.append(v8_to_blink_defs)
    source_blink_ns.body.append(EmptyNode())

    class_def.private_section.append(v8_names_decls)
    class_def.private_section.append(EmptyNode())
    source_blink_ns.body.append(v8_names_defs)
    source_blink_ns.body.append(EmptyNode())

    class_def.private_section.append(member_vars_def)
    class_def.private_section.append(EmptyNode())

    # Write down to the files.
    write_code_node_to_file(header_node, path_manager.gen_path_to(header_path))
    write_code_node_to_file(source_node, path_manager.gen_path_to(source_path))


def generate_dictionaries(task_queue):
    assert isinstance(task_queue, TaskQueue)

    web_idl_database = package_initializer().web_idl_database()

    for dictionary in web_idl_database.dictionaries:
        task_queue.post_task(generate_dictionary, dictionary.identifier)