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

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 make_v8_to_blink_value
from .blink_v8_bridge import make_v8_to_blink_value_variadic
from .blink_v8_bridge import native_value_tag
from .blink_v8_bridge import v8_bridge_class_name
from .code_node import EmptyNode
from .code_node import FormatNode
from .code_node import ListNode
from .code_node import SequenceNode
from .code_node import SymbolDefinitionNode
from .code_node import SymbolNode
from .code_node import SymbolScopeNode
from .code_node import SymbolSensitiveSelectionNode
from .code_node import TextNode
from .code_node import WeakDependencyNode
from .code_node_cxx import CxxBlockNode
from .code_node_cxx import CxxBreakableBlockNode
from .code_node_cxx import CxxClassDefNode
from .code_node_cxx import CxxForLoopNode
from .code_node_cxx import CxxFuncDeclNode
from .code_node_cxx import CxxFuncDefNode
from .code_node_cxx import CxxLikelyIfNode
from .code_node_cxx import CxxMultiBranchesNode
from .code_node_cxx import CxxNamespaceNode
from .code_node_cxx import CxxSwitchNode
from .code_node_cxx import CxxUnlikelyIfNode
from .codegen_accumulator import CodeGenAccumulator
from .codegen_context import CodeGenContext
from .codegen_expr import CodeGenExpr
from .codegen_expr import expr_from_exposure
from .codegen_expr import expr_not
from .codegen_expr import expr_or
from .codegen_format import format_template as _format
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


def _is_none_or_str(arg):
    return arg is None or isinstance(arg, str)


def backward_compatible_api_func(cg_context):
    """
    Returns the Blink function name compatible with the old bindings generator.
    """
    assert isinstance(cg_context, CodeGenContext)

    name = cg_context.member_like.code_generator_info.property_implemented_as
    if name:
        pass
    elif cg_context.constructor:
        if cg_context.is_legacy_factory_function:
            name = "CreateForJSConstructor"
        else:
            name = "Create"
    else:
        name = (cg_context.member_like.identifier
                or cg_context.property_.identifier)
        if name:
            pass
        elif cg_context.indexed_property_getter:
            name = "AnonymousIndexedGetter"
        elif cg_context.indexed_property_setter:
            name = "AnonymousIndexedSetter"
        elif cg_context.named_property_getter:
            name = "AnonymousNamedGetter"
        elif cg_context.named_property_setter:
            name = "AnonymousNamedSetter"
        elif cg_context.named_property_deleter:
            name = "AnonymousNamedDeleter"

    if cg_context.attribute_get:
        # modules/webaudio/biquad_filter_node.idl has readonly attribute "Q"
        # and somehow it's implemented as "q" in Blink.
        if name == "Q":
            name = "q"

    if cg_context.attribute_set:
        tokens = name_style.raw.tokenize(name)
        if tokens[0] in ("IDL", "css", "xml"):
            tokens[0] = tokens[0].upper()
        else:
            tokens[0] = tokens[0].capitalize()
        tokens.insert(0, "set")
        name = "".join(tokens)

    return name


def callback_function_name(cg_context,
                           overload_index=None,
                           argument_count=None,
                           for_cross_origin=False):
    """
    Args:
        cg_context: A CodeGenContext of the target IDL construct.
        overload_index: An overload index if the target is an overloaded
            IDL operation.
        argument_count: When the target is an IDL operation that has optional
            arguments and is annotated with [NoAllocDirectCall], the value is
            the number of arguments that V8 passes in (excluding the fixed
            arguments like the receiver object.)
        for_cross_origin: True if the target is the cross origin accessible
            version.
    """

    assert isinstance(cg_context, CodeGenContext)
    assert overload_index is None or isinstance(overload_index, int)
    assert argument_count is None or isinstance(argument_count, int)
    assert isinstance(for_cross_origin, bool)

    def _cxx_name(name):
        """
        Returns a property name that the bindings generator can use in
        generated code.

        Note that Web IDL allows '-' (hyphen-minus) and '_' (low line) in
        identifiers but C++ does not allow or recommend them.  This function
        encodes these characters.
        """
        # In Python3, we can use str.maketrans and str.translate.
        #
        # We're optimistic about name conflict.  It's highly unlikely that
        # these replacements will cause a conflict.
        assert "Dec45" not in name
        assert "Dec95" not in name
        name = name.replace("-", "Dec45")
        name = name.replace("_", "Dec95")
        return name

    if cg_context.constant:
        property_name = cg_context.property_.identifier
    else:
        property_name = _cxx_name(cg_context.property_.identifier)

    if cg_context.attribute_get:
        kind = "AttributeGet"
    elif cg_context.attribute_set:
        kind = "AttributeSet"
    elif cg_context.constant:
        kind = "Constant"
    elif cg_context.constructor_group:
        if cg_context.is_legacy_factory_function:
            kind = "LegacyFactoryFunction"
        else:
            property_name = ""
            kind = "Constructor"
    elif cg_context.exposed_construct:
        if cg_context.is_legacy_factory_function:
            kind = "LegacyFactoryFunctionProperty"
        elif cg_context.legacy_window_alias:
            kind = "LegacyWindowAlias"
        else:
            kind = "ExposedConstruct"
    elif cg_context.operation_group:
        if cg_context.operation_group.is_static:
            kind = "StaticOperation"
        else:
            kind = "Operation"
    elif cg_context.stringifier:
        kind = "Operation"

    if cg_context.no_alloc_direct_call:
        nadc = "NoAllocDirectCall"
    else:
        nadc = ""

    overload = ""
    if overload_index is not None and (len(cg_context.constructor_group
                                           or cg_context.operation_group) > 1):
        overload += "Overload{}".format(overload_index + 1)
    if argument_count is not None:
        overload += "Arg{}".format(argument_count)

    if for_cross_origin:
        suffix = "CrossOrigin"
    elif nadc or overload:
        suffix = nadc + overload
    else:
        suffix = "Callback"

    if cg_context.for_world == CodeGenContext.MAIN_WORLD:
        world_suffix = "ForMainWorld"
    elif cg_context.for_world == CodeGenContext.NON_MAIN_WORLDS:
        world_suffix = "ForNonMainWorlds"
    elif cg_context.for_world == CodeGenContext.ALL_WORLDS:
        world_suffix = ""

    return name_style.func(property_name, kind, suffix, world_suffix)


def constant_name(cg_context):
    assert isinstance(cg_context, CodeGenContext)
    assert cg_context.constant

    property_name = cg_context.property_.identifier.lower()

    return name_style.constant(property_name)


# ----------------------------------------------------------------------------
# Callback functions
# ----------------------------------------------------------------------------


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

    if cg_context.attribute_get:
        return

    if cg_context.is_interceptor_returning_v8intercepted:
        error_exit_return_statement = "return v8::Intercepted::kYes;"
    else:
        error_exit_return_statement = "return;"

    if cg_context.attribute_set:
        real_type = cg_context.attribute.idl_type.unwrap(typedef=True)
        if real_type.is_enumeration:
            pattern = """\
// https://webidl.spec.whatwg.org/#dfn-attribute-setter
// step 4.6.1. Let S be ? ToString(V).
const auto&& arg1_value_string =
    NativeValueTraits<IDLString>::NativeValue(
        ${isolate}, ${v8_property_value}, ${exception_state});
if (${exception_state}.HadException()) [[unlikely]] {{
  return;
}}
// step 4.6.2. If S is not one of the enumeration's values, then return
//   undefined.
const auto arg1_value_maybe_enum = {enum_type}::Create(arg1_value_string);
if (!arg1_value_maybe_enum) {{
  bindings::ReportInvalidEnumSetToAttribute(
      ${isolate}, arg1_value_string, "{enum_type_name}", ${exception_state});
  return;  // Return undefined.
}}
const auto ${arg1_value} = arg1_value_maybe_enum.value();
"""
            text = _format(pattern,
                           enum_type=blink_class_name(
                               real_type.type_definition_object),
                           enum_type_name=real_type.identifier)
            code_node.register_code_symbol(SymbolNode("arg1_value", text))
            return

        name = "arg1_value"
        v8_value = "${v8_property_value}"
        code_node.register_code_symbol(
            make_v8_to_blink_value(
                name,
                v8_value,
                cg_context.attribute.idl_type,
                error_exit_return_statement=error_exit_return_statement))
        return

    for argument in cg_context.function_like.arguments:
        name = name_style.arg_f("arg{}_{}", argument.index + 1,
                                argument.identifier)
        if argument.is_variadic:
            code_node.register_code_symbol(
                make_v8_to_blink_value_variadic(name, "${info}",
                                                argument.index,
                                                argument.idl_type))
        else:
            v8_value = "${{info}}[{}]".format(argument.index)
            code_node.register_code_symbol(
                make_v8_to_blink_value(
                    name,
                    v8_value,
                    argument.idl_type,
                    argument=argument,
                    error_exit_return_statement=error_exit_return_statement,
                    cg_context=cg_context))


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

    S = SymbolNode
    T = TextNode
    F = FormatNode

    local_vars = []
    template_vars = {}

    local_vars.extend([
        S("blink_property_name",
          ("const AtomicString& ${blink_property_name} = "
           "ToCoreAtomicString(${isolate}, ${v8_property_name});")),
        S("blink_property_index",
          ("const AtomicString& ${blink_property_index} = "
           "AtomicString::Number(${index});")),
        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("current_script_state",
          ("ScriptState* ${current_script_state} = "
           "ScriptState::From(${isolate}, ${current_context});")),
        S("isolate", "v8::Isolate* ${isolate} = ${info}.GetIsolate();"),
        S("non_undefined_argument_length",
          ("const int ${non_undefined_argument_length} = "
           "bindings::NonUndefinedArgumentLength(${info});")),
        S("per_context_data", ("V8PerContextData* ${per_context_data} = "
                               "${script_state}->PerContextData();")),
        S("per_isolate_data", ("V8PerIsolateData* ${per_isolate_data} = "
                               "V8PerIsolateData::From(${isolate});")),
        S("property_name",
          "const char* const ${property_name} = \"${property.identifier}\";"),
    ])

    is_receiver_context = not (
        (cg_context.member_like and cg_context.member_like.is_static)
        or cg_context.constructor)

    # creation_context
    pattern = "const v8::Local<v8::Context>& ${creation_context} = {_1};"
    _1 = "${receiver_context}" if is_receiver_context else "${current_context}"
    local_vars.append(S("creation_context", _format(pattern, _1=_1)))

    # script_state
    pattern = "ScriptState* ${script_state} = {_1};"
    _1 = ("${receiver_script_state}"
          if is_receiver_context else "${current_script_state}")
    local_vars.append(S("script_state", _format(pattern, _1=_1)))

    # execution_context
    pattern = "ExecutionContext* ${execution_context} = {_1};"
    _1 = ("${receiver_execution_context}"
          if is_receiver_context else "${current_execution_context}")
    local_vars.append(S("execution_context", _format(pattern, _1=_1)))
    node = S("current_execution_context",
             ("ExecutionContext* ${current_execution_context} = "
              "ToExecutionContext(${current_script_state});"))
    node.accumulate(
        CodeGenAccumulator.require_include_headers([
            "third_party/blink/renderer/core/execution_context/execution_context.h"
        ]))
    local_vars.append(node)
    node = S("receiver_execution_context",
             ("ExecutionContext* ${receiver_execution_context} = "
              "ToExecutionContext(${receiver_script_state});"))
    node.accumulate(
        CodeGenAccumulator.require_include_headers([
            "third_party/blink/renderer/core/execution_context/execution_context.h"
        ]))
    local_vars.append(node)

    # execution_context_of_document_tree
    pattern = "ExecutionContext* ${execution_context_of_document_tree} = {_1};"
    if is_receiver_context:
        _1 = "bindings::ExecutionContextFromV8Wrappable(${blink_receiver})"
    else:
        _1 = "${current_execution_context}"
    text = _format(pattern, _1=_1)
    local_vars.append(S("execution_context_of_document_tree", text))

    # exception_context_type
    pattern = ("const v8::ExceptionContext ${exception_context_type} = "
               "{_1};")
    if cg_context.attribute_get:
        _1 = "v8::ExceptionContext::kAttributeGet"
    elif cg_context.attribute_set:
        _1 = "v8::ExceptionContext::kAttributeSet"
    elif cg_context.constructor_group:
        _1 = "v8::ExceptionContext::kConstructor"
    elif cg_context.indexed_interceptor_kind:
        _1 = "v8::ExceptionContext::kIndexed{}".format(
            cg_context.indexed_interceptor_kind)
    elif cg_context.named_interceptor_kind:
        _1 = "v8::ExceptionContext::kNamed{}".format(
            cg_context.named_interceptor_kind)
    else:
        _1 = "v8::ExceptionContext::kOperation"
    local_vars.append(S("exception_context_type", _format(pattern, _1=_1)))

    # exception_state
    def create_exception_state(symbol_node):
        node = SymbolDefinitionNode(symbol_node)
        pattern = ("{exception_state_type} ${exception_state}({init_args});"
                   "{exception_to_reject_promise}")
        exception_state_type = "ExceptionState"
        init_args = ["${isolate}", "${exception_context_type}"]
        exception_to_reject_promise = ""
        if cg_context.is_legacy_factory_function:
            init_args.append("\"{}\"".format(cg_context.property_.identifier))
        else:
            init_args.append("${class_like_name}")
        if cg_context.indexed_interceptor_kind:
            init_args.append("${blink_property_index}")
            init_args.append("ExceptionState::kForInterceptor")
        elif (cg_context.named_interceptor_kind
              and cg_context.named_interceptor_kind != "Enumerator"):
            init_args.append("${blink_property_name}")
            init_args.append("ExceptionState::kForInterceptor")
        elif (cg_context.property_ and cg_context.property_.identifier
              and not cg_context.constructor_group):
            init_args.append("${property_name}")
        if cg_context.is_return_type_promise_type:
            exception_to_reject_promise = (
                "\n"
                "ExceptionToRejectPromiseScope reject_promise_scope"
                "(${info}, ${exception_state});")
        node.append(
            F(pattern,
              exception_state_type=exception_state_type,
              init_args=", ".join(init_args),
              exception_to_reject_promise=exception_to_reject_promise))
        return node

    local_vars.append(
        S("exception_state", definition_constructor=create_exception_state))

    # receiver_context
    def create_receiver_context(symbol_node):
        node = SymbolDefinitionNode(symbol_node)
        # tl;dr: This is an optimization to leverage
        # `v8::Object::GetAlignedPointerFromEmbedderDataInCreationContext`.
        # See also the comment in `create_receiver_script_state`.
        #
        # When ${receiver_script_state} is already defined,
        #     ${receiver_script_state}->GetContext()
        # is faster than
        #     ${v8_receiver}->GetCreationContextChecked()
        node.append(
            SymbolSensitiveSelectionNode([
                SymbolSensitiveSelectionNode.Choice(
                    ["receiver_script_state"],
                    T("v8::Local<v8::Context> ${receiver_context} = "
                      "${receiver_script_state}->GetContext();")),
                SymbolSensitiveSelectionNode.Choice(
                    [],
                    T("v8::Local<v8::Context> ${receiver_context} = "
                      "${v8_receiver}->GetCreationContextChecked();")),
            ]))
        return node

    local_vars.append(
        S("receiver_context", definition_constructor=create_receiver_context))

    # receiver_script_state
    def create_receiver_script_state(symbol_node):
        node = SymbolDefinitionNode(symbol_node)
        # tl;dr: This is an optimization to leverage
        # `v8::Object::GetAlignedPointerFromEmbedderDataInCreationContext`.
        #
        # If ${receiver_context} is not used at all, or if
        # ${receiver_script_state} is used before ${receiver_context} is used,
        # then
        #     v8::Object::GetAlignedPointerFromEmbedderDataInCreationContext
        #   + ScriptState::GetContext
        # i.e.
        #     ScriptState::ForRelevantRealm(v8::Local<v8::Object>)
        #   + ScriptState::GetContext
        # is faster than
        #     v8::Object::GetCreationContextChecked
        #   + ScriptState::From(v8::Isolate*, v8::Local<v8::Context>)
        # Depending on already-defined symbols, select the best way to get
        # ${receiver_script_state}.
        node.append(
            SymbolSensitiveSelectionNode([
                SymbolSensitiveSelectionNode.Choice(
                    ["receiver_context"],
                    T("ScriptState* ${receiver_script_state} = "
                      "ScriptState::From(${isolate}, ${receiver_context});")),
                SymbolSensitiveSelectionNode.Choice(
                    [],
                    T("ScriptState* ${receiver_script_state} = "
                      "ScriptState::ForRelevantRealm(${isolate}, "
                      "${v8_receiver});")),
            ]))
        return node

    local_vars.append(
        S("receiver_script_state",
          definition_constructor=create_receiver_script_state))


    # blink_receiver
    if cg_context.class_like.identifier == "Window":
        # TODO(yukishiino): Window interface should be
        # [ImplementedAs=LocalDOMWindow] instead of [ImplementedAs=DOMWindow],
        # and [CrossOrigin] properties should be implemented specifically with
        # DOMWindow class.  Then, we'll have less hacks.
        if (not cg_context.member_like or
                "CrossOrigin" in cg_context.member_like.extended_attributes):
            text = (
                "DOMWindow* ${blink_receiver} = "
                "${class_name}::ToWrappableUnsafe(${isolate},${v8_receiver});")
        else:
            # ToWrappableUnsafe will always return non-null, so we can use
            # UnsafeTo via a reference to avoid the nullptr check as well.
            text = (
                "LocalDOMWindow* ${blink_receiver} = &UnsafeTo<LocalDOMWindow>("
                "*${class_name}::ToWrappableUnsafe(${isolate},${v8_receiver}));"
            )
    else:
        pattern = (
            "{_1}* ${blink_receiver} = "
            "${class_name}::ToWrappableUnsafe(${isolate}, ${v8_receiver});")
        _1 = blink_class_name(cg_context.class_like)
        text = _format(pattern, _1=_1)
    local_vars.append(S("blink_receiver", text))

    # v8_property_value
    if cg_context.v8_callback_type == CodeGenContext.V8_FUNCTION_CALLBACK:
        # In case of V8_ACCESSOR_NAME_SETTER_CALLBACK, |v8_property_value| is
        # defined as an argument.  In case of V8_FUNCTION_CALLBACK (of IDL
        # attribute set function), |info[0]| is the value to be set.
        local_vars.append(
            S("v8_property_value",
              "v8::Local<v8::Value> ${v8_property_value} = ${info}[0];"))

    # v8_receiver
    if cg_context.v8_callback_type == CodeGenContext.V8_FUNCTION_CALLBACK:
        # In case of v8::FunctionCallbackInfo, This() is the receiver object.
        local_vars.append(
            S("v8_receiver",
              "v8::Local<v8::Object> ${v8_receiver} = ${info}.This();"))
    else:
        # In case of v8::PropertyCallbackInfo, Holder() is the object that has
        # the property being processed.
        local_vars.append(
            S("v8_receiver",
              "v8::Local<v8::Object> ${v8_receiver} = ${info}.Holder();"))

    # v8_return_value
    def create_v8_return_value(symbol_node):
        return SymbolDefinitionNode(symbol_node, [
            F(
                "v8::Local<v8::Value> ${v8_return_value} = "
                "ToV8Traits<{}>::ToV8"
                "(${script_state}, ${return_value})"
                ";", native_value_tag(cg_context.return_type)),
        ])

    local_vars.append(
        S("v8_return_value", definition_constructor=create_v8_return_value))

    code_node.add_template_vars(template_vars)
    # Allow implementation-specific symbol definitions to have priority.
    for symbol_node in local_vars:
        if symbol_node.name not in code_node.own_template_vars:
            code_node.register_code_symbol(symbol_node)


def _make_throw_security_error():
    # CxxUnlikelyIfNode() is used here as a hint to the bindings generator that
    # we are relatively unlikely to reach a security error, and therefore
    # should prefer to scope variables inside the if(true). We want to lean
    # toward creating the ExceptionState inside the if(true) because it is
    # relatively expensive.
    return SequenceNode([
        TextNode("""\
// if(true) is used as part of a hint to the bindings generator. See
// _make_throw_security_error for details.\
"""),
        CxxUnlikelyIfNode(cond="true",
                          attribute=None,
                          body=TextNode(
                              "BindingSecurity::FailedAccessCheckFor("
                              "${isolate}, "
                              "${class_name}::GetWrapperTypeInfo(), "
                              "${info}.Holder(), "
                              "${exception_state});"))
    ])


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

    name = (cg_context.attribute.extended_attributes.value_of("Reflect")
            or cg_context.attribute.identifier.lower())
    if cg_context.attribute_get and name in ("class", "id", "name"):
        return None

    if cg_context.class_like.identifier.startswith("SVG"):
        namespace = "svg_names"
        code_node.accumulate(
            CodeGenAccumulator.require_include_headers(
                ["third_party/blink/renderer/core/svg_names.h"]))
    else:
        namespace = "html_names"
        code_node.accumulate(
            CodeGenAccumulator.require_include_headers(
                ["third_party/blink/renderer/core/html_names.h"]))
    return "{}::{}".format(namespace, name_style.constant(name, "attr"))


def _make_reflect_accessor_func_name(cg_context):
    assert isinstance(cg_context, CodeGenContext)
    assert cg_context.attribute_get or cg_context.attribute_set

    if cg_context.attribute_get:
        name = (cg_context.attribute.extended_attributes.value_of("Reflect")
                or cg_context.attribute.identifier.lower())
        if name in ("class", "id", "name"):
            return name_style.func("get", name, "attribute")

        if "URL" in cg_context.attribute.extended_attributes:
            return "GetURLAttribute"

    FAST_ACCESSORS = {
        "boolean": ("FastHasAttribute", "SetBooleanAttribute"),
        "long": ("GetIntegralAttribute", "SetIntegralAttribute"),
        "unsigned long": ("GetUnsignedIntegralAttribute",
                          "SetUnsignedIntegralAttribute"),
    }
    idl_type = cg_context.attribute.idl_type.unwrap()
    accessors = FAST_ACCESSORS.get(idl_type.keyword_typename)
    if accessors:
        return accessors[0 if cg_context.attribute_get else 1]

    if (idl_type.is_interface
            and idl_type.type_definition_object.does_implement("Element")):
        if cg_context.attribute_get:
            return "GetElementAttribute"
        else:
            return "SetElementAttribute"

    if cg_context.attribute_get:
        return "FastGetAttribute"
    else:
        return "setAttribute"


def _make_reflect_process_keyword_state(cg_context):
    # https://html.spec.whatwg.org/C/#keywords-and-enumerated-attributes

    assert isinstance(cg_context, CodeGenContext)
    assert cg_context.attribute_get or cg_context.attribute_set

    T = TextNode
    F = FormatNode

    if not cg_context.attribute_get:
        return None

    is_nullable = cg_context.return_type.unwrap(nullable=False).is_nullable
    ext_attrs = cg_context.attribute.extended_attributes

    def constant(keyword):
        if keyword is None and is_nullable:
            return "g_null_atom"
        if not keyword:
            return "g_empty_atom"
        return "keywords::{}".format(name_style.constant(keyword))

    branches = CxxMultiBranchesNode()
    branches.accumulate(
        CodeGenAccumulator.require_include_headers(
            ["third_party/blink/renderer/core/keywords.h"]))
    nodes = [
        T("// [ReflectOnly]"),
        T("const AtomicString reflect_value(${return_value}.LowerASCII());"),
        branches,
    ]

    if "ReflectMissing" in ext_attrs:
        missing_default = ext_attrs.value_of("ReflectMissing")
        branches.append(
            cond="reflect_value.IsNull()",
            body=F("${return_value} = {};", constant(missing_default)))
    elif is_nullable:
        branches.append(
            cond="reflect_value.IsNull()",
            body=T("// Null string to IDL null."))

    if "ReflectEmpty" in ext_attrs:
        empty_default = ext_attrs.value_of("ReflectEmpty")
        branches.append(cond="reflect_value.empty()",
                        body=F("${return_value} = {};",
                               constant(empty_default)))

    keywords = ext_attrs.values_of("ReflectOnly")
    expr = " || ".join(
        map(lambda keyword: "reflect_value == {}".format(constant(keyword)),
            keywords))
    branches.append(cond=expr, body=T("${return_value} = reflect_value;"))

    if "ReflectInvalid" in ext_attrs:
        invalid_default = ext_attrs.value_of("ReflectInvalid")
        branches.append(
            cond=True,
            body=F("${return_value} = {};", constant(invalid_default)))
    else:
        branches.append(cond=True,
                        body=F("${return_value} = {};", constant(None)))

    return SequenceNode(nodes)


def _make_blink_api_call(code_node,
                         cg_context,
                         num_of_args=None,
                         overriding_args=None):
    """
    Returns an expression of Blink C++ function call.

    This function doesn't create a complete C++ statement.  The returned string
    should be used to create an appropriate symbol binding like
    `bind_return_value` does.  (Actually `bind_return_value` is the only
    expected caller except for [NoAllocDirectCall] hack.)

    Args:
        code_node: A CodeNode which is supposed to contain the returned
            expression.
        cg_context: A CodeGenContext of the target IDL construct.
        num_of_args: The number of arguments to be passed to the function.
            This is used to determine which overload should be called.
        overriding_args: By default, the function is called with the arguments
            which are bound by `bind_blink_api_arguments`.  This argument has
            priority over them, and allows that the function is called with
            the explicitly given arguments.

    Returns:
        C++ expression of a function call, e.g. "func(arg1, arg2, ...)".
    """
    assert isinstance(code_node, SymbolScopeNode)
    assert isinstance(cg_context, CodeGenContext)
    assert num_of_args is None or isinstance(num_of_args, int)
    assert (overriding_args is None
            or (isinstance(overriding_args, (list, tuple))
                and all(isinstance(arg, str) for arg in overriding_args)))

    arguments = []
    ext_attrs = cg_context.member_like.extended_attributes

    values = ext_attrs.values_of("CallWith") + (
        ext_attrs.values_of("GetterCallWith") if cg_context.attribute_get else
        ext_attrs.values_of("SetterCallWith") if cg_context.attribute_set else
        ())
    if "Isolate" in values:
        arguments.append("${isolate}")
    if "ScriptState" in values:
        arguments.append("${script_state}")
    if "ExecutionContext" in values:
        arguments.append("${execution_context}")
    if "Document" in values:
        arguments.append(
            "*bindings::ToDocumentFromExecutionContext(${execution_context})")
    if "ThisValue" in values:
        arguments.append("ScriptValue(${isolate}, ${v8_receiver})")

    code_generator_info = cg_context.member_like.code_generator_info
    is_partial = code_generator_info.defined_in_partial
    if (is_partial and
            not (cg_context.constructor or cg_context.member_like.is_static)):
        arguments.append("*${blink_receiver}")

    if "Reflect" in ext_attrs:  # [Reflect]
        key = _make_reflect_content_attribute_key(code_node, cg_context)
        if key:
            arguments.append(key)

    if overriding_args is not None:
        arguments.extend(overriding_args)
    elif cg_context.attribute_get:
        pass
    elif cg_context.attribute_set:
        arguments.append("${arg1_value}")
    else:
        for index, argument in enumerate(cg_context.function_like.arguments):
            if num_of_args is not None and index == num_of_args:
                break
            name = name_style.arg_f("arg{}_{}", index + 1, argument.identifier)
            arguments.append(_format("${{{}}}", name))

    if cg_context.may_throw_exception:
        arguments.append("${exception_state}")

    func_name = backward_compatible_api_func(cg_context)
    if "Reflect" in ext_attrs:  # [Reflect]
        func_name = _make_reflect_accessor_func_name(cg_context)

    if (cg_context.constructor or cg_context.member_like.is_static
            or is_partial):
        class_like = cg_context.member_like.owner_mixin or cg_context.class_like
        class_name = (code_generator_info.receiver_implemented_as
                      or name_style.class_(class_like.identifier))
        func_designator = "{}::{}".format(class_name, func_name)
    else:
        func_designator = _format("${blink_receiver}->{}", func_name)

    expr = _format("{_1}({_2})", _1=func_designator, _2=", ".join(arguments))
    return expr


def bind_return_value(code_node, cg_context, overriding_args=None):
    assert isinstance(code_node, SymbolScopeNode)
    assert isinstance(cg_context, CodeGenContext)
    assert (overriding_args is None
            or (isinstance(overriding_args, (list, tuple))
                and all(isinstance(arg, str) for arg in overriding_args)))

    T = TextNode
    F = FormatNode

    def create_definition(symbol_node):
        api_calls = []  # Pairs of (num_of_args, api_call_text)
        if overriding_args is None:
            arguments = (cg_context.function_like.arguments
                         if cg_context.function_like else [])
            for index, arg in enumerate(arguments):
                if arg.is_optional and not arg.default_value:
                    api_calls.append((index,
                                      _make_blink_api_call(
                                          code_node, cg_context, index)))
            api_calls.append((None, _make_blink_api_call(
                code_node, cg_context)))
        else:
            api_calls.append((None,
                              _make_blink_api_call(
                                  code_node,
                                  cg_context,
                                  overriding_args=overriding_args)))

        nodes = []
        is_return_type_void = (
            (not cg_context.return_type
             or cg_context.return_type.unwrap().is_undefined)
            and not cg_context.does_override_idl_return_type)
        is_return_type_promise = (
            cg_context.return_type
            and cg_context.return_type.unwrap().is_promise
            and not "IDLTypeImplementedAsV8Promise"
            in cg_context.return_type.unwrap().extended_attributes
            and not "PromiseIDLTypeMismatch"
            in cg_context.member_like.extended_attributes)
        if not (is_return_type_void
                or cg_context.does_override_idl_return_type):
            return_type = blink_type_info(cg_context.return_type).value_t
        if len(api_calls) == 1:
            _, api_call = api_calls[0]
            if is_return_type_void:
                nodes.append(F("{};", api_call))
            elif "ReflectOnly" in cg_context.member_like.extended_attributes:
                # [ReflectOnly]
                nodes.append(F("auto ${return_value} = {};", api_call))
            elif is_return_type_promise:
                return_type = "ScriptPromise<{}>".format(
                    native_value_tag(
                        cg_context.return_type.unwrap().result_type))
                nodes.append(
                    F("{} ${return_value} = {};", return_type, api_call))
            else:
                nodes.append(F("auto&& ${return_value} = {};", api_call))
        else:
            branches = SequenceNode()
            for index, api_call in api_calls:
                if is_return_type_void:
                    assignment = "{};".format(api_call)
                else:
                    assignment = _format("${return_value} = {};", api_call)
                if index is not None:
                    branches.append(
                        CxxLikelyIfNode(cond=_format(
                            "${non_undefined_argument_length} <= {}", index),
                                        attribute=None,
                                        body=[
                                            T(assignment),
                                            T("break;"),
                                        ]))
                else:
                    branches.append(T(assignment))

            if not is_return_type_void:
                nodes.append(F("{} ${return_value};", return_type))
            nodes.append(CxxBreakableBlockNode(branches))

        if cg_context.may_throw_exception:
            if cg_context.is_interceptor_returning_v8intercepted:
                error_exit_return_statement = "return v8::Intercepted::kYes;"
            else:
                error_exit_return_statement = "return;"
            nodes.append(
                CxxUnlikelyIfNode(cond="${exception_state}.HadException()",
                                  attribute="[[unlikely]]",
                                  body=T(error_exit_return_statement)))

        if "ReflectOnly" in cg_context.member_like.extended_attributes:
            # [ReflectOnly]
            node = _make_reflect_process_keyword_state(cg_context)
            if node:
                nodes.append(EmptyNode())
                nodes.append(node)

        return SymbolDefinitionNode(symbol_node, nodes)

    code_node.register_code_symbol(
        SymbolNode("return_value", definition_constructor=create_definition))


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

    logging_id = "{}.{}".format(cg_context.class_like.identifier,
                                cg_context.property_.identifier)
    if cg_context.attribute_get:
        logging_id = "{}.{}".format(logging_id, "get")
    elif cg_context.attribute_set:
        logging_id = "{}.{}".format(logging_id, "set")
    elif (cg_context.constructor_group
          and not cg_context.is_legacy_factory_function):
        logging_id = "{}.{}".format(cg_context.class_like.identifier,
                                    "constructor")
    return logging_id


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

    return TextNode("BLINK_BINDINGS_TRACE_EVENT(\"{}\");".format(
        _make_bindings_logging_id(cg_context)))


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

    T = TextNode
    F = FormatNode

    if cg_context.v8_callback_type != CodeGenContext.V8_FUNCTION_CALLBACK:
        return None

    if cg_context.attribute_get:
        num_of_required_args = 0
    elif cg_context.attribute_set:
        idl_type = cg_context.attribute.idl_type
        if not (idl_type.does_include_nullable_or_dict
                or idl_type.unwrap().is_any or "LegacyTreatNonObjectAsNull" in
                idl_type.unwrap().extended_attributes
                or "PutForwards" in cg_context.attribute.extended_attributes
                or "Replaceable" in cg_context.attribute.extended_attributes):
            # ES undefined in ${v8_property_value} will cause a TypeError
            # anyway, so omit the check against the number of arguments.
            return None
        num_of_required_args = 1
    elif cg_context.function_like:
        num_of_required_args = (
            cg_context.function_like.num_of_required_arguments)
    elif isinstance(cg_context.property_, web_idl.OverloadGroup):
        num_of_required_args = (
            cg_context.property_.min_num_of_required_arguments)
    else:
        assert False

    if num_of_required_args == 0:
        return None

    return CxxUnlikelyIfNode(cond=_format("${info}.Length() < {}",
                                          num_of_required_args),
                             attribute="[[unlikely]]",
                             body=[
                                 F(("${exception_state}.ThrowTypeError("
                                    "ExceptionMessages::NotEnoughArguments"
                                    "({}, ${info}.Length()));"),
                                   num_of_required_args),
                                 T("return;"),
                             ])


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

    T = TextNode

    node = SequenceNode([
        CxxUnlikelyIfNode(
            cond="!${info}.IsConstructCall()",
            attribute=None,
            body=T("${exception_state}.ThrowTypeError("
                   "ExceptionMessages::ConstructorCalledAsFunction());\n"
                   "return;")),
    ])
    if not cg_context.is_legacy_factory_function:
        node.append(
            CxxLikelyIfNode(
                cond=("ConstructorMode::Current(${isolate}) == "
                      "ConstructorMode::kWrapExistingObject"),
                attribute=None,
                body=T("bindings::V8SetReturnValue(${info}, ${v8_receiver});\n"
                       "return;")))
    node.accumulate(
        CodeGenAccumulator.require_include_headers([
            "third_party/blink/renderer/platform/bindings/v8_object_constructor.h"
        ]))
    return node


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

    T = TextNode

    if cg_context.class_like.identifier != "Window":
        return None

    ext_attrs = cg_context.member_like.extended_attributes
    if "CrossOrigin" not in ext_attrs:
        return None

    # COOP: restrict-properties never restricts postMessage() and closed
    # accesses, which should still be possible across browsing context groups.
    if cg_context.property_.identifier in ("postMessage", "closed"):
        return None

    values = ext_attrs.values_of("CrossOrigin")
    if cg_context.attribute_get and not (not values or "Getter" in values):
        return None
    elif cg_context.attribute_set and not ("Setter" in values):
        return None

    if cg_context.is_interceptor_returning_v8intercepted:
        error_exit_return_statement = "return v8::Intercepted::kYes;"
    else:
        error_exit_return_statement = "return;"

    return CxxUnlikelyIfNode(
        cond=("${blink_receiver}->"
              "IsAccessBlockedByCoopRestrictProperties(${isolate})"),
        attribute="[[unlikely]]",
        body=[
            T("""\
${exception_state}.ThrowSecurityError(
"Cross-Origin-Opener-Policy: 'restrict-properties' blocked the access.",
"Cross-Origin-Opener-Policy: 'restrict-properties' blocked the access.");\
"""),
            T(error_exit_return_statement),
        ])


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

    T = TextNode

    if cg_context.member_like.is_static:
        return None

    if (cg_context.attribute and
            "LegacyLenientThis" in cg_context.attribute.extended_attributes):
        return SequenceNode([
            T("// [LegacyLenientThis]"),
            CxxUnlikelyIfNode(
                cond="!${class_name}::HasInstance(${isolate}, ${v8_receiver})",
                attribute=None,
                body=T("return;")),
        ])

    if cg_context.is_return_type_promise_type:
        return SequenceNode([
            T("// Promise returning function: "
              "Convert a TypeError to a reject promise."),
            CxxUnlikelyIfNode(
                cond="!${class_name}::HasInstance(${isolate}, ${v8_receiver})",
                attribute=None,
                body=[
                    T("${exception_state}.ThrowTypeError("
                      "\"Illegal invocation\");"),
                    T("return;"),
                ])
        ])

    return None


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

    T = TextNode

    check_security = cg_context.member_like.extended_attributes.value_of(
        "CheckSecurity")
    if check_security != "ReturnValue":
        return None

    web_feature = _format(
        "WebFeature::{}",
        name_style.constant("CrossOrigin", cg_context.class_like.identifier,
                            cg_context.property_.identifier))
    use_counter = _format(
        "UseCounter::Count(${current_execution_context}, {});", web_feature)
    cond = T("!BindingSecurity::ShouldAllowAccessTo("
             "ToLocalDOMWindow(${current_context}), ${return_value})")
    body = [
        T(use_counter),
        T("bindings::V8SetReturnValue(${info}, nullptr);\n"
          "return;"),
    ]
    node = SequenceNode([
        T("// [CheckSecurity=ReturnValue]"),
        CxxUnlikelyIfNode(cond=cond, attribute=None, body=body),
    ])
    node.accumulate(
        CodeGenAccumulator.require_include_headers([
            "third_party/blink/renderer/bindings/core/v8/binding_security.h",
            "third_party/blink/renderer/core/frame/web_feature.h",
            "third_party/blink/renderer/platform/instrumentation/use_counter.h",
        ]))
    return node


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

    node = TextNode("BINDINGS_COOPERATIVE_SCHEDULING_SAFEPOINT();")
    node.accumulate(
        CodeGenAccumulator.require_include_headers([
            "third_party/blink/renderer/platform/bindings/cooperative_scheduling_helpers.h"
        ]))
    return node


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

    ext_attrs = cg_context.logging_target.extended_attributes
    if "LogActivity" not in ext_attrs:
        return None
    target = ext_attrs.value_of("LogActivity")
    if target:
        assert target in ("GetterOnly", "SetterOnly")
        if ((target == "GetterOnly" and not cg_context.attribute_get)
                or (target == "SetterOnly" and not cg_context.attribute_set)):
            return None
    if (cg_context.for_world == cg_context.MAIN_WORLD
            and "LogAllWorlds" not in ext_attrs):
        return None

    pattern = "{_1}${per_context_data} && ${per_context_data}->ActivityLogger()"
    _1 = ""
    if (cg_context.attribute and "PerWorldBindings" not in ext_attrs
            and "LogAllWorlds" not in ext_attrs):
        _1 = "${script_state}->World().IsIsolatedWorld() && "
    cond = _format(pattern, _1=_1)

    pattern = ("${per_context_data}->ActivityLogger()->{_1}(${script_state}, "
               "\"{_2}.{_3}\"{_4});")
    _2 = cg_context.class_like.identifier
    _3 = cg_context.property_.identifier
    if cg_context.attribute_get:
        _1 = "LogGetter"
        _4 = ""
    elif cg_context.attribute_set:
        _1 = "LogSetter"
        _4 = ", ${v8_property_value}"
    elif cg_context.operation_group:
        _1 = "LogMethod"
        _4 = ", ${info}"
    body = _format(pattern, _1=_1, _2=_2, _3=_3, _4=_4)

    pattern = ("// [LogActivity], [LogAllWorlds]\n"
               "if ({_1}) [[unlikely]] {{ {_2} }}")
    node = TextNode(_format(pattern, _1=cond, _2=body))
    node.accumulate(
        CodeGenAccumulator.require_include_headers([
            "third_party/blink/renderer/platform/bindings/v8_dom_activity_logger.h",
            "third_party/blink/renderer/platform/bindings/v8_per_context_data.h",
        ]))
    return node


def _make_overload_dispatcher_per_arg_size(cg_context, items):
    """
    https://webidl.spec.whatwg.org/#dfn-overload-resolution-algorithm

    Args:
        items: Partial list of an "effective overload set" with the same
            type list size.

    Returns:
        A pair of a resulting CodeNode and a boolean flag that is True if there
        exists a case that overload resolution will fail, i.e. a bailout that
        throws a TypeError is necessary.
    """
    assert isinstance(cg_context, CodeGenContext)
    assert isinstance(items, (list, tuple))
    assert all(
        isinstance(item, web_idl.OverloadGroup.EffectiveOverloadItem)
        for item in items)

    # Variables shared with nested functions
    if len(items) > 1:
        arg_index = web_idl.OverloadGroup.distinguishing_argument_index(items)
    else:
        arg_index = None
    func_like = None
    dispatcher_nodes = SequenceNode()

    # True if there exists a case that overload resolution will fail.
    can_fail = True

    def find_test(item, test):
        # |test| is a callable that takes (t, u) where:
        #   t = the idl_type (in the original form)
        #   u = the unwrapped version of t
        idl_type = item.type_list[arg_index]
        t = idl_type
        u = idl_type.unwrap()
        return test(t, u) or (u.is_union and any(
            [test(m, m.unwrap()) for m in u.flattened_member_types]))

    def find(test):
        for item in items:
            if find_test(item, test):
                return item.function_like
        return None

    def find_all_interfaces():
        result = []  # [(func_like, idl_type), ...]
        for item in items:
            idl_type = item.type_list[arg_index].unwrap()
            if idl_type.is_interface:
                result.append((item.function_like, idl_type))
            if idl_type.is_union:
                for member_type in idl_type.flattened_member_types:
                    if member_type.unwrap().is_interface:
                        result.append((item.function_like,
                                       member_type.unwrap()))
        return result

    def make_node(pattern):
        value = _format("${info}[{}]", arg_index)
        func_name = callback_function_name(
            cg_context, overload_index=func_like.overload_index)
        return TextNode(_format(pattern, value=value, func_name=func_name))

    def dispatch_if(expr):
        if expr is True:
            pattern = "return {func_name}(${info});"
        else:
            pattern = ("if (" + expr + ") {{\n"
                       "  return {func_name}(${info});\n"
                       "}}")
        node = make_node(pattern)
        conditional = expr_from_exposure(func_like.exposure)
        if not conditional.is_always_true:
            node = CxxUnlikelyIfNode(cond=conditional,
                                     attribute=None,
                                     body=node)
        dispatcher_nodes.append(node)
        return expr is True and conditional.is_always_true

    if len(items) == 1:
        func_like = items[0].function_like
        can_fail = False
        return make_node("return {func_name}(${info});"), can_fail

    # 12.2. If V is undefined, ...
    func_like = find(lambda t, u: t.is_optional)
    if func_like:
        dispatch_if("{value}->IsUndefined()")

    # 12.3. if V is null or undefined, ...
    func_like = find(lambda t, u: t.does_include_nullable_or_dict)
    if func_like:
        dispatch_if("{value}->IsNullOrUndefined()")

    # 12.4. if V is a platform object, ...
    def inheritance_length(func_and_type):
        return (len(func_and_type[1].type_definition_object.
                    inclusive_inherited_interfaces),
                func_and_type[1].type_definition_object.identifier)

    # Attempt to match from most derived to least derived.
    for func_like, idl_type in sorted(
            find_all_interfaces(), key=inheritance_length, reverse=True):
        v8_bridge_name = v8_bridge_class_name(
            idl_type.unwrap().type_definition_object)
        dispatch_if(
            _format("{}::HasInstance(${isolate}, {value})", v8_bridge_name))

    # V8 specific optimization: BufferSource = ArrayBufferView or ArrayBuffer
    is_typedef_name = lambda t, name: t.is_typedef and t.identifier == name
    func_like = find(
        lambda t, u: is_typedef_name(t.unwrap(typedef=False), "BufferSource"))
    if func_like:
        dispatch_if("{value}->IsArrayBufferView() || "
                    "{value}->IsArrayBuffer() || "
                    "{value}->IsSharedArrayBuffer()")
    else:
        # 12.5. if Type(V) is Object, V has an [[ArrayBufferData]] internal
        #   slot, ...
        func_like = find(lambda t, u: u.is_array_buffer)
        if func_like:
            dispatch_if("{value}->IsArrayBuffer() || "
                        "{value}->IsSharedArrayBuffer()")

        # V8 specific optimization: ArrayBufferView
        func_like = find(lambda t, u: u.is_array_buffer_view)
        if func_like:
            dispatch_if("{value}->IsArrayBufferView()")

    # 12.6. if Type(V) is Object, V has a [[DataView]] internal slot, ...
    func_like = find(lambda t, u: u.is_data_view)
    if func_like:
        dispatch_if("{value}->IsDataView()")

    # 12.7. if Type(V) is Object, V has a [[TypedArrayName]] internal slot, ...
    typed_array_types = ("Int8Array", "Int16Array", "Int32Array",
                         "BigInt64Array", "Uint8Array", "Uint16Array",
                         "Uint32Array", "BigUint64Array", "Uint8ClampedArray",
                         "Float32Array", "Float64Array")
    for typed_array_type in typed_array_types:
        func_like = find(lambda t, u: u.keyword_typename == typed_array_type)
        if func_like:
            dispatch_if(_format("{value}->Is{}()", typed_array_type))

    # 12.8. if IsCallable(V) is true, ...
    func_like = find(lambda t, u: u.is_callback_function)
    if func_like:
        dispatch_if("{value}->IsFunction()")

    # 12.9. if Type(V) is Object and ... @@iterator ...
    func_like = find(lambda t, u: u.is_sequence or u.is_frozen_array)
    if func_like:
        dispatch_if("{value}->IsArray() || "  # Excessive optimization
                    "bindings::IsEsIterableObject"
                    "(${isolate}, {value}, ${exception_state})")
        dispatcher_nodes.append(
            CxxUnlikelyIfNode(cond="${exception_state}.HadException()",
                              attribute="[[unlikely]]",
                              body=TextNode("return;")))

    # 12.10. if Type(V) is Object and ...
    func_like = find(lambda t, u: u.is_callback_interface or u.is_dictionary or
                     u.is_record or u.is_object)
    if func_like:
        dispatch_if("{value}->IsObject()")

    # 12.11. if Type(V) is Boolean and ...
    func_like = find(lambda t, u: u.is_boolean)
    if func_like:
        dispatch_if("{value}->IsBoolean()")

    # 12.12. if Type(V) is Number and ...
    func_like = find(lambda t, u: u.is_numeric)
    if func_like:
        dispatch_if("{value}->IsNumber()")

    # 12.13. if there is an entry in S that has ... a string type ...
    # 12.14. if there is an entry in S that has ... a numeric type ...
    # 12.15. if there is an entry in S that has ... boolean ...
    # 12.16. if there is an entry in S that has any ...
    func_likes = [
        find(lambda t, u: u.is_enumeration),
        find(lambda t, u: u.is_string),
        find(lambda t, u: u.is_numeric),
        find(lambda t, u: u.is_boolean),
        find(lambda t, u: u.is_any),
    ]
    for func_like in func_likes:
        if func_like:
            if dispatch_if(True):
                can_fail = False
                break

    return dispatcher_nodes, can_fail


def make_overload_dispatcher(cg_context):
    # https://webidl.spec.whatwg.org/#dfn-overload-resolution-algorithm

    assert isinstance(cg_context, CodeGenContext)

    T = TextNode
    F = FormatNode

    overload_group = cg_context.property_
    items = overload_group.effective_overload_set()
    args_size = lambda item: len(item.type_list)
    items_grouped_by_arg_size = itertools.groupby(
        sorted(items, key=args_size, reverse=True), key=args_size)

    # TODO(yukishiino): Runtime-enabled features should be taken into account
    # when calculating the max argument size.
    max_arg_size = max(map(args_size, items))
    arg_count_def = F("const int arg_count = std::min(${info}.Length(), {});",
                      max_arg_size)

    branches = SequenceNode()
    did_use_break = False
    for arg_size, items in items_grouped_by_arg_size:
        items = list(items)

        node, can_fail = _make_overload_dispatcher_per_arg_size(
            cg_context, items)

        if arg_size > 0:
            node = CxxLikelyIfNode(
                cond="arg_count == {}".format(arg_size),
                attribute=None,
                body=[node, T("break;") if can_fail else None])
            did_use_break = did_use_break or can_fail

        conditional = expr_or(
            list(
                map(
                    lambda item: expr_from_exposure(item.function_like.exposure
                                                    ), items)))
        if not conditional.is_always_true:
            node = CxxUnlikelyIfNode(cond=conditional,
                                     attribute=None,
                                     body=node)

        branches.append(node)

    if did_use_break:
        branches = CxxBreakableBlockNode(branches)
    branches = SequenceNode([
        arg_count_def,
        branches,
    ])

    if not did_use_break and arg_size == 0 and conditional.is_always_true:
        return branches

    return SequenceNode([
        branches,
        EmptyNode(),
        make_check_argument_length(cg_context),
        T("${exception_state}.ThrowTypeError"
          "(\"Overload resolution failed.\");\n"
          "return;"),
    ])


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

    if cg_context.class_like.identifier != "Window":
        return None

    ext_attrs = cg_context.member_like.extended_attributes
    if "CrossOrigin" not in ext_attrs:
        return None

    values = ext_attrs.values_of("CrossOrigin")
    if (cg_context.attribute_get and not (not values or "Getter" in values)):
        return None
    elif (cg_context.attribute_set and not ("Setter" in values)):
        return None

    return TextNode("${blink_receiver}->ReportCoopAccess(${property_name});")


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

    name = cg_context.logging_target.extended_attributes.value_of(
        "DeprecateAs")
    if not name:
        return None

    pattern = ("// [DeprecateAs]\n"
               "Deprecation::CountDeprecation("
               "${current_execution_context}, WebFeature::k{_1});")
    _1 = name
    node = TextNode(_format(pattern, _1=_1))
    node.accumulate(
        CodeGenAccumulator.require_include_headers([
            "third_party/blink/renderer/core/frame/deprecation/deprecation.h"
        ]))
    return node


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

    ext_attrs = cg_context.logging_target.extended_attributes

    suffix = ""
    if cg_context.attribute_get:
        suffix = "_AttributeGetter"
    elif cg_context.attribute_set:
        suffix = "_AttributeSetter"
    elif cg_context.constructor:
        suffix = "_Constructor"
    elif cg_context.exposed_construct:
        suffix = "_ConstructorGetter"
    elif cg_context.operation:
        suffix = "_Method"

    name = ext_attrs.value_of("MeasureAs") or ext_attrs.value_of("Measure")
    if name:
        assert not name.startswith("WebDXFeature::")

        name = "k{}".format(name)
    elif cg_context.constructor:
        name = "kV8{}{}".format(cg_context.class_like.identifier, suffix)
    else:
        name = "kV8{}_{}{}".format(
            cg_context.class_like.identifier,
            name_style.raw.upper_camel_case(cg_context.property_.identifier),
            suffix)

    return "WebFeature::{}".format(name)


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

    ext_attrs = cg_context.logging_target.extended_attributes
    if "HighEntropy" not in ext_attrs:
        return None

    node = SequenceNode([
        TextNode("// [HighEntropy]"),
        FormatNode(
            "const Dactyloscoper::HighEntropyTracer"
            "  high_entropy_tracer(\"{logging_id}\", ${info});",
            logging_id=_make_bindings_logging_id(cg_context)),
    ])
    node.accumulate(
        CodeGenAccumulator.require_include_headers([
            "third_party/blink/renderer/core/frame/dactyloscoper.h",
        ]))
    return node


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

    ext_attrs = cg_context.logging_target.extended_attributes
    if not ext_attrs.value_of("HighEntropy") == "Direct":
        return None
    if cg_context.attribute_set:
        return None

    assert "Measure" in ext_attrs or "MeasureAs" in ext_attrs, "{}: {}".format(
        cg_context.idl_location_and_name,
        "[HighEntropy=Direct] must be specified with either [Measure] or "
        "[MeasureAs].")

    assert "MeasureAs" not in ext_attrs or not ext_attrs.value_of(
        "MeasureAs").startswith("WebDXFeature::"), "{}: {}".format(
            cg_context.idl_location_and_name,
            "[HighEntropy=Direct] is not yet supported for a WebDXFeature "
            "use counter.")

    node = SequenceNode([
        TextNode("// [HighEntropy=Direct]"),
        FormatNode(
            "Dactyloscoper::RecordDirectSurface("
            "${current_execution_context}, {measure_constant}, "
            "${return_value});",
            measure_constant=_make_measure_web_feature_constant(cg_context)),
    ])
    node.accumulate(
        CodeGenAccumulator.require_include_headers(
            ["third_party/blink/renderer/core/frame/dactyloscoper.h"]))
    return node


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

    ext_attrs = cg_context.logging_target.extended_attributes
    if not ("Measure" in ext_attrs or "MeasureAs" in ext_attrs):
        return None

    measure_as = ext_attrs.value_of("MeasureAs")

    if measure_as and measure_as.startswith("WebDXFeature::"):
        text = _format(
            "// [Measure], [MeasureAs]\n"
            "bindings::CountWebDXFeature(${isolate}, {measure_constant});",
            measure_constant=measure_as)
    else:
        text = _format(
            "// [Measure], [MeasureAs]\n"
            "UseCounter::Count(${current_execution_context}, {measure_constant});",
            measure_constant=_make_measure_web_feature_constant(cg_context))

    node = TextNode(text)
    node.accumulate(
        CodeGenAccumulator.require_include_headers([
            "third_party/blink/renderer/core/frame/web_feature.h",
            "third_party/blink/renderer/platform/instrumentation/use_counter.h",
        ]))
    return node


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

    pred = cg_context.member_like.extended_attributes.value_of(
        "CachedAttribute")
    if pred:
        return TextNode("""\
// [CachedAttribute]
static const V8PrivateProperty::SymbolKey kPrivatePropertyCachedAttribute;
auto&& v8_private_cached_attribute =
    V8PrivateProperty::GetSymbol(${isolate}, kPrivatePropertyCachedAttribute);
if (!${blink_receiver}->""" + pred + """()) {
  v8::Local<v8::Value> v8_value;
  if (!v8_private_cached_attribute.GetOrUndefined(${v8_receiver})
           .ToLocal(&v8_value)) {
    return;
  }
  if (!v8_value->IsUndefined()) {
    bindings::V8SetReturnValue(${info}, v8_value);
    return;
  }
}""")

    if "SaveSameObject" in cg_context.member_like.extended_attributes:
        return TextNode("""\
// [SaveSameObject]
static const V8PrivateProperty::SymbolKey kPrivatePropertySaveSameObject;
auto&& v8_private_save_same_object =
    V8PrivateProperty::GetSymbol(${isolate}, kPrivatePropertySaveSameObject);
{
  v8::Local<v8::Value> v8_value;
  if (!v8_private_save_same_object.GetOrUndefined(${v8_receiver})
           .ToLocal(&v8_value)) {
    return;
  }
  if (!v8_value->IsUndefined()) {
    bindings::V8SetReturnValue(${info}, v8_value);
    return;
  }
}""")


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

    if "CachedAttribute" in cg_context.member_like.extended_attributes:
        return TextNode("// [CachedAttribute]\n"
                        "v8_private_cached_attribute.Set"
                        "(${v8_receiver}, ${info}.GetReturnValue().Get());")

    if "SaveSameObject" in cg_context.member_like.extended_attributes:
        return TextNode("// [SaveSameObject]\n"
                        "v8_private_save_same_object.Set"
                        "(${v8_receiver}, ${info}.GetReturnValue().Get());")


def make_runtime_call_timer_scope(cg_context, overriding_name=None):
    assert isinstance(cg_context, CodeGenContext)
    assert _is_none_or_str(overriding_name)

    target = cg_context.logging_target

    suffix = ""
    if cg_context.attribute_get:
        suffix = "_Getter"
    elif cg_context.attribute_set:
        suffix = "_Setter"
    elif cg_context.exposed_construct:
        suffix = "_ConstructorGetterCallback"

    counter = (target and
               target.extended_attributes.value_of("RuntimeCallStatsCounter"))
    if counter:
        macro_name = "RUNTIME_CALL_TIMER_SCOPE"
        counter_name = "RuntimeCallStats::CounterId::k{}{}".format(
            counter, suffix)
    else:
        macro_name = "RUNTIME_CALL_TIMER_SCOPE_DISABLED_BY_DEFAULT"
        counter_name = "\"Blink_{}_{}{}\"".format(
            blink_class_name(cg_context.class_like), overriding_name
            or target.identifier, suffix)

    return TextNode(
        _format(
            "{macro_name}(${info}.GetIsolate(), {counter_name});",
            macro_name=macro_name,
            counter_name=counter_name))


def make_steps_of_ce_reactions(cg_context):
    assert isinstance(cg_context, CodeGenContext)
    assert (cg_context.attribute_set or cg_context.operation
            or cg_context.indexed_property_setter
            or cg_context.named_property_setter
            or cg_context.named_property_deleter)

    if "CEReactions" not in cg_context.member_like.extended_attributes:
        return None

    nodes = [
        TextNode("// [CEReactions]"),
        TextNode("CEReactionsScope ce_reactions_scope;"),
    ]

    nodes[-1].accumulate(
        CodeGenAccumulator.require_include_headers([
            "third_party/blink/renderer/core/html/custom/ce_reactions_scope.h"
        ]))

    # CEReactions scope is not tolerant of V8 exception, so it's necessary to
    # invoke custom element reactions before throwing an exception.  Thus, put
    # an ExceptionState before CEReactions scope.
    nodes.insert(0, WeakDependencyNode(dep_syms=["exception_state"]))

    return SequenceNode(nodes)


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

    T = TextNode

    if cg_context.is_interceptor_returning_v8intercepted:
        return_statement = "return v8::Intercepted::kYes;"
        error_exit_return_statement = "return v8::Intercepted::kYes;"
    else:
        return_statement = "return;"
        error_exit_return_statement = "return;"

    return SequenceNode([
        T("// [PutForwards]"),
        T("v8::Local<v8::Value> target;"),
        CxxUnlikelyIfNode(
            cond=("!${v8_receiver}->Get(${current_context}, "
                  "V8AtomicString(${isolate}, ${property_name}))"
                  ".ToLocal(&target)"),
            attribute=None,
            body=T(error_exit_return_statement),
        ),
        CxxUnlikelyIfNode(cond="!target->IsObject()",
                          attribute=None,
                          body=[
                              T("${exception_state}.ThrowTypeError("
                                "\"The attribute value is not an object\");"),
                              T(error_exit_return_statement),
                          ]),
        T("bool did_set;"),
        CxxUnlikelyIfNode(cond=(
            "!target.As<v8::Object>()->Set(${current_context}, "
            "V8AtomicString(${isolate}, "
            "\"${attribute.extended_attributes.value_of(\"PutForwards\")}\""
            "), ${v8_property_value})"
            ".To(&did_set)"),
                          attribute=None,
                          body=T(error_exit_return_statement)),
        T(return_statement)
    ])


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

    T = TextNode

    return SequenceNode([
        T("// [Replaceable]"),
        T("bool did_create;"),
        CxxUnlikelyIfNode(
            cond=("!${v8_receiver}->CreateDataProperty(${current_context}, "
                  "V8AtomicString(${isolate}, ${property_name}), "
                  "${v8_property_value}).To(&did_create)"),
            attribute=None,
            body=T("return;")),
    ])


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

    T = TextNode
    F = FormatNode

    if cg_context.does_override_idl_return_type:
        return T("bindings::V8SetReturnValue(${info}, ${return_value});")

    if (not cg_context.return_type
            or cg_context.return_type.unwrap().is_undefined):
        # Request a SymbolNode |return_value| to define itself without
        # rendering any text.
        return T("<% return_value.request_symbol_definition() %>")

    operation = cg_context.operation
    if operation and (operation.is_setter or operation.is_deleter):
        # Blink implementation returns in a type different from the IDL type.
        # Namely, IndexedPropertySetterResult, NamedPropertySetterResult, and
        # NamedPropertyDeleterResult are returned ignoring the operation's
        # return type.
        return T("bindings::V8SetReturnValue(${info}, ${return_value});")

    return_type = cg_context.return_type
    if return_type.is_event_handler:
        return T("bindings::V8SetReturnValue(${info}, ${return_value}, "
                 "${isolate}, ${blink_receiver});")

    # [CheckSecurity=ReturnValue]
    #
    # The returned object must be wrapped in its own realm instead of the
    # receiver object's relevant realm or the current realm.
    #
    # [CheckSecurity=ReturnValue] is used only for 'contentDocument' attribute
    # and 'getSVGDocument' operation of HTML{IFrame,Frame,Object,Embed}Element
    # interfaces, and Window.frameElement attribute, so far.
    #
    # Note that the global object has its own context and there is no need to
    # pass the creation context to ToV8.
    if (cg_context.member_like.extended_attributes.value_of("CheckSecurity") ==
            "ReturnValue"):
        node = CxxBlockNode([
            T("// [CheckSecurity=ReturnValue]"),
            F(
                "Frame* blink_frame = {};",
                "${blink_receiver}->GetFrame()->Parent()"
                if cg_context.member_like.identifier == "frameElement" else
                "${blink_receiver}->contentWindow()->GetFrame()"),
            T("DCHECK(IsA<LocalFrame>(blink_frame));"),
            CxxUnlikelyIfNode(
                cond=T("!blink_frame->IsAttached() && "
                       "To<LocalFrame>(blink_frame)"
                       "->WindowProxyMaybeUninitialized("
                       "${script_state}->World())->ContextIfInitialized()"
                       ".IsEmpty()"),
                attribute="[[unlikely]]",
                body=[
                    T("""\
// Don't wrap the return value if its frame is in the process of detaching and
// has already invalidated its v8::Context, as it is not safe to
// re-initialize the v8::Context in that state. Return null instead.\
"""),
                    T("bindings::V8SetReturnValue(${info}, nullptr);"),
                    T("return;")
                ]),
            F(
                "v8::Local<v8::Value> v8_value = "
                "ToV8Traits<{}>::ToV8("
                "ToScriptState(To<LocalFrame>(blink_frame), "
                "${script_state}->World()),"
                "${return_value});", native_value_tag(return_type)),
            T("bindings::V8SetReturnValue(${info}, v8_value);"),
        ])
        node.accumulate(
            CodeGenAccumulator.require_include_headers([
                "third_party/blink/renderer/bindings/core/v8/local_window_proxy.h",
                "third_party/blink/renderer/core/frame/local_frame.h",
            ]))
        return node

    return_type = return_type.unwrap(typedef=True)
    return_type_body = return_type.unwrap()

    PRIMITIVE_TYPE_TO_CXX_TYPE = {
        "boolean": "bool",
        "byte": "int8_t",
        "octet": "uint8_t",
        "short": "int16_t",
        "unsigned short": "uint16_t",
        "long": "int32_t",
        "unsigned long": "uint32_t",
        "long long": "int64_t",
        "unsigned long long": "uint64_t",
        "float": "float",
        "unrestricted float": "float",
        "double": "double",
        "unrestricted double": "double",
    }
    cxx_type = PRIMITIVE_TYPE_TO_CXX_TYPE.get(
        return_type_body.keyword_typename)
    if cxx_type:
        return F(
            "bindings::V8SetReturnValue(${info}, ${return_value}, "
            "bindings::V8ReturnValue::PrimitiveType<{cxx_type}>());",
            cxx_type=cxx_type)

    if return_type_body.is_string or return_type_body.is_enumeration:
        args = ["${info}", "${return_value}", "${isolate}"]
        if return_type.is_nullable:
            args.append("bindings::V8ReturnValue::kNullable")
        else:
            args.append("bindings::V8ReturnValue::kNonNullable")
        return T("bindings::V8SetReturnValue({});".format(", ".join(args)))

    if return_type_body.is_interface:
        args = ["${info}", "${return_value}"]
        if (return_type_body.identifier == "Window"
                or return_type_body.identifier == "Location"):
            args.append("${blink_receiver}")
            args.append("bindings::V8ReturnValue::kMaybeCrossOrigin")
        elif cg_context.constructor or cg_context.member_like.is_static:
            args.append("${creation_context}")
        elif cg_context.for_world == cg_context.MAIN_WORLD:
            args.append("bindings::V8ReturnValue::kMainWorld")
        else:
            args.append("${blink_receiver}")
        return T("bindings::V8SetReturnValue({});".format(", ".join(args)))

    if return_type_body.is_observable_array:
        return T("bindings::V8SetReturnValue"
                 "(${info}, ${return_value}->GetExoticObject(), "
                 "${blink_receiver});")

    if return_type_body.is_async_iterator or return_type_body.is_sync_iterator:
        # Async iterator objects and sync iterator objects (default iterator
        # objects, map iterator objects, and set iterator objects) are
        # implemented as ScriptWrappable instances.
        return T("bindings::V8SetReturnValue(${info}, ${return_value}, "
                 "${blink_receiver});")

    if return_type.is_promise:
        return T("bindings::V8SetReturnValue(${info}, ${return_value});")

    if return_type.is_any or return_type_body.is_object:
        return T("bindings::V8SetReturnValue(${info}, ${return_value});")

    return T("bindings::V8SetReturnValue(${info}, ${v8_return_value});")


def _make_empty_callback_def(cg_context, function_name):
    assert isinstance(cg_context, CodeGenContext)
    assert isinstance(function_name, str)

    return_type = "void"
    if cg_context.v8_callback_type == CodeGenContext.V8_FUNCTION_CALLBACK:
        arg_decls = ["const v8::FunctionCallbackInfo<v8::Value>& info"]
        arg_names = ["info"]
    elif (cg_context.v8_callback_type == CodeGenContext.
          V8_ACCESSOR_NAME_GETTER_CALLBACK):
        arg_decls = [
            "v8::Local<v8::Name> v8_property_name",
            "const v8::PropertyCallbackInfo<v8::Value>& info",
        ]
        arg_names = ["v8_property_name", "info"]
    elif (cg_context.v8_callback_type == CodeGenContext.
          V8_ACCESSOR_NAME_SETTER_CALLBACK):
        arg_decls = [
            "v8::Local<v8::Name> v8_property_name",
            "v8::Local<v8::Value> v8_property_value",
            "const v8::PropertyCallbackInfo<void>& info",
        ]
        arg_names = ["v8_property_name", "v8_property_value", "info"]
    elif (cg_context.v8_callback_type ==
          CodeGenContext.V8_NAMED_PROPERTY_GETTER_CALLBACK):
        return_type = "v8::Intercepted"
        arg_decls = [
            "v8::Local<v8::Name> v8_property_name",
            "const v8::PropertyCallbackInfo<v8::Value>& info",
        ]
        arg_names = ["v8_property_name", "info"]
    elif (cg_context.v8_callback_type ==
          CodeGenContext.V8_NAMED_PROPERTY_SETTER_CALLBACK):
        return_type = "v8::Intercepted"
        arg_decls = [
            "v8::Local<v8::Name> v8_property_name",
            "v8::Local<v8::Value> v8_property_value",
            "const v8::PropertyCallbackInfo<void>& info",
        ]
        arg_names = ["v8_property_name", "v8_property_value", "info"]

    func_def = CxxFuncDefNode(name=function_name,
                              arg_decls=arg_decls,
                              return_type=return_type)
    func_def.set_base_template_vars(cg_context.template_bindings())
    body = func_def.body

    for arg_name in arg_names:
        body.add_template_var(arg_name, arg_name)

    bind_callback_local_vars(body, cg_context)
    if cg_context.attribute or cg_context.function_like:
        bind_blink_api_arguments(body, cg_context)
        bind_return_value(body, cg_context)

    return func_def


def make_attribute_get_callback_def(cg_context, function_name):
    assert isinstance(cg_context, CodeGenContext)
    assert isinstance(function_name, str)

    func_def = _make_empty_callback_def(cg_context, function_name)
    body = func_def.body

    body.extend([
        make_check_receiver(cg_context),
        EmptyNode(),
        make_runtime_call_timer_scope(cg_context),
        make_bindings_trace_event(cg_context),
        make_report_coop_access(cg_context),
        make_report_deprecate_as(cg_context),
        make_report_high_entropy(cg_context),
        make_report_measure_as(cg_context),
        make_log_activity(cg_context),
        EmptyNode(),
        make_check_coop_restrict_properties_access(cg_context),
        EmptyNode(),
        make_return_value_cache_return_early(cg_context),
        EmptyNode(),
        make_check_security_of_return_value(cg_context),
        make_v8_set_return_value(cg_context),
        make_report_high_entropy_direct(cg_context),
        make_return_value_cache_update_value(cg_context),
    ])
    if cg_context.is_interceptor_returning_v8intercepted:
        body.append(TextNode("return v8::Intercepted::kYes;"))

    return func_def


def make_attribute_set_callback_def(cg_context, function_name):
    assert isinstance(cg_context, CodeGenContext)
    assert isinstance(function_name, str)

    ext_attrs = cg_context.attribute.extended_attributes
    if cg_context.attribute.is_readonly and not any(
            ext_attr in ext_attrs
            for ext_attr in ("LegacyLenientSetter", "PutForwards",
                             "Replaceable")):
        return None

    func_def = _make_empty_callback_def(cg_context, function_name)
    body = func_def.body

    if "LegacyLenientSetter" in ext_attrs:
        body.append(TextNode("// [LegacyLenientSetter]"))
        return func_def

    body.extend([
        make_check_receiver(cg_context),
        EmptyNode(),
        make_runtime_call_timer_scope(cg_context),
        make_bindings_trace_event(cg_context),
        make_report_deprecate_as(cg_context),
        make_report_high_entropy(cg_context),
        make_report_measure_as(cg_context),
        make_log_activity(cg_context),
        EmptyNode(),
    ])

    # Binary size reduction hack
    # 1. Drop the check of argument length although this is a violation of
    #   Web IDL.
    # 2. Leverage the nature of [LegacyTreatNonObjectAsNull] (ES to IDL
    #   conversion never fails).
    if cg_context.attribute.idl_type.is_event_handler:
        body.append(
            TextNode("""\
EventListener* event_handler = JSEventHandler::CreateOrNull(
    ${v8_property_value},
    JSEventHandler::HandlerType::k${attribute.idl_type.identifier});\
"""))
        code_generator_info = cg_context.attribute.code_generator_info
        func_name = name_style.api_func("set", cg_context.attribute.identifier)
        if code_generator_info.defined_in_partial:
            class_name = (code_generator_info.receiver_implemented_as
                          or name_style.class_(
                              cg_context.attribute.owner_mixin.identifier))
            text = _format(
                "{class_name}::{func_name}"
                "(*${blink_receiver}, event_handler);",
                class_name=class_name,
                func_name=func_name)
        else:
            text = _format("${blink_receiver}->{func_name}(event_handler);",
                           func_name=func_name)
        body.append(TextNode(text))
        return func_def

    # Binary size reduction hack
    # When the following conditions are met, the implementation is shared.
    # 1. The attribute is annotated with [CEReactions, Reflect] and not
    #   annotated with other extended attributes having side effect.
    # 2. The interface is implementing Element.
    def optimize_element_cereactions_reflect():
        has_cereactions = False
        has_reflect = False
        for key in ext_attrs.keys():
            if key == "CEReactions":
                has_cereactions = True
            elif key == "Reflect":
                has_reflect = True
            elif key in ("Affects", "CrossOriginIsolated", "DeprecateAs",
                         "Exposed", "InjectionMitigated", "IsolatedContext",
                         "LogActivity", "LogAllWorlds", "Measure", "MeasureAs",
                         "ReflectEmpty", "ReflectInvalid", "ReflectMissing",
                         "ReflectOnly", "RuntimeCallStatsCounter",
                         "RuntimeEnabled", "SecureContext", "URL",
                         "Unscopable"):
                pass
            else:
                return None
        if not (has_cereactions and has_reflect):
            return None
        if not cg_context.interface.does_implement("Element"):
            return None
        content_attribute = _make_reflect_content_attribute_key(
            body, cg_context)
        idl_type = cg_context.attribute.idl_type.unwrap(typedef=True)
        if idl_type.is_boolean:
            func_name = "PerformAttributeSetCEReactionsReflectTypeBoolean"
        elif idl_type.type_name == "String":
            func_name = "PerformAttributeSetCEReactionsReflectTypeString"
        elif idl_type.type_name == "StringLegacyNullToEmptyString":
            func_name = ("PerformAttributeSetCEReactionsReflect"
                         "TypeStringLegacyNullToEmptyString")
        elif idl_type.type_name == "StringOrNull":
            func_name = "PerformAttributeSetCEReactionsReflectTypeStringOrNull"
        else:
            return None
        text = _format(
            "bindings::{func_name}"
            "(${info}, {content_attribute}, "
            "${class_like_name}, ${property_name});",
            func_name=func_name,
            content_attribute=content_attribute)
        return TextNode(text)

    node = optimize_element_cereactions_reflect()
    if node:
        body.append(node)
        return func_def

    body.extend([
        make_check_argument_length(cg_context),
        EmptyNode(),
    ])

    if "PutForwards" in ext_attrs:
        body.append(make_steps_of_put_forwards(cg_context))
        return func_def

    if "Replaceable" in ext_attrs:
        body.append(make_steps_of_replaceable(cg_context))
        return func_def

    body.extend([
        make_steps_of_ce_reactions(cg_context),
        EmptyNode(),
    ])

    if cg_context.attribute.idl_type.unwrap(typedef=True).is_observable_array:
        # Make an expression of "attribute get" instead of "attribute set" in
        # order to acquire the observable array (backing list) object.
        attribute_get_call = _make_blink_api_call(
            body, cg_context.make_copy(attribute_get=True,
                                       attribute_set=False))
        body.extend([
            FormatNode("auto&& observable_array = {attribute_get_call};",
                       attribute_get_call=attribute_get_call),
            TextNode("observable_array->PerformAttributeSet("
                     "${script_state}, ${v8_property_value}, "
                     "${exception_state});"),
        ])
        return func_def

    body.append(make_v8_set_return_value(cg_context))

    if cg_context.is_interceptor_returning_v8intercepted:
        body.append(TextNode("return v8::Intercepted::kYes;"))

    return func_def


def make_constant_callback_def(cg_context, function_name):
    assert isinstance(cg_context, CodeGenContext)
    assert isinstance(function_name, str)

    logging_nodes = SequenceNode([
        make_report_deprecate_as(cg_context),
        make_report_measure_as(cg_context),
        make_log_activity(cg_context),
    ])
    if not logging_nodes:
        return None

    func_def = _make_empty_callback_def(cg_context, function_name)
    body = func_def.body

    v8_set_return_value = _format(
        "bindings::V8SetReturnValue(${info}, ${class_name}::Constant::{});",
        constant_name(cg_context))
    body.extend([
        make_runtime_call_timer_scope(cg_context),
        make_bindings_trace_event(cg_context),
        logging_nodes,
        EmptyNode(),
        TextNode(v8_set_return_value),
    ])

    return func_def


def make_constant_constant_def(cg_context, constant_name):
    # IDL constant's C++ constant definition
    assert isinstance(cg_context, CodeGenContext)
    assert isinstance(constant_name, str)

    constant_type = blink_type_info(cg_context.constant.idl_type).value_t
    return TextNode("static constexpr {type} {name} = {value};".format(
        type=constant_type,
        name=constant_name,
        value=cg_context.constant.value.literal))


def make_overload_dispatcher_function_def(cg_context, function_name):
    assert isinstance(cg_context, CodeGenContext)
    assert isinstance(function_name, str)

    func_def = _make_empty_callback_def(cg_context, function_name)
    body = func_def.body

    if cg_context.operation_group:
        body.append(make_operation_entry(cg_context))
        body.append(EmptyNode())
        body.append(make_cooperative_scheduling_safepoint(cg_context))
        body.append(EmptyNode())

    if cg_context.constructor_group:
        body.append(make_constructor_entry(cg_context))
        body.append(EmptyNode())

    body.append(make_overload_dispatcher(cg_context))

    return func_def


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

    return SequenceNode([
        make_runtime_call_timer_scope(cg_context),
        make_bindings_trace_event(cg_context),
        EmptyNode(),
        make_check_constructor_call(cg_context),
    ])


def make_constructor_function_def(cg_context, function_name):
    assert isinstance(cg_context, CodeGenContext)
    assert isinstance(function_name, str)

    T = TextNode

    func_def = _make_empty_callback_def(cg_context, function_name)
    body = func_def.body

    if len(cg_context.constructor_group) == 1:
        # The constructor callback is installed with
        # v8::FunctionTemplate::SetCallHandler, where no way to control the
        # installation context-by-context. So, we check the exposure and may
        # throw a TypeError if not exposed. For the case of multiple overloads,
        # the overload resolution is already exposure sensitive.
        body.append(make_constructor_entry(cg_context))
        if cg_context.constructor.exposure.is_context_dependent():
            body.append(
                CxxUnlikelyIfNode(cond=expr_not(
                    expr_from_exposure(cg_context.constructor.exposure)),
                                  attribute=None,
                                  body=[
                                      T("${exception_state}.ThrowTypeError("
                                        "\"Illegal constructor\");"),
                                      T("return;"),
                                  ]))
        body.append(EmptyNode())

    body.extend([
        make_report_deprecate_as(cg_context),
        make_report_high_entropy(cg_context),
        make_report_measure_as(cg_context),
        make_log_activity(cg_context),
        EmptyNode(),
        make_check_argument_length(cg_context),
        EmptyNode(),
    ])

    if "HTMLConstructor" in cg_context.constructor.extended_attributes:
        body.append(T("// [HTMLConstructor]"))
        text = _format(
            "V8HTMLConstructor::HtmlConstructor("
            "${info}, *${class_name}::GetWrapperTypeInfo(), "
            "HTMLElementType::{});",
            name_style.constant(cg_context.class_like.identifier))
        body.append(T(text))
        body.accumulate(
            CodeGenAccumulator.require_include_headers([
                "third_party/blink/renderer/bindings/core/v8/v8_html_constructor.h"
            ]))
    else:
        body.append(
            T("v8::Local<v8::Object> v8_wrapper = "
              "${return_value}->AssociateWithWrapper(${isolate}, "
              "${class_name}::GetWrapperTypeInfo(), ${v8_receiver});"))
        body.append(T("bindings::V8SetReturnValue(${info}, v8_wrapper);"))

    body.extend([
        make_report_high_entropy_direct(cg_context),
    ])

    return func_def


def make_constructor_callback_def(cg_context, function_name):
    assert isinstance(cg_context, CodeGenContext)
    assert isinstance(function_name, str)

    constructor_group = cg_context.constructor_group

    if len(constructor_group) == 1:
        return make_constructor_function_def(
            cg_context.make_copy(constructor=constructor_group[0]),
            function_name)

    node = SequenceNode()
    for constructor in constructor_group:
        cgc = cg_context.make_copy(constructor=constructor)
        node.extend([
            make_constructor_function_def(
                cgc,
                callback_function_name(
                    cgc, overload_index=constructor.overload_index)),
            EmptyNode(),
        ])
    node.append(
        make_overload_dispatcher_function_def(cg_context, function_name))
    return node


def make_exposed_construct_callback_def(cg_context, function_name):
    assert isinstance(cg_context, CodeGenContext)
    assert isinstance(function_name, str)

    func_def = _make_empty_callback_def(cg_context, function_name)
    body = func_def.body

    if (cg_context.exposed_construct.is_interface
            or cg_context.exposed_construct.is_callback_interface):
        tag = "bindings::V8ReturnValue::kInterfaceObject"
    elif cg_context.exposed_construct.is_namespace:
        tag = "bindings::V8ReturnValue::kNamespaceObject"
    else:
        assert False
    v8_set_return_value = _format(
        "bindings::V8SetReturnValue"
        "(${info}, {bridge}::GetWrapperTypeInfo(), {tag});",
        bridge=v8_bridge_class_name(cg_context.exposed_construct),
        tag=tag)
    body.extend([
        make_runtime_call_timer_scope(cg_context),
        make_bindings_trace_event(cg_context),
        make_report_deprecate_as(cg_context),
        make_report_measure_as(cg_context),
        make_log_activity(cg_context),
        EmptyNode(),
        TextNode(v8_set_return_value),
    ])

    return func_def


def make_legacy_factory_function_property_callback_def(cg_context,
                                                       function_name):
    assert isinstance(cg_context, CodeGenContext)
    assert isinstance(function_name, str)

    func_def = _make_empty_callback_def(cg_context, function_name)
    body = func_def.body

    body.extend([
        make_runtime_call_timer_scope(cg_context),
        make_bindings_trace_event(cg_context),
        make_report_deprecate_as(cg_context),
        make_report_measure_as(cg_context),
        make_log_activity(cg_context),
        EmptyNode(),
    ])

    constructor_group = cg_context.exposed_construct
    assert isinstance(constructor_group, web_idl.ConstructorGroup)
    assert isinstance(constructor_group.owner, web_idl.Interface)
    named_ctor_v8_bridge = v8_bridge_class_name(constructor_group.owner)
    cgc = CodeGenContext(interface=constructor_group.owner,
                         constructor_group=constructor_group,
                         is_legacy_factory_function=True,
                         class_name=named_ctor_v8_bridge)
    named_ctor_name = callback_function_name(cgc)
    named_ctor_def = make_constructor_callback_def(cgc, named_ctor_name)

    return_value_cache_return_early = """\
static const V8PrivateProperty::SymbolKey
    kPrivatePropertyLegacyFactoryFunction;
auto&& v8_private_legacy_factory_function =
    V8PrivateProperty::GetSymbol(
        ${isolate},
        kPrivatePropertyLegacyFactoryFunction);
v8::Local<v8::Value> v8_legacy_factory_function;
if (!v8_private_legacy_factory_function.GetOrUndefined(${v8_receiver})
         .ToLocal(&v8_legacy_factory_function)) {
  return;
}
if (!v8_legacy_factory_function->IsUndefined()) {
  bindings::V8SetReturnValue(${info}, v8_legacy_factory_function);
  return;
}
"""

    pattern = """\
v8::Local<v8::Value> v8_value;
if (!bindings::CreateLegacyFactoryFunctionFunction(
         ${script_state},
         {callback},
         "{func_name}",
         {func_length},
         {v8_bridge}::GetWrapperTypeInfo())
     .ToLocal(&v8_value)) {
  return;
}
bindings::V8SetReturnValue(${info}, v8_value);
"""
    create_legacy_factory_function_function = _format(
        pattern,
        callback=named_ctor_name,
        func_name=constructor_group.identifier,
        func_length=constructor_group.min_num_of_required_arguments,
        v8_bridge=named_ctor_v8_bridge)

    return_value_cache_update_value = """\
v8_private_legacy_factory_function.Set(${v8_receiver}, v8_value);
"""

    body.extend([
        TextNode(return_value_cache_return_early),
        TextNode(create_legacy_factory_function_function),
        TextNode(return_value_cache_update_value),
    ])

    return SequenceNode([named_ctor_def, EmptyNode(), func_def])


def list_no_alloc_direct_call_callbacks(cg_context):
    """
    Returns a list of [NoAllocDirectCall] callback functions to be registered
    at V8, including all overloaded operations annotated with
    [NoAllocDirectCall] and their variants of optional arguments.

    Example:
      Given the following Web IDL fragments,
        undefined f(DOMString);                            // (a)
        [NoAllocDirectCall] undefined f(Node node);        // (b)
        [NoAllocDirectCall] undefined f(optional long a,
                                        optional long b);  // (c)
      the following callback functions should be generated,
        void F(v8::Local<v8::Value> node);  // (b)
        void F();                           // (c)
        void F(int32_t a);                  // (c)
        void F(int32_t a, int32_t b);       // (c)
      thus the following entries are returned.
        [
          Entry(operation=(b), argument_count=1),  # overload_index=2
          Entry(operation=(c), argument_count=2),  # overload_index=3
          Entry(operation=(c), argument_count=1),  # overload_index=3
          Entry(operation=(c), argument_count=0),  # overload_index=3
        ]
    """
    assert isinstance(cg_context, CodeGenContext)

    class Entry(object):
        def __init__(self, operation, argument_count):
            self.operation = operation
            self.argument_count = argument_count
            self.callback_name = callback_function_name(
                cg_context,
                overload_index=self.operation.overload_index,
                argument_count=self.argument_count)

    entries = []
    for operation in cg_context.operation_group:
        if "NoAllocDirectCall" not in operation.extended_attributes:
            continue
        for argument in reversed(operation.arguments):
            entries.append(Entry(operation, argument.index + 1))
            if not argument.is_optional:
                break
        else:
            entries.append(Entry(operation, 0))
    return entries


def make_no_alloc_direct_call_callback_def(cg_context, function_name,
                                           argument_count):
    """
    Args:
        cg_context: A CodeGenContext of the target IDL construct.
        function_name: The function name to be produced.
        argument_count: The number of arguments that the produced function
            takes, which may be different from the number of arguments of
            the target cg_context.function_like due to optional arguments.
    """
    assert isinstance(cg_context, CodeGenContext)
    assert isinstance(function_name, str)
    assert isinstance(argument_count, int)

    S = SymbolNode
    T = TextNode
    F = FormatNode

    function_like = cg_context.function_like

    class ArgumentInfo(object):
        def __init__(self, v8_type, v8_arg_name, blink_arg_name, symbol_node):
            self.v8_type = v8_type
            self.v8_arg_name = v8_arg_name
            self.blink_arg_name = blink_arg_name
            self.symbol_node = symbol_node

    def v8_type_and_symbol_node(argument, v8_arg_name, blink_arg_name):
        unwrapped_idl_type = argument.idl_type.unwrap()
        if unwrapped_idl_type.is_interface or unwrapped_idl_type.is_sequence:
            return ("v8::Local<v8::Value>" if unwrapped_idl_type.is_interface
                    else "v8::Local<v8::Array>",
                    make_v8_to_blink_value(
                        blink_arg_name,
                        "${{{}}}".format(v8_arg_name),
                        argument.idl_type,
                        argument=argument,
                        error_exit_return_statement="return;",
                        cg_context=cg_context))
        elif unwrapped_idl_type.is_typed_array_type:
            assert "AllowShared" in argument.idl_type.effective_annotations
            assert "PassAsSpan" in argument.idl_type.effective_annotations
            return ("v8::Local<v8::Value>",
                    make_v8_to_blink_value(
                        blink_arg_name,
                        "${{{}}}".format(v8_arg_name),
                        argument.idl_type,
                        argument=argument,
                        error_exit_return_statement="return;",
                        cg_context=cg_context))
        else:
            return (blink_type_info(argument.idl_type).value_t,
                    S(blink_arg_name,
                      "auto&& {} = {};".format(blink_arg_name, v8_arg_name)))

    arg_list = []
    for argument in function_like.arguments:
        if not (argument.index < argument_count):
            break
        blink_arg_name = name_style.arg_f("arg{}_{}", argument.index + 1,
                                          argument.identifier)
        v8_arg_name = name_style.arg_f("v8_arg{}_{}", argument.index + 1,
                                       argument.identifier)
        v8_type, symbol_node = v8_type_and_symbol_node(argument, v8_arg_name,
                                                       blink_arg_name)

        arg_list.append(
            ArgumentInfo(v8_type, v8_arg_name, blink_arg_name, symbol_node))

    arg_decls = (["v8::Local<v8::Object> v8_arg0_receiver"] + list(
        map(lambda arg: "{} {}".format(arg.v8_type, arg.v8_arg_name),
            arg_list)) +
                 ["v8::FastApiCallbackOptions& v8_arg_callback_options"])
    return_type = ("void" if function_like.return_type.is_undefined else
                   blink_type_info(function_like.return_type).value_t)

    func_def = CxxFuncDefNode(name=function_name,
                              arg_decls=arg_decls,
                              return_type=return_type)
    func_def.set_base_template_vars(cg_context.template_bindings())
    body = func_def.body
    for arg in arg_list:
        body.add_template_var(arg.v8_arg_name, arg.v8_arg_name)
        body.register_code_symbol(arg.symbol_node)
    body.add_template_vars({
        "v8_arg0_receiver": "v8_arg0_receiver",
        "v8_arg_callback_options": "v8_arg_callback_options"
    })
    body.register_code_symbols([
        S("blink_receiver", (_format(
            "{}* ${blink_receiver} = "
            "${class_name}::ToWrappableUnsafe(${isolate}, ${v8_receiver});",
            blink_class_name(cg_context.interface)))),
        S("isolate",
          "v8::Isolate* ${isolate} = ${v8_arg_callback_options}.isolate;"),
        S("v8_receiver", ("v8::Local<v8::Object> ${v8_receiver} = "
                          "${v8_arg0_receiver};")),
    ])
    bind_callback_local_vars(body, cg_context)

    body.extend([
        T("v8::HandleScope handle_scope(${isolate});"),
        EmptyNode(),
    ])

    # If [CallWith=Isolate] is specified, make sure ${isolate} is passed first.
    blink_arguments = list()
    if "Isolate" in cg_context.member_like.extended_attributes.values_of(
            "CallWith"):
        blink_arguments.append("${isolate}")

    # Append the method arguments next.
    blink_arguments += list(
        map(lambda arg: "${{{}}}".format(arg.blink_arg_name), arg_list))

    # If there are trailing optional arguments with default values, append
    # them filled with the default values.
    for argument in function_like.arguments[argument_count:]:
        if not argument.default_value:
            break
        blink_arg_name = name_style.arg_f("arg{}_{}", argument.index + 1,
                                          argument.identifier)
        default_expr = make_default_value_expr(argument.idl_type,
                                               argument.default_value)
        body.register_code_symbol(
            S((blink_arg_name),
              "auto&& {}{{{}}};".format(blink_arg_name,
                                        default_expr.initializer_expr)))
        blink_arguments.append("${{{}}}".format(blink_arg_name))

    # Pass ${exception_state} after the method arguments.
    if cg_context.may_throw_exception:
        blink_arguments.append("${exception_state}")

    is_return_type_void = function_like.return_type.is_undefined

    if is_return_type_void:
        body.append(
            F("${blink_receiver}->{member_func}({blink_arguments});",
              member_func=backward_compatible_api_func(cg_context),
              blink_arguments=", ".join(blink_arguments)))
    else:
        body.append(
            F("auto&& return_value = ${blink_receiver}->{member_func}({blink_arguments});",
              member_func=backward_compatible_api_func(cg_context),
              blink_arguments=", ".join(blink_arguments)))
    if cg_context.may_throw_exception:
        body.append(
            CxxUnlikelyIfNode(cond="${exception_state}.HadException()",
                              attribute="[[unlikely]]",
                              body=T("return;")))

    if not is_return_type_void:
        body.extend([T("return return_value;")])

    return func_def


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

    return SequenceNode([
        make_runtime_call_timer_scope(cg_context),
        make_bindings_trace_event(cg_context),
    ])


def make_operation_function_def(cg_context, function_name):
    assert isinstance(cg_context, CodeGenContext)
    assert isinstance(function_name, str)

    func_def = _make_empty_callback_def(cg_context, function_name)
    body = func_def.body

    if not cg_context.operation_group or len(cg_context.operation_group) == 1:
        body.append(make_operation_entry(cg_context))
        body.append(EmptyNode())

    body.extend([
        make_check_receiver(cg_context),
        EmptyNode(),
        make_report_coop_access(cg_context),
        make_report_deprecate_as(cg_context),
        make_report_high_entropy(cg_context),
        make_report_measure_as(cg_context),
        make_log_activity(cg_context),
        EmptyNode(),
        make_check_coop_restrict_properties_access(cg_context),
        EmptyNode(),
        make_check_argument_length(cg_context),
        EmptyNode(),
        make_steps_of_ce_reactions(cg_context),
        EmptyNode(),
        make_check_security_of_return_value(cg_context),
        make_v8_set_return_value(cg_context),
        make_report_high_entropy_direct(cg_context),
    ])

    return func_def


def make_operation_callback_def(cg_context, function_name):
    assert isinstance(cg_context, CodeGenContext)
    assert isinstance(function_name, str)

    operation_group = cg_context.operation_group

    nodes = SequenceNode()
    if "NoAllocDirectCall" in operation_group.extended_attributes:
        for entry in list_no_alloc_direct_call_callbacks(cg_context):
            cgc = cg_context.make_copy(operation=entry.operation,
                                       no_alloc_direct_call=True)
            nodes.extend([
                make_no_alloc_direct_call_callback_def(
                    cgc,
                    callback_function_name(
                        cgc,
                        overload_index=entry.operation.overload_index,
                        argument_count=entry.argument_count),
                    argument_count=entry.argument_count),
                EmptyNode(),
            ])

    if len(operation_group) == 1:
        nodes.append(
            make_operation_function_def(
                cg_context.make_copy(operation=operation_group[0]),
                function_name))
        return nodes

    for operation in operation_group:
        cgc = cg_context.make_copy(operation=operation)
        nodes.extend([
            make_operation_function_def(
                cgc,
                callback_function_name(
                    cgc, overload_index=operation.overload_index)),
            EmptyNode(),
        ])
    nodes.append(
        make_overload_dispatcher_function_def(cg_context, function_name))
    return nodes


def make_stringifier_callback_def(cg_context, function_name):
    assert isinstance(cg_context, CodeGenContext)
    assert isinstance(function_name, str)

    if cg_context.stringifier.attribute:
        return make_attribute_get_callback_def(
            cg_context.make_copy(
                attribute=cg_context.stringifier.attribute,
                attribute_get=True), function_name)
    elif cg_context.stringifier.operation:
        return make_operation_function_def(
            cg_context.make_copy(operation=cg_context.stringifier.operation),
            function_name)
    assert False


# ----------------------------------------------------------------------------
# Callback functions of indexed and named interceptors
# ----------------------------------------------------------------------------


def _make_interceptor_callback(cg_context, function_name, return_type,
                               arg_decls, arg_names, class_name,
                               runtime_call_timer_name):
    assert isinstance(cg_context, CodeGenContext)
    assert isinstance(function_name, str)
    assert isinstance(arg_decls, (list, tuple))
    assert all(isinstance(arg_decl, str) for arg_decl in arg_decls)
    assert isinstance(arg_names, (list, tuple))
    assert all(isinstance(arg_name, str) for arg_name in arg_names)
    assert _is_none_or_str(class_name)
    assert isinstance(runtime_call_timer_name, str)

    func_decl = CxxFuncDeclNode(name=function_name,
                                arg_decls=arg_decls,
                                return_type=return_type,
                                static=True)

    func_def = _make_interceptor_callback_def(cg_context, function_name,
                                              return_type, arg_decls,
                                              arg_names, class_name,
                                              runtime_call_timer_name)

    return func_decl, func_def


def _make_interceptor_callback_def(cg_context, function_name, return_type,
                                   arg_decls, arg_names, class_name,
                                   runtime_call_timer_name):
    assert isinstance(cg_context, CodeGenContext)
    assert isinstance(function_name, str)
    assert isinstance(arg_decls, (list, tuple))
    assert all(isinstance(arg_decl, str) for arg_decl in arg_decls)
    assert isinstance(arg_names, (list, tuple))
    assert all(isinstance(arg_name, str) for arg_name in arg_names)
    assert _is_none_or_str(class_name)
    assert isinstance(runtime_call_timer_name, str)

    func_def = CxxFuncDefNode(name=function_name,
                              arg_decls=arg_decls,
                              return_type=return_type,
                              class_name=class_name)
    func_def.set_base_template_vars(cg_context.template_bindings())
    body = func_def.body
    for arg_name in arg_names:
        body.add_template_var(arg_name, arg_name)
    bind_callback_local_vars(body, cg_context)

    body.extend([
        make_runtime_call_timer_scope(cg_context, runtime_call_timer_name),
        EmptyNode(),
    ])

    return func_def


def _make_interceptor_callback_args(cg_context, named_or_indexed,
                                    callback_type):
    return_type = "v8::Intercepted"
    arg_decls = []
    arg_names = []

    # name/index parameter is used for every interceptor except Enumerator.
    if callback_type != "Enumerator":
        if named_or_indexed == "Named":
            arg_decls.append("v8::Local<v8::Name> v8_property_name")
            arg_names.append("v8_property_name")
        elif named_or_indexed == "Indexed":
            arg_decls.append("uint32_t index")
            arg_names.append("index")
        else:
            assert False

    if callback_type == "Getter":
        callback_info_type = "v8::Value"
    elif callback_type == "Setter":
        arg_decls.append("v8::Local<v8::Value> v8_property_value")
        arg_names.append("v8_property_value")
        callback_info_type = "void"
    elif callback_type == "Query":
        callback_info_type = "v8::Integer"
    elif callback_type == "Deleter":
        callback_info_type = "v8::Boolean"
    elif callback_type == "Enumerator":
        return_type = "void"
        callback_info_type = "v8::Array"
    elif callback_type == "Definer":
        arg_decls.append("const v8::PropertyDescriptor& v8_property_desc")
        arg_names.append("v8_property_desc")
        callback_info_type = "void"
    elif callback_type == "Descriptor":
        callback_info_type = "v8::Value"
    else:
        assert False
    arg_decls.append(
        _format("const v8::PropertyCallbackInfo<{}>& info",
                callback_info_type))
    arg_names.append("info")

    return return_type, arg_decls, arg_names


def make_indexed_property_getter_callback(cg_context, function_name):
    assert isinstance(cg_context, CodeGenContext)
    assert isinstance(function_name, str)

    return_type, arg_decls, arg_names = _make_interceptor_callback_args(
        cg_context, "Indexed", "Getter")
    func_decl, func_def = _make_interceptor_callback(cg_context, function_name,
                                                     return_type, arg_decls,
                                                     arg_names,
                                                     cg_context.class_name,
                                                     "IndexedPropertyGetter")
    body = func_def.body

    if not cg_context.interface.indexed_and_named_properties.indexed_getter:
        body.append(
            TextNode("""\
return ${class_name}::NamedPropertyGetterCallback(
    V8AtomicString(${isolate}, ${blink_property_index}), ${info});
"""))
        return func_decl, func_def

    bind_return_value(body, cg_context, overriding_args=["${index}"])

    body.extend([
        TextNode("""\
// LegacyPlatformObjectGetOwnProperty
// https://webidl.spec.whatwg.org/#LegacyPlatformObjectGetOwnProperty
// step 1.2. If index is a supported property index, then:\
"""),
        CxxUnlikelyIfNode(cond="${index} >= ${blink_receiver}->length()",
                          attribute=None,
                          body=TextNode("""\
// step 3. Return OrdinaryGetOwnProperty(O, P).
// Do not intercept.  Fallback to OrdinaryGetOwnProperty.
return v8::Intercepted::kNo;\
""")),
        make_v8_set_return_value(cg_context),
        TextNode("return v8::Intercepted::kYes;"),
    ])

    return func_decl, func_def


def make_indexed_property_setter_callback(cg_context, function_name):
    assert isinstance(cg_context, CodeGenContext)
    assert isinstance(function_name, str)

    return_type, arg_decls, arg_names = _make_interceptor_callback_args(
        cg_context, "Indexed", "Setter")
    func_decl, func_def = _make_interceptor_callback(cg_context, function_name,
                                                     return_type, arg_decls,
                                                     arg_names,
                                                     cg_context.class_name,
                                                     "IndexedPropertySetter")
    body = func_def.body

    if not cg_context.interface.indexed_and_named_properties.indexed_getter:
        body.append(
            TextNode("""\
return ${class_name}::NamedPropertySetterCallback(
    V8AtomicString(${isolate}, ${blink_property_index}), ${v8_property_value},
    ${info});
"""))
        return func_decl, func_def

    if not cg_context.indexed_property_setter:
        body.extend([
            TextNode("""\
// 3.9.2. [[Set]]
// https://webidl.spec.whatwg.org/#legacy-platform-object-set
// OrdinarySetWithOwnDescriptor will end up calling DefineOwnProperty,
// which will fail when the receiver object is this legacy platform
// object.\
"""),
            CxxLikelyIfNode(
                cond="${info}.ShouldThrowOnError()",
                attribute=None,
                body=TextNode(
                    "${exception_state}.ThrowTypeError("
                    "\"Indexed property setter is not supported.\");")),
            TextNode("return v8::Intercepted::kYes;"),
        ])
        return func_decl, func_def

    bind_return_value(
        body,
        cg_context,
        overriding_args=["${index}", "${blink_property_value}"])
    body.register_code_symbol(
        make_v8_to_blink_value(
            "blink_property_value",
            "${v8_property_value}",
            cg_context.indexed_property_setter.arguments[1].idl_type,
            argument=cg_context.indexed_property_setter.arguments[1],
            error_exit_return_statement="return v8::Intercepted::kYes;"))

    body.extend([
        TextNode("""\
// 3.9.2. [[Set]]
// https://webidl.spec.whatwg.org/#legacy-platform-object-set
// step 1. If O and Receiver are the same object, then:\
"""),
        CxxLikelyIfNode(cond="${info}.Holder() == ${info}.This()",
                        attribute=None,
                        body=[
                            TextNode("""\
// step 1.1.1. Invoke the indexed property setter with P and V.\
"""),
                            make_steps_of_ce_reactions(cg_context),
                            EmptyNode(),
                            make_v8_set_return_value(cg_context),
                            TextNode(
                                "return BlinkInterceptorResultToV8Intercepted("
                                "${return_value});"),
                        ]),
        EmptyNode(),
        TextNode("""\
// Do not intercept.  Fallback to OrdinarySetWithOwnDescriptor.
return v8::Intercepted::kNo;
"""),
    ])

    return func_decl, func_def


def make_indexed_property_deleter_callback(cg_context, function_name):
    assert isinstance(cg_context, CodeGenContext)
    assert isinstance(function_name, str)

    return_type, arg_decls, arg_names = _make_interceptor_callback_args(
        cg_context, "Indexed", "Deleter")
    func_decl, func_def = _make_interceptor_callback(cg_context, function_name,
                                                     return_type, arg_decls,
                                                     arg_names,
                                                     cg_context.class_name,
                                                     "IndexedPropertyDeleter")
    body = func_def.body

    if not cg_context.interface.indexed_and_named_properties.indexed_getter:
        body.append(
            TextNode("""\
return ${class_name}::NamedPropertyDeleterCallback(
    V8AtomicString(${isolate}, ${blink_property_index}), ${info});
"""))
        return func_decl, func_def

    body.extend([
        TextNode("""\
// 3.9.4. [[Delete]]
// https://webidl.spec.whatwg.org/#legacy-platform-object-delete
// step 1.2. If index is not a supported property index, then return true.
// step 1.3. Return false.\
"""),
        TextNode("const bool is_supported = "
                 "${index} < ${blink_receiver}->length();"),
        TextNode("bindings::V8SetReturnValue(${info}, !is_supported);"),
        CxxLikelyIfNode(cond="is_supported && ${info}.ShouldThrowOnError()",
                        attribute=None,
                        body=TextNode(
                            "${exception_state}.ThrowTypeError("
                            "\"Index property deleter is not supported.\");")),
        TextNode("return v8::Intercepted::kYes;")
    ])

    return func_decl, func_def


def make_indexed_property_definer_callback(cg_context, function_name):
    assert isinstance(cg_context, CodeGenContext)
    assert isinstance(function_name, str)

    return_type, arg_decls, arg_names = _make_interceptor_callback_args(
        cg_context, "Indexed", "Definer")
    func_decl, func_def = _make_interceptor_callback(cg_context, function_name,
                                                     return_type, arg_decls,
                                                     arg_names,
                                                     cg_context.class_name,
                                                     "IndexedPropertyDefiner")
    body = func_def.body

    if not cg_context.interface.indexed_and_named_properties.indexed_getter:
        body.append(
            TextNode("""\
return ${class_name}::NamedPropertyDefinerCallback(
    V8AtomicString(${isolate}, ${blink_property_index}), ${v8_property_desc},
    ${info});
"""))
        return func_decl, func_def

    body.extend([
        TextNode("""\
// 3.9.3. [[DefineOwnProperty]]
// https://webidl.spec.whatwg.org/#legacy-platform-object-defineownproperty
// step 1.1. If the result of calling IsDataDescriptor(Desc) is false, then
//   return false.\
"""),
        CxxUnlikelyIfNode(
            cond="v8_property_desc.has_get() || v8_property_desc.has_set()",
            attribute=None,
            body=[
                CxxLikelyIfNode(
                    cond="${info}.ShouldThrowOnError()",
                    attribute=None,
                    body=TextNode(
                        "${exception_state}.ThrowTypeError("
                        " \"Accessor properties are not allowed.\");")),
                TextNode("return v8::Intercepted::kYes;")
            ])
    ])

    if not cg_context.interface.indexed_and_named_properties.indexed_setter:
        body.extend([
            TextNode("""\
// step 1.2. If O does not implement an interface with an indexed property
//   setter, then return false.\
"""),
            CxxLikelyIfNode(
                cond="${info}.ShouldThrowOnError()",
                attribute=None,
                body=TextNode(
                    "${exception_state}.ThrowTypeError(\"Index property"
                    " setter is not supported.\");")),
            TextNode("return v8::Intercepted::kYes;"),
        ])
    else:
        body.append(
            TextNode("""\
// step 1.3. Invoke the indexed property setter with P and Desc.[[Value]].
return ${class_name}::IndexedPropertySetterCallback(
    ${index},
    ${v8_property_desc}.has_value()
        ? ${v8_property_desc}.value()
        : v8::Undefined(${isolate}).As<v8::Value>(),
    ${info});
"""))

    return func_decl, func_def


def make_indexed_property_descriptor_callback(cg_context, function_name):
    assert isinstance(cg_context, CodeGenContext)
    assert isinstance(function_name, str)

    return_type, arg_decls, arg_names = _make_interceptor_callback_args(
        cg_context, "Indexed", "Descriptor")
    func_decl, func_def = _make_interceptor_callback(
        cg_context, function_name, return_type, arg_decls, arg_names,
        cg_context.class_name, "IndexedPropertyDescriptor")
    body = func_def.body

    if not cg_context.interface.indexed_and_named_properties.indexed_getter:
        body.append(
            TextNode("""\
return ${class_name}::NamedPropertyDescriptorCallback(
    V8AtomicString(${isolate}, ${blink_property_index}), ${info});
"""))
        return func_decl, func_def

    pattern = """\
// LegacyPlatformObjectGetOwnProperty
// https://webidl.spec.whatwg.org/#LegacyPlatformObjectGetOwnProperty
// step 1.2. If index is a supported property index, then:
// step 1.2.3. If operation was defined without an identifier, then set
//   value to the result of performing the steps listed in the interface
//   description to determine the value of an indexed property with index
//   as the index.
// step 1.2.4. Otherwise, operation was defined with an identifier. Set
//   value to the result of performing the steps listed in the description
//   of operation with index as the only argument value.
auto intercepted =
    ${class_name}::IndexedPropertyGetterCallback(${index}, ${info});
if (intercepted == v8::Intercepted::kNo) {{
  // step 3. Return OrdinaryGetOwnProperty(O, P).
  // Do not intercept.  Fallback to OrdinaryGetOwnProperty.
  return v8::Intercepted::kNo;
}}
// step 1.2.6. Set desc.[[Value]] to the result of converting value to an
//   ECMAScript value.
// step 1.2.7. If O implements an interface with an indexed property setter,
//   then set desc.[[Writable]] to true, otherwise set it to false.
// step 1.2.8. Set desc.[[Enumerable]] and desc.[[Configurable]] to true.
v8::Local<v8::Value> v8_value = ${info}.GetReturnValue().Get();
v8::PropertyDescriptor desc(v8_value, /*writable=*/{cxx_writable});
desc.set_enumerable(true);
desc.set_configurable(true);
bindings::V8SetReturnValue(${info}, desc);
return v8::Intercepted::kYes;
"""
    writable = bool(
        cg_context.interface.indexed_and_named_properties.indexed_setter)
    cxx_writable = "true" if writable else "false"
    body.append(TextNode(_format(pattern, cxx_writable=cxx_writable)))

    return func_decl, func_def


def make_indexed_property_enumerator_callback(cg_context, function_name):
    assert isinstance(cg_context, CodeGenContext)
    assert isinstance(function_name, str)

    if not cg_context.interface.indexed_and_named_properties.indexed_getter:
        return None, None

    return_type, arg_decls, arg_names = _make_interceptor_callback_args(
        cg_context, "Indexed", "Enumerator")
    func_decl, func_def = _make_interceptor_callback(
        cg_context, function_name, return_type, arg_decls, arg_names,
        cg_context.class_name, "IndexedPropertyEnumerator")
    body = func_def.body

    body.append(
        TextNode("""\
// 3.9.6. [[OwnPropertyKeys]]
// https://webidl.spec.whatwg.org/#legacy-platform-object-ownpropertykeys
// step 2. If O supports indexed properties, then for each index of O's
//   supported property indices, in ascending numerical order, append
//   ! ToString(index) to keys.
uint32_t length = ${blink_receiver}->length();
v8::Local<v8::Array> array =
    bindings::EnumerateIndexedProperties(${isolate}, length);
bindings::V8SetReturnValue(${info}, array);
"""))
    body.accumulate(
        CodeGenAccumulator.require_include_headers([
            "third_party/blink/renderer/bindings/core/v8/generated_code_helper.h"
        ]))

    return func_decl, func_def


def make_named_property_getter_callback(cg_context, function_name):
    assert isinstance(cg_context, CodeGenContext)
    assert isinstance(function_name, str)

    return_type, arg_decls, arg_names = _make_interceptor_callback_args(
        cg_context, "Named", "Getter")
    func_decl, func_def = _make_interceptor_callback(cg_context, function_name,
                                                     return_type, arg_decls,
                                                     arg_names,
                                                     cg_context.class_name,
                                                     "NamedPropertyGetter")
    body = func_def.body

    bind_return_value(
        body, cg_context, overriding_args=["${blink_property_name}"])

    # The named property getter's implementation of Blink is not designed to
    # represent the property existence, and we have to determine the property
    # existence by heuristics.
    type = cg_context.return_type.unwrap()
    if type.is_any or type.is_object:
        not_found_expr = "${return_value}.IsEmpty()"
    elif type.is_string:
        not_found_expr = "${return_value}.IsNull()"
    elif type.is_interface:
        not_found_expr = "!${return_value}"
    elif type.is_union:
        not_found_expr = "!${return_value}"
    else:
        assert False

    if cg_context.class_like.identifier == "WindowProperties":
        body.append(
            TextNode("""\
// 3.7.4.1. [[GetOwnProperty]]
// https://webidl.spec.whatwg.org/#named-properties-object-getownproperty\
"""))
    else:
        body.append(
            TextNode("""\
// LegacyPlatformObjectGetOwnProperty
// https://webidl.spec.whatwg.org/#LegacyPlatformObjectGetOwnProperty\
"""))

    body.extend([
        TextNode("""\
// "If the result of running the named property visibility
//  algorithm with property name P and object O is true, then:"\
"""),
        CxxUnlikelyIfNode(cond=not_found_expr,
                          attribute=None,
                          body=[
                              TextNode("""\
// "Return OrdinaryGetOwnProperty(O, P)."
return v8::Intercepted::kNo;\
""")
                          ]),
        TextNode("""\
% if interface.identifier == "HTMLFormElement":
// At this point we know that the named property exists.
// We then UseCount whether the original property was shadowed or not.
${blink_receiver}->UseCountPropertyAccess(${v8_property_name}, ${info});
% endif\
"""),
        TextNode("""\
// "If operation was defined without an identifier, then set value to the result
//  of performing the steps listed in the interface description to determine the
//  value of a named property with P as the name."
// "Otherwise, operation was defined with an identifier. Set value to the result
//  of performing the steps listed in the description of operation with P as the
//  only argument value."\
"""),
        make_v8_set_return_value(cg_context),
        TextNode("return v8::Intercepted::kYes;"),
    ])

    return func_decl, func_def


def make_named_property_setter_callback(cg_context, function_name):
    assert isinstance(cg_context, CodeGenContext)
    assert isinstance(function_name, str)

    return_type, arg_decls, arg_names = _make_interceptor_callback_args(
        cg_context, "Named", "Setter")
    func_decl, func_def = _make_interceptor_callback(cg_context, function_name,
                                                     return_type, arg_decls,
                                                     arg_names,
                                                     cg_context.class_name,
                                                     "NamedPropertySetter")
    body = func_def.body

    if not cg_context.named_property_setter:
        throw_error_nodes = [
            CxxLikelyIfNode(
                cond="${info}.ShouldThrowOnError()",
                attribute=None,
                body=TextNode(
                    "${exception_state}.ThrowTypeError("
                    "\"Named property setter is not supported.\");")),
            TextNode("return v8::Intercepted::kYes;")
        ]

        if cg_context.class_like.identifier == "WindowProperties":
            body.append(
                TextNode("""\
// 3.7.4.2. [[DefineOwnProperty]]
// https://webidl.spec.whatwg.org/#named-properties-object-defineownproperty\
"""))
            body.extend(throw_error_nodes)
            return func_decl, func_def

        body.append(
            TextNode("""\
// 3.9.2. [[Set]]
// https://webidl.spec.whatwg.org/#legacy-platform-object-set
// step 3. Perform ? OrdinarySetWithOwnDescriptor(O, P, V, Receiver, ownDesc).\
"""))
        if ("LegacyOverrideBuiltIns" in
                cg_context.interface.extended_attributes):
            body.append(
                TextNode("""\
// [LegacyOverrideBuiltIns]
if (${info}.Holder()->GetRealNamedPropertyAttributesInPrototypeChain(
        ${current_context}, ${v8_property_name}).IsJust()) {
  // Do not intercept. Fallback to the existing property.
  return v8::Intercepted::kNo;
}
"""))

        body.extend([
            TextNode("bool does_exist = ${blink_receiver}->NamedPropertyQuery("
                     "${blink_property_name}, ${exception_state});"),
            CxxUnlikelyIfNode(cond="${exception_state}.HadException()",
                              attribute="[[unlikely]]",
                              body=TextNode("return v8::Intercepted::kYes;")),
            CxxUnlikelyIfNode(cond="does_exist",
                              attribute=None,
                              body=throw_error_nodes),
            TextNode("""\
// Do not intercept. Fallback and let it define a new own property.
return v8::Intercepted::kNo;
""")
        ])
        return func_decl, func_def

    bind_return_value(
        body,
        cg_context,
        overriding_args=["${blink_property_name}", "${blink_property_value}"])
    body.register_code_symbol(
        make_v8_to_blink_value(
            "blink_property_value",
            "${v8_property_value}",
            cg_context.named_property_setter.arguments[1].idl_type,
            argument=cg_context.named_property_setter.arguments[1],
            error_exit_return_statement="return v8::Intercepted::kYes;"))

    body.extend([
        TextNode("""\
// 3.9.2. [[Set]]
// https://webidl.spec.whatwg.org/#legacy-platform-object-set
// step 1. If O and Receiver are the same object, then:\
"""),
        CxxLikelyIfNode(cond="${info}.Holder() == ${info}.This()",
                        attribute=None,
                        body=[
                            TextNode("""\
// step 1.2.1. Invoke the named property setter with P and V.\
"""),
                            make_steps_of_ce_reactions(cg_context),
                            EmptyNode(),
                            make_v8_set_return_value(cg_context),
                            TextNode("""\
% if interface.identifier == "CSSStyleDeclaration" or \
     interface.identifier == "HTMLEmbedElement" or \
     interface.identifier == "HTMLObjectElement":
// ${interface.identifier} is abusing named properties.
// Do not intercept if the property is not found.
return BlinkInterceptorResultToV8Intercepted(${return_value});
% else:
// Pretend like the set request was intercepted regardless of the actual
// ${return_value} returned.
return v8::Intercepted::kYes;
% endif\
"""),
                        ]),
        EmptyNode(),
        TextNode("""\
// Do not intercept.  Fallback to OrdinarySetWithOwnDescriptor.
return v8::Intercepted::kNo;\
"""),
    ])

    return func_decl, func_def


def make_named_property_deleter_callback(cg_context, function_name):
    assert isinstance(cg_context, CodeGenContext)
    assert isinstance(function_name, str)

    return_type, arg_decls, arg_names = _make_interceptor_callback_args(
        cg_context, "Named", "Deleter")
    func_decl, func_def = _make_interceptor_callback(cg_context, function_name,
                                                     return_type, arg_decls,
                                                     arg_names,
                                                     cg_context.class_name,
                                                     "NamedPropertyDeleter")
    body = func_def.body

    props = cg_context.interface.indexed_and_named_properties

    throw_error_nodes = [
        TextNode("bindings::V8SetReturnValue(${info}, false);"),
        CxxLikelyIfNode(cond="${info}.ShouldThrowOnError()",
                        attribute=None,
                        body=TextNode(
                            "${exception_state}.ThrowTypeError(\""
                            "Named property deleter is not supported.\");")),
        TextNode("return v8::Intercepted::kYes;"),
    ]

    if cg_context.class_like.identifier == "WindowProperties":
        body.append(
            TextNode("""\
// 3.7.4.3. [[Delete]]
// https://webidl.spec.whatwg.org/#named-properties-object-delete\
"""))
        body.extend(throw_error_nodes)
        return func_decl, func_def

    if (not cg_context.named_property_deleter
            and "NotEnumerable" in props.named_getter.extended_attributes):
        body.append(
            TextNode("""\
// 3.9.4. [[Delete]]
// https://webidl.spec.whatwg.org/#legacy-platform-object-delete
// step 2. If O supports named properties, O does not implement an interface
//   with the [Global] extended attribute and the result of calling the
//   named property visibility algorithm with property name P and object O
//   is true, then:
//
// There is no easy way to determine whether the named property is visible
// or not.  Just do not intercept and fallback to the default behavior.
return v8::Intercepted::kNo;
"""))
        return func_decl, func_def

    if not cg_context.named_property_deleter:
        body.extend([
            TextNode("""\
// 3.9.4. [[Delete]]
// https://webidl.spec.whatwg.org/#legacy-platform-object-delete
// step 2. If O supports named properties, O does not implement an interface
//   with the [Global] extended attribute and the result of calling the
//   named property visibility algorithm with property name P and object O
//   is true, then:
// step 2.1. If O does not implement an interface with a named property
//   deleter, then return false.\
"""),
            TextNode("bool does_exist = ${blink_receiver}->NamedPropertyQuery("
                     "${blink_property_name}, ${exception_state});"),
            CxxUnlikelyIfNode(cond="${exception_state}.HadException()",
                              attribute="[[unlikely]]",
                              body=TextNode("return v8::Intercepted::kYes;")),
            CxxUnlikelyIfNode(cond="does_exist",
                              attribute=None,
                              body=throw_error_nodes),
            EmptyNode(),
            TextNode("""\
// Do not intercept.
return v8::Intercepted::kNo;\
""")
        ])
        return func_decl, func_def

    bind_return_value(
        body, cg_context, overriding_args=["${blink_property_name}"])

    body.extend([
        TextNode("""\
// 3.9.4. [[Delete]]
// https://webidl.spec.whatwg.org/#legacy-platform-object-delete\
"""),
        make_steps_of_ce_reactions(cg_context),
        EmptyNode(),
        make_v8_set_return_value(cg_context),
        CxxUnlikelyIfNode(
            cond="${return_value} == NamedPropertyDeleterResult::kDidNotDelete",
            attribute=None,
            body=[
                CxxLikelyIfNode(cond="${info}.ShouldThrowOnError()",
                                attribute=None,
                                body=TextNode(
                                    "${exception_state}.ThrowTypeError("
                                    "\"Failed to delete a property.\");")),
                TextNode("return v8::Intercepted::kYes;"),
            ]),
        TextNode(
            "return BlinkInterceptorResultToV8Intercepted(${return_value});"),
    ])

    return func_decl, func_def


def make_named_property_definer_callback(cg_context, function_name):
    assert isinstance(cg_context, CodeGenContext)
    assert isinstance(function_name, str)

    return_type, arg_decls, arg_names = _make_interceptor_callback_args(
        cg_context, "Named", "Definer")
    func_decl, func_def = _make_interceptor_callback(cg_context, function_name,
                                                     return_type, arg_decls,
                                                     arg_names,
                                                     cg_context.class_name,
                                                     "NamedPropertyDefiner")
    body = func_def.body

    throw_error_nodes = [
        CxxLikelyIfNode(cond="${info}.ShouldThrowOnError()",
                        attribute=None,
                        body=TextNode(
                            "${exception_state}.ThrowTypeError("
                            "\"Named property setter is not supported.\");")),
        TextNode("return v8::Intercepted::kYes;")
    ]

    if cg_context.interface.identifier == "WindowProperties":
        body.append(
            TextNode("""\
// 3.7.4.2. [[DefineOwnProperty]]
// https://webidl.spec.whatwg.org/#named-properties-object-defineownproperty \
"""))
        body.extend(throw_error_nodes)
        return func_decl, func_def

    if cg_context.interface.identifier in ("CSSStyleDeclaration",
                                           "HTMLEmbedElement",
                                           "HTMLObjectElement"):
        body.append(
            TextNode("""\
// ${interface.identifier} is abusing named properties.
// Do not intercept.  Fallback to OrdinaryDefineOwnProperty.
return v8::Intercepted::kNo;
"""))
        return func_decl, func_def

    if not cg_context.interface.indexed_and_named_properties.named_setter:
        body.extend([
            TextNode("""\
// 3.9.3. [[DefineOwnProperty]]
// https://webidl.spec.whatwg.org/#legacy-platform-object-defineownproperty
// step 2.1. Let creating be true if P is not a supported property name, and
//   false otherwise.
// step 2.2.1. If creating is false and O does not implement an interface
//   with a named property setter, then return false.\
"""),
            TextNode("bool does_exist = ${blink_receiver}->NamedPropertyQuery("
                     "${blink_property_name}, ${exception_state});"),
            CxxUnlikelyIfNode(cond="${exception_state}.HadException()",
                              attribute="[[unlikely]]",
                              body=TextNode("return v8::Intercepted::kYes;")),
            CxxUnlikelyIfNode(cond="does_exist",
                              attribute=None,
                              body=throw_error_nodes),
            EmptyNode(),
            TextNode("""\
// Do not intercept. Fallback to OrdinaryDefineOwnProperty.
return v8::Intercepted::kNo;
""")
        ])
    else:
        body.extend([
            TextNode("""\
// 3.9.3. [[DefineOwnProperty]]
// https://webidl.spec.whatwg.org/#legacy-platform-object-defineownproperty
// step 2.2.2. If O implements an interface with a named property setter,
//   then:
// step 2.2.2.1. If the result of calling IsDataDescriptor(Desc) is false,
//   then return false.
"""),
            CxxUnlikelyIfNode(
                cond="v8_property_desc.has_get() || v8_property_desc.has_set()",
                attribute=None,
                body=[
                    CxxLikelyIfNode(
                        cond="${info}.ShouldThrowOnError()",
                        attribute=None,
                        body=[
                            TextNode(
                                "${exception_state}.ThrowTypeError("
                                " \"Accessor properties are not allowed.\");"),
                        ]),
                    TextNode("return v8::Intercepted::kYes;"),
                ]),
            EmptyNode(),
            TextNode("""\
// step 2.2.2.2. Invoke the named property setter with P and Desc.[[Value]].
return ${class_name}::NamedPropertySetterCallback(
    ${v8_property_name},
    ${v8_property_desc}.has_value()
        ? ${v8_property_desc}.value()
        : v8::Undefined(${isolate}).As<v8::Value>(),
    ${info});
""")
        ])

    return func_decl, func_def


def make_named_property_descriptor_callback(cg_context, function_name):
    assert isinstance(cg_context, CodeGenContext)
    assert isinstance(function_name, str)

    return_type, arg_decls, arg_names = _make_interceptor_callback_args(
        cg_context, "Named", "Descriptor")
    func_decl, func_def = _make_interceptor_callback(
        cg_context, function_name, return_type, arg_decls, arg_names,
        cg_context.class_name, "NamedPropertyDescriptor")
    body = func_def.body

    if cg_context.class_like.identifier == "WindowProperties":
        body.append(
            TextNode("""\
// 3.7.4.1. [[GetOwnProperty]]
// https://webidl.spec.whatwg.org/#named-properties-object-getownproperty
"""))
    else:
        body.append(
            TextNode("""\
// LegacyPlatformObjectGetOwnProperty
// https://webidl.spec.whatwg.org/#LegacyPlatformObjectGetOwnProperty
"""))

    if ("LegacyOverrideBuiltIns" not in
            cg_context.interface.extended_attributes):
        body.extend([
            TextNode("""\
// "If the result of running the named property visibility algorithm with
//  property name P and object O is true, then:"\
"""),
            TextNode("""\
if (${v8_receiver}->GetRealNamedPropertyAttributesInPrototypeChain(
        ${current_context}, ${v8_property_name}).IsJust()) {
  // Do not intercept.  Fallback to OrdinaryGetOwnProperty.
  return v8::Intercepted::kNo;
}
""")
        ])

    pattern = """\
// "If operation was defined without an identifier, then set value to the result
//  of performing the steps listed in the interface description to determine the
//  value of a named property with P as the name."
// "Otherwise, operation was defined with an identifier. Set value to the result
//  of performing the steps listed in the description of operation with P as the
//  only argument value."
auto intercepted =
    ${class_name}::NamedPropertyGetterCallback(${v8_property_name}, ${info});
// "Return OrdinaryGetOwnProperty(O, P)."
if (intercepted == v8::Intercepted::kNo) {{
  // Do not intercept.  Fallback to OrdinaryGetOwnProperty.
  return v8::Intercepted::kNo;
}}

// "Let desc be a newly created Property Descriptor with no fields."
// "Set desc.[[Value]] to the result of converting value to an ECMAScript
//  value."
// "If O implements an interface with a named property setter, then set
//  desc.[[Writable]] to true, otherwise set it to false."
// "If O implements an interface with the [LegacyUnenumerableNamedProperties]
//  extended attribute, then set desc.[[Enumerable]] to false, otherwise set
//  it to true."
// "Set desc.[[Configurable]] to true."
// "Return desc."
v8::Local<v8::Value> v8_value = ${info}.GetReturnValue().Get();
v8::PropertyDescriptor desc(v8_value, /*writable=*/{cxx_writable});
desc.set_enumerable({cxx_enumerable});
desc.set_configurable(true);
bindings::V8SetReturnValue(${info}, desc);
return v8::Intercepted::kYes;
"""
    props = cg_context.interface.indexed_and_named_properties
    # https://webidl.spec.whatwg.org/#named-properties-object-getownproperty
    # sets [[Writable]] to true for the named properties object, which is called
    # WindowProperties in our implementation.
    writable = (bool(props.named_setter)
                or cg_context.class_like.identifier == "WindowProperties")
    cxx_writable = "true" if writable else "false"
    enumerable = props.is_named_property_enumerable
    cxx_enumerable = "true" if enumerable else "false"
    body.append(
        TextNode(
            _format(pattern,
                    cxx_writable=cxx_writable,
                    cxx_enumerable=cxx_enumerable)))

    return func_decl, func_def


def make_named_property_query_callback(cg_context, function_name):
    assert isinstance(cg_context, CodeGenContext)
    assert isinstance(function_name, str)

    props = cg_context.interface.indexed_and_named_properties
    if "NotEnumerable" in props.named_getter.extended_attributes:
        return None, None

    return_type, arg_decls, arg_names = _make_interceptor_callback_args(
        cg_context, "Named", "Query")
    func_decl, func_def = _make_interceptor_callback(cg_context, function_name,
                                                     return_type, arg_decls,
                                                     arg_names,
                                                     cg_context.class_name,
                                                     "NamedPropertyQuery")
    body = func_def.body

    flags = []
    if not props.named_setter:
        flags.append("v8::ReadOnly")
    if not props.is_named_property_enumerable:
        flags.append("v8::DontEnum")
    if not flags:
        flags.append("v8::None")
    if len(flags) == 1:
        property_attribute = flags[0]
    else:
        property_attribute = " | ".join(flags)

    body.extend([
        TextNode("bool does_exist = ${blink_receiver}->NamedPropertyQuery("
                 "${blink_property_name}, ${exception_state});"),
        CxxLikelyIfNode(cond="!does_exist",
                        attribute=None,
                        body=TextNode("return v8::Intercepted::kNo;")),
        TextNode(
            _format(
                "bindings::V8SetReturnValue"
                "(${info}, uint32_t({property_attribute}));",
                property_attribute=property_attribute)),
        TextNode("return v8::Intercepted::kYes;"),
    ])

    return func_decl, func_def


def make_named_property_enumerator_callback(cg_context, function_name):
    assert isinstance(cg_context, CodeGenContext)
    assert isinstance(function_name, str)

    props = cg_context.interface.indexed_and_named_properties
    if "NotEnumerable" in props.named_getter.extended_attributes:
        return None, None

    return_type, arg_decls, arg_names = _make_interceptor_callback_args(
        cg_context, "Named", "Enumerator")
    func_decl, func_def = _make_interceptor_callback(
        cg_context, function_name, return_type, arg_decls, arg_names,
        cg_context.class_name, "NamedPropertyEnumerator")
    body = func_def.body

    body.extend([
        TextNode("""\
// 3.9.6. [[OwnPropertyKeys]]
// https://webidl.spec.whatwg.org/#legacy-platform-object-ownpropertykeys
// step 3. If O supports named properties, then for each P of O's supported
//   property names that is visible according to the named property
//   visibility algorithm, append P to keys.\
"""),
        TextNode("Vector<String> blink_property_names;"),
        TextNode("${blink_receiver}->NamedPropertyEnumerator("
                 "blink_property_names, ${exception_state});"),
        CxxUnlikelyIfNode(cond="${exception_state}.HadException()",
                          attribute="[[unlikely]]",
                          body=TextNode("return;")),
        TextNode("""\
bindings::V8SetReturnValue(
    ${info},
    ToV8Traits<IDLSequence<IDLString>>::ToV8(${script_state},
                                             blink_property_names)
         .As<v8::Array>());
""")
    ])

    return func_decl, func_def


# ----------------------------------------------------------------------------
# Callback functions of cross origin interceptors
# ----------------------------------------------------------------------------


def make_cross_origin_access_check_callback(cg_context, function_name):
    assert isinstance(cg_context, CodeGenContext)
    assert isinstance(function_name, str)

    func_def = CxxFuncDefNode(
        name=function_name,
        arg_decls=[
            "v8::Local<v8::Context> accessing_context",
            "v8::Local<v8::Object> accessed_object",
            "v8::Local<v8::Value> unused_data",
        ],
        return_type="bool")
    func_def.set_base_template_vars(cg_context.template_bindings())
    body = func_def.body
    body.add_template_var("accessing_context", "accessing_context")
    body.add_template_var("accessed_object", "accessed_object")

    if cg_context.interface.identifier == "Window":
        blink_class = "DOMWindow"
    else:
        blink_class = blink_class_name(cg_context.interface)

    body.extend([
        TextNode(
            _format(
                "{blink_class}* blink_accessed_object = "
                "${class_name}::ToWrappableUnsafe("
                "accessing_context->GetIsolate(),"
                "${accessed_object});",
                blink_class=blink_class)),
        TextNode("return BindingSecurity::ShouldAllowAccessTo("
                 "ToLocalDOMWindow(${accessing_context}), "
                 "blink_accessed_object);"),
    ])

    return func_def


def make_cross_origin_throwing_callback(cg_context):
    """
    This generates the following functions:
    CrossOriginNamedDeleterCallback
    CrossOriginNamedDefinerCallback
    CrossOriginIndexedSetterCallback
    CrossOriginIndexedDeleterCallback
    CrossOriginIndexedDefinerCallback
    """
    assert isinstance(cg_context, CodeGenContext)
    assert (cg_context.named_interceptor_kind
            or cg_context.indexed_interceptor_kind)
    assert (not cg_context.named_interceptor_kind
            or not cg_context.indexed_interceptor_kind)

    if cg_context.named_interceptor_kind:
        named_or_indexed = "Named"
        callback_type = cg_context.named_interceptor_kind
    else:
        named_or_indexed = "Indexed"
        callback_type = cg_context.indexed_interceptor_kind

    return_type, arg_decls, arg_names = _make_interceptor_callback_args(
        cg_context, named_or_indexed, callback_type)
    func_def = _make_interceptor_callback_def(
        cg_context, "CrossOrigin{}{}Callback".format(named_or_indexed,
                                                     callback_type),
        return_type, arg_decls, arg_names, None,
        "CrossOriginProperty_{}Property{}".format(named_or_indexed,
                                                  callback_type))

    func_def.body.extend([
        _make_throw_security_error(),
        TextNode("return v8::Intercepted::kYes;"),
    ])

    return func_def


def make_cross_origin_indexed_getter_callback(cg_context, function_name):
    assert isinstance(cg_context, CodeGenContext)
    assert isinstance(function_name, str)

    return_type, arg_decls, arg_names = _make_interceptor_callback_args(
        cg_context, "Indexed", "Getter")
    func_def = _make_interceptor_callback_def(
        cg_context, function_name, return_type, arg_decls, arg_names, None,
        "CrossOriginProperty_IndexedPropertyGetter")
    body = func_def.body

    if cg_context.interface.identifier != "Window":
        body.extend([
            _make_throw_security_error(),
            TextNode("return v8::Intercepted::kYes;"),
        ])
        return func_def

    bind_return_value(body, cg_context, overriding_args=["${index}"])

    # Do this before the index verification below, because we do not want to
    # reveal any information about the number of frames in this window.
    body.extend([
        make_check_coop_restrict_properties_access(cg_context),
        EmptyNode(),
    ])

    body.extend([
        CxxLikelyIfNode(cond="${index} >= ${blink_receiver}->length()",
                        attribute=None,
                        body=[
                            _make_throw_security_error(),
                            TextNode("return v8::Intercepted::kYes;"),
                        ]),
        make_v8_set_return_value(cg_context),
        TextNode("return v8::Intercepted::kYes;"),
    ])

    return func_def


def make_cross_origin_indexed_descriptor_callback(cg_context, function_name):
    assert isinstance(cg_context, CodeGenContext)
    assert isinstance(function_name, str)

    return_type, arg_decls, arg_names = _make_interceptor_callback_args(
        cg_context, "Indexed", "Descriptor")
    func_def = _make_interceptor_callback_def(
        cg_context, function_name, return_type, arg_decls, arg_names, None,
        "CrossOriginProperty_IndexedPropertyDescriptor")
    body = func_def.body

    if cg_context.interface.identifier != "Window":
        body.extend([
            _make_throw_security_error(),
            TextNode("return v8::Intercepted::kYes;"),
        ])
        return func_def

    body.append(
        TextNode("""\
auto intercepted = CrossOriginIndexedGetterCallback(${index}, ${info});
if (intercepted == v8::Intercepted::kNo) {
  return v8::Intercepted::kNo;
}
// TODO(ishell, 328490288): inline CrossOriginIndexedGetterCallback() here
// in order to avoid this non-robust way of detecting whether exception
// was thrown.
v8::Local<v8::Value> v8_value = ${info}.GetReturnValue().Get();
if (v8_value->IsUndefined()) {
  // Must have already thrown a SecurityError.
  return v8::Intercepted::kYes;
}

v8::PropertyDescriptor desc(v8_value, /*writable=*/false);
desc.set_enumerable(true);
desc.set_configurable(true);
bindings::V8SetReturnValue(${info}, desc);
return v8::Intercepted::kYes;
"""))

    return func_def


def make_cross_origin_indexed_enumerator_callback(cg_context, function_name):
    assert isinstance(cg_context, CodeGenContext)
    assert isinstance(function_name, str)

    return_type, arg_decls, arg_names = _make_interceptor_callback_args(
        cg_context, "Indexed", "Enumerator")
    func_def = _make_interceptor_callback_def(
        cg_context, function_name, return_type, arg_decls, arg_names, None,
        "CrossOriginProperty_IndexedPropertyEnumerator")
    body = func_def.body

    if cg_context.interface.identifier != "Window":
        return func_def

    body.append(
        TextNode("""\
uint32_t length = ${blink_receiver}->length();
v8::Local<v8::Array> array =
    bindings::EnumerateIndexedProperties(${isolate}, length);
bindings::V8SetReturnValue(${info}, array);
"""))

    return func_def


def make_cross_origin_named_getter_callback(cg_context, function_name):
    assert isinstance(cg_context, CodeGenContext)
    assert isinstance(function_name, str)

    return_type, arg_decls, arg_names = _make_interceptor_callback_args(
        cg_context, "Named", "Getter")
    func_def = _make_interceptor_callback_def(
        cg_context, function_name, return_type, arg_decls, arg_names, None,
        "CrossOriginProperty_NamedPropertyGetter")
    body = func_def.body

    string_case_body = []
    string_case_body.extend([
        CxxForLoopNode(
            cond="const auto& attribute : kCrossOriginAttributeTable",
            body=[
                CxxLikelyIfNode(
                    cond="${blink_property_name} != attribute.name",
                    attribute=None,
                    body=TextNode("continue;")),
                CxxUnlikelyIfNode(
                    cond="!attribute.get_value",
                    attribute="[[unlikely]]",
                    body=[
                        _make_throw_security_error(),
                        TextNode("return v8::Intercepted::kYes;"),
                    ]),
                TextNode(
                    "return attribute.get_value(${v8_property_name}, ${info});"
                ),
            ]),
        CxxForLoopNode(
            cond="const auto& operation : kCrossOriginOperationTable",
            body=[
                CxxLikelyIfNode(
                    cond="${blink_property_name} != operation.name",
                    attribute=None,
                    body=TextNode("continue;")),
                TextNode("v8::Local<v8::Function> function;"),
                CxxLikelyIfNode(
                    cond="bindings::GetCrossOriginFunction("
                    "${isolate}, operation.name, operation.callback, "
                    "operation.func_length,"
                    "${class_name}::GetWrapperTypeInfo(), "
                    "v8::ExceptionContext::kOperation, ${class_like_name})"
                    ".ToLocal(&function)",
                    attribute=None,
                    body=TextNode(
                        "bindings::V8SetReturnValue(${info}, function);")),
                TextNode("return v8::Intercepted::kYes;")
            ])
    ])
    if cg_context.interface.identifier == "Window":
        string_case_body.append(
            TextNode("""\
// Window object's document-tree child browsing context name property set
auto&& return_value = ${blink_receiver}->AnonymousNamedGetter(
    ${blink_property_name});
if (!return_value.IsEmpty()) {
  bindings::V8SetReturnValue(${info}, return_value);
  return v8::Intercepted::kYes;
}
"""))

    body.extend([
        CxxLikelyIfNode(cond="${v8_property_name}->IsString()",
                        attribute=None,
                        body=string_case_body),
        EmptyNode(),
        TextNode("""\
// 7.2.3.2 CrossOriginPropertyFallback ( P )
// https://html.spec.whatwg.org/C/#crossoriginpropertyfallback-(-p-)
if (bindings::IsSupportedInCrossOriginPropertyFallback(
        ${info}.GetIsolate(), ${v8_property_name})) {
  ${info}.GetReturnValue().SetUndefined();
  return v8::Intercepted::kYes;
}
"""),
        _make_throw_security_error(),
        TextNode("return v8::Intercepted::kYes;"),
    ])

    return func_def


def make_cross_origin_named_setter_callback(cg_context, function_name):
    assert isinstance(cg_context, CodeGenContext)
    assert isinstance(function_name, str)

    return_type, arg_decls, arg_names = _make_interceptor_callback_args(
        cg_context, "Named", "Setter")
    func_def = _make_interceptor_callback_def(
        cg_context, function_name, return_type, arg_decls, arg_names, None,
        "CrossOriginProperty_NamedPropertySetter")
    body = func_def.body

    string_case_body = []
    string_case_body.append(
        TextNode("""\
for (const auto& attribute : kCrossOriginAttributeTable) {
  if (${blink_property_name} == attribute.name && attribute.set_value) {
    return attribute.set_value(${v8_property_name}, ${v8_property_value},
                               ${info});
  }
}
"""))

    body.extend([
        CxxLikelyIfNode(cond="${v8_property_name}->IsString()",
                        attribute=None,
                        body=string_case_body),
        EmptyNode(),
        _make_throw_security_error(),
        TextNode("return v8::Intercepted::kYes;"),
    ])

    return func_def


def make_cross_origin_named_descriptor_callback(cg_context, function_name):
    assert isinstance(cg_context, CodeGenContext)
    assert isinstance(function_name, str)

    return_type, arg_decls, arg_names = _make_interceptor_callback_args(
        cg_context, "Named", "Descriptor")
    func_def = _make_interceptor_callback_def(
        cg_context, function_name, return_type, arg_decls, arg_names, None,
        "CrossOriginProperty_NamedPropertyDescriptor")
    body = func_def.body

    string_case_body = []
    string_case_body.append(
        TextNode("""\
// 7.2.3.4 CrossOriginGetOwnPropertyHelper ( O, P )
// https://html.spec.whatwg.org/C/#crossorigingetownpropertyhelper-(-o,-p-)
for (const auto& attribute : kCrossOriginAttributeTable) {
  if (${blink_property_name} != attribute.name)
    continue;
  v8::Local<v8::Value> get;
  v8::Local<v8::Value> set;
  if (!bindings::GetCrossOriginGetterSetter(
           ${info}.GetIsolate(), attribute.name, attribute.get_callback, 0,
           ${class_name}::GetWrapperTypeInfo(),
           v8::ExceptionContext::kAttributeGet, ${class_like_name})
           .ToLocal(&get) ||
      !bindings::GetCrossOriginGetterSetter(
           ${info}.GetIsolate(), attribute.name, attribute.set_callback, 1,
           ${class_name}::GetWrapperTypeInfo(),
           v8::ExceptionContext::kAttributeSet, ${class_like_name})
           .ToLocal(&set)) {
    // Exception was thrown which means that the request was intercepted.
    return v8::Intercepted::kYes;
  }
  v8::PropertyDescriptor desc(get, set);
  desc.set_enumerable(false);
  desc.set_configurable(true);
  bindings::V8SetReturnValue(${info}, desc);
  return v8::Intercepted::kYes;
}
for (const auto& operation : kCrossOriginOperationTable) {
  if (${blink_property_name} != operation.name)
    continue;
  v8::Local<v8::Function> function;
  if (!bindings::GetCrossOriginFunction(
           ${info}.GetIsolate(), operation.name, operation.callback,
           operation.func_length, ${class_name}::GetWrapperTypeInfo(),
           v8::ExceptionContext::kOperation, ${class_like_name})
           .ToLocal(&function)) {
    // Exception was thrown which means that the request was intercepted.
    return v8::Intercepted::kYes;
  }
  v8::PropertyDescriptor desc(function, /*writable=*/false);
  desc.set_enumerable(false);
  desc.set_configurable(true);
  bindings::V8SetReturnValue(${info}, desc);
  return v8::Intercepted::kYes;
}
"""))
    if cg_context.interface.identifier == "Window":
        string_case_body.append(
            TextNode("""\
// Window object's document-tree child browsing context name property set
auto&& return_value = ${blink_receiver}->AnonymousNamedGetter(
    ${blink_property_name});
if (!return_value.IsEmpty()) {
  v8::PropertyDescriptor desc(return_value, /*writable=*/false);
  desc.set_enumerable(false);
  desc.set_configurable(true);
  bindings::V8SetReturnValue(${info}, desc);
  return v8::Intercepted::kYes;
}
"""))

    body.extend([
        CxxLikelyIfNode(cond="${v8_property_name}->IsString()",
                        attribute=None,
                        body=string_case_body),
        EmptyNode(),
        TextNode("""\
// 7.2.3.2 CrossOriginPropertyFallback ( P )
// https://html.spec.whatwg.org/C/#crossoriginpropertyfallback-(-p-)
if (bindings::IsSupportedInCrossOriginPropertyFallback(
        ${info}.GetIsolate(), ${v8_property_name})) {
  v8::PropertyDescriptor desc(v8::Undefined(${info}.GetIsolate()),
                              /*writable=*/false);
  desc.set_enumerable(false);
  desc.set_configurable(true);
  bindings::V8SetReturnValue(${info}, desc);
  return v8::Intercepted::kYes;
}
"""),
        _make_throw_security_error(),
        TextNode("return v8::Intercepted::kYes;"),
    ])

    return func_def


def make_cross_origin_named_query_callback(cg_context, function_name):
    assert isinstance(cg_context, CodeGenContext)
    assert isinstance(function_name, str)

    return_type, arg_decls, arg_names = _make_interceptor_callback_args(
        cg_context, "Named", "Query")
    func_def = _make_interceptor_callback_def(
        cg_context, function_name, return_type, arg_decls, arg_names, None,
        "CrossOriginProperty_NamedPropertyQuery")
    body = func_def.body

    string_case_body = []
    string_case_body.append(
        TextNode("""\
// 7.2.3.4 CrossOriginGetOwnPropertyHelper ( O, P )
// https://html.spec.whatwg.org/C/#crossorigingetownpropertyhelper-(-o,-p-)
for (const auto& attribute : kCrossOriginAttributeTable) {
  if (${blink_property_name} != attribute.name)
    continue;
  int32_t v8_property_attribute = v8::DontEnum;
  if (!attribute.set_callback)
    v8_property_attribute |= v8::ReadOnly;
  bindings::V8SetReturnValue(${info}, v8_property_attribute);
  return v8::Intercepted::kYes;
}
for (const auto& operation : kCrossOriginOperationTable) {
  if (${blink_property_name} != operation.name)
    continue;
  int32_t v8_property_attribute = v8::DontEnum | v8::ReadOnly;
  bindings::V8SetReturnValue(${info}, v8_property_attribute);
  return v8::Intercepted::kYes;
}
return v8::Intercepted::kNo;
"""))

    body.extend([
        CxxLikelyIfNode(cond="${v8_property_name}->IsString()",
                        attribute=None,
                        body=string_case_body),
        EmptyNode(),
        TextNode("""\
// 7.2.3.2 CrossOriginPropertyFallback ( P )
// https://html.spec.whatwg.org/C/#crossoriginpropertyfallback-(-p-)
if (bindings::IsSupportedInCrossOriginPropertyFallback(
        ${info}.GetIsolate(), ${v8_property_name})) {
  int32_t v8_property_attribute = v8::DontEnum | v8::ReadOnly;
  bindings::V8SetReturnValue(${info}, v8_property_attribute);
  return v8::Intercepted::kYes;
}
return v8::Intercepted::kNo;
"""),
    ])

    return func_def


def make_cross_origin_named_enumerator_callback(cg_context, function_name):
    assert isinstance(cg_context, CodeGenContext)
    assert isinstance(function_name, str)

    return_type, arg_decls, arg_names = _make_interceptor_callback_args(
        cg_context, "Named", "Enumerator")
    func_def = _make_interceptor_callback_def(
        cg_context, function_name, return_type, arg_decls, arg_names, None,
        "CrossOriginProperty_NamedPropertyEnumerator")
    body = func_def.body

    body.append(
        TextNode("""\
bindings::V8SetReturnValue(
    ${info},
    bindings::EnumerateCrossOriginProperties(
        ${isolate},
        kCrossOriginAttributeTable,
        kCrossOriginOperationTable));
"""))

    return func_def


# ----------------------------------------------------------------------------
# Installer functions
# ----------------------------------------------------------------------------

# FN = function name
FN_INSTALL_INTERFACE_TEMPLATE = name_style.func("InstallInterfaceTemplate")
FN_INSTALL_UNCONDITIONAL_PROPS = name_style.func(
    "InstallUnconditionalProperties")
FN_INSTALL_CONTEXT_INDEPENDENT_PROPS = name_style.func(
    "InstallContextIndependentProperties")
FN_INSTALL_CONTEXT_DEPENDENT_PROPS = name_style.func(
    "InstallContextDependentProperties")

# TP = trampoline name
TP_INSTALL_INTERFACE_TEMPLATE = name_style.member_var(
    "install_interface_template_func")
TP_INSTALL_UNCONDITIONAL_PROPS = name_style.member_var(
    "install_unconditional_props_func")
TP_INSTALL_CONTEXT_INDEPENDENT_PROPS = name_style.member_var(
    "install_context_independent_props_func")
TP_INSTALL_CONTEXT_DEPENDENT_PROPS = name_style.member_var(
    "install_context_dependent_props_func")


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

    S = SymbolNode

    local_vars = []

    local_vars.extend([
        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("isolate", "v8::Isolate* ${isolate} = ${v8_context}->GetIsolate();"),
        S("script_state", ("ScriptState* ${script_state} = "
                           "ScriptState::From(${isolate}, ${v8_context});")),
        S("wrapper_type_info",
          ("const WrapperTypeInfo* const ${wrapper_type_info} = "
           "${class_name}::GetWrapperTypeInfo();")),
    ])

    if (cg_context.interface or cg_context.async_iterator
            or cg_context.sync_iterator):
        local_vars.extend([
            S("interface_function_template",
              ("v8::Local<v8::FunctionTemplate> "
               "${interface_function_template} = "
               "${interface_template}.As<v8::FunctionTemplate>();")),
            S("instance_object_template",
              ("v8::Local<v8::ObjectTemplate> ${instance_object_template} = "
               "${interface_function_template}->InstanceTemplate();")),
            S("instance_template",
              ("v8::Local<v8::Template> ${instance_template} = "
               "${instance_object_template};")),
            S("prototype_object_template",
              ("v8::Local<v8::ObjectTemplate> ${prototype_object_template} = "
               "${interface_function_template}->PrototypeTemplate();")),
            S("prototype_template",
              ("v8::Local<v8::Template> ${prototype_template} = "
               "${prototype_object_template};")),
            S("signature", ("v8::Local<v8::Signature> ${signature} = "
                            "v8::Signature::New(${isolate}, "
                            "${interface_function_template});")),
        ])
    elif cg_context.namespace:
        local_vars.extend([
            S("namespace_object_template",
              ("v8::Local<v8::ObjectTemplate> "
               "${namespace_object_template} = "
               "${interface_template}.As<v8::ObjectTemplate>();")),
            S("instance_template",
              "v8::Local<v8::Template> ${instance_template};"),
            S("prototype_template",
              "v8::Local<v8::Template> ${prototype_template};"),
            S("signature", "v8::Local<v8::Signature> ${signature};"),
        ])
    elif cg_context.callback_interface:
        local_vars.extend([
            S("interface_function_template",
              ("v8::Local<v8::FunctionTemplate> "
               "${interface_function_template} = "
               "${interface_template}.As<v8::FunctionTemplate>();")),
            S("instance_template",
              "v8::Local<v8::Template> ${instance_template};"),
            S("prototype_template",
              "v8::Local<v8::Template> ${prototype_template};"),
            S("signature", "v8::Local<v8::Signature> ${signature};"),
        ])

    # context_feature_settings
    node = S("context_feature_settings",
             ("const ContextFeatureSettings* ${context_feature_settings} = "
              "ContextFeatureSettings::From("
              "${execution_context}, "
              "ContextFeatureSettings::CreationMode::kDontCreateIfNotExists"
              ");"))
    node.accumulate(
        CodeGenAccumulator.require_include_headers([
            "third_party/blink/renderer/core/context_features/context_feature_settings.h"
        ]))
    local_vars.append(node)

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

    # parent_interface_template
    pattern = (
        "v8::Local<v8::FunctionTemplate> ${parent_interface_template}{_1};")
    interface = cg_context.interface
    if not interface:
        _1 = ""
    elif interface.inherited:
        _1 = (" = ${wrapper_type_info}->parent_class"
              "->GetV8ClassTemplate(${isolate}, ${world})"
              ".As<v8::FunctionTemplate>()")
    else:
        _1 = ""
    local_vars.append(S("parent_interface_template", _format(pattern, _1=_1)))

    # Arguments have priority over local vars.
    for symbol_node in local_vars:
        if symbol_node.name not in code_node.own_template_vars:
            code_node.register_code_symbol(symbol_node)


def _make_property_entry_cross_origin_check(property_,
                                            is_get=False,
                                            is_set=False):
    constants = {
        False: "unsigned(IDLMemberInstaller::FlagCrossOriginCheck::kCheck)",
        True:
        "unsigned(IDLMemberInstaller::FlagCrossOriginCheck::kDoNotCheck)",
    }
    if property_.is_static:
        return constants[True]
    if "CrossOrigin" not in property_.extended_attributes:
        return constants[False]
    values = property_.extended_attributes.values_of("CrossOrigin")
    if is_get:
        return constants[not values or "Getter" in values]
    elif is_set:
        return constants["Setter" in values]
    else:
        return constants[True]


def _make_property_entry_location(property_):
    if hasattr(property_, "is_static") and property_.is_static:
        return "unsigned(IDLMemberInstaller::FlagLocation::kInterface)"
    if "Global" in property_.owner.extended_attributes:
        return "unsigned(IDLMemberInstaller::FlagLocation::kInstance)"
    if "LegacyUnforgeable" in property_.extended_attributes:
        return "unsigned(IDLMemberInstaller::FlagLocation::kInstance)"
    return "unsigned(IDLMemberInstaller::FlagLocation::kPrototype)"


def _make_property_entry_receiver_check(property_):
    if ("LegacyLenientThis" in property_.extended_attributes
            or property_.is_static
            or (isinstance(property_, web_idl.Attribute)
                and property_.idl_type.unwrap().is_promise)
            or (isinstance(property_, web_idl.OverloadGroup)
                and property_[0].return_type.unwrap().is_promise)):
        return "unsigned(IDLMemberInstaller::FlagReceiverCheck::kDoNotCheck)"
    else:
        return "unsigned(IDLMemberInstaller::FlagReceiverCheck::kCheck)"


def _make_property_entry_v8_cached_accessor(property_):
    return "unsigned(V8PrivateProperty::CachedAccessor::{})".format(
        property_.extended_attributes.value_of("CachedAccessor") or "kNone")


def _make_property_entry_v8_property_attribute(property_):
    values = []
    if "NotEnumerable" in property_.extended_attributes:
        values.append("v8::DontEnum")
    if "LegacyUnforgeable" in property_.extended_attributes:
        if not isinstance(property_, web_idl.Attribute):
            values.append("v8::ReadOnly")
        values.append("v8::DontDelete")
    if not values:
        values.append("v8::None")
    if len(values) == 1:
        return "unsigned({})".format(values[0])
    else:
        return "unsigned({})".format(" | ".join(values))


def _make_property_entry_v8_side_effect(property_):
    value = property_.extended_attributes.value_of("Affects")
    if value:
        if value == "Everything":
            return "unsigned(v8::SideEffectType::kHasSideEffect)"
        elif value == "Nothing":
            return "unsigned(v8::SideEffectType::kHasNoSideEffect)"
        else:
            assert False
    elif isinstance(property_, web_idl.Attribute):
        return "unsigned(v8::SideEffectType::kHasNoSideEffect)"
    elif isinstance(property_, web_idl.Operation):
        assert property_.identifier == "toString"
        # The stringifier should have no side effect.
        return "unsigned(v8::SideEffectType::kHasNoSideEffect)"
    elif isinstance(property_, web_idl.OperationGroup):
        return "unsigned(v8::SideEffectType::kHasSideEffect)"
    assert False


def _make_property_entry_world(world):
    if world == CodeGenContext.MAIN_WORLD:
        return "unsigned(IDLMemberInstaller::FlagWorld::kMainWorld)"
    if world == CodeGenContext.NON_MAIN_WORLDS:
        return "unsigned(IDLMemberInstaller::FlagWorld::kNonMainWorlds)"
    if world == CodeGenContext.ALL_WORLDS:
        return "unsigned(IDLMemberInstaller::FlagWorld::kAllWorlds)"
    assert False


def _make_attribute_registration_table(table_name, attribute_entries):
    assert isinstance(table_name, str)
    assert isinstance(attribute_entries, (list, tuple))
    assert all(
        isinstance(entry, _PropEntryAttribute) for entry in attribute_entries)

    T = TextNode

    entry_nodes = []
    pattern = ("{{"
               "\"{property_name}\", "
               "\"${{class_like.identifier}}\", "
               "{attribute_get_callback}, "
               "{attribute_set_callback}, "
               "{v8_property_attribute}, "
               "{location}, "
               "{world}, "
               "{receiver_check}, "
               "{cross_origin_check_for_get}, "
               "{cross_origin_check_for_set}, "
               "{v8_side_effect}, "
               "{v8_cached_accessor}"
               "}},")
    for entry in attribute_entries:
        text = _format(
            pattern,
            property_name=entry.property_.identifier,
            attribute_get_callback=entry.attr_get_callback_name,
            attribute_set_callback=(entry.attr_set_callback_name or "nullptr"),
            v8_property_attribute=_make_property_entry_v8_property_attribute(
                entry.property_),
            location=_make_property_entry_location(entry.property_),
            world=_make_property_entry_world(entry.world),
            receiver_check=_make_property_entry_receiver_check(
                entry.property_),
            cross_origin_check_for_get=(
                _make_property_entry_cross_origin_check(entry.property_,
                                                        is_get=True)),
            cross_origin_check_for_set=(
                _make_property_entry_cross_origin_check(entry.property_,
                                                        is_set=True)),
            v8_side_effect=_make_property_entry_v8_side_effect(
                entry.property_),
            v8_cached_accessor=_make_property_entry_v8_cached_accessor(
                entry.property_))
        entry_nodes.append(T(text))

    return ListNode([
        T("static const IDLMemberInstaller::AttributeConfig " + table_name +
          "[] = {"),
        ListNode(entry_nodes),
        T("};"),
    ])


def _make_constant_callback_registration_table(table_name, constant_entries):
    assert isinstance(table_name, str)
    assert isinstance(constant_entries, (list, tuple))
    assert all(
        isinstance(entry, _PropEntryConstant)
        and isinstance(entry.const_callback_name, str)
        for entry in constant_entries)

    T = TextNode

    entry_nodes = []
    pattern = (
        "{{"  #
        "\"{property_name}\", "
        "{constant_callback}"
        "}},")
    for entry in constant_entries:
        text = _format(
            pattern,
            property_name=entry.property_.identifier,
            constant_callback=entry.const_callback_name)
        entry_nodes.append(T(text))

    return ListNode([
        T("static const IDLMemberInstaller::ConstantCallbackConfig " +
          table_name + "[] = {"),
        ListNode(entry_nodes),
        T("};"),
    ])


def _make_constant_value_registration_table(table_name, constant_entries):
    assert isinstance(table_name, str)
    assert isinstance(constant_entries, (list, tuple))
    assert all(
        isinstance(entry, _PropEntryConstant)
        and entry.const_callback_name is None for entry in constant_entries)

    T = TextNode

    entry_nodes = []
    pattern = (
        "{{"  #
        "\"{property_name}\", "
        "{constant_value}"
        "}},")
    for entry in constant_entries:
        text = _format(pattern,
                       property_name=entry.property_.identifier,
                       constant_value=entry.const_constant_name)
        entry_nodes.append(T(text))

    return ListNode([
        T("static const IDLMemberInstaller::ConstantValueConfig " +
          table_name + "[] = {"),
        ListNode(entry_nodes),
        T("};"),
    ])


def _make_exposed_construct_registration_table(table_name,
                                               exposed_construct_entries):
    assert isinstance(table_name, str)
    assert isinstance(exposed_construct_entries, (list, tuple))
    assert all(
        isinstance(entry, _PropEntryExposedConstruct)
        for entry in exposed_construct_entries)

    T = TextNode

    entry_nodes = []
    for entry in exposed_construct_entries:
        pattern = ("{{"
                   "\"{property_name}\", "
                   "{exposed_construct_callback}"
                   "}}, ")
        text = _format(pattern,
                       property_name=entry.property_.identifier,
                       exposed_construct_callback=entry.prop_callback_name)
        entry_nodes.append(T(text))

    return ListNode([
        T("static const IDLMemberInstaller::ExposedConstructConfig " +
          table_name + "[] = {"),
        ListNode(entry_nodes),
        T("};"),
    ])


def _make_operation_registration_table(table_name, operation_entries):
    assert isinstance(table_name, str)
    assert isinstance(operation_entries, (list, tuple))
    assert all(
        isinstance(entry, _PropEntryOperationGroup)
        for entry in operation_entries)

    T = TextNode
    F = FormatNode

    no_alloc_direct_call_count = len(
        list(
            filter(lambda entry: entry.no_alloc_direct_call_callbacks,
                   operation_entries)))
    assert (no_alloc_direct_call_count == 0
            or no_alloc_direct_call_count == len(operation_entries))
    no_alloc_direct_call_enabled = bool(no_alloc_direct_call_count)

    entry_nodes = []
    entry_nodes_wo_nadc = []
    nadc_overload_nodes = ListNode()
    pattern = ("{{"
               "\"{property_name}\", "
               "\"${{class_like.identifier}}\", "
               "{operation_callback}, "
               "{function_length}, "
               "{v8_property_attribute}, "
               "{location}, "
               "{world}, "
               "{receiver_check}, "
               "{cross_origin_check}, "
               "{v8_side_effect}"
               "}}, ")
    pattern_wo_nadc = pattern
    if no_alloc_direct_call_enabled:
        pattern = ("{{" + pattern + "{v8_cfunction_table}, "
                   "std::size({v8_cfunction_table})}}, ")
    has_no_alloc_direct_call_with_enforce_range = False
    for entry in operation_entries:
        if no_alloc_direct_call_enabled:
            nadc_overload_table_name = name_style.constant(
                "no_alloc_direct_call_overloads_of_",
                entry.property_.identifier)
            nadc_overloads = []
            for nadc_entry in entry.no_alloc_direct_call_callbacks:
                nadc_arg_flags = ""
                for arg in nadc_entry.operation.arguments:
                    if arg.index >= nadc_entry.argument_count:
                        break
                    nadc_v8_type_info_flags = []
                    if "Clamp" in arg.idl_type.effective_annotations:
                        nadc_v8_type_info_flags.append(
                            "v8::CTypeInfo::Flags::kClampBit")
                    if "EnforceRange" in arg.idl_type.effective_annotations:
                        nadc_v8_type_info_flags.append(
                            "v8::CTypeInfo::Flags::kEnforceRangeBit")
                        has_no_alloc_direct_call_with_enforce_range = True
                    arg_type = arg.idl_type.unwrap()
                    if arg_type.is_floating_point_numeric and (
                            not arg_type.keyword_typename.startswith(
                                "unrestricted ")):
                        nadc_v8_type_info_flags.append(
                            "v8::CTypeInfo::Flags::kIsRestrictedBit")
                    if nadc_v8_type_info_flags:
                        nadc_arg_flags += ".Arg<{index}, {flags}>()".format(
                            index=arg.index + 1,
                            flags=", ".join(nadc_v8_type_info_flags))
                nadc_overloads.append(
                    F("v8::CFunctionBuilder().Fn({}){}.Build(),",
                      nadc_entry.callback_name, nadc_arg_flags))
            nadc_overload_nodes.append(
                ListNode([
                    T("static const v8::CFunction " +
                      nadc_overload_table_name + "[] = {"),
                    ListNode(nadc_overloads),
                    T("};"),
                ]))
        else:
            nadc_overload_table_name = None

        def get_formatted_text(input_pattern):
            return _format(
                input_pattern,
                property_name=entry.property_.identifier,
                operation_callback=entry.op_callback_name,
                function_length=entry.op_func_length,
                v8_property_attribute=
                _make_property_entry_v8_property_attribute(entry.property_),
                location=_make_property_entry_location(entry.property_),
                world=_make_property_entry_world(entry.world),
                receiver_check=_make_property_entry_receiver_check(
                    entry.property_),
                cross_origin_check=_make_property_entry_cross_origin_check(
                    entry.property_),
                v8_side_effect=_make_property_entry_v8_side_effect(
                    entry.property_),
                v8_cfunction_table=nadc_overload_table_name)

        entry_nodes.append(T(get_formatted_text(pattern)))
        entry_nodes_wo_nadc.append(T(get_formatted_text(pattern_wo_nadc)))

    table_decl_before_name = (
        "static const IDLMemberInstaller::OperationConfig")
    table_decl_before_name_wo_nadc = table_decl_before_name
    if no_alloc_direct_call_enabled:
        table_decl_before_name = (
            "static const "
            "IDLMemberInstaller::NoAllocDirectCallOperationConfig")
    node = ListNode()
    if nadc_overload_nodes:
        node.extend([
            nadc_overload_nodes,
            EmptyNode(),
        ])
    node.extend([
        T(table_decl_before_name + " " + table_name + "[] = {"),
        ListNode(entry_nodes),
        T("};"),
    ])

    # Disable [NoAllocDirectCall] on x86 due to https://crbug.com/1433212
    if has_no_alloc_direct_call_with_enforce_range:
        node = ListNode([
            T("// Disable [NoAllocDirectCall] on x86 due to "
              "https://crbug.com/1433212"),
            T("#if defined(ARCH_CPU_X86)"),
            T(table_decl_before_name_wo_nadc + " " + table_name + "[] = {"),
            ListNode(entry_nodes_wo_nadc),
            T("};"),
            T("// Disable compiler warnings for unused functions."),
            ListNode([
                F("std::ignore = {};", nadc_entry.callback_name)
                for entry in operation_entries
                for nadc_entry in entry.no_alloc_direct_call_callbacks
            ]),
            T("#else   // defined(ARCH_CPU_X86)"),
            node,
            T("#endif  // defined(ARCH_CPU_X86)"),
        ])
    return node


class _PropEntryBase(object):
    def __init__(self, is_context_dependent, exposure_conditional, world,
                 property_):
        assert isinstance(is_context_dependent, bool)
        assert isinstance(exposure_conditional, CodeGenExpr)

        self.is_context_dependent = is_context_dependent
        self.exposure_conditional = exposure_conditional
        self.world = world
        self.property_ = property_


class _PropEntryAttribute(_PropEntryBase):
    def __init__(self, is_context_dependent, exposure_conditional, world,
                 attribute, attr_get_callback_name, attr_set_callback_name):
        assert isinstance(attr_get_callback_name, str)
        assert _is_none_or_str(attr_set_callback_name)

        _PropEntryBase.__init__(self, is_context_dependent,
                                exposure_conditional, world, attribute)
        self.attr_get_callback_name = attr_get_callback_name
        self.attr_set_callback_name = attr_set_callback_name


class _PropEntryConstant(_PropEntryBase):
    def __init__(self, is_context_dependent, exposure_conditional, world,
                 constant, const_callback_name, const_constant_name):
        assert _is_none_or_str(const_callback_name)
        assert isinstance(const_constant_name, str)

        _PropEntryBase.__init__(self, is_context_dependent,
                                exposure_conditional, world, constant)
        self.const_callback_name = const_callback_name
        self.const_constant_name = const_constant_name


class _PropEntryConstructorGroup(_PropEntryBase):
    def __init__(self, is_context_dependent, exposure_conditional, world,
                 constructor_group, ctor_callback_name, ctor_func_length):
        assert isinstance(ctor_callback_name, str)
        assert isinstance(ctor_func_length, int)

        _PropEntryBase.__init__(self, is_context_dependent,
                                exposure_conditional, world, constructor_group)
        self.ctor_callback_name = ctor_callback_name
        self.ctor_func_length = ctor_func_length


class _PropEntryExposedConstruct(_PropEntryBase):
    def __init__(self, is_context_dependent, exposure_conditional, world,
                 exposed_construct, prop_callback_name):
        assert isinstance(prop_callback_name, str)

        _PropEntryBase.__init__(self, is_context_dependent,
                                exposure_conditional, world, exposed_construct)
        self.prop_callback_name = prop_callback_name


class _PropEntryOperationGroup(_PropEntryBase):
    def __init__(self,
                 is_context_dependent,
                 exposure_conditional,
                 world,
                 operation_group,
                 op_callback_name,
                 op_func_length,
                 no_alloc_direct_call_callbacks=None):
        assert isinstance(op_callback_name, str)
        assert isinstance(op_func_length, int)

        _PropEntryBase.__init__(self, is_context_dependent,
                                exposure_conditional, world, operation_group)
        self.op_callback_name = op_callback_name
        self.op_func_length = op_func_length
        self.no_alloc_direct_call_callbacks = no_alloc_direct_call_callbacks


def make_property_entries_and_callback_defs(cg_context, attribute_entries,
                                            constant_entries,
                                            constructor_entries,
                                            exposed_construct_entries,
                                            operation_entries):
    """
    Creates intermediate objects to help property installation and also makes
    code nodes of callback functions.

    Args:
        attribute_entries:
        constructor_entries:
        exposed_construct_entries:
        operation_entries:
            Output parameters to store the intermediate objects.
    """
    assert isinstance(cg_context, CodeGenContext)
    assert isinstance(attribute_entries, list)
    assert isinstance(constant_entries, list)
    assert isinstance(constructor_entries, list)
    assert isinstance(exposed_construct_entries, list)
    assert isinstance(operation_entries, list)

    class_like = cg_context.class_like
    interface = cg_context.interface
    # This function produces the property installation code and we'd like to
    # expose IDL constructs with [Exposed] not only on [Global] but also on
    # [TargetOfExposed].
    global_names = (
        class_like.extended_attributes.values_of("Global") +
        class_like.extended_attributes.values_of("TargetOfExposed"))

    callback_def_nodes = ListNode()

    def iterate(members, callback):
        for member in members:
            is_context_dependent = member.exposure.is_context_dependent(
                global_names)
            if isinstance(member, web_idl.OverloadGroup):
                exposure_conditional = expr_or([
                    expr_from_exposure(overload.exposure,
                                       global_names=global_names,
                                       may_use_feature_selector=True)
                    for overload in member
                ])
            else:
                exposure_conditional = expr_from_exposure(
                    member.exposure,
                    global_names=global_names,
                    may_use_feature_selector=True)

            if "PerWorldBindings" in member.extended_attributes:
                assert not isinstance(
                    member, web_idl.ConstructorGroup
                ), "[PerWorldBindings] is not supported for constructors"
                worlds = (CodeGenContext.MAIN_WORLD,
                          CodeGenContext.NON_MAIN_WORLDS)
            else:
                worlds = (CodeGenContext.ALL_WORLDS, )

            for world in worlds:
                callback(member, is_context_dependent, exposure_conditional,
                         world)

    def process_attribute(attribute, is_context_dependent,
                          exposure_conditional, world):
        cgc_attr = cg_context.make_copy(attribute=attribute, for_world=world)
        cgc = cgc_attr.make_copy(attribute_get=True)
        attr_get_callback_name = callback_function_name(cgc)
        attr_get_callback_node = make_attribute_get_callback_def(
            cgc, attr_get_callback_name)
        cgc = cgc_attr.make_copy(attribute_set=True)
        attr_set_callback_name = callback_function_name(cgc)
        attr_set_callback_node = make_attribute_set_callback_def(
            cgc, attr_set_callback_name)
        if attr_set_callback_node is None:
            attr_set_callback_name = None

        callback_def_nodes.extend([
            attr_get_callback_node,
            EmptyNode(),
            attr_set_callback_node,
            EmptyNode(),
        ])

        attribute_entries.append(
            _PropEntryAttribute(
                is_context_dependent=is_context_dependent,
                exposure_conditional=exposure_conditional,
                world=world,
                attribute=attribute,
                attr_get_callback_name=attr_get_callback_name,
                attr_set_callback_name=attr_set_callback_name))

    def process_constant(constant, is_context_dependent, exposure_conditional,
                         world):
        cgc = cg_context.make_copy(
            constant=constant,
            for_world=world,
            v8_callback_type=CodeGenContext.V8_ACCESSOR_NAME_GETTER_CALLBACK)
        const_callback_name = callback_function_name(cgc)
        const_callback_node = make_constant_callback_def(
            cgc, const_callback_name)
        if const_callback_node is None:
            const_callback_name = None
        # IDL constant's C++ constant name
        const_constant_name = _format("${class_name}::Constant::{}",
                                      constant_name(cgc))

        callback_def_nodes.extend([
            const_callback_node,
            EmptyNode(),
        ])

        constant_entries.append(
            _PropEntryConstant(
                is_context_dependent=is_context_dependent,
                exposure_conditional=exposure_conditional,
                world=world,
                constant=constant,
                const_callback_name=const_callback_name,
                const_constant_name=const_constant_name))

    def process_constructor_group(constructor_group, is_context_dependent,
                                  exposure_conditional, world):
        cgc = cg_context.make_copy(
            constructor_group=constructor_group, for_world=world)
        ctor_callback_name = callback_function_name(cgc)
        ctor_callback_node = make_constructor_callback_def(
            cgc, ctor_callback_name)

        callback_def_nodes.extend([
            ctor_callback_node,
            EmptyNode(),
        ])

        constructor_entries.append(
            _PropEntryConstructorGroup(
                is_context_dependent=is_context_dependent,
                exposure_conditional=exposure_conditional,
                world=world,
                constructor_group=constructor_group,
                ctor_callback_name=ctor_callback_name,
                ctor_func_length=(
                    constructor_group.min_num_of_required_arguments)))

    def process_exposed_construct(exposed_construct, is_context_dependent,
                                  exposure_conditional, world):
        if isinstance(exposed_construct, web_idl.LegacyWindowAlias):
            cgc = cg_context.make_copy(
                exposed_construct=exposed_construct.original,
                legacy_window_alias=exposed_construct,
                for_world=world,
                v8_callback_type=CodeGenContext.
                V8_ACCESSOR_NAME_GETTER_CALLBACK)
        elif ("LegacyNoInterfaceObject" in
              exposed_construct.extended_attributes):
            return  # Skip due to [LegacyNoInterfaceObject].
        else:
            cgc = cg_context.make_copy(
                exposed_construct=exposed_construct,
                for_world=world,
                v8_callback_type=CodeGenContext.
                V8_ACCESSOR_NAME_GETTER_CALLBACK)
        prop_callback_name = callback_function_name(cgc)
        prop_callback_node = make_exposed_construct_callback_def(
            cgc, prop_callback_name)

        callback_def_nodes.extend([
            prop_callback_node,
            EmptyNode(),
        ])

        exposed_construct_entries.append(
            _PropEntryExposedConstruct(
                is_context_dependent=is_context_dependent,
                exposure_conditional=exposure_conditional,
                world=world,
                exposed_construct=exposed_construct,
                prop_callback_name=prop_callback_name))

    def process_legacy_factory_function_group(legacy_factory_function_group,
                                              is_context_dependent,
                                              exposure_conditional, world):
        cgc = cg_context.make_copy(
            exposed_construct=legacy_factory_function_group,
            is_legacy_factory_function=True,
            for_world=world,
            v8_callback_type=CodeGenContext.V8_ACCESSOR_NAME_GETTER_CALLBACK)
        prop_callback_name = callback_function_name(cgc)
        prop_callback_node = (
            make_legacy_factory_function_property_callback_def(
                cgc, prop_callback_name))

        callback_def_nodes.extend([
            prop_callback_node,
            EmptyNode(),
        ])

        exposed_construct_entries.append(
            _PropEntryExposedConstruct(
                is_context_dependent=is_context_dependent,
                exposure_conditional=exposure_conditional,
                world=world,
                exposed_construct=legacy_factory_function_group,
                prop_callback_name=prop_callback_name))

    def process_operation_group(operation_group, is_context_dependent,
                                exposure_conditional, world):
        cgc = cg_context.make_copy(
            operation_group=operation_group, for_world=world)
        op_callback_name = callback_function_name(cgc)
        op_callback_node = make_operation_callback_def(cgc, op_callback_name)
        no_alloc_direct_call_callbacks = (
            list_no_alloc_direct_call_callbacks(
                cgc.make_copy(no_alloc_direct_call=True))
            if "NoAllocDirectCall" in operation_group.extended_attributes else
            None)

        callback_def_nodes.extend([
            op_callback_node,
            EmptyNode(),
        ])

        operation_entries.append(
            _PropEntryOperationGroup(
                is_context_dependent=is_context_dependent,
                exposure_conditional=exposure_conditional,
                world=world,
                operation_group=operation_group,
                op_callback_name=op_callback_name,
                op_func_length=operation_group.min_num_of_required_arguments,
                no_alloc_direct_call_callbacks=no_alloc_direct_call_callbacks))

    def process_stringifier(_, is_context_dependent, exposure_conditional,
                            world):
        cgc = cg_context.make_copy(
            stringifier=interface.stringifier, for_world=world)
        op_callback_name = callback_function_name(cgc)
        op_callback_node = make_stringifier_callback_def(cgc, op_callback_name)

        callback_def_nodes.extend([
            op_callback_node,
            EmptyNode(),
        ])

        operation_entries.append(
            _PropEntryOperationGroup(
                is_context_dependent=is_context_dependent,
                exposure_conditional=exposure_conditional,
                world=world,
                operation_group=cgc.property_,
                op_callback_name=op_callback_name,
                op_func_length=0))

    iterate(class_like.attributes, process_attribute)
    iterate(class_like.constants, process_constant)
    if interface:
        iterate(interface.constructor_groups, process_constructor_group)
        iterate(interface.exposed_constructs, process_exposed_construct)
        iterate(interface.legacy_window_aliases, process_exposed_construct)
        legacy_factory_function_groups = [
            group for construct in interface.exposed_constructs
            for group in construct.legacy_factory_function_groups
            if construct.legacy_factory_function_groups
        ]
        iterate(legacy_factory_function_groups,
                process_legacy_factory_function_group)
    if not class_like.is_callback_interface:
        iterate(class_like.operation_groups, process_operation_group)
    if interface and interface.stringifier:
        iterate([interface.stringifier.operation], process_stringifier)
    collectionlike = (interface
                      and (interface.async_iterable or interface.iterable
                           or interface.maplike or interface.setlike))
    if collectionlike:

        def should_define(target):
            if not target[0].is_optionally_defined:
                return True
            return all(target.identifier != member.identifier
                       for member in itertools.chain(
                           interface.attributes, interface.constants,
                           interface.operation_groups))

        iterate(collectionlike.attributes, process_attribute)
        iterate(
            filter(should_define, collectionlike.operation_groups),
            process_operation_group)

    return callback_def_nodes


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

    nodes = []

    class_like = cg_context.class_like
    interface = cg_context.interface

    unscopables = []
    if interface:
        # Iff the interface has an unscopable member, then collect all
        # unscopable members including ones in inherited interfaces.
        # Otherwise, do not create an @@unscopables object.
        is_unscopable = lambda member: "Unscopable" in member.extended_attributes
        unscopables.extend(filter(is_unscopable, interface.attributes))
        unscopables.extend(filter(is_unscopable, interface.operations))
        if unscopables:
            for i in interface.inclusive_inherited_interfaces:
                if i == interface:
                    continue
                unscopables.extend(filter(is_unscopable, i.attributes))
                unscopables.extend(filter(is_unscopable, i.operations))
    if unscopables:
        nodes.extend([
            TextNode("""\
// [Unscopable]
// 3.7.3. Interface prototype object
// https://webidl.spec.whatwg.org/#interface-prototype-object
// step 10. If interface has any member declared with the [Unscopable]
//   extended attribute, then:\
"""),
            ListNode([
                TextNode("static constexpr const char* "
                         "kUnscopablePropertyNames[] = {"),
                ListNode([
                    TextNode("\"{}\", ".format(name)) for name in sorted(
                        map(lambda member: member.identifier, unscopables))
                ]),
                TextNode("};"),
            ]),
            TextNode("""\
bindings::InstallUnscopablePropertyNames(
    ${isolate}, ${v8_context}, ${prototype_object}, kUnscopablePropertyNames);
"""),
        ])

    if "LegacyNoInterfaceObject" in class_like.extended_attributes:
        nodes.append(
            TextNode("""\
// [LegacyNoInterfaceObject]
// 3.7.3. Interface prototype object
// https://webidl.spec.whatwg.org/#interface-prototype-object
// step 13. If the [LegacyNoInterfaceObject] extended attribute was not
//   specified on interface, then:
//
// V8 defines "constructor" property on the prototype object by default.
${prototype_object}->Delete(
    ${v8_context}, V8AtomicString(${isolate}, "constructor")).ToChecked();
"""))

    if class_like.is_async_iterator or class_like.is_sync_iterator:
        nodes.append(
            TextNode("""\
// V8 defines "constructor" property on the prototype object by default.
${prototype_object}->Delete(
    ${v8_context}, V8AtomicString(${isolate}, "constructor")).ToChecked();
"""))

    if interface and interface.iterable and not interface.iterable.key_type:
        conditional = expr_from_exposure(interface.iterable.exposure)
        if not conditional.is_always_true:
            body = [
                TextNode("""\
// The value-iterator-returning properties of the intrinsic values can be
// installed only per v8::Template (via
// v8::Template::SetIntrinsicDataProperty). So the property installation
// logic is reversed in this case like below.
//   1. Unconditionally install the properties on prototype_object_template
//      per V8 isolate in ${class_name}::InstallInterfaceTemplate.
//   2. Conditionally remove the properties from prototype_object per V8
//      context if they're not enabled.
// https://webidl.spec.whatwg.org/#define-the-iteration-methods\
""")
            ]
            body.extend([
                FormatNode(
                    "${prototype_object}->Delete("
                    "${v8_context}, "
                    "V8AtomicString(${isolate}, \"{property}\"))"
                    ".ToChecked();",
                    property=property)
                for property in ("entries", "keys", "values", "forEach")
            ])
            nodes.append(
                CxxUnlikelyIfNode(cond=expr_not(conditional),
                                  attribute=None,
                                  body=body))

    # Install @@asyncIterator property.
    if interface and interface.async_iterable:
        for operation_group in interface.async_iterable.operation_groups:
            if operation_group[0].is_async_iterator:
                property_name = operation_group.identifier
                break
        else:
            assert False
        pattern = """\
// @@asyncIterator == "{property_name}"
{{
  v8::Local<v8::Value> v8_value = ${prototype_object}->Get(
      ${v8_context}, V8AtomicString(${isolate}, "{property_name}"))
      .ToLocalChecked();
  // "{property_name}" may be hidden in this context.
  if (!v8_value->IsUndefined()) {{
    ${prototype_object}->DefineOwnProperty(
        ${v8_context}, v8::Symbol::GetAsyncIterator(${isolate}), v8_value,
        v8::DontEnum).ToChecked();
  }}
}}
"""
        nodes.append(FormatNode(pattern, property_name=property_name))

    # Install @@iterator property.
    if (interface and ((interface.iterable and interface.iterable.key_type)
                       or interface.maplike or interface.setlike)):
        collectionlike = (interface.iterable or interface.maplike
                          or interface.setlike)
        for operation_group in collectionlike.operation_groups:
            if operation_group[0].is_iterator:
                property_name = operation_group.identifier
                break
        else:
            assert False
        pattern = """\
// @@iterator == "{property_name}"
{{
  v8::Local<v8::Value> v8_value = ${prototype_object}->Get(
      ${v8_context}, V8AtomicString(${isolate}, "{property_name}"))
      .ToLocalChecked();
  // "{property_name}" may be hidden in this context.
  if (!v8_value->IsUndefined()) {{
    ${prototype_object}->DefineOwnProperty(
        ${v8_context}, v8::Symbol::GetIterator(${isolate}), v8_value,
        v8::DontEnum).ToChecked();
  }}
}}
"""
        nodes.append(FormatNode(pattern, property_name=property_name))

    return SequenceNode(nodes) if nodes else None


def make_install_interface_template(cg_context, function_name, class_name,
                                    trampoline_var_name, constructor_entries,
                                    supplemental_install_node,
                                    install_unconditional_func_name,
                                    install_context_independent_func_name):
    """
    Returns:
        A triplet of CodeNode of:
        - function declaration
        - function definition
        - trampoline function definition (from the API class to the
          implementation class), which is supposed to be defined inline
    """
    assert isinstance(cg_context, CodeGenContext)
    assert isinstance(function_name, str)
    assert _is_none_or_str(class_name)
    assert _is_none_or_str(trampoline_var_name)
    assert isinstance(constructor_entries, (list, tuple))
    assert all(
        isinstance(entry, _PropEntryConstructorGroup)
        for entry in constructor_entries)
    assert isinstance(supplemental_install_node, SequenceNode)
    assert _is_none_or_str(install_unconditional_func_name)
    assert _is_none_or_str(install_context_independent_func_name)

    T = TextNode

    class_like = cg_context.class_like
    interface = cg_context.interface

    arg_decls = [
        "v8::Isolate* isolate",
        "const DOMWrapperWorld& world",
        "v8::Local<v8::Template> interface_template",
    ]
    return_type = "void"

    if trampoline_var_name is None:
        trampoline_def = None
    else:
        trampoline_def = CxxFuncDefNode(
            name=function_name,
            arg_decls=arg_decls,
            return_type=return_type,
            static=True)
        trampoline_def.body.append(
            TextNode(
                _format("return {}(isolate, world, interface_template);",
                        trampoline_var_name)))

    func_decl = CxxFuncDeclNode(
        name=function_name,
        arg_decls=arg_decls,
        return_type=return_type,
        static=True)

    func_def = CxxFuncDefNode(
        name=function_name,
        arg_decls=arg_decls,
        return_type=return_type,
        class_name=class_name)
    func_def.set_base_template_vars(cg_context.template_bindings())

    body = func_def.body
    body.add_template_vars({
        "isolate": "isolate",
        "world": "world",
        "interface_template": "interface_template",
    })
    bind_installer_local_vars(body, cg_context)

    if cg_context.interface:
        body.extend([
            T("bindings::SetupIDLInterfaceTemplate("
              "${isolate}, ${wrapper_type_info}, "
              "${instance_object_template}, "
              "${prototype_object_template}, "
              "${interface_function_template}, "
              "${parent_interface_template});"),
            EmptyNode(),
        ])
    elif cg_context.namespace:
        body.extend([
            T("bindings::SetupIDLNamespaceTemplate("
              "${isolate}, ${wrapper_type_info}, "
              "${namespace_object_template});"),
            EmptyNode(),
        ])
    elif cg_context.callback_interface:
        body.extend([
            T("bindings::SetupIDLCallbackInterfaceTemplate("
              "${isolate}, ${wrapper_type_info}, "
              "${interface_function_template});"),
            EmptyNode(),
        ])
    elif cg_context.async_iterator or cg_context.sync_iterator:
        iterator_interface = (cg_context.async_iterator
                              or cg_context.sync_iterator).interface
        if iterator_interface.async_iterable:
            parent_intrinsic_prototype = (
                "v8::Intrinsic::kAsyncIteratorPrototype")
        elif iterator_interface.iterable:
            parent_intrinsic_prototype = "v8::Intrinsic::kIteratorPrototype"
        elif iterator_interface.maplike:
            parent_intrinsic_prototype = "v8::Intrinsic::kMapIteratorPrototype"
        elif iterator_interface.setlike:
            parent_intrinsic_prototype = "v8::Intrinsic::kSetIteratorPrototype"
        else:
            assert False
        if iterator_interface.async_iterable:
            class_string = "{} AsyncIterator".format(
                iterator_interface.identifier)
        else:
            class_string = "{} Iterator".format(iterator_interface.identifier)
        body.extend([
            FormatNode(
                "bindings::SetupIDLIteratorTemplate("
                "${isolate}, ${wrapper_type_info}, "
                "${instance_object_template}, "
                "${prototype_object_template}, "
                "${interface_function_template}, "
                "{parent_intrinsic_prototype}, "
                "\"{class_string}\");",
                parent_intrinsic_prototype=parent_intrinsic_prototype,
                class_string=class_string),
            EmptyNode(),
        ])
    else:
        assert False

    for entry in constructor_entries:
        nodes = [
            FormatNode("${interface_function_template}->SetCallHandler({});",
                       entry.ctor_callback_name),
            FormatNode("${interface_function_template}->SetLength({});",
                       entry.ctor_func_length),
            FormatNode(
                "${interface_function_template}->SetInterfaceName("
                "V8String(${isolate}, \"{}\"));",
                cg_context.class_like.identifier),
            T("${interface_function_template}->SetExceptionContext("
              "v8::ExceptionContext::kConstructor);"),
        ]
        if not (entry.exposure_conditional.is_always_true
                or entry.is_context_dependent):
            nodes = [
                CxxUnlikelyIfNode(cond=entry.exposure_conditional,
                                  attribute=None,
                                  body=nodes),
            ]
        assert entry.world == CodeGenContext.ALL_WORLDS
        body.extend(nodes)
        body.append(EmptyNode())

    body.extend([
        supplemental_install_node,
        EmptyNode(),
    ])

    if class_like.identifier == "DOMException":
        body.append(
            T("""\
// DOMException-specific settings
// https://webidl.spec.whatwg.org/#es-DOMException-specialness
{
  v8::Local<v8::FunctionTemplate> intrinsic_error_prototype_interface_template =
      v8::FunctionTemplate::New(${isolate}, nullptr, v8::Local<v8::Value>(),
                                v8::Local<v8::Signature>(), 0,
                                v8::ConstructorBehavior::kThrow);
  intrinsic_error_prototype_interface_template->SetIntrinsicDataProperty(
      V8AtomicString(${isolate}, "prototype"), v8::kErrorPrototype);
  ${interface_function_template}->Inherit(
      intrinsic_error_prototype_interface_template);
}
"""))

    if class_like.identifier == "HTMLAllCollection":
        body.append(
            T("""\
// HTMLAllCollection-specific settings
// https://html.spec.whatwg.org/C/#the-htmlallcollection-interface
${instance_object_template}->SetCallAsFunctionHandler(ItemOperationCallback);
${instance_object_template}->MarkAsUndetectable();
"""))

    if class_like.identifier == "Location":
        body.append(
            T("""\
// Location-specific settings
// https://html.spec.whatwg.org/C/#the-location-interface
// To create a Location object, run these steps:
// step 3. Let valueOf be location's relevant
//   Realm.[[Intrinsics]].[[%ObjProto_valueOf%]].
// step 3. Perform ! location.[[DefineOwnProperty]]("valueOf",
//   { [[Value]]: valueOf, [[Writable]]: false, [[Enumerable]]: false,
//     [[Configurable]]: false }).
${instance_template}->SetIntrinsicDataProperty(
    V8AtomicString(${isolate}, "valueOf"),
    v8::kObjProto_valueOf,
    static_cast<v8::PropertyAttribute>(
        v8::ReadOnly | v8::DontEnum | v8::DontDelete));
// step 4. Perform ! location.[[DefineOwnProperty]](@@toPrimitive,
//   { [[Value]]: undefined, [[Writable]]: false, [[Enumerable]]: false,
//     [[Configurable]]: false }).
${instance_template}->Set(
    v8::Symbol::GetToPrimitive(${isolate}),
    v8::Undefined(${isolate}),
    static_cast<v8::PropertyAttribute>(
        v8::ReadOnly | v8::DontEnum | v8::DontDelete));
// 7.7.4.2 [[SetPrototypeOf]] ( V )
// https://html.spec.whatwg.org/C/#location-setprototypeof
${instance_object_template}->SetImmutableProto();
${prototype_object_template}->SetImmutableProto();
"""))

    if (interface and interface.indexed_and_named_properties
            and interface.indexed_and_named_properties.indexed_getter
            and "Global" not in interface.extended_attributes):
        body.append(
            T("""\
// @@iterator for indexed properties
// https://webidl.spec.whatwg.org/#define-the-iteration-methods
${prototype_template}->SetIntrinsicDataProperty(
    v8::Symbol::GetIterator(${isolate}), v8::kArrayProto_values, v8::DontEnum);
"""))
    if interface and interface.iterable and not interface.iterable.key_type:
        body.append(
            T("""\
// Value iterator's properties
// https://webidl.spec.whatwg.org/#define-the-iteration-methods
${prototype_template}->SetIntrinsicDataProperty(
    V8AtomicString(${isolate}, "entries"), v8::kArrayProto_entries, v8::None);
${prototype_template}->SetIntrinsicDataProperty(
    V8AtomicString(${isolate}, "keys"), v8::kArrayProto_keys, v8::None);
${prototype_template}->SetIntrinsicDataProperty(
    V8AtomicString(${isolate}, "values"), v8::kArrayProto_values, v8::None);
${prototype_template}->SetIntrinsicDataProperty(
    V8AtomicString(${isolate}, "forEach"), v8::kArrayProto_forEach, v8::None);
"""))

    if interface and "IsCodeLike" in interface.extended_attributes:
        body.append(
            CxxUnlikelyIfNode(
                cond="RuntimeEnabledFeatures::TrustedTypesUseCodeLikeEnabled()",
                attribute=None,
                body=[
                    TextNode("// [IsCodeLike]"),
                    TextNode("${instance_object_template}->SetCodeLike();"),
                ]))

    if "Global" in class_like.extended_attributes:
        body.append(
            TextNode("""\
// [Global]
// 3.7.1. [[SetPrototypeOf]]
// https://webidl.spec.whatwg.org/#platform-object-setprototypeof
${instance_object_template}->SetImmutableProto();
${prototype_object_template}->SetImmutableProto();
"""))
    elif interface and any("Global" in derived.extended_attributes
                           for derived in interface.subclasses):
        body.append(
            TextNode("""\
// [Global] - prototype object in the prototype chain of global objects
// 3.7.1. [[SetPrototypeOf]]
// https://webidl.spec.whatwg.org/#platform-object-setprototypeof
${prototype_object_template}->SetImmutableProto();
"""))

    func_call_pattern = ("{}(${isolate}, ${world}, ${instance_template}, "
                         "${prototype_template}, ${interface_template});")
    if install_unconditional_func_name:
        func_call = _format(func_call_pattern, install_unconditional_func_name)
        body.append(T(func_call))
    if install_context_independent_func_name:
        func_call = _format(func_call_pattern,
                            install_context_independent_func_name)
        body.append(T(func_call))

    return func_decl, func_def, trampoline_def


class PropInstallMode(object):
    class Mode(int):
        pass

    UNCONDITIONAL = Mode(0)
    CONTEXT_INDEPENDENT = Mode(1)
    CONTEXT_DEPENDENT = Mode(2)
    V8_CONTEXT_SNAPSHOT = Mode(3)


def make_install_properties(cg_context, function_name, class_name,
                            prop_install_mode, trampoline_var_name,
                            attribute_entries, constant_entries,
                            exposed_construct_entries, operation_entries):
    """
    Returns:
        A triplet of CodeNode of:
        - function declaration
        - function definition
        - trampoline function definition (from the API class to the
          implementation class), which is supposed to be defined inline
    """
    assert isinstance(cg_context, CodeGenContext)
    assert isinstance(function_name, str)
    assert _is_none_or_str(class_name)
    assert isinstance(prop_install_mode, PropInstallMode.Mode)
    assert _is_none_or_str(trampoline_var_name)
    assert isinstance(attribute_entries, (list, tuple))
    assert all(
        isinstance(entry, _PropEntryAttribute) for entry in attribute_entries)
    assert isinstance(constant_entries, (list, tuple))
    assert all(
        isinstance(entry, _PropEntryConstant) for entry in constant_entries)
    assert isinstance(exposed_construct_entries, (list, tuple))
    assert all(
        isinstance(entry, _PropEntryExposedConstruct)
        for entry in exposed_construct_entries)
    assert isinstance(operation_entries, (list, tuple))
    assert all(
        isinstance(entry, _PropEntryOperationGroup)
        for entry in operation_entries)

    if prop_install_mode == PropInstallMode.CONTEXT_DEPENDENT:
        install_prototype_object_node = _make_install_prototype_object(
            cg_context)
    else:
        install_prototype_object_node = None

    if not (attribute_entries or constant_entries or exposed_construct_entries
            or operation_entries or install_prototype_object_node):
        if prop_install_mode != PropInstallMode.V8_CONTEXT_SNAPSHOT:
            return None, None, None

    if prop_install_mode in (PropInstallMode.UNCONDITIONAL,
                             PropInstallMode.CONTEXT_INDEPENDENT):
        arg_decls = [
            "v8::Isolate* isolate",
            "const DOMWrapperWorld& world",
            "v8::Local<v8::Template> instance_template",
            "v8::Local<v8::Template> prototype_template",
            "v8::Local<v8::Template> interface_template",
        ]
        arg_names = [
            "isolate",
            "world",
            "instance_template",
            "prototype_template",
            "interface_template",
        ]
    elif prop_install_mode == PropInstallMode.CONTEXT_DEPENDENT:
        arg_decls = [
            "v8::Local<v8::Context> context",
            "const DOMWrapperWorld& world",
            "v8::Local<v8::Object> instance_object",
            "v8::Local<v8::Object> prototype_object",
            "v8::Local<v8::Object> interface_object",
            "v8::Local<v8::Template> interface_template",
            "FeatureSelector feature_selector",
        ]
        arg_names = [
            "context",
            "world",
            "instance_object",
            "prototype_object",
            "interface_object",
            "interface_template",
            "feature_selector",
        ]
    elif prop_install_mode == PropInstallMode.V8_CONTEXT_SNAPSHOT:
        arg_decls = [
            "v8::Local<v8::Context> context",
            "const DOMWrapperWorld& world",
            "v8::Local<v8::Object> instance_object",
            "v8::Local<v8::Object> prototype_object",
            "v8::Local<v8::Object> interface_object",
            "v8::Local<v8::Template> interface_template",
        ]
        arg_names = [
            "context",
            "world",
            "instance_object",
            "prototype_object",
            "interface_object",
            "interface_template",
        ]
    return_type = "void"

    is_per_context_install = (
        prop_install_mode in (PropInstallMode.CONTEXT_DEPENDENT,
                              PropInstallMode.V8_CONTEXT_SNAPSHOT))

    if trampoline_var_name is None:
        trampoline_def = None
    else:
        trampoline_def = CxxFuncDefNode(
            name=function_name,
            arg_decls=arg_decls,
            return_type=return_type,
            static=True)
        text = _format(
            "return {func}({args});",
            func=trampoline_var_name,
            args=", ".join(arg_names))
        trampoline_def.body.append(TextNode(text))

    func_decl = CxxFuncDeclNode(name=function_name,
                                arg_decls=arg_decls,
                                return_type=return_type,
                                static=bool(class_name))

    func_def = CxxFuncDefNode(
        name=function_name,
        arg_decls=arg_decls,
        return_type=return_type,
        class_name=class_name)
    func_def.set_base_template_vars(cg_context.template_bindings())

    body = func_def.body
    for arg_name in arg_names:
        if arg_name == "context":  # 'context' is reserved by Mako.
            body.add_template_var("v8_context", "context")
        else:
            body.add_template_var(arg_name, arg_name)
    bind_installer_local_vars(body, cg_context)

    body.extend([
        TextNode("using bindings::IDLMemberInstaller;"),
        EmptyNode(),
    ])

    if (is_per_context_install
            and "Global" in cg_context.class_like.extended_attributes):
        body.extend([
            CxxLikelyIfNode(cond="${instance_object}.IsEmpty()",
                            attribute=None,
                            body=[
                                TextNode("""\
${instance_object} = ${v8_context}->Global()->GetPrototype().As<v8::Object>();\
"""),
                            ]),
            EmptyNode(),
        ])

    if install_prototype_object_node:
        body.extend([
            CxxLikelyIfNode(cond="${feature_selector}.IsAll()",
                            attribute=None,
                            body=[install_prototype_object_node]),
            EmptyNode(),
        ])

    def group_by_condition(entries):
        unconditional_entries = []
        conditional_to_entries = {}
        for entry in entries:
            if entry.exposure_conditional.is_always_true:
                unconditional_entries.append(entry)
            else:
                conditional_to_entries.setdefault(entry.exposure_conditional,
                                                  []).append(entry)
        return unconditional_entries, conditional_to_entries

    def install_properties(table_name, target_entries, make_table_func,
                           installer_call_text):
        unconditional_entries, conditional_to_entries = group_by_condition(
            target_entries)
        if unconditional_entries:
            body.append(
                CxxBlockNode([
                    make_table_func(table_name, unconditional_entries),
                    TextNode(installer_call_text),
                ]))
            body.append(EmptyNode())
        for conditional, entries in conditional_to_entries.items():
            body.append(
                CxxUnlikelyIfNode(cond=conditional,
                                  attribute=None,
                                  body=[
                                      make_table_func(table_name, entries),
                                      TextNode(installer_call_text),
                                  ]))
        body.append(EmptyNode())

    if is_per_context_install:
        pattern = ("{install_func}("
                   "${isolate}, ${world}, "
                   "${instance_object}, "
                   "${prototype_object}, "
                   "${interface_object}, "
                   "${signature}, {table_name});")
    else:
        pattern = ("{install_func}("
                   "${isolate}, ${world}, "
                   "${instance_template}, "
                   "${prototype_template}, "
                   "${interface_template}, "
                   "${signature}, {table_name});")

    table_name = "kAttributeTable"
    installer_call_text = _format(
        pattern,
        install_func="IDLMemberInstaller::InstallAttributes",
        table_name=table_name)
    install_properties(table_name, attribute_entries,
                       _make_attribute_registration_table, installer_call_text)

    table_name = "kConstantCallbackTable"
    installer_call_text = _format(
        pattern,
        install_func="IDLMemberInstaller::InstallConstants",
        table_name=table_name)
    constant_callback_entries = list(
        filter(lambda entry: entry.const_callback_name, constant_entries))
    install_properties(table_name, constant_callback_entries,
                       _make_constant_callback_registration_table,
                       installer_call_text)

    table_name = "kConstantValueTable"
    installer_call_text = _format(
        pattern,
        install_func="IDLMemberInstaller::InstallConstants",
        table_name=table_name)
    constant_value_entries = list(
        filter(lambda entry: not entry.const_callback_name, constant_entries))
    install_properties(table_name, constant_value_entries,
                       _make_constant_value_registration_table,
                       installer_call_text)

    table_name = "kExposedConstructTable"
    installer_call_text = _format(
        pattern,
        install_func="IDLMemberInstaller::InstallExposedConstructs",
        table_name=table_name)
    install_properties(table_name, exposed_construct_entries,
                       _make_exposed_construct_registration_table,
                       installer_call_text)

    table_name = "kOperationTable"
    installer_call_text = _format(
        pattern,
        install_func="IDLMemberInstaller::InstallOperations",
        table_name=table_name)
    entries = list(
        filter(lambda entry: not entry.no_alloc_direct_call_callbacks,
               operation_entries))
    install_properties(table_name, entries, _make_operation_registration_table,
                       installer_call_text)
    entries = list(
        filter(lambda entry: entry.no_alloc_direct_call_callbacks,
               operation_entries))
    install_properties(table_name, entries, _make_operation_registration_table,
                       installer_call_text)

    return func_decl, func_def, trampoline_def


def make_indexed_and_named_property_callbacks_and_install_node(cg_context):
    """
    Implements non-ordinary internal methods of legacy platform objects.
    https://webidl.spec.whatwg.org/#es-legacy-platform-objects

    Also implements the same origin case of indexed access to WindowProxy
    objects just same as indexed properties of legacy platform objects.
    https://html.spec.whatwg.org/C/#the-windowproxy-exotic-object
    """

    assert isinstance(cg_context, CodeGenContext)

    F = FormatNode

    func_decls = ListNode()
    func_defs = ListNode()
    install_node = SequenceNode()

    interface = cg_context.interface
    if not (interface and interface.indexed_and_named_properties):
        return func_decls, func_defs, install_node
    props = interface.indexed_and_named_properties
    def add_callback(func_decl, func_def):
        func_decls.append(func_decl)
        if func_def:
            func_defs.append(func_def)
            func_defs.append(EmptyNode())

    def most_derived_interface(*interfaces):
        key = lambda interface: len(interface.inclusive_inherited_interfaces)
        return sorted(filter(None, interfaces), key=key)[-1]

    cg_context = cg_context.make_copy(
        v8_callback_type=CodeGenContext.V8_OTHER_CALLBACK)

    if props.own_named_getter and "Global" not in interface.extended_attributes:
        add_callback(*make_named_property_getter_callback(
            cg_context.make_copy(named_property_getter=props.named_getter,
                                 named_interceptor_kind="Getter"),
            "NamedPropertyGetterCallback"))
        add_callback(*make_named_property_setter_callback(
            cg_context.make_copy(named_property_setter=props.named_setter,
                                 named_interceptor_kind="Setter"),
            "NamedPropertySetterCallback"))
        add_callback(*make_named_property_deleter_callback(
            cg_context.make_copy(named_property_deleter=props.named_deleter,
                                 named_interceptor_kind="Deleter"),
            "NamedPropertyDeleterCallback"))
        add_callback(*make_named_property_definer_callback(
            cg_context.make_copy(named_interceptor_kind="Definer"),
            "NamedPropertyDefinerCallback"))
        add_callback(*make_named_property_descriptor_callback(
            cg_context.make_copy(named_interceptor_kind="Descriptor"),
            "NamedPropertyDescriptorCallback"))
        add_callback(*make_named_property_query_callback(
            cg_context.make_copy(
                named_interceptor_kind="Query"), "NamedPropertyQueryCallback"))
        add_callback(*make_named_property_enumerator_callback(
            cg_context.make_copy(named_interceptor_kind="Enumerator"),
            "NamedPropertyEnumeratorCallback"))

    if cg_context.class_like.identifier == "WindowProperties":
        interceptor_template = "${prototype_object_template}"
    else:
        interceptor_template = "${instance_object_template}"

    if props.named_getter and "Global" not in interface.extended_attributes:
        impl_bridge = v8_bridge_class_name(
            most_derived_interface(
                props.named_getter.owner, props.named_setter
                and props.named_setter.owner, props.named_deleter
                and props.named_deleter.owner))
        flags = ["v8::PropertyHandlerFlags::kOnlyInterceptStrings"]
        if "LegacyOverrideBuiltIns" not in interface.extended_attributes:
            flags.append("v8::PropertyHandlerFlags::kNonMasking")
        if (props.named_getter.extended_attributes.value_of("Affects") !=
                "Everything"):
            flags.append("v8::PropertyHandlerFlags::kHasNoSideEffect")
        property_handler_flags = (
            "static_cast<v8::PropertyHandlerFlags>({})".format(" | ".join(
                map(lambda flag: "int32_t({})".format(flag), flags))))
        pattern = """\
// Named interceptors
{interceptor_template}->SetHandler(
    v8::NamedPropertyHandlerConfiguration(
        {impl_bridge}::NamedPropertyGetterCallback,
        {impl_bridge}::NamedPropertySetterCallback,
% if "NotEnumerable" not in \
interface.indexed_and_named_properties.named_getter.extended_attributes:
        {impl_bridge}::NamedPropertyQueryCallback,
% else:
        nullptr,  // query
% endif
        {impl_bridge}::NamedPropertyDeleterCallback,
% if "NotEnumerable" not in \
interface.indexed_and_named_properties.named_getter.extended_attributes:
        {impl_bridge}::NamedPropertyEnumeratorCallback,
% else:
        nullptr,  // enumerator
% endif
        {impl_bridge}::NamedPropertyDefinerCallback,
        {impl_bridge}::NamedPropertyDescriptorCallback,
        v8::Local<v8::Value>(),
        {property_handler_flags}));"""
        install_node.append(
            F(pattern,
              interceptor_template=interceptor_template,
              impl_bridge=impl_bridge,
              property_handler_flags=property_handler_flags))

    if props.own_indexed_getter or props.own_named_getter:
        add_callback(*make_indexed_property_getter_callback(
            cg_context.make_copy(indexed_property_getter=props.indexed_getter,
                                 indexed_interceptor_kind="Getter"),
            "IndexedPropertyGetterCallback"))
        add_callback(*make_indexed_property_setter_callback(
            cg_context.make_copy(indexed_property_setter=props.indexed_setter,
                                 indexed_interceptor_kind="Setter"),
            "IndexedPropertySetterCallback"))
        add_callback(*make_indexed_property_deleter_callback(
            cg_context.make_copy(indexed_interceptor_kind="Deleter"),
            "IndexedPropertyDeleterCallback"))
        add_callback(*make_indexed_property_definer_callback(
            cg_context.make_copy(indexed_interceptor_kind="Definer"),
            "IndexedPropertyDefinerCallback"))
        add_callback(*make_indexed_property_descriptor_callback(
            cg_context.make_copy(indexed_interceptor_kind="Descriptor"),
            "IndexedPropertyDescriptorCallback"))
        add_callback(*make_indexed_property_enumerator_callback(
            cg_context.make_copy(indexed_interceptor_kind="Enumerator"),
            "IndexedPropertyEnumeratorCallback"))

    if props.indexed_getter or props.named_getter:
        impl_bridge = v8_bridge_class_name(
            most_derived_interface(
                props.indexed_getter and props.indexed_getter.owner,
                props.indexed_setter and props.indexed_setter.owner,
                props.named_getter and props.named_getter.owner,
                props.named_setter and props.named_setter.owner,
                props.named_deleter and props.named_deleter.owner))
        flags = []
        if (props.indexed_getter and
                props.indexed_getter.extended_attributes.value_of("Affects") !=
                "Everything"):
            flags.append("v8::PropertyHandlerFlags::kHasNoSideEffect")
        else:
            flags.append("v8::PropertyHandlerFlags::kNone")
        property_handler_flags = flags[0]
        pattern = """\
// Indexed interceptors
{interceptor_template}->SetHandler(
    v8::IndexedPropertyHandlerConfiguration(
        {impl_bridge}::IndexedPropertyGetterCallback,
        {impl_bridge}::IndexedPropertySetterCallback,
        nullptr,  // query
        {impl_bridge}::IndexedPropertyDeleterCallback,
% if interface.indexed_and_named_properties.indexed_getter:
        {impl_bridge}::IndexedPropertyEnumeratorCallback,
% else:
        nullptr,  // enumerator
% endif
        {impl_bridge}::IndexedPropertyDefinerCallback,
        {impl_bridge}::IndexedPropertyDescriptorCallback,
        v8::Local<v8::Value>(),
        {property_handler_flags}));"""
        install_node.append(
            F(pattern,
              interceptor_template=interceptor_template,
              impl_bridge=impl_bridge,
              property_handler_flags=property_handler_flags))

    func_defs.accumulate(
        CodeGenAccumulator.require_include_headers([
            "third_party/blink/renderer/bindings/core/v8/v8_set_return_value_for_core.h"
        ]))

    return func_decls, func_defs, install_node


def make_cross_origin_property_callbacks_and_install_node(
        cg_context, attribute_entries, operation_entries):
    """
    Implements non-ordinary internal methods of WindowProxy and Location
    objects.
    https://html.spec.whatwg.org/C/#the-windowproxy-exotic-object
    https://html.spec.whatwg.org/C/#the-location-interface
    """

    assert isinstance(cg_context, CodeGenContext)

    callback_defs = []
    install_node = SequenceNode()

    CROSS_ORIGIN_INTERFACES = ("Window", "Location")
    if cg_context.class_like.identifier not in CROSS_ORIGIN_INTERFACES:
        return callback_defs, install_node
    props = cg_context.interface.indexed_and_named_properties

    entry_nodes = []
    for entry in attribute_entries:
        attribute = entry.property_
        if "CrossOrigin" not in attribute.extended_attributes:
            continue
        assert entry.world == CodeGenContext.ALL_WORLDS
        values = attribute.extended_attributes.values_of("CrossOrigin")
        get_func = "nullptr"
        set_func = "nullptr"
        get_value = "nullptr"
        set_value = "nullptr"
        if not values or "Getter" in values:
            get_func = entry.attr_get_callback_name
            cgc = cg_context.make_copy(
                attribute=attribute,
                attribute_get=True,
                v8_callback_type=(
                    CodeGenContext.V8_NAMED_PROPERTY_GETTER_CALLBACK))
            get_value = callback_function_name(cgc, for_cross_origin=True)
            func_def = make_attribute_get_callback_def(cgc, get_value)
            callback_defs.extend([func_def, EmptyNode()])
        if values and "Setter" in values:
            set_func = entry.attr_set_callback_name
            cgc = cg_context.make_copy(
                attribute=attribute,
                attribute_set=True,
                v8_callback_type=(
                    CodeGenContext.V8_NAMED_PROPERTY_SETTER_CALLBACK))
            set_value = callback_function_name(cgc, for_cross_origin=True)
            func_def = make_attribute_set_callback_def(cgc, set_value)
            callback_defs.extend([func_def, EmptyNode()])
        pattern = ("{{\"{property_name}\", "
                   "{get_func}, {set_func}, {get_value}, {set_value}}},")
        entry_nodes.append(
            TextNode(
                _format(
                    pattern,
                    property_name=attribute.identifier,
                    get_func=get_func,
                    set_func=set_func,
                    get_value=get_value,
                    set_value=set_value)))
    callback_defs.append(
        ListNode([
            TextNode("constexpr bindings::CrossOriginAttributeTableEntry "
                     "kCrossOriginAttributeTable[] = {"),
            ListNode(entry_nodes),
            TextNode("};"),
            EmptyNode(),
        ]))

    entry_nodes = []
    for entry in operation_entries:
        operation_group = entry.property_
        if "CrossOrigin" not in operation_group.extended_attributes:
            continue
        assert entry.world == CodeGenContext.ALL_WORLDS
        entry_nodes.append(
            TextNode(
                _format(
                    "{{\"{property_name}\", {op_callback}, {op_func_length}}},",
                    property_name=operation_group.identifier,
                    op_callback=entry.op_callback_name,
                    op_func_length=entry.op_func_length)))
    callback_defs.append(
        ListNode([
            TextNode("constexpr bindings::CrossOriginOperationTableEntry "
                     "kCrossOriginOperationTable[] = {"),
            ListNode(entry_nodes),
            TextNode("};"),
            EmptyNode(),
        ]))

    cg_context = cg_context.make_copy(
        v8_callback_type=CodeGenContext.V8_OTHER_CALLBACK)

    func_defs = [
        make_cross_origin_access_check_callback(
            cg_context, "CrossOriginAccessCheckCallback"),
        make_cross_origin_named_getter_callback(
            cg_context.make_copy(named_interceptor_kind="Getter"),
            "CrossOriginNamedGetterCallback"),
        make_cross_origin_named_setter_callback(
            cg_context.make_copy(named_interceptor_kind="Setter"),
            "CrossOriginNamedSetterCallback"),
        make_cross_origin_throwing_callback(
            cg_context.make_copy(named_interceptor_kind="Deleter")),
        make_cross_origin_throwing_callback(
            cg_context.make_copy(named_interceptor_kind="Definer")),
        make_cross_origin_named_descriptor_callback(
            cg_context.make_copy(named_interceptor_kind="Descriptor"),
            "CrossOriginNamedDescriptorCallback"),
        make_cross_origin_named_query_callback(
            cg_context.make_copy(named_interceptor_kind="Query"),
            "CrossOriginNamedQueryCallback"),
        make_cross_origin_named_enumerator_callback(
            cg_context.make_copy(named_interceptor_kind="Enumerator"),
            "CrossOriginNamedEnumeratorCallback"),
        make_cross_origin_indexed_getter_callback(
            cg_context.make_copy(
                indexed_property_getter=(props and props.indexed_getter),
                indexed_interceptor_kind="Getter"),
            "CrossOriginIndexedGetterCallback"),
        make_cross_origin_throwing_callback(
            cg_context.make_copy(indexed_interceptor_kind="Setter")),
        make_cross_origin_throwing_callback(
            cg_context.make_copy(indexed_interceptor_kind="Deleter")),
        make_cross_origin_throwing_callback(
            cg_context.make_copy(indexed_interceptor_kind="Definer")),
        make_cross_origin_indexed_descriptor_callback(
            cg_context.make_copy(indexed_interceptor_kind="Descriptor"),
            "CrossOriginIndexedDescriptorCallback"),
        make_cross_origin_indexed_enumerator_callback(
            cg_context.make_copy(indexed_interceptor_kind="Enumerator"),
            "CrossOriginIndexedEnumeratorCallback"),
    ]
    for func_def in func_defs:
        callback_defs.append(func_def)
        callback_defs.append(EmptyNode())

    text = """\
// Cross origin properties
${instance_object_template}->SetAccessCheckCallbackAndHandler(
    CrossOriginAccessCheckCallback,
    v8::NamedPropertyHandlerConfiguration(
        CrossOriginNamedGetterCallback,
        CrossOriginNamedSetterCallback,
        CrossOriginNamedQueryCallback,
        CrossOriginNamedDeleterCallback,
        CrossOriginNamedEnumeratorCallback,
        CrossOriginNamedDefinerCallback,
        CrossOriginNamedDescriptorCallback,
        v8::Local<v8::Value>(),
        v8::PropertyHandlerFlags::kNone),
    v8::IndexedPropertyHandlerConfiguration(
        CrossOriginIndexedGetterCallback,
        CrossOriginIndexedSetterCallback,
        nullptr,  // query
        CrossOriginIndexedDeleterCallback,
        CrossOriginIndexedEnumeratorCallback,
        CrossOriginIndexedDefinerCallback,
        CrossOriginIndexedDescriptorCallback,
        v8::Local<v8::Value>(),
        v8::PropertyHandlerFlags::kNone),
    v8::External::New(
        ${isolate},
        const_cast<WrapperTypeInfo*>(${class_name}::GetWrapperTypeInfo())));
"""
    install_node.append(TextNode(text))
    install_node.accumulate(
        CodeGenAccumulator.require_include_headers([
            "third_party/blink/renderer/bindings/core/v8/binding_security.h",
            "third_party/blink/renderer/platform/bindings/v8_cross_origin_property_support.h",
        ]))

    return callback_defs, install_node


def make_cross_component_init(
        cg_context, function_name, class_name, has_unconditional_props,
        has_context_independent_props, has_context_dependent_props):
    """
    Returns:
        A triplet of CodeNode of:
        - function declaration
        - function definition
        - trampoline member variable definitions
    """
    assert isinstance(cg_context, CodeGenContext)
    assert isinstance(function_name, str)
    assert isinstance(class_name, str)
    assert isinstance(has_unconditional_props, bool)
    assert isinstance(has_context_independent_props, bool)
    assert isinstance(has_context_dependent_props, bool)

    F = FormatNode

    def filter_four_trampolines(nodes):
        assert len(nodes) == 4
        flags = (True, has_unconditional_props, has_context_independent_props,
                 has_context_dependent_props)
        return [node for node, flag in zip(nodes, flags) if flag]

    trampoline_var_decls = ListNode(
        filter_four_trampolines([
            F("static InstallInterfaceTemplateFuncType {};",
              TP_INSTALL_INTERFACE_TEMPLATE),
            F("static InstallUnconditionalPropertiesFuncType {};",
              TP_INSTALL_UNCONDITIONAL_PROPS),
            F("static InstallContextIndependentPropertiesFuncType {};",
              TP_INSTALL_CONTEXT_INDEPENDENT_PROPS),
            F("static InstallContextDependentPropertiesFuncType {};",
              TP_INSTALL_CONTEXT_DEPENDENT_PROPS),
        ]))

    trampoline_var_defs = ListNode(
        filter_four_trampolines([
            F(("${class_name}::InstallInterfaceTemplateFuncType "
               "${class_name}::{} = nullptr;"), TP_INSTALL_INTERFACE_TEMPLATE),
            F(("${class_name}::InstallUnconditionalPropertiesFuncType "
               "${class_name}::{} = nullptr;"),
              TP_INSTALL_UNCONDITIONAL_PROPS),
            F(("${class_name}::InstallContextIndependentPropertiesFuncType "
               "${class_name}::{} = nullptr;"),
              TP_INSTALL_CONTEXT_INDEPENDENT_PROPS),
            F(("${class_name}::InstallContextDependentPropertiesFuncType "
               "${class_name}::{} = nullptr;"),
              TP_INSTALL_CONTEXT_DEPENDENT_PROPS),
        ]))
    trampoline_var_defs.set_base_template_vars(cg_context.template_bindings())

    func_decl = CxxFuncDeclNode(
        name=function_name, arg_decls=[], return_type="void", static=True)

    func_def = CxxFuncDefNode(
        name=function_name,
        arg_decls=[],
        return_type="void",
        class_name=class_name)
    func_def.set_base_template_vars(cg_context.template_bindings())

    body = func_def.body
    body.extend(
        filter_four_trampolines([
            F("${class_name}::{} = {};", TP_INSTALL_INTERFACE_TEMPLATE,
              FN_INSTALL_INTERFACE_TEMPLATE),
            F("${class_name}::{} = {};", TP_INSTALL_UNCONDITIONAL_PROPS,
              FN_INSTALL_UNCONDITIONAL_PROPS),
            F("${class_name}::{} = {};", TP_INSTALL_CONTEXT_INDEPENDENT_PROPS,
              FN_INSTALL_CONTEXT_INDEPENDENT_PROPS),
            F("${class_name}::{} = {};", TP_INSTALL_CONTEXT_DEPENDENT_PROPS,
              FN_INSTALL_CONTEXT_DEPENDENT_PROPS),
        ]))

    return func_decl, func_def, trampoline_var_decls, trampoline_var_defs


# ----------------------------------------------------------------------------
# IsExposed
# ----------------------------------------------------------------------------


def make_is_exposed(cg_context, function_name):
    assert isinstance(cg_context, CodeGenContext)
    assert function_name == "IsExposed"
    class_like = cg_context.class_like

    is_exposed_decl = CxxFuncDeclNode(
        name=function_name,
        arg_decls=["ExecutionContext* execution_context"],
        return_type="bool",
        static=True)
    is_exposed_decl.accumulate(
        CodeGenAccumulator.require_class_decls(["ExecutionContext"]))

    is_exposed_def = CxxFuncDefNode(
        name=function_name,
        arg_decls=["ExecutionContext* execution_context"],
        return_type="bool",
        class_name=cg_context.class_name)

    def define_execution_context(symbol_node):
        # execution_context doesn't really need a definition because it's a
        # function argument, but needs to require ".../execution_context.h".
        node = SymbolDefinitionNode(symbol_node)
        node.accumulate(
            CodeGenAccumulator.require_include_headers([
                "third_party/blink/renderer/core/execution_context/execution_context.h"
            ]))
        return node

    is_exposed_def.body.register_code_symbol(
        SymbolNode("execution_context",
                   definition_constructor=define_execution_context))
    bind_installer_local_vars(is_exposed_def.body, cg_context)
    # If [Exposed] exists at all, then this exposure condition should be valid.
    # Otherwise, it is not an exposed interface at all.
    if class_like.exposure.global_names_and_features:
        is_exposed_def.body.append(
            FormatNode("return {};",
                       expr_from_exposure(class_like.exposure).to_text()))
    else:
        is_exposed_def.body.append(TextNode("return false;"))
    return (is_exposed_decl, is_exposed_def)


# ----------------------------------------------------------------------------
# WrapperTypeInfo
# ----------------------------------------------------------------------------


def make_wrapper_type_info(cg_context, function_name,
                           has_context_dependent_props):
    assert isinstance(cg_context, CodeGenContext)
    assert function_name == "GetWrapperTypeInfo"
    assert isinstance(has_context_dependent_props, bool)

    F = FormatNode
    class_like = cg_context.class_like

    func_def = CxxFuncDefNode(name=function_name,
                              arg_decls=[],
                              return_type="constexpr const WrapperTypeInfo*",
                              static=True)
    func_def.set_base_template_vars(cg_context.template_bindings())
    func_def.body.append(TextNode("return &wrapper_type_info_;"))

    public_defs = SequenceNode()
    public_defs.append(func_def)

    public_defs.append(
        TextNode("""\
  static constexpr v8::CppHeapPointerTag kThisTag =
      static_cast<v8::CppHeapPointerTag>({this_tag});
  static constexpr v8::CppHeapPointerTag kMaxSubclassTag =
      static_cast<v8::CppHeapPointerTag>({max_subclass_tag});
  static constexpr v8::CppHeapPointerTagRange kTagRange =
      v8::CppHeapPointerTagRange(kThisTag, kMaxSubclassTag);
""".format(this_tag=class_like.tag,
           max_subclass_tag=class_like.max_subclass_tag)))

    member_var_def = TextNode(
        "static const WrapperTypeInfo wrapper_type_info_;")
    member_var_def.accumulate(
        CodeGenAccumulator.require_struct_decls(["WrapperTypeInfo"]))

    wrapper_type_info_def = ListNode()
    wrapper_type_info_def.set_base_template_vars(
        cg_context.template_bindings())
    wrapper_type_info_def.accumulate(
        CodeGenAccumulator.require_include_headers([
            "third_party/blink/renderer/platform/bindings/wrapper_type_info.h"
        ]))

    pattern = """\
// Construction of WrapperTypeInfo may require non-trivial initialization due
// to cross-component address resolution in order to load the pointer to the
// parent interface's WrapperTypeInfo.  We ignore this issue because the issue
// happens only on component builds and the official release builds
// (statically-linked builds) are never affected by this issue.
#if defined(COMPONENT_BUILD) && defined(WIN32) && defined(__clang__)
#pragma clang diagnostic push
#pragma clang diagnostic ignored "-Wglobal-constructors"
#endif

const WrapperTypeInfo ${class_name}::wrapper_type_info_{{
    gin::kEmbedderBlink,
    ${class_name}::{install_interface_template_func},
    {install_context_dependent_func},
    "${{class_like.identifier}}",
    {wrapper_type_info_of_inherited},
    ${class_name}::kThisTag,
    ${class_name}::kMaxSubclassTag,
    {wrapper_type_prototype},
    {wrapper_class_id},
    {active_script_wrappable_inheritance},
    {idl_definition_kind},
    {is_skipped_in_interface_object_prototype_chain},
}};

#if defined(COMPONENT_BUILD) && defined(WIN32) && defined(__clang__)
#pragma clang diagnostic pop
#endif
"""
    if has_context_dependent_props:
        install_context_dependent_func = _format(
            "${class_name}::{}", FN_INSTALL_CONTEXT_DEPENDENT_PROPS)
    else:
        install_context_dependent_func = "nullptr"
    if class_like.is_interface and class_like.inherited:
        wrapper_type_info_of_inherited = "{}::GetWrapperTypeInfo()".format(
            v8_bridge_class_name(class_like.inherited))
    else:
        wrapper_type_info_of_inherited = "nullptr"
    if (class_like.is_interface or class_like.is_async_iterator
            or class_like.is_sync_iterator):
        wrapper_type_prototype = "WrapperTypeInfo::kWrapperTypeObjectPrototype"
    else:
        wrapper_type_prototype = "WrapperTypeInfo::kWrapperTypeNoPrototype"
    if class_like.is_interface and class_like.does_implement("Node"):
        wrapper_class_id = "WrapperTypeInfo::kNodeClassId"
    else:
        wrapper_class_id = "WrapperTypeInfo::kObjectClassId"
    if class_like.code_generator_info.is_active_script_wrappable:
        active_script_wrappable_inheritance = (
            "WrapperTypeInfo::kInheritFromActiveScriptWrappable")
    else:
        active_script_wrappable_inheritance = (
            "WrapperTypeInfo::kNotInheritFromActiveScriptWrappable")
    if class_like.is_interface:
        idl_definition_kind = "WrapperTypeInfo::kIdlInterface"
    elif class_like.is_namespace:
        idl_definition_kind = "WrapperTypeInfo::kIdlNamespace"
    elif class_like.is_callback_interface:
        idl_definition_kind = "WrapperTypeInfo::kIdlCallbackInterface"
    elif class_like.is_async_iterator or class_like.is_sync_iterator:
        idl_definition_kind = "WrapperTypeInfo::kIdlAsyncOrSyncIterator"
    else:
        assert False
    is_skipped_in_interface_object_prototype_chain = (
        "true" if class_like.identifier == "WindowProperties" else "false")
    wrapper_type_info_def.append(
        F(pattern,
          install_interface_template_func=FN_INSTALL_INTERFACE_TEMPLATE,
          install_context_dependent_func=install_context_dependent_func,
          wrapper_type_info_of_inherited=wrapper_type_info_of_inherited,
          wrapper_type_prototype=wrapper_type_prototype,
          wrapper_class_id=wrapper_class_id,
          active_script_wrappable_inheritance=(
              active_script_wrappable_inheritance),
          idl_definition_kind=idl_definition_kind,
          is_skipped_in_interface_object_prototype_chain=(
              is_skipped_in_interface_object_prototype_chain)))

    if (class_like.is_interface or class_like.is_async_iterator
            or class_like.is_sync_iterator):
        blink_class = blink_class_name(class_like)
        pattern = """\
const WrapperTypeInfo& {blink_class}::wrapper_type_info_ =
    ${class_name}::wrapper_type_info_;
"""
        wrapper_type_info_def.append(F(pattern, blink_class=blink_class))

    if class_like.code_generator_info.is_active_script_wrappable:
        pattern = """\
// [ActiveScriptWrappable]
static_assert(
    std::is_base_of<ActiveScriptWrappableBase, {blink_class}>::value,
    "{blink_class} does not inherit from ActiveScriptWrappable<> despite "
    "the IDL has [ActiveScriptWrappable] extended attribute.");"""
    else:
        pattern = """\
// non-[ActiveScriptWrappable]
static_assert(
    !std::is_base_of<ActiveScriptWrappableBase, {blink_class}>::value,
    "{blink_class} inherits from ActiveScriptWrappable<> without "
    "[ActiveScriptWrappable] extended attribute.");"""
    if class_like.is_interface:
        wrapper_type_info_def.append(F(pattern, blink_class=blink_class))

    return public_defs, member_var_def, wrapper_type_info_def


# ----------------------------------------------------------------------------
# V8 Context Snapshot
# ----------------------------------------------------------------------------


def make_v8_context_snapshot_api(cg_context, component, attribute_entries,
                                 constant_entries, constructor_entries,
                                 exposed_construct_entries, operation_entries,
                                 indexed_and_named_property_defs,
                                 cross_origin_property_callback_defs,
                                 install_context_independent_func_name):
    assert isinstance(cg_context, CodeGenContext)
    assert isinstance(component, web_idl.Component)

    if not cg_context.interface:
        return None, None

    subclass_interfaces = cg_context.interface.subclasses
    subclass_names = list(
        map(lambda interface: interface.identifier, subclass_interfaces))
    subclass_names.append(cg_context.interface.identifier)
    if not ("Window" in subclass_names or "HTMLDocument" in subclass_names):
        return None, None

    header_ns = CxxNamespaceNode(name_style.namespace("v8_context_snapshot"))
    source_ns = CxxNamespaceNode(name_style.namespace("v8_context_snapshot"))

    export_text = component_export(component, False)

    def add_func(func_decl, func_def):
        header_ns.body.extend([
            TextNode(export_text),
            func_decl,
            EmptyNode(),
        ])
        source_ns.body.extend([
            func_def,
            EmptyNode(),
        ])

    add_func(*_make_v8_context_snapshot_get_reference_table_function(
        cg_context, name_style.func("GetRefTableOf", cg_context.class_name),
        attribute_entries, constant_entries, constructor_entries,
        exposed_construct_entries, operation_entries,
        indexed_and_named_property_defs, cross_origin_property_callback_defs))

    add_func(*_make_v8_context_snapshot_install_props_per_context_function(
        cg_context, name_style.func("InstallPropsOf",
                                    cg_context.class_name), attribute_entries,
        constant_entries, exposed_construct_entries, operation_entries))

    add_func(*_make_v8_context_snapshot_install_props_per_isolate_function(
        cg_context, name_style.func("InstallPropsOf", cg_context.class_name),
        install_context_independent_func_name))

    return header_ns, source_ns


def _make_v8_context_snapshot_get_reference_table_function(
        cg_context, function_name, attribute_entries, constant_entries,
        constructor_entries, exposed_construct_entries, operation_entries,
        indexed_and_named_property_defs, cross_origin_property_callback_defs):
    callback_names = ["${class_name}::GetWrapperTypeInfo()"]

    for entry in attribute_entries:
        if entry.exposure_conditional.is_always_true:
            callback_names.append(entry.attr_get_callback_name)
            callback_names.append(entry.attr_set_callback_name)
    for entry in constant_entries:
        if entry.exposure_conditional.is_always_true:
            callback_names.append(entry.const_callback_name)
    for entry in constructor_entries:
        if entry.exposure_conditional.is_always_true:
            callback_names.append(entry.ctor_callback_name)
    for entry in exposed_construct_entries:
        if entry.exposure_conditional.is_always_true:
            callback_names.append(entry.prop_callback_name)
    for entry in operation_entries:
        if entry.exposure_conditional.is_always_true:
            callback_names.append(entry.op_callback_name)

    def collect_callbacks(node):
        if isinstance(node, CxxFuncDefNode):
            callback_names.append(node.function_name)
        elif hasattr(node, "__iter__"):
            for child_node in node:
                collect_callbacks(child_node)

    collect_callbacks(cross_origin_property_callback_defs)

    for node in indexed_and_named_property_defs:
        if isinstance(node, CxxFuncDefNode):
            callback_names.append(
                _format("${class_name}::{}", node.function_name))

    entry_nodes = list(
        map(
            lambda name: TextNode("reinterpret_cast<intptr_t>({}),".format(name
                                                                           )),
            filter(None, callback_names)))
    table_node = ListNode([
        TextNode("static const intptr_t kReferenceTable[] = {"),
        ListNode(entry_nodes),
        TextNode("};"),
    ])

    func_decl = CxxFuncDeclNode(name=function_name,
                                arg_decls=[],
                                return_type="base::span<const intptr_t>")

    func_def = CxxFuncDefNode(name=function_name,
                              arg_decls=[],
                              return_type="base::span<const intptr_t>")
    func_def.set_base_template_vars(cg_context.template_bindings())
    body = func_def.body
    body.extend([table_node, TextNode("return kReferenceTable;")])

    return func_decl, func_def


def _make_v8_context_snapshot_install_props_per_context_function(
        cg_context, function_name, attribute_entries, constant_entries,
        exposed_construct_entries, operation_entries):
    def selector(entry):
        if entry.exposure_conditional.is_always_true:
            return False
        if entry.is_context_dependent:
            return False
        return True

    func_decl, func_def, _ = make_install_properties(
        cg_context,
        function_name,
        class_name=None,
        prop_install_mode=PropInstallMode.V8_CONTEXT_SNAPSHOT,
        trampoline_var_name=None,
        attribute_entries=list(filter(selector, attribute_entries)),
        constant_entries=list(filter(selector, constant_entries)),
        exposed_construct_entries=list(
            filter(selector, exposed_construct_entries)),
        operation_entries=list(filter(selector, operation_entries)))

    return func_decl, func_def


def _make_v8_context_snapshot_install_props_per_isolate_function(
        cg_context, function_name, install_context_independent_func_name):
    arg_decls = [
        "v8::Isolate* isolate",
        "const DOMWrapperWorld& world",
        "v8::Local<v8::Template> instance_template",
        "v8::Local<v8::Template> prototype_template",
        "v8::Local<v8::Template> interface_template",
    ]
    arg_names = [
        "isolate",
        "world",
        "instance_template",
        "prototype_template",
        "interface_template",
    ]
    return_type = "void"

    func_decl = CxxFuncDeclNode(name=function_name,
                                arg_decls=arg_decls,
                                return_type=return_type)
    func_def = CxxFuncDefNode(name=function_name,
                              arg_decls=arg_decls,
                              return_type=return_type)

    if not install_context_independent_func_name:
        return func_decl, func_def

    func_def.set_base_template_vars(cg_context.template_bindings())
    body = func_def.body
    for arg_name in arg_names:
        body.add_template_var(arg_name, arg_name)
    pattern = """\
return ${class_name}::{func}(
    ${isolate}, ${world},
    ${instance_template},
    ${prototype_template},
    ${interface_template});\
"""
    body.append(
        TextNode(_format(pattern, func=install_context_independent_func_name)))
    return func_decl, func_def


# ----------------------------------------------------------------------------
# Main functions
# ----------------------------------------------------------------------------


def _collect_include_headers(class_like):
    assert isinstance(class_like,
                      (web_idl.Interface, web_idl.Namespace,
                       web_idl.AsyncIterator, web_idl.SyncIterator))

    headers = set(class_like.code_generator_info.blink_headers or [])

    def collect_from_idl_type(idl_type):
        idl_type.apply_to_all_composing_elements(add_include_headers)

    def add_include_headers(idl_type):
        type_def_obj = idl_type.type_definition_object
        if type_def_obj is not None:
            if (type_def_obj.identifier in (
                    "OnErrorEventHandlerNonNull",
                    "OnBeforeUnloadEventHandlerNonNull")):
                raise StopIteration(idl_type.syntactic_form)

            headers.add(PathManager(type_def_obj).api_path(ext="h"))
            if type_def_obj.is_interface or type_def_obj.is_namespace:
                headers.add(PathManager(type_def_obj).blink_path(ext="h"))
            raise StopIteration(idl_type.syntactic_form)

        union_def_obj = idl_type.union_definition_object
        if union_def_obj is not None:
            headers.add(PathManager(union_def_obj).api_path(ext="h"))
            return

        if idl_type.is_frozen_array:
            headers.add(
                "third_party/blink/renderer/bindings/core/v8/frozen_array.h")

    for attribute in class_like.attributes:
        collect_from_idl_type(attribute.idl_type)

    operations = []
    operations.extend(class_like.constructors)
    operations.extend(class_like.operations)
    if class_like.is_interface:
        for x in (class_like.async_iterable, class_like.iterable,
                  class_like.maplike, class_like.setlike):
            if x:
                operations.extend(x.operations)
        for exposed_construct in class_like.exposed_constructs:
            operations.extend(exposed_construct.legacy_factory_functions)
    for operation in operations:
        collect_from_idl_type(operation.return_type)
        for argument in operation.arguments:
            collect_from_idl_type(argument.idl_type)

    if class_like.is_interface:
        for exposed_construct in class_like.exposed_constructs:
            headers.add(PathManager(exposed_construct).api_path(ext="h"))
        for legacy_window_alias in class_like.legacy_window_aliases:
            headers.add(
                PathManager(legacy_window_alias.original).api_path(ext="h"))

    path_manager = PathManager(class_like)
    headers.discard(path_manager.api_path(ext="h"))
    headers.discard(path_manager.impl_path(ext="h"))

    # TODO(yukishiino): Window interface should be
    # [ImplementedAs=LocalDOMWindow] instead of [ImplementedAs=DOMWindow], and
    # [CrossOrigin] properties should be implemented specifically with
    # DOMWindow class.  Then, we'll have less hacks.
    if class_like.identifier == "Window":
        headers.add("third_party/blink/renderer/core/frame/local_dom_window.h")

    return headers


def generate_class_like(class_like,
                        generate_iterator_blink_impl_class_callback=None):
    assert isinstance(class_like,
                      (web_idl.Interface, web_idl.Namespace,
                       web_idl.AsyncIterator, web_idl.SyncIterator))

    path_manager = PathManager(class_like)
    api_component = path_manager.api_component
    impl_component = path_manager.impl_component
    is_cross_components = path_manager.is_cross_components
    for_testing = class_like.code_generator_info.for_testing

    # Class names
    api_class_name = v8_bridge_class_name(class_like)
    if is_cross_components:
        impl_class_name = "{}::Impl".format(api_class_name)
    else:
        impl_class_name = api_class_name

    interface = None
    namespace = None
    if class_like.is_interface:
        interface = class_like
        cg_context = CodeGenContext(interface=interface,
                                    class_name=api_class_name)
    elif class_like.is_namespace:
        namespace = class_like
        cg_context = CodeGenContext(namespace=namespace,
                                    class_name=api_class_name)
    elif class_like.is_async_iterator:
        cg_context = CodeGenContext(async_iterator=class_like,
                                    class_name=api_class_name)
    elif class_like.is_sync_iterator:
        cg_context = CodeGenContext(sync_iterator=class_like,
                                    class_name=api_class_name)
    else:
        assert False

    # Filepaths
    api_header_path = path_manager.api_path(ext="h")
    api_source_path = path_manager.api_path(ext="cc")
    if is_cross_components:
        impl_header_path = path_manager.impl_path(ext="h")
        impl_source_path = path_manager.impl_path(ext="cc")

    # Root nodes
    api_header_node = ListNode(tail="\n")
    api_header_node.set_accumulator(CodeGenAccumulator())
    api_header_node.set_renderer(MakoRenderer())
    api_source_node = ListNode(tail="\n")
    api_source_node.set_accumulator(CodeGenAccumulator())
    api_source_node.set_renderer(MakoRenderer())
    if is_cross_components:
        impl_header_node = ListNode(tail="\n")
        impl_header_node.set_accumulator(CodeGenAccumulator())
        impl_header_node.set_renderer(MakoRenderer())
        impl_source_node = ListNode(tail="\n")
        impl_source_node.set_accumulator(CodeGenAccumulator())
        impl_source_node.set_renderer(MakoRenderer())
    else:
        impl_header_node = api_header_node
        impl_source_node = api_source_node

    # Namespaces
    api_header_blink_ns = CxxNamespaceNode(name_style.namespace("blink"))
    api_source_blink_ns = CxxNamespaceNode(name_style.namespace("blink"))
    if is_cross_components:
        impl_header_blink_ns = CxxNamespaceNode(name_style.namespace("blink"))
        impl_source_blink_ns = CxxNamespaceNode(name_style.namespace("blink"))
    else:
        impl_header_blink_ns = api_header_blink_ns
        impl_source_blink_ns = api_source_blink_ns

    # Class definitions
    api_class_def = CxxClassDefNode(
        cg_context.class_name,
        base_class_names=[
            _format("bindings::V8InterfaceBridge<${class_name}, {}>",
                    blink_class_name(class_like)),
        ],
        final=True,
        export=component_export(api_component, for_testing))
    api_class_def.set_base_template_vars(cg_context.template_bindings())
    api_class_def.bottom_section.append(
        TextNode("friend class {};".format(blink_class_name(class_like))))
    if is_cross_components:
        impl_class_def = CxxClassDefNode(impl_class_name,
                                         final=True,
                                         export=component_export(
                                             impl_component, for_testing))
        impl_class_def.set_base_template_vars(cg_context.template_bindings())
        api_class_def.public_section.extend([
            TextNode("// Cross-component implementation class"),
            TextNode("class Impl;"),
            EmptyNode(),
        ])
    else:
        impl_class_def = api_class_def

    # Constants
    constants_def = None
    if class_like.constants:
        constants_def = CxxClassDefNode(name="Constant", final=True)
        constants_def.top_section.append(TextNode("STATIC_ONLY(Constant);"))
        for constant in class_like.constants:
            cgc = cg_context.make_copy(constant=constant)
            constants_def.public_section.append(
                make_constant_constant_def(cgc, constant_name(cgc)))

    # Cross-component trampolines
    if is_cross_components:
        # tp_ = trampoline name
        tp_install_interface_template = TP_INSTALL_INTERFACE_TEMPLATE
        tp_install_unconditional_props = TP_INSTALL_UNCONDITIONAL_PROPS
        tp_install_context_independent_props = (
            TP_INSTALL_CONTEXT_INDEPENDENT_PROPS)
        tp_install_context_dependent_props = TP_INSTALL_CONTEXT_DEPENDENT_PROPS
    else:
        tp_install_interface_template = None
        tp_install_unconditional_props = None
        tp_install_context_independent_props = None
        tp_install_context_dependent_props = None

    # Callback functions
    attribute_entries = []
    constant_entries = []
    constructor_entries = []
    exposed_construct_entries = []
    operation_entries = []
    callback_defs = make_property_entries_and_callback_defs(
        cg_context,
        attribute_entries=attribute_entries,
        constant_entries=constant_entries,
        constructor_entries=constructor_entries,
        exposed_construct_entries=exposed_construct_entries,
        operation_entries=operation_entries)
    supplemental_install_node = SequenceNode()

    # Cross origin properties
    (cross_origin_property_callback_defs,
     cross_origin_property_install_node) = (
         make_cross_origin_property_callbacks_and_install_node(
             cg_context, attribute_entries, operation_entries))
    callback_defs.extend(cross_origin_property_callback_defs)
    supplemental_install_node.append(cross_origin_property_install_node)
    supplemental_install_node.append(EmptyNode())

    # Indexed and named properties
    # Shorten a function name to mitigate a style check error.
    f = make_indexed_and_named_property_callbacks_and_install_node
    (indexed_and_named_property_decls, indexed_and_named_property_defs,
     indexed_and_named_property_install_node) = f(cg_context)
    supplemental_install_node.append(indexed_and_named_property_install_node)
    supplemental_install_node.append(EmptyNode())

    # Installer functions
    is_unconditional = lambda entry: entry.exposure_conditional.is_always_true
    is_context_dependent = lambda entry: entry.is_context_dependent
    is_context_independent = (
        lambda e: not is_context_dependent(e) and not is_unconditional(e))
    (install_unconditional_props_decl, install_unconditional_props_def,
     install_unconditional_props_trampoline) = make_install_properties(
         cg_context,
         FN_INSTALL_UNCONDITIONAL_PROPS,
         class_name=impl_class_name,
         prop_install_mode=PropInstallMode.UNCONDITIONAL,
         trampoline_var_name=tp_install_unconditional_props,
         attribute_entries=list(filter(is_unconditional, attribute_entries)),
         constant_entries=list(filter(is_unconditional, constant_entries)),
         exposed_construct_entries=list(
             filter(is_unconditional, exposed_construct_entries)),
         operation_entries=list(filter(is_unconditional, operation_entries)))
    (install_context_independent_props_decl,
     install_context_independent_props_def,
     install_context_independent_props_trampoline) = make_install_properties(
         cg_context,
         FN_INSTALL_CONTEXT_INDEPENDENT_PROPS,
         class_name=impl_class_name,
         prop_install_mode=PropInstallMode.CONTEXT_INDEPENDENT,
         trampoline_var_name=tp_install_context_independent_props,
         attribute_entries=list(
             filter(is_context_independent, attribute_entries)),
         constant_entries=list(filter(is_context_independent,
                                      constant_entries)),
         exposed_construct_entries=list(
             filter(is_context_independent, exposed_construct_entries)),
         operation_entries=list(
             filter(is_context_independent, operation_entries)))
    (install_context_dependent_props_decl, install_context_dependent_props_def,
     install_context_dependent_props_trampoline) = make_install_properties(
         cg_context,
         FN_INSTALL_CONTEXT_DEPENDENT_PROPS,
         class_name=impl_class_name,
         prop_install_mode=PropInstallMode.CONTEXT_DEPENDENT,
         trampoline_var_name=tp_install_context_dependent_props,
         attribute_entries=list(filter(is_context_dependent,
                                       attribute_entries)),
         constant_entries=list(filter(is_context_dependent, constant_entries)),
         exposed_construct_entries=list(
             filter(is_context_dependent, exposed_construct_entries)),
         operation_entries=list(filter(is_context_dependent,
                                       operation_entries)))
    (install_interface_template_decl, install_interface_template_def,
     install_interface_template_trampoline) = make_install_interface_template(
         cg_context,
         FN_INSTALL_INTERFACE_TEMPLATE,
         class_name=impl_class_name,
         trampoline_var_name=tp_install_interface_template,
         constructor_entries=constructor_entries,
         supplemental_install_node=supplemental_install_node,
         install_unconditional_func_name=(install_unconditional_props_def
                                          and FN_INSTALL_UNCONDITIONAL_PROPS),
         install_context_independent_func_name=(
             install_context_independent_props_def
             and FN_INSTALL_CONTEXT_INDEPENDENT_PROPS))
    installer_function_decls = ListNode([
        install_interface_template_decl,
        install_unconditional_props_decl,
        install_context_independent_props_decl,
        install_context_dependent_props_decl,
    ])
    installer_function_defs = ListNode([
        install_interface_template_def,
        EmptyNode(),
        install_unconditional_props_def,
        EmptyNode(),
        install_context_independent_props_def,
        EmptyNode(),
        install_context_dependent_props_def,
    ])
    installer_function_trampolines = ListNode([
        install_interface_template_trampoline,
        install_unconditional_props_trampoline,
        install_context_independent_props_trampoline,
        install_context_dependent_props_trampoline,
    ])

    # WrapperTypeInfo
    (get_wrapper_type_info_def, wrapper_type_info_var_def,
     wrapper_type_info_init) = make_wrapper_type_info(
         cg_context,
         "GetWrapperTypeInfo",
         has_context_dependent_props=bool(
             install_context_dependent_props_decl))

    # Exposure
    (is_exposed_decl,
     is_exposed_def) = make_is_exposed(cg_context, "IsExposed")

    # Cross-component trampolines
    if is_cross_components:
        (cross_component_init_decl, cross_component_init_def,
         trampoline_var_decls,
         trampoline_var_defs) = make_cross_component_init(
             cg_context,
             "Init",
             class_name=impl_class_name,
             has_unconditional_props=bool(install_unconditional_props_decl),
             has_context_independent_props=bool(
                 install_context_independent_props_decl),
             has_context_dependent_props=bool(
                 install_context_dependent_props_decl))

    # V8 Context Snapshot
    (header_v8_context_snapshot_ns,
     source_v8_context_snapshot_ns) = make_v8_context_snapshot_api(
         cg_context, impl_component, attribute_entries, constant_entries,
         constructor_entries, exposed_construct_entries, operation_entries,
         indexed_and_named_property_defs, cross_origin_property_callback_defs,
         (install_context_independent_props_def
          and FN_INSTALL_CONTEXT_INDEPENDENT_PROPS))

    # Header part (copyright, include directives, and forward declarations)
    api_header_node.extend([
        make_copyright_header(),
        EmptyNode(),
        enclose_with_header_guard(
            ListNode([
                make_header_include_directives(api_header_node.accumulator),
                EmptyNode(),
                api_header_blink_ns,
            ]), name_style.header_guard(api_header_path)),
    ])
    api_header_blink_ns.body.extend([
        make_forward_declarations(api_header_node.accumulator),
        EmptyNode(),
    ])
    api_source_node.extend([
        make_copyright_header(),
        EmptyNode(),
        TextNode("#include \"{}\"".format(api_header_path)),
        EmptyNode(),
        make_header_include_directives(api_source_node.accumulator),
        EmptyNode(),
        api_source_blink_ns,
    ])
    api_source_blink_ns.body.extend([
        make_forward_declarations(api_source_node.accumulator),
        EmptyNode(),
    ])
    if is_cross_components:
        impl_header_node.extend([
            make_copyright_header(),
            EmptyNode(),
            enclose_with_header_guard(
                ListNode([
                    make_header_include_directives(
                        impl_header_node.accumulator),
                    EmptyNode(),
                    impl_header_blink_ns,
                ]), name_style.header_guard(impl_header_path)),
        ])
        impl_header_blink_ns.body.extend([
            make_forward_declarations(impl_header_node.accumulator),
            EmptyNode(),
        ])
        impl_source_node.extend([
            make_copyright_header(),
            EmptyNode(),
            TextNode("#include \"{}\"".format(impl_header_path)),
            EmptyNode(),
            make_header_include_directives(impl_source_node.accumulator),
            EmptyNode(),
            impl_source_blink_ns,
        ])
        impl_source_blink_ns.body.extend([
            make_forward_declarations(impl_source_node.accumulator),
            EmptyNode(),
        ])
    if class_like.is_async_iterator or class_like.is_sync_iterator:
        api_header_node.accumulator.add_class_decls(
            [blink_class_name(class_like.interface)])
    else:
        api_header_node.accumulator.add_class_decls(
            [blink_class_name(class_like)])
    api_header_node.accumulator.add_include_headers([
        component_export_header(api_component, for_testing),
        "third_party/blink/renderer/platform/bindings/v8_interface_bridge.h",
    ])
    api_source_node.accumulator.add_include_headers([
        # Blink implementation class' header (e.g. node.h for Node)
        (class_like.code_generator_info.blink_headers
         and class_like.code_generator_info.blink_headers[0]),
        "third_party/blink/public/mojom/origin_trial_feature/origin_trial_feature.mojom-shared.h",
    ])
    if interface and interface.inherited:
        api_source_node.accumulator.add_include_headers(
            [PathManager(interface.inherited).api_path(ext="h")])
    if is_cross_components:
        impl_header_node.accumulator.add_include_headers([
            api_header_path,
            component_export_header(impl_component, for_testing),
        ])
    impl_source_node.accumulator.add_include_headers([
        # Blink implementation class' header (e.g. node.h for Node)
        (class_like.code_generator_info.blink_headers
         and class_like.code_generator_info.blink_headers[0]),
        "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/bindings/core/v8/v8_set_return_value_for_core.h",
        "third_party/blink/renderer/platform/bindings/exception_messages.h",
        "third_party/blink/renderer/platform/bindings/idl_member_installer.h",
        "third_party/blink/renderer/platform/bindings/runtime_call_stats.h",
        "third_party/blink/renderer/platform/bindings/v8_binding.h",
        "third_party/blink/public/mojom/origin_trial_feature/origin_trial_feature.mojom-shared.h",
    ])
    impl_source_node.accumulator.add_include_headers(
        _collect_include_headers(class_like))

    # Assemble the parts.
    if generate_iterator_blink_impl_class_callback:
        assert isinstance(class_like,
                          (web_idl.AsyncIterator, web_idl.SyncIterator))
        generate_iterator_blink_impl_class_callback(
            iterator_class_like=class_like,
            api_component=api_component,
            for_testing=for_testing,
            header_blink_ns=api_header_blink_ns,
            source_blink_ns=api_source_blink_ns)

    api_header_blink_ns.body.extend([
        api_class_def,
        EmptyNode(),
    ])
    if is_cross_components:
        impl_header_blink_ns.body.extend([
            impl_class_def,
            EmptyNode(),
        ])

    if constants_def:
        api_class_def.public_section.extend([
            TextNode("// Constants"),
            constants_def,
            EmptyNode(),
        ])

    api_class_def.public_section.extend([
        is_exposed_decl,
        EmptyNode(),
    ])
    api_source_blink_ns.body.extend([
        is_exposed_def,
        EmptyNode(),
    ])

    api_class_def.public_section.append(get_wrapper_type_info_def)
    api_class_def.public_section.append(EmptyNode())
    api_class_def.private_section.append(wrapper_type_info_var_def)
    api_class_def.private_section.append(EmptyNode())
    api_source_blink_ns.body.extend([
        wrapper_type_info_init,
        EmptyNode(),
    ])

    if is_cross_components:
        api_class_def.public_section.append(installer_function_trampolines)
        api_class_def.public_section.append(EmptyNode())
        api_class_def.private_section.extend([
            TextNode("// Cross-component trampolines"),
            trampoline_var_decls,
            EmptyNode(),
        ])
        api_source_blink_ns.body.extend([
            TextNode("// Cross-component trampolines"),
            trampoline_var_defs,
            EmptyNode(),
        ])
        impl_class_def.public_section.append(cross_component_init_decl)
        impl_class_def.private_section.append(installer_function_decls)
        impl_source_blink_ns.body.extend([
            cross_component_init_def,
            EmptyNode(),
        ])
    else:
        api_class_def.public_section.append(installer_function_decls)
        api_class_def.public_section.append(EmptyNode())

    if indexed_and_named_property_decls:
        api_class_def.public_section.extend([
            TextNode("// Indexed properties and named properties"),
            indexed_and_named_property_decls,
            EmptyNode(),
        ])
        api_source_blink_ns.body.extend([
            indexed_and_named_property_defs,
            EmptyNode(),
        ])

    debugging_namespace_name = name_style.namespace("v8",
                                                    class_like.identifier)
    impl_source_blink_ns.body.extend([
        CxxNamespaceNode(
            name="",
            body=[
                # Enclose the implementations with a namespace just in order to
                # include the class_like name in a stacktrace, such as
                #
                #   blink::(anonymous namespace)::v8_class_like::XxxCallback
                #
                # Note that XxxCallback doesn't include the class_like name.
                CxxNamespaceNode(name=debugging_namespace_name,
                                 body=callback_defs),
                EmptyNode(),
                TextNode(
                    "using namespace {};".format(debugging_namespace_name)),
            ]),
        EmptyNode(),
        installer_function_defs,
        EmptyNode(),
    ])

    if header_v8_context_snapshot_ns:
        impl_header_blink_ns.body.extend([
            CxxNamespaceNode(name=name_style.namespace("bindings"),
                             body=header_v8_context_snapshot_ns),
            EmptyNode(),
        ])
        impl_source_blink_ns.body.extend([
            CxxNamespaceNode(name=name_style.namespace("bindings"),
                             body=source_v8_context_snapshot_ns),
            EmptyNode(),
        ])

    # Write down to the files.
    write_code_node_to_file(api_header_node,
                            path_manager.gen_path_to(api_header_path))
    write_code_node_to_file(api_source_node,
                            path_manager.gen_path_to(api_source_path))
    if path_manager.is_cross_components:
        write_code_node_to_file(impl_header_node,
                                path_manager.gen_path_to(impl_header_path))
        write_code_node_to_file(impl_source_node,
                                path_manager.gen_path_to(impl_source_path))


def generate_interface(interface_identifier):
    assert isinstance(interface_identifier, web_idl.Identifier)

    web_idl_database = package_initializer().web_idl_database()
    interface = web_idl_database.find(interface_identifier)

    generate_class_like(interface)


def generate_install_properties_per_feature(function_name,
                                            filepath_basename,
                                            for_testing=False):
    assert isinstance(function_name, str)
    assert isinstance(filepath_basename, str)
    assert isinstance(for_testing, bool)

    web_idl_database = package_initializer().web_idl_database()

    # Filepaths
    header_path = PathManager.component_path("modules",
                                             "{}.h".format(filepath_basename))
    source_path = PathManager.component_path("modules",
                                             "{}.cc".format(filepath_basename))

    # 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"))
    header_bindings_ns = CxxNamespaceNode(name_style.namespace("bindings"))
    source_bindings_ns = CxxNamespaceNode(name_style.namespace("bindings"))
    header_blink_ns.body.extend([
        make_forward_declarations(header_node.accumulator),
        EmptyNode(),
        header_bindings_ns,
    ])
    source_blink_ns.body.append(source_bindings_ns)

    # Function nodes
    arg_decls = [
        "ScriptState* script_state",
        "mojom::blink::OriginTrialFeature feature",
    ]
    func_decl = CxxFuncDeclNode(
        name=function_name, arg_decls=arg_decls, return_type="void")
    func_def = CxxFuncDefNode(
        name=function_name, arg_decls=arg_decls, return_type="void")
    func_def.body.add_template_vars({
        "script_state": "script_state",
        "feature": "feature",
    })
    helper_func_def = CxxFuncDefNode(
        name="InstallPropertiesPerFeatureInternal",
        arg_decls=[
            "ScriptState* script_state",
            "mojom::blink::OriginTrialFeature feature",
            "base::span<const WrapperTypeInfo* const> wrapper_type_info_list",
        ],
        return_type="void")

    # Assemble the parts.
    header_node.accumulator.add_class_decls(["ScriptState"])
    header_node.accumulator.add_include_headers([
        "third_party/blink/renderer/platform/feature_context.h",
    ])
    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)),
    ])
    source_node.accumulator.add_include_headers([
        "base/containers/span.h",
        "third_party/blink/renderer/platform/bindings/script_state.h",
        "third_party/blink/renderer/platform/bindings/v8_per_context_data.h",
        "third_party/blink/public/mojom/origin_trial_feature/origin_trial_feature.mojom-shared.h",
    ])
    source_node.extend([
        make_copyright_header(),
        EmptyNode(),
        TextNode("#include \"{}\"".format(header_path)),
        EmptyNode(),
        make_header_include_directives(source_node.accumulator),
        EmptyNode(),
        source_blink_ns,
    ])
    header_bindings_ns.body.extend([
        TextNode("""\
// Install ES properties associated with the given origin trial feature.\
"""),
        func_decl,
    ])
    source_bindings_ns.body.extend([
        CxxNamespaceNode(name="", body=helper_func_def),
        EmptyNode(),
        func_def,
    ])

    # The public function
    feature_to_class_likes = {}
    set_of_class_likes = set()
    for class_like in itertools.chain(web_idl_database.interfaces,
                                      web_idl_database.namespaces):
        if class_like.code_generator_info.for_testing != for_testing:
            continue

        for member in itertools.chain(class_like.attributes,
                                      class_like.constants,
                                      class_like.operation_groups,
                                      class_like.exposed_constructs):
            features = list(member.exposure.origin_trial_features)
            for entry in member.exposure.global_names_and_features:
                if entry.feature and entry.feature.is_origin_trial:
                    features.append(entry.feature)
            for feature in (member.exposure.
                            only_in_coi_contexts_or_runtime_enabled_features):
                if feature.is_origin_trial:
                    features.append(feature)
            for feature in features:
                feature_to_class_likes.setdefault(feature,
                                                  set()).add(class_like)
            if features:
                set_of_class_likes.add(class_like)

    switch_node = CxxSwitchNode(cond="${feature}")
    switch_node.append(
        case=None,
        body=[
            TextNode("// Ignore unknown, deprecated, and unused features."),
            TextNode("return;"),
        ],
        should_add_break=False)
    for feature, class_likes in sorted(feature_to_class_likes.items()):
        entries = [
            TextNode("{}::GetWrapperTypeInfo(), ".format(
                v8_bridge_class_name(class_like)))
            for class_like in sorted(class_likes, key=lambda x: x.identifier)
        ]
        table_def = ListNode([
            TextNode("static const WrapperTypeInfo* const wti_list[] = {"),
            ListNode(entries),
            TextNode("};"),
        ])
        switch_node.append(
            case="mojom::blink::OriginTrialFeature::k{}".format(feature),
            body=[
                table_def,
                TextNode("selected_wti_list = wti_list;"),
            ])

    func_def.body.extend([
        TextNode(
            "base::span<const WrapperTypeInfo* const> selected_wti_list;"),
        EmptyNode(),
        switch_node,
        EmptyNode(),
        TextNode("InstallPropertiesPerFeatureInternal"
                 "(${script_state}, ${feature}, selected_wti_list);"),
    ])

    for class_like in set_of_class_likes:
        path_manager = PathManager(class_like)
        source_node.accumulator.add_include_headers(
            [path_manager.api_path(ext="h")])

    # The helper function
    helper_func_def.body.append(
        TextNode("""\
V8PerContextData* per_context_data = script_state->PerContextData();
v8::Isolate* isolate = script_state->GetIsolate();
v8::Local<v8::Context> context = script_state->GetContext();
const DOMWrapperWorld& world = script_state->World();
V8InterfaceBridgeBase::FeatureSelector feature_selector(feature);

for (const auto* wrapper_type_info : wrapper_type_info_list) {
  v8::Local<v8::Object> instance_object;
  v8::Local<v8::Object> prototype_object;
  v8::Local<v8::Function> interface_object;
  v8::Local<v8::Template> interface_template =
      wrapper_type_info->GetV8ClassTemplate(isolate, world);

  switch (wrapper_type_info->idl_definition_kind) {
    case WrapperTypeInfo::kIdlInterface:
      if (!per_context_data->GetExistingConstructorAndPrototypeForType(
              wrapper_type_info, &prototype_object, &interface_object)) {
        continue;
      }
      break;
    case WrapperTypeInfo::kIdlNamespace:
      NOTIMPLEMENTED();
      break;
    default:
      NOTREACHED_IN_MIGRATION();
  }

  wrapper_type_info->install_context_dependent_props_func(
      context, world, instance_object, prototype_object,  interface_object,
      interface_template, feature_selector);
}\
"""))

    # 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_init_idl_interfaces(function_name,
                                 filepath_basename,
                                 for_testing=False):
    assert isinstance(function_name, str)
    assert isinstance(filepath_basename, str)
    assert isinstance(for_testing, bool)

    web_idl_database = package_initializer().web_idl_database()

    # Filepaths
    header_path = PathManager.component_path("modules",
                                             "{}.h".format(filepath_basename))
    source_path = PathManager.component_path("modules",
                                             "{}.cc".format(filepath_basename))

    # 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"))
    header_bindings_ns = CxxNamespaceNode(name_style.namespace("bindings"))
    source_bindings_ns = CxxNamespaceNode(name_style.namespace("bindings"))
    header_blink_ns.body.append(header_bindings_ns)
    source_blink_ns.body.append(source_bindings_ns)

    # Function nodes
    func_decl = CxxFuncDeclNode(
        name=function_name, arg_decls=[], return_type="void")
    func_def = CxxFuncDefNode(
        name=function_name, arg_decls=[], return_type="void")
    header_bindings_ns.body.extend([
        TextNode("""\
// Initializes cross-component trampolines of IDL interface / namespace.\
"""),
        func_decl,
    ])
    source_bindings_ns.body.append(func_def)

    # Assemble the parts.
    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)),
    ])
    source_node.extend([
        make_copyright_header(),
        EmptyNode(),
        TextNode("#include \"{}\"".format(header_path)),
        EmptyNode(),
        make_header_include_directives(source_node.accumulator),
        EmptyNode(),
        source_blink_ns,
    ])

    init_calls = []
    for class_like in itertools.chain(web_idl_database.interfaces,
                                      web_idl_database.namespaces):
        if class_like.code_generator_info.for_testing != for_testing:
            continue

        path_manager = PathManager(class_like)
        if path_manager.is_cross_components:
            source_node.accumulator.add_include_headers(
                [path_manager.impl_path(ext="h")])

            class_name = v8_bridge_class_name(class_like)
            init_calls.append(_format("{}::Impl::Init();", class_name))
    for init_call in sorted(init_calls):
        func_def.body.append(TextNode(init_call))

    # 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_interfaces(task_queue):
    assert isinstance(task_queue, TaskQueue)

    web_idl_database = package_initializer().web_idl_database()

    for interface in web_idl_database.interfaces:
        # Use the number of attributes + constants + operations as a very rough
        # heuristic for workload. This is by no means close-to-accurate, but is
        # better than nothing.
        task_queue.post_task_with_workload(
            len(interface.attributes) + len(interface.constants) +
            len(interface.operations), generate_interface,
            interface.identifier)

    task_queue.post_task(generate_install_properties_per_feature,
                         "InstallPropertiesPerFeature",
                         "properties_per_feature_installer")
    task_queue.post_task(generate_install_properties_per_feature,
                         "InstallPropertiesPerFeatureForTesting",
                         "properties_per_feature_installer_for_testing",
                         for_testing=True)
    task_queue.post_task(generate_init_idl_interfaces, "InitIDLInterfaces",
                         "init_idl_interfaces")
    task_queue.post_task(generate_init_idl_interfaces,
                         "InitIDLInterfacesForTesting",
                         "init_idl_interfaces_for_testing",
                         for_testing=True)