chromium/third_party/blink/renderer/bindings/scripts/bind_gen/blink_v8_bridge.py

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

import web_idl

from . import name_style
from .code_node import FormatNode
from .code_node import Likeliness
from .code_node import SymbolDefinitionNode
from .code_node import SymbolNode
from .code_node import TextNode
from .code_node_cxx import CxxIfElseNode
from .code_node_cxx import CxxLikelyIfNode
from .code_node_cxx import CxxUnlikelyIfNode
from .codegen_format import format_template as _format


def blink_class_name(idl_definition):
    """
    Returns the class name of Blink implementation.
    """
    class_name = idl_definition.code_generator_info.receiver_implemented_as
    if class_name:
        return class_name

    assert idl_definition.identifier[0].isupper()
    # Do not apply |name_style.class_| in order to respect the original name
    # (Web spec'ed name) as much as possible.  For example, |interface EXTsRGB|
    # is implemented as |class EXTsRGB|, not as |ExtSRgb| nor |ExtsRgb|.
    if isinstance(idl_definition,
                  (web_idl.CallbackFunction, web_idl.CallbackInterface,
                   web_idl.Enumeration, web_idl.Typedef)):
        return "V8{}".format(idl_definition.identifier)
    elif isinstance(idl_definition, web_idl.ObservableArray):
        return "V8ObservableArray{}".format(
            idl_definition.element_type.
            type_name_with_extended_attribute_key_values)
    elif isinstance(idl_definition, web_idl.Union):
        # Technically this name is not guaranteed to be unique because
        # (X or sequence<Y or Z>) and (X or Y or sequence<Z>) have the same
        # name, but it's highly unlikely to cause a conflict in the actual use
        # cases.  Plus, we prefer a simple naming rule conformant to the
        # Chromium coding style.  So, we go with this way.
        return "V8Union{}".format("Or".join(idl_definition.member_tokens))
    elif isinstance(idl_definition, web_idl.AsyncIterator):
        return "AsyncIterator<{}>".format(
            blink_class_name(idl_definition.interface))
    elif isinstance(idl_definition, web_idl.SyncIterator):
        return "SyncIterator<{}>".format(
            blink_class_name(idl_definition.interface))
    else:
        return idl_definition.identifier


def v8_bridge_class_name(idl_definition):
    """
    Returns the name of V8-from/to-Blink bridge class.
    """
    assert isinstance(
        idl_definition,
        (web_idl.AsyncIterator, web_idl.CallbackInterface, web_idl.Interface,
         web_idl.Namespace, web_idl.SyncIterator))

    assert idl_definition.identifier[0].isupper()
    # Do not apply |name_style.class_| due to the same reason as
    # |blink_class_name|.
    if isinstance(idl_definition, web_idl.AsyncIterator):
        return "V8AsyncIterator{}".format(idl_definition.interface.identifier)
    if isinstance(idl_definition, web_idl.SyncIterator):
        return "V8SyncIterator{}".format(idl_definition.interface.identifier)
    return "V8{}".format(idl_definition.identifier)


def blink_type_info(idl_type):
    """
    Returns an object that represents the types of Blink implementation
    corresponding to the given IDL type, such as reference type, value type,
    member type, etc.
    """
    assert isinstance(idl_type, web_idl.IdlType)

    class TypeInfo(object):
        def __init__(self,
                     typename,
                     member_fmt="{}",
                     ref_fmt="{}",
                     const_ref_fmt="const {}",
                     value_fmt="{}",
                     has_null_value=False,
                     is_gc_type=False,
                     is_heap_vector_type=False,
                     is_move_effective=False,
                     is_traceable=False,
                     clear_member_var_fmt="{}.Clear()"):
            self._typename = typename
            self._has_null_value = has_null_value
            self._is_gc_type = is_gc_type
            self._is_heap_vector_type = is_heap_vector_type
            self._is_move_effective = is_move_effective
            self._is_traceable = (is_gc_type or is_heap_vector_type
                                  or is_traceable)
            self._is_member_t_cppgc_member = member_fmt == "Member<{}>"
            self._clear_member_var_fmt = clear_member_var_fmt

            self._ref_t = ref_fmt.format(typename)
            self._const_ref_t = const_ref_fmt.format(typename)
            self._value_t = value_fmt.format(typename)
            self._member_t = member_fmt.format(typename)
            self._member_ref_t = (self._ref_t
                                  if self._is_gc_type else self._const_ref_t)

        @property
        def typename(self):
            """Returns the internal-use-only name.  Do not use this."""
            return self._typename

        @property
        def ref_t(self):
            """
            Returns the type of a local variable that references to an existing
            value.  E.g. String => String&
            """
            return self._ref_t

        @property
        def const_ref_t(self):
            """
            Returns the const-qualified version of |ref_t|.  E.g. String =>
            const String&
            """
            return self._const_ref_t

        @property
        def value_t(self):
            """
            Returns the type of a variable that behaves as a value. E.g. String
            => String
            """
            return self._value_t

        @property
        def member_t(self):
            """
            Returns the type of a member variable.  E.g. Node => Member<Node>
            """
            return self._member_t

        @property
        def member_ref_t(self):
            """
            Returns the type used for input to and output from a member
            variable.  E.g. Node* for Member<Node> and const String& for String
            """
            return self._member_ref_t

        @property
        def has_null_value(self):
            """
            Returns True if the Blink implementation type can represent IDL
            null value without use of std::optional<T>.  E.g. pointer type =>
            True and int32_t => False
            """
            return self._has_null_value

        @property
        def is_gc_type(self):
            """
            Returns True if the Blink implementation type is a GarbageCollected
            type.
            """
            return self._is_gc_type

        @property
        def is_heap_vector_type(self):
            """
            Returns True if the Blink implementation type is HeapVector<T>.

            HeapVector is very special because HeapVector is GarbageCollected
            but it's used as a value type rather than a reference type for the
            most cases because HeapVector had been implemented as a non-GC type
            for a long time and it turned into a GC type in 2021 January.
            """
            return self._is_heap_vector_type

        @property
        def is_move_effective(self):
            """
            Returns True if support of std::move is effective and desired.
            E.g. Vector => True and int32_t => False
            """
            return self._is_move_effective

        @property
        def is_traceable(self):
            """
            Returns True if the Blink implementation type has Trace method.
            E.g. ScriptValue => True and int32_t => False
            """
            return self._is_traceable

        def member_var_to_ref_expr(self, var_name):
            """
            Returns an expression to convert the given member variable into
            a reference type. E.g. Member<T> => var_name.Get()
            """
            if self._is_member_t_cppgc_member:
                return "{}.Get()".format(var_name)
            return var_name

        def clear_member_var_expr(self, var_name):
            """
            Returns an expression to reset the given member variable.  E.g.
            Vector => var_name.clear() and int32_t => var_name = 0
            """
            return self._clear_member_var_fmt.format(var_name)

    real_type = idl_type.unwrap(typedef=True)

    if real_type.is_boolean or real_type.is_numeric:
        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",
        }
        return TypeInfo(cxx_type[real_type.keyword_typename],
                        const_ref_fmt="{}",
                        clear_member_var_fmt="{} = 0")

    if real_type.is_bigint:
        return TypeInfo("BigInt",
                        ref_fmt="{}&",
                        const_ref_fmt="const {}&",
                        clear_member_var_fmt="{} = BigInt()")

    if real_type.is_string:
        return TypeInfo("String",
                        ref_fmt="{}&",
                        const_ref_fmt="const {}&",
                        has_null_value=True,
                        is_move_effective=True,
                        clear_member_var_fmt="{} = String()")

    if real_type.is_array_buffer:
        if "AllowShared" in idl_type.effective_annotations:
            # DOMArrayBufferBase is the common base class of DOMArrayBuffer and
            # DOMSharedArrayBuffer, so it works as [AllowShared] ArrayBuffer.
            typename = "DOMArrayBufferBase"
        else:
            typename = "DOMArrayBuffer"
        return TypeInfo(typename,
                        member_fmt="Member<{}>",
                        ref_fmt="{}*",
                        const_ref_fmt="const {}*",
                        value_fmt="{}*",
                        has_null_value=True,
                        is_gc_type=True)

    if real_type.is_buffer_source_type:
        if "AllowShared" in idl_type.effective_annotations:
            return TypeInfo("MaybeShared<DOM{}>".format(
                real_type.keyword_typename),
                            has_null_value=True,
                            is_gc_type=True)
        else:
            return TypeInfo("NotShared<DOM{}>".format(
                real_type.keyword_typename),
                            has_null_value=True,
                            is_gc_type=True)

    if real_type.is_symbol:
        assert False, "Blink does not support/accept IDL symbol type."

    if real_type.is_any or real_type.is_object:
        return TypeInfo("ScriptValue",
                        ref_fmt="{}&",
                        const_ref_fmt="const {}&",
                        has_null_value=True,
                        is_traceable=True)

    if real_type.is_undefined:
        assert False, "Blink does not support/accept IDL undefined type."

    if real_type.type_definition_object:
        typename = blink_class_name(real_type.type_definition_object)
        if real_type.is_enumeration:
            return TypeInfo(typename,
                            ref_fmt="{}",
                            const_ref_fmt="{}",
                            clear_member_var_fmt="")
        return TypeInfo(typename,
                        member_fmt="Member<{}>",
                        ref_fmt="{}*",
                        const_ref_fmt="const {}*",
                        value_fmt="{}*",
                        has_null_value=True,
                        is_gc_type=True)

    if (real_type.is_sequence or real_type.is_frozen_array
            or real_type.is_variadic):
        element_type = blink_type_info(real_type.element_type)
        if element_type.is_traceable:
            # HeapVector is GarbageCollected but we'd like to treat it as
            # a value type (is_gc_type=False, has_null_value=False) rather than
            # a reference type (is_gc_type=True, has_null_value=True) by
            # default.
            typename = "HeapVector<{}>".format(element_type.member_t)
            return TypeInfo(typename,
                            ref_fmt="{}&",
                            const_ref_fmt="const {}&",
                            has_null_value=False,
                            is_gc_type=False,
                            is_move_effective=True,
                            is_heap_vector_type=True,
                            clear_member_var_fmt="{}.clear()")
        else:
            return TypeInfo("Vector<{}>".format(element_type.value_t),
                            ref_fmt="{}&",
                            const_ref_fmt="const {}&",
                            is_move_effective=True,
                            clear_member_var_fmt="{}.clear()")

    if real_type.is_observable_array:
        typename = blink_class_name(
            real_type.observable_array_definition_object)
        return TypeInfo(typename,
                        member_fmt="Member<{}>",
                        ref_fmt="{}*",
                        const_ref_fmt="const {}*",
                        value_fmt="{}*",
                        has_null_value=True,
                        is_gc_type=True)

    if real_type.is_record:
        assert real_type.key_type.is_string
        key_type = blink_type_info(real_type.key_type)
        value_type = blink_type_info(real_type.value_type)
        if value_type.is_traceable:
            # HeapVector is GarbageCollected but we'd like to treat it as
            # a value type (is_gc_type=False, has_null_value=False) rather than
            # a reference type (is_gc_type=True, has_null_value=True) by
            # default.
            typename = ("HeapVector<std::pair<{}, {}>>".format(
                key_type.member_t, value_type.member_t))
            return TypeInfo(typename,
                            ref_fmt="{}&",
                            const_ref_fmt="const {}&",
                            has_null_value=False,
                            is_gc_type=False,
                            is_move_effective=True,
                            is_heap_vector_type=True,
                            clear_member_var_fmt="{}.clear()")
        else:
            typename = "Vector<std::pair<{}, {}>>".format(
                key_type.value_t, value_type.value_t)
            return TypeInfo(typename,
                            ref_fmt="{}&",
                            const_ref_fmt="const {}&",
                            is_move_effective=True,
                            clear_member_var_fmt="{}.clear()")

    if real_type.is_promise:
        if "IDLTypeImplementedAsV8Promise" in real_type.extended_attributes:
            type_name = "v8::Local<v8::Promise>"
        else:
            type_name = "ScriptPromiseUntyped"
        return TypeInfo(type_name,
                        ref_fmt="{}&",
                        const_ref_fmt="const {}&",
                        is_traceable=True)

    if real_type.is_union:
        typename = blink_class_name(real_type.union_definition_object)
        return TypeInfo(typename,
                        member_fmt="Member<{}>",
                        ref_fmt="{}*",
                        const_ref_fmt="const {}*",
                        value_fmt="{}*",
                        has_null_value=True,
                        is_gc_type=True)

    if real_type.is_nullable:
        inner_type = blink_type_info(real_type.inner_type)
        if inner_type.has_null_value:
            return inner_type
        if inner_type.is_heap_vector_type:
            return TypeInfo(inner_type.typename,
                            member_fmt="Member<{}>",
                            ref_fmt="{}*",
                            const_ref_fmt="const {}*",
                            value_fmt="{}*",
                            has_null_value=True,
                            is_gc_type=True,
                            is_move_effective=False,
                            is_heap_vector_type=False)
        assert not inner_type.is_traceable
        return TypeInfo("std::optional<{}>".format(inner_type.value_t),
                        ref_fmt="{}&",
                        const_ref_fmt="const {}&",
                        is_move_effective=inner_type.is_move_effective,
                        clear_member_var_fmt="{}.reset()")

    assert False, "Unknown type: {}".format(idl_type.syntactic_form)


def native_value_tag(idl_type, argument=None, apply_optional_to_last_arg=True):
    """Returns the tag type of NativeValueTraits."""
    assert isinstance(idl_type, web_idl.IdlType)
    assert argument is None or isinstance(argument, web_idl.Argument)

    if (idl_type.is_optional and argument
            and not (idl_type.is_nullable or argument.default_value)
            and (apply_optional_to_last_arg
                 or argument != argument.owner.arguments[-1])):
        return "IDLOptional<{}>".format(_native_value_tag_impl(idl_type))

    return _native_value_tag_impl(idl_type)


def _pass_as_span_conversion_arguments(idl_type):
    real_type = idl_type.unwrap(typedef=True)
    types = real_type.flattened_member_types if real_type.is_union else [
        real_type
    ]
    is_buffer_source_type = all(t.is_buffer_source_type for t in types)
    assert is_buffer_source_type, (
        "PassAsSpan is only supported for buffer source types")
    native_type = typed_array_element_type(
        real_type) if real_type.is_typed_array_type else "void"
    flags = []
    allow_shared = "AllowShared" in idl_type.effective_annotations or any(
        "AllowShared" in t.effective_annotations for t in types)
    if allow_shared:
        flags.append("PassAsSpanMarkerBase::Flags::kAllowShared")
    return [
        " | ".join(flags) or "PassAsSpanMarkerBase::Flags::kNone", native_type
    ]


def _native_value_tag_impl(idl_type):
    """Returns the tag type of NativeValueTraits."""
    assert isinstance(idl_type, web_idl.IdlType)

    if idl_type.is_event_handler:
        return "IDL{}".format(idl_type.identifier)

    real_type = idl_type.unwrap(typedef=True)

    if "PassAsSpan" in idl_type.effective_annotations:
        conversion_arguments = _pass_as_span_conversion_arguments(idl_type)
        return "PassAsSpan<{}>".format(", ".join(conversion_arguments))

    if (real_type.is_boolean or real_type.is_numeric or real_type.is_string
            or real_type.is_any or real_type.is_object or real_type.is_bigint
            or real_type.is_undefined):
        return "IDL{}".format(
            idl_type.type_name_with_extended_attribute_key_values)

    if real_type.is_array_buffer:
        if "BufferSourceTypeNoSizeLimit" in real_type.effective_annotations:
            return "IDLBufferSourceTypeNoSizeLimit<{}>".format(
                blink_type_info(real_type).typename)
        return blink_type_info(real_type).typename

    if real_type.is_buffer_source_type:
        if "BufferSourceTypeNoSizeLimit" in real_type.effective_annotations:
            return "IDLBufferSourceTypeNoSizeLimit<{}>".format(
                blink_type_info(real_type).value_t)
        return blink_type_info(real_type).value_t

    if real_type.is_symbol:
        assert False, "Blink does not support/accept IDL symbol type."

    if real_type.type_definition_object:
        return blink_class_name(real_type.type_definition_object)

    if real_type.is_sequence:
        return "IDLSequence<{}>".format(
            _native_value_tag_impl(real_type.element_type))

    if real_type.is_frozen_array:
        return "IDLArray<{}>".format(
            _native_value_tag_impl(real_type.element_type))

    if real_type.is_observable_array:
        return blink_class_name(real_type.observable_array_definition_object)

    if real_type.is_record:
        return "IDLRecord<{}, {}>".format(
            _native_value_tag_impl(real_type.key_type),
            _native_value_tag_impl(real_type.value_type))

    if real_type.is_promise:
        return "IDLPromise"

    if real_type.is_union:
        return blink_class_name(real_type.union_definition_object)

    if real_type.is_nullable:
        return "IDLNullable<{}>".format(
            _native_value_tag_impl(real_type.inner_type))

    assert False, "Unknown type: {}".format(idl_type.syntactic_form)


def make_blink_to_v8_value(
        v8_var_name,
        blink_value_expr,
        idl_type,
        argument=None,
        error_exit_return_statement="return v8::MaybeLocal<v8::Value>();",
        creation_context_script_state="${script_state}"):
    """
    Returns a SymbolNode whose definition converts a Blink value to a v8::Value.
    """
    assert isinstance(v8_var_name, str)
    assert isinstance(blink_value_expr, str)
    assert isinstance(idl_type, web_idl.IdlType)
    assert argument is None or isinstance(argument, web_idl.Argument)
    assert isinstance(error_exit_return_statement, str)
    assert isinstance(creation_context_script_state, str)

    T = TextNode
    F = FormatNode

    def create_definition(symbol_node):
        binds = {
            "blink_value_expr": blink_value_expr,
            "creation_context_script_state": creation_context_script_state,
            "native_value_tag": native_value_tag(idl_type, argument=argument),
            "v8_var_name": v8_var_name,
        }
        pattern = ("{v8_var_name} = ToV8Traits<{native_value_tag}>::ToV8("
                   "{creation_context_script_state}, {blink_value_expr});")
        nodes = [
            F("v8::Local<v8::Value> {v8_var_name};", **binds),
            F(pattern, **binds)
        ]
        return SymbolDefinitionNode(symbol_node, nodes)

    return SymbolNode(v8_var_name, definition_constructor=create_definition)


def make_default_value_expr(idl_type, default_value):
    """
    Returns a set of C++ expressions to be used for initialization with default
    values.  The returned object has the following attributes.

      initializer_expr: Used as "Type var{|initializer_expr|};".  This is None
          if "Type var;" sets an appropriate default value.
      initializer_deps: A list of symbol names that |initializer_expr| depends
          on.
      is_initialization_lightweight: True if a possibly-redundant initialization
          will not be more expensive than assignment.  See bellow for an
          example.
      assignment_value: Used as "var = |assignment_value|;".
      assignment_deps: A list of symbol names that |assignment_value| depends
          on.


    |is_initialization_lightweight| is True if

      Type var{${initializer_expr}};
      if (value_is_given)
        var = value;

    is not more expensive than

      Type var;
      if (value_is_given)
        var = value;
      else
        var = ${assignment_value};
    """
    assert isinstance(idl_type, web_idl.IdlType)
    assert (default_value is None
            or isinstance(default_value, web_idl.LiteralConstant))
    assert default_value.is_type_compatible_with(idl_type)

    class DefaultValueExpr(object):
        _ALLOWED_SYMBOLS_IN_DEPS = ("isolate")

        def __init__(self, initializer_expr, initializer_deps,
                     is_initialization_lightweight, assignment_value,
                     assignment_deps):
            assert initializer_expr is None or isinstance(
                initializer_expr, str)
            assert (isinstance(initializer_deps, (list, tuple)) and all(
                dependency in DefaultValueExpr._ALLOWED_SYMBOLS_IN_DEPS
                for dependency in initializer_deps))
            assert isinstance(is_initialization_lightweight, bool)
            assert isinstance(assignment_value, str)
            assert (isinstance(assignment_deps, (list, tuple)) and all(
                dependency in DefaultValueExpr._ALLOWED_SYMBOLS_IN_DEPS
                for dependency in assignment_deps))

            self.initializer_expr = initializer_expr
            self.initializer_deps = tuple(initializer_deps)
            self.is_initialization_lightweight = is_initialization_lightweight
            self.assignment_value = assignment_value
            self.assignment_deps = tuple(assignment_deps)

    if idl_type.unwrap(typedef=True).is_union:
        union_type = idl_type.unwrap(typedef=True)
        member_type = None
        for member_type in union_type.flattened_member_types:
            if default_value.is_type_compatible_with(member_type):
                member_type = member_type
                break
        assert not (member_type is None) or default_value.idl_type.is_nullable

        pattern = "MakeGarbageCollected<{}>({})"
        union_class_name = blink_class_name(union_type.union_definition_object)

        if default_value.idl_type.is_nullable:
            value = pattern.format(union_class_name, "nullptr")
            return DefaultValueExpr(initializer_expr=value,
                                    initializer_deps=[],
                                    is_initialization_lightweight=False,
                                    assignment_value=value,
                                    assignment_deps=[])
        else:
            member_default_expr = make_default_value_expr(
                member_type, default_value)
            value = pattern.format(union_class_name,
                                   member_default_expr.assignment_value)
            return DefaultValueExpr(
                initializer_expr=value,
                initializer_deps=member_default_expr.initializer_deps,
                is_initialization_lightweight=False,
                assignment_value=value,
                assignment_deps=member_default_expr.assignment_deps)

    type_info = blink_type_info(idl_type)

    is_initialization_lightweight = False
    initializer_deps = []
    assignment_deps = []
    if default_value.idl_type.is_nullable:
        if not type_info.has_null_value:
            initializer_expr = None  # !std::optional::has_value() by default
            assignment_value = "std::nullopt"
        elif idl_type.unwrap().type_definition_object is not None:
            initializer_expr = "nullptr"
            is_initialization_lightweight = True
            assignment_value = "nullptr"
        elif idl_type.unwrap().is_string:
            initializer_expr = None  # String::IsNull() by default
            assignment_value = "String()"
        elif idl_type.unwrap().is_buffer_source_type:
            initializer_expr = "nullptr"
            is_initialization_lightweight = True
            assignment_value = "nullptr"
        elif type_info.typename == "ScriptValue":
            initializer_expr = "${isolate}, v8::Null(${isolate})"
            initializer_deps = ["isolate"]
            assignment_value = "ScriptValue::CreateNull(${isolate})"
            assignment_deps = ["isolate"]
        elif idl_type.unwrap().is_union:
            initializer_expr = "nullptr"
            is_initialization_lightweight = True
            assignment_value = "nullptr"
        else:
            assert False
    elif default_value.idl_type.is_sequence:
        initializer_expr = None  # VectorOf<T>::size() == 0 by default
        assignment_value = "{}()".format(type_info.value_t)
    elif default_value.idl_type.is_object:
        dictionary = idl_type.unwrap().type_definition_object
        # Currently "isolate" is the only possible dependency, so whenever
        # .initializer_deps exists, it must be ["isolate"].
        if any((make_default_value_expr(member.idl_type,
                                        member.default_value).initializer_deps)
               for member in dictionary.members if member.default_value):
            value = _format("{}::Create(${isolate})",
                            blink_class_name(dictionary))
            initializer_expr = value
            initializer_deps = ["isolate"]
            assignment_value = value
            assignment_deps = ["isolate"]
        else:
            value = _format("{}::Create()", blink_class_name(dictionary))
            initializer_expr = value
            assignment_value = value
    elif default_value.idl_type.is_boolean:
        value = "true" if default_value.value else "false"
        initializer_expr = value
        is_initialization_lightweight = True
        assignment_value = value
    elif default_value.idl_type.is_integer:
        initializer_expr = default_value.literal
        is_initialization_lightweight = True
        assignment_value = default_value.literal
    elif default_value.idl_type.is_floating_point_numeric:
        if default_value.value == float("NaN"):
            value_fmt = "std::numeric_limits<{type}>::quiet_NaN()"
        elif default_value.value == float("Infinity"):
            value_fmt = "std::numeric_limits<{type}>::infinity()"
        elif default_value.value == float("-Infinity"):
            value_fmt = "-std::numeric_limits<{type}>::infinity()"
        else:
            value_fmt = "{value}"
        value = value_fmt.format(
            type=type_info.value_t, value=default_value.literal)
        initializer_expr = value
        is_initialization_lightweight = True
        assignment_value = value
    elif default_value.idl_type.is_string:
        if idl_type.unwrap().is_string:
            value = "\"{}\"".format(default_value.value)
            initializer_expr = value
            assignment_value = value
        elif idl_type.unwrap().is_enumeration:
            enum_class_name = blink_class_name(
                idl_type.unwrap().type_definition_object)
            enum_value_name = name_style.constant(default_value.value)
            initializer_expr = "{}::Enum::{}".format(enum_class_name,
                                                     enum_value_name)
            is_initialization_lightweight = True
            assignment_value = "{}({})".format(enum_class_name,
                                               initializer_expr)
        else:
            assert False
    else:
        assert False

    return DefaultValueExpr(
        initializer_expr=initializer_expr,
        initializer_deps=initializer_deps,
        is_initialization_lightweight=is_initialization_lightweight,
        assignment_value=assignment_value,
        assignment_deps=assignment_deps)


def make_v8_to_blink_value(blink_var_name,
                           v8_value_expr,
                           idl_type,
                           argument=None,
                           error_exit_return_statement=None,
                           cg_context=None):
    """
    Returns a SymbolNode whose definition converts a v8::Value to a Blink value.
    """
    assert isinstance(blink_var_name, str)
    assert isinstance(v8_value_expr, str)
    assert isinstance(idl_type, web_idl.IdlType)
    assert argument is None or isinstance(argument, web_idl.Argument)
    assert isinstance(error_exit_return_statement, str)

    T = TextNode
    F = FormatNode

    # Use of fast path is a trade-off between speed and binary size, so apply
    # it only when it's effective.  This hack is most significant on Android.
    use_fast_path = (
        cg_context and cg_context.operation
        and not (cg_context.is_return_type_promise_type or
                 "RaisesException" in cg_context.operation.extended_attributes)
        and all((arg.idl_type.type_name == "String" or arg.idl_type.unwrap(
            typedef=True).is_callback_function)
                for arg in cg_context.operation.arguments))
    fast_path_cond = None
    fast_path_body_text = None
    if not use_fast_path:
        pass
    elif idl_type.type_name == "String":
        # A key point of this fast path is that it doesn't require an
        # ExceptionState.
        fast_path_cond = "{}->IsString()".format(v8_value_expr)
        fast_path_body_text = _format(
            "{}.Init(${isolate}, {}.As<v8::String>());", blink_var_name,
            v8_value_expr)
    elif idl_type.unwrap(typedef=True).is_callback_function:
        # A key point of this fast path is that it doesn't require an
        # ExceptionState.
        fast_path_cond = "{}->IsFunction()".format(v8_value_expr)
        fast_path_body_text = "{} = {}::Create({}.As<v8::Function>());".format(
            blink_var_name,
            blink_class_name(idl_type.unwrap().type_definition_object),
            v8_value_expr)

    def create_definition(symbol_node):
        if argument is None:
            func_name = "NativeValue"
            arguments = ["${isolate}", v8_value_expr, "${exception_state}"]
        else:
            func_name = "ArgumentValue"
            arguments = [
                "${isolate}",
                str(argument.index),
                v8_value_expr,
                "${exception_state}",
            ]
        if "StringContext" in idl_type.effective_annotations:
            arguments.append("${execution_context_of_document_tree}")
        blink_value_expr = _format("NativeValueTraits<{_1}>::{_2}({_3})",
                                   _1=native_value_tag(
                                       idl_type,
                                       argument=argument,
                                       apply_optional_to_last_arg=False),
                                   _2=func_name,
                                   _3=", ".join(arguments))
        if argument and argument.default_value:
            default_expr = make_default_value_expr(idl_type,
                                                   argument.default_value)
        else:
            default_expr = None
        exception_exit_node = CxxUnlikelyIfNode(
            cond="${exception_state}.HadException()",
            attribute="[[unlikely]]",
            body=T(error_exit_return_statement))

        if not (default_expr or fast_path_cond):
            return SymbolDefinitionNode(symbol_node, [
                F("auto&& ${{{}}} = {};", blink_var_name, blink_value_expr),
                exception_exit_node,
            ])

        blink_var_type = _format(
            "decltype(NativeValueTraits<{}>::NativeValue("
            "std::declval<v8::Isolate*>(), "
            "std::declval<v8::Local<v8::Value>>(), "
            "std::declval<ExceptionState&>()))",
            native_value_tag(idl_type,
                             argument=argument,
                             apply_optional_to_last_arg=False))
        if default_expr and default_expr.is_initialization_lightweight:
            pattern = "{} ${{{}}}{{{}}};"
            args = [
                blink_var_type, blink_var_name, default_expr.initializer_expr
            ]
        else:
            pattern = "{} ${{{}}};"
            args = [blink_var_type, blink_var_name]
        blink_var_def_node = F(pattern, *args)
        assignment = [
            F("${{{}}} = {};", blink_var_name, blink_value_expr),
            exception_exit_node,
        ]
        if not default_expr:
            pass
        elif (default_expr.initializer_expr is None
              or default_expr.is_initialization_lightweight):
            assignment = CxxLikelyIfNode(
                cond="!{}->IsUndefined()".format(v8_value_expr),
                attribute=None,
                body=assignment)
        else:
            assignment = CxxIfElseNode(
                cond="{}->IsUndefined()".format(v8_value_expr),
                attribute=None,
                then=F("${{{}}} = {};", blink_var_name,
                       default_expr.assignment_value),
                then_likeliness=Likeliness.LIKELY,
                else_=assignment,
                else_likeliness=Likeliness.LIKELY)
        if fast_path_cond:
            assignment = CxxIfElseNode(cond=fast_path_cond,
                                       attribute="[[likely]]",
                                       then=T(fast_path_body_text),
                                       then_likeliness=Likeliness.LIKELY,
                                       else_=assignment,
                                       else_likeliness=Likeliness.UNLIKELY)
        return SymbolDefinitionNode(symbol_node, [
            blink_var_def_node,
            assignment,
        ])

    return SymbolNode(blink_var_name, definition_constructor=create_definition)


def make_v8_to_blink_value_variadic(blink_var_name, v8_array,
                                    v8_array_start_index, idl_type):
    """
    Returns a SymbolNode whose definition converts an array of v8::Value
    (variadic arguments) to a Blink value.
    """
    assert isinstance(blink_var_name, str)
    assert isinstance(v8_array, str)
    assert isinstance(v8_array_start_index, int)
    assert isinstance(idl_type, web_idl.IdlType)

    pattern = ("auto&& ${{{_1}}} = "
               "bindings::VariadicArgumentsToNativeValues<{_2}>({_3});")
    arguments = [
        "${isolate}", v8_array,
        str(v8_array_start_index), "${exception_state}"
    ]
    if "StringContext" in idl_type.element_type.effective_annotations:
        arguments.append("${execution_context_of_document_tree}")
    text = _format(
        pattern,
        _1=blink_var_name,
        _2=native_value_tag(idl_type.element_type),
        _3=", ".join(arguments))

    def create_definition(symbol_node):
        return SymbolDefinitionNode(symbol_node, [
            TextNode(text),
            CxxUnlikelyIfNode(cond="${exception_state}.HadException()",
                              attribute="[[unlikely]]",
                              body=TextNode("return;")),
        ])

    return SymbolNode(blink_var_name, definition_constructor=create_definition)


def typed_array_element_type(idl_type):
    assert isinstance(idl_type, web_idl.IdlType)
    assert idl_type.is_typed_array_type
    element_type_map = {
        'Int8Array': 'int8_t',
        'Int16Array': 'int16_t',
        'Int32Array': 'int32_t',
        'BigInt64Array': 'int64_t',
        'Uint8Array': 'uint8_t',
        'Uint16Array': 'uint16_t',
        'Uint32Array': 'uint32_t',
        'BigUint64Array': 'uint64_t',
        'Uint8ClampedArray': 'uint8_t',
        'Float32Array': 'float',
        'Float64Array': 'double',
    }
    return element_type_map.get(idl_type.keyword_typename)