Store parser errors, source range filenames, source code filenames, module source, and Rust parser errors as UTF-16 where they flow back into JavaScript-visible strings. Keep byte-oriented source buffers byte-backed. Remove temporary PrimitiveString, ByteString, and UTF-8 detours from JSON, RegExp, module debug logging, print formatting, and tests.
1540 lines
68 KiB
Python
1540 lines
68 KiB
Python
# Copyright (c) 2026-present, the Ladybird developers.
|
||
#
|
||
# SPDX-License-Identifier: BSD-2-Clause
|
||
|
||
from dataclasses import dataclass
|
||
from dataclasses import field
|
||
from typing import List
|
||
from typing import NoReturn
|
||
from typing import Optional
|
||
from typing import TextIO
|
||
|
||
from Generators.libweb_bindings.context import GenerationContext
|
||
from Generators.libweb_bindings.cpp_types import DictionaryMemberOrAttribute
|
||
from Generators.libweb_bindings.cpp_types import InterfaceLikeType
|
||
from Generators.libweb_bindings.cpp_types import add_binding_include_for_type
|
||
from Generators.libweb_bindings.cpp_types import add_buffer_source_type_include
|
||
from Generators.libweb_bindings.cpp_types import add_header_includes_for_type
|
||
from Generators.libweb_bindings.cpp_types import add_include_for_string_cpp_type
|
||
from Generators.libweb_bindings.cpp_types import converter_function_name
|
||
from Generators.libweb_bindings.cpp_types import cpp_empty_value
|
||
from Generators.libweb_bindings.cpp_types import cpp_name
|
||
from Generators.libweb_bindings.cpp_types import cpp_type
|
||
from Generators.libweb_bindings.cpp_types import cpp_type_for_idl_type
|
||
from Generators.libweb_bindings.cpp_types import cpp_type_for_idl_type_details
|
||
from Generators.libweb_bindings.cpp_types import cpp_type_name_for_string
|
||
from Generators.libweb_bindings.cpp_types import cpp_value_type
|
||
from Generators.libweb_bindings.cpp_types import fully_qualified_name_for_interface
|
||
from Generators.libweb_bindings.cpp_types import implementation_header_for_interface
|
||
from Generators.libweb_bindings.cpp_types import interface_like_type_for_idl_type
|
||
from Generators.libweb_bindings.cpp_types import is_buffer_source_type
|
||
from Generators.libweb_bindings.cpp_types import is_numeric_type
|
||
from Generators.libweb_bindings.cpp_types import is_string_type
|
||
from Generators.libweb_bindings.cpp_types import is_typed_array_type
|
||
from Generators.libweb_bindings.cpp_types import union_type_to_variant
|
||
from Generators.libweb_bindings.default_values import cpp_default_value_conversion
|
||
from Generators.libweb_bindings.includes import GeneratedIncludes
|
||
from Utils.utils import make_name_acceptable_cpp
|
||
from Utils.utils import string_to_cpp_enum_name
|
||
from Utils.utils import title_case_to_snake_case
|
||
from Utils.utils import underlying_type_for_enum
|
||
from Utils.webidl_parser import CallbackFunction
|
||
from Utils.webidl_parser import Dictionary
|
||
from Utils.webidl_parser import Enumeration
|
||
from Utils.webidl_parser import IDLParameterizedType
|
||
from Utils.webidl_parser import IDLType
|
||
from Utils.webidl_parser import IDLUnionType
|
||
from Utils.webidl_parser import Interface
|
||
|
||
|
||
@dataclass
|
||
class UnionTypes:
|
||
boolean_type: Optional[IDLType] = None
|
||
numeric_type: Optional[IDLType] = None
|
||
string_type: Optional[IDLType] = None
|
||
bigint_type: Optional[IDLType] = None
|
||
array_buffer_type: Optional[IDLType] = None
|
||
data_view_type: Optional[IDLType] = None
|
||
sequence_type: Optional[IDLParameterizedType] = None
|
||
frozen_array_type: Optional[IDLParameterizedType] = None
|
||
record_type: Optional[IDLParameterizedType] = None
|
||
dictionary_type: Optional[IDLType] = None
|
||
|
||
enumeration_types: List[IDLType] = field(default_factory=list)
|
||
interface_types: List[IDLType] = field(default_factory=list)
|
||
callback_function_types: List[IDLType] = field(default_factory=list)
|
||
callback_interface_types: List[IDLType] = field(default_factory=list)
|
||
typed_array_types: List[IDLType] = field(default_factory=list)
|
||
|
||
includes_object_type: bool = False
|
||
|
||
|
||
def collect_union_types(flattened_member_types: List[IDLType], context: GenerationContext) -> UnionTypes:
|
||
types = UnionTypes()
|
||
|
||
for member_type in flattened_member_types:
|
||
if types.boolean_type is None and member_type.name == "boolean":
|
||
types.boolean_type = member_type
|
||
if types.numeric_type is None and is_numeric_type(member_type.name):
|
||
types.numeric_type = member_type
|
||
if types.string_type is None and is_string_type(member_type.name):
|
||
types.string_type = member_type
|
||
if types.bigint_type is None and member_type.name == "bigint":
|
||
types.bigint_type = member_type
|
||
|
||
if context.enumeration(member_type) is not None:
|
||
types.enumeration_types.append(member_type)
|
||
if interface_like_type_for_idl_type(member_type, context) is not None:
|
||
types.interface_types.append(member_type)
|
||
if context.callback_function(member_type) is not None:
|
||
types.callback_function_types.append(member_type)
|
||
|
||
interface = context.interface(member_type)
|
||
if interface is not None and interface.is_callback_interface:
|
||
types.callback_interface_types.append(member_type)
|
||
|
||
if member_type.name == "object":
|
||
types.includes_object_type = True
|
||
if types.array_buffer_type is None and member_type.name == "ArrayBuffer":
|
||
types.array_buffer_type = member_type
|
||
if types.data_view_type is None and member_type.name == "DataView":
|
||
types.data_view_type = member_type
|
||
if is_typed_array_type(member_type):
|
||
types.typed_array_types.append(member_type)
|
||
if types.dictionary_type is None and context.dictionary(member_type) is not None:
|
||
types.dictionary_type = member_type
|
||
|
||
if not isinstance(member_type, IDLParameterizedType):
|
||
continue
|
||
|
||
if types.sequence_type is None and member_type.name == "sequence":
|
||
types.sequence_type = member_type
|
||
if types.frozen_array_type is None and member_type.name == "FrozenArray":
|
||
types.frozen_array_type = member_type
|
||
if types.record_type is None and member_type.name == "record":
|
||
types.record_type = member_type
|
||
|
||
return types
|
||
|
||
|
||
def unsupported_to_idl_value(idl_type: IDLType) -> str:
|
||
raise RuntimeError(f"Unsupported IDL value conversion for '{idl_type}'")
|
||
|
||
|
||
def idl_value_conversion_is_throwing(idl_type: IDLType, context: GenerationContext) -> bool:
|
||
return idl_type.name not in ("any", "boolean")
|
||
|
||
|
||
def write_enumeration_declaration(out: TextIO, enumeration: Enumeration, includes: GeneratedIncludes) -> None:
|
||
includes.add("LibJS/Forward.h")
|
||
includes.add("LibJS/Runtime/Value.h")
|
||
|
||
out.write(f"enum class {enumeration.name} : {underlying_type_for_enum(len(enumeration.values))} {{\n")
|
||
for value in enumeration.values:
|
||
out.write(f" {string_to_cpp_enum_name(value)},\n")
|
||
out.write("};\n\n")
|
||
out.write(
|
||
f"JS::ThrowCompletionOr<{enumeration.name}> {converter_function_name(enumeration)}(JS::VM&, JS::Value);\n\n"
|
||
)
|
||
|
||
|
||
def write_dictionary_declaration(
|
||
out: TextIO,
|
||
dictionary: Dictionary,
|
||
includes: GeneratedIncludes,
|
||
context: GenerationContext,
|
||
) -> None:
|
||
includes.add("LibJS/Forward.h")
|
||
includes.add("LibJS/Runtime/Value.h")
|
||
parent_dictionary = context.dictionary_parent(dictionary)
|
||
if parent_dictionary is not None:
|
||
includes.add_binding(parent_dictionary.path.stem)
|
||
|
||
parent = f" : public {dictionary.parent_name}" if dictionary.parent_name else ""
|
||
out.write(f"struct {dictionary.name}{parent} {{\n")
|
||
for member in dictionary.members:
|
||
add_header_includes_for_type(member, includes, context)
|
||
|
||
default_value = ""
|
||
if member.default_value is not None:
|
||
default_value = f" {cpp_default_value_conversion(member, context)} "
|
||
member_cpp_type = cpp_type(member, context)
|
||
if member.required and member.default_value is None:
|
||
out.write(f" {member_cpp_type} {cpp_name(member)};\n")
|
||
else:
|
||
out.write(f" {member_cpp_type} {cpp_name(member)} {{{default_value}}};\n")
|
||
out.write("};\n\n")
|
||
out.write(
|
||
f"JS::ThrowCompletionOr<{dictionary.name}> {converter_function_name(dictionary)}(JS::VM&, JS::Value);\n\n"
|
||
)
|
||
|
||
|
||
def dictionaries_in_dependency_order(dictionaries: List[Dictionary], context: GenerationContext) -> List[Dictionary]:
|
||
local_dictionaries = {dictionary.name: dictionary for dictionary in dictionaries}
|
||
emitted: set[str] = set()
|
||
visiting: set[str] = set()
|
||
ordered_dictionaries: List[Dictionary] = []
|
||
|
||
def dependency_names_for(dictionary: Dictionary) -> set[str]:
|
||
dependency_names = {dictionary.parent_name} if dictionary.parent_name else set()
|
||
for member in dictionary.members:
|
||
dependency_names.update(context.dictionary_type_names(member.type))
|
||
dependency_names.discard(dictionary.name)
|
||
return dependency_names
|
||
|
||
def visit(dictionary: Dictionary) -> None:
|
||
if dictionary.name in emitted:
|
||
return
|
||
if dictionary.name in visiting:
|
||
raise RuntimeError(f"Dictionary '{dictionary.name}' depends on itself")
|
||
|
||
visiting.add(dictionary.name)
|
||
for dependency_name in dependency_names_for(dictionary):
|
||
dependency = local_dictionaries.get(dependency_name)
|
||
if dependency is not None:
|
||
visit(dependency)
|
||
visiting.remove(dictionary.name)
|
||
|
||
ordered_dictionaries.append(dictionary)
|
||
emitted.add(dictionary.name)
|
||
|
||
for dictionary in local_dictionaries.values():
|
||
visit(dictionary)
|
||
|
||
return ordered_dictionaries
|
||
|
||
|
||
def write_enumeration_conversion(out: TextIO, enumeration: Enumeration, includes: GeneratedIncludes) -> None:
|
||
includes.add("LibJS/Runtime/Error.h")
|
||
includes.add("LibJS/Runtime/VM.h")
|
||
includes.add("LibJS/Runtime/ValueInlines.h")
|
||
|
||
out.write(
|
||
f"""// https://webidl.spec.whatwg.org/#idl-enumeration
|
||
JS::ThrowCompletionOr<{enumeration.name}> {converter_function_name(enumeration)}(JS::VM& vm, JS::Value value)
|
||
{{
|
||
// 1. Let S be the result of calling ? ToString(V).
|
||
auto value_as_string = TRY(value.to_utf16_string(vm));
|
||
|
||
// 2. If S is not one of E’s enumeration values, then throw a TypeError.
|
||
// 3. Return the enumeration value of type E that is equal to S.
|
||
"""
|
||
)
|
||
|
||
for value in enumeration.values:
|
||
out.write(f' if (value_as_string == "{value}"sv)\n')
|
||
out.write(f" return {enumeration.name}::{string_to_cpp_enum_name(value)};\n")
|
||
out.write(
|
||
f""" return vm.throw_completion<JS::TypeError>(JS::ErrorType::InvalidEnumerationValue, value_as_string, "{enumeration.name}");
|
||
}}
|
||
|
||
"""
|
||
)
|
||
|
||
|
||
def write_dictionary_conversion(
|
||
out: TextIO,
|
||
dictionary: Dictionary,
|
||
includes: GeneratedIncludes,
|
||
context: GenerationContext,
|
||
) -> None:
|
||
includes.add("LibJS/Runtime/Error.h")
|
||
includes.add("LibJS/Runtime/VM.h")
|
||
includes.add("LibJS/Runtime/Value.h")
|
||
includes.add("LibJS/Runtime/ValueInlines.h")
|
||
includes.add("LibWeb/Bindings/ExceptionOrUtils.h")
|
||
|
||
parent_dictionary = context.dictionary_parent(dictionary)
|
||
|
||
out.write(f"""// https://webidl.spec.whatwg.org/#es-dictionary
|
||
JS::ThrowCompletionOr<{dictionary.name}> {converter_function_name(dictionary)}(JS::VM& vm, JS::Value js_dict)
|
||
{{
|
||
// 1. If jsDict is not an Object and jsDict is neither undefined nor null, then throw a TypeError.
|
||
if (!js_dict.is_object() && !js_dict.is_nullish())
|
||
return vm.throw_completion<JS::TypeError>(JS::ErrorType::NotAnObjectOfType, "{dictionary.name}");
|
||
|
||
// 2. Let idlDict be an empty ordered map, representing a dictionary of type D.
|
||
// 3. Let dictionaries be a list consisting of D and all of D’s inherited dictionaries, in order from least to most derived.
|
||
// 4. For each dictionary dictionary in dictionaries, in order:
|
||
// NB: We defer construction until the return initializer because some members may not be default-constructible. Inherited dictionaries are represented by the generated C++ struct inheritance.
|
||
|
||
// 5. Return idlDict.
|
||
return {dictionary.name} {{
|
||
""")
|
||
if parent_dictionary is not None:
|
||
out.write(f" TRY({converter_function_name(parent_dictionary)}(vm, js_dict)),\n")
|
||
|
||
# 4.1. For each dictionary member member declared on dictionary, in lexicographical order:
|
||
for member in dictionary.members:
|
||
conversion = to_idl_value(member, "js_member_value", includes, context)
|
||
member_cpp_type = cpp_type(member, context)
|
||
member_designator = "" if parent_dictionary is not None else f".{cpp_name(member)} = "
|
||
out.write(
|
||
f""" {member_designator}TRY([&]() -> JS::ThrowCompletionOr<{member_cpp_type}> {{
|
||
// 1. Let key be the identifier of member.
|
||
// 2. If jsDict is either undefined or null, then:
|
||
// 1. Let jsMemberValue be undefined.
|
||
// 3. Otherwise,
|
||
// 1. Let jsMemberValue be ? Get(jsDict, key).
|
||
auto js_member_value = JS::js_undefined();
|
||
if (js_dict.is_object())
|
||
js_member_value = TRY(js_dict.as_object().get("{member.name}"_utf16_fly_string));
|
||
|
||
// 4. If jsMemberValue is not undefined, then:
|
||
if (!js_member_value.is_undefined()) {{
|
||
// 1. Let idlMemberValue be the result of converting jsMemberValue to an IDL value whose type is the type member is declared to be of.
|
||
auto idl_member_value = TRY(throw_dom_exception_if_needed(vm, [&] {{ return {conversion}; }}));
|
||
|
||
// 2. Set idlDict[key] to idlMemberValue.
|
||
return idl_member_value;
|
||
}}
|
||
"""
|
||
)
|
||
if member.default_value is not None:
|
||
out.write(
|
||
f""" // 5. Otherwise, if jsMemberValue is undefined but member has a default value, then:
|
||
// 1. Let idlMemberValue be the result of converting member's default value to an IDL value whose type is the type member is declared to be of.
|
||
auto idl_member_value = {cpp_default_value_conversion(member, context)};
|
||
|
||
// 2. Set idlDict[key] to idlMemberValue.
|
||
return idl_member_value;
|
||
"""
|
||
)
|
||
elif member.required:
|
||
out.write(
|
||
f""" // 6. Otherwise, if jsMemberValue is undefined and member is required, then throw a TypeError.
|
||
return vm.throw_completion<JS::TypeError>(JS::ErrorType::MissingRequiredProperty, "{member.name}");
|
||
"""
|
||
)
|
||
else:
|
||
out.write(
|
||
f""" // 7. Otherwise, jsMemberValue is undefined and the member is optional.
|
||
return {cpp_empty_value(member, context)};
|
||
"""
|
||
)
|
||
|
||
out.write(" }()),\n")
|
||
out.write(
|
||
""" };
|
||
}
|
||
|
||
"""
|
||
)
|
||
|
||
|
||
# 3.2.1. any, https://webidl.spec.whatwg.org/#js-any
|
||
def any_to_idl_value(value_name: str, includes: GeneratedIncludes) -> str:
|
||
includes.add("LibJS/Runtime/Value.h")
|
||
|
||
# 1. If V is undefined, then return the unique undefined IDL value.
|
||
# 2. If V is null, then return the null object? reference.
|
||
# 3. If V is a Boolean, then return the boolean value that represents the same truth value.
|
||
# 4. If V is a Number, then return the result of converting V to an unrestricted double.
|
||
# 5. If V is a BigInt, then return the result of converting V to a bigint.
|
||
# 6. If V is a String, then return the result of converting V to a DOMString.
|
||
# 7. If V is a Symbol, then return the result of converting V to a symbol.
|
||
# 8. If V is an Object, then return an IDL object value that references V.
|
||
# NB: We're getting passed a JS::Value - which is already our C++ representation, so we can just return it as-is.
|
||
return value_name
|
||
|
||
|
||
# 3.2.3. boolean, https://webidl.spec.whatwg.org/#js-boolean
|
||
def boolean_to_idl_value(value_name: str, includes: GeneratedIncludes) -> str:
|
||
includes.add("LibJS/Runtime/Value.h")
|
||
|
||
# 1. Let x be the result of computing ToBoolean(V).
|
||
# 2. Return the IDL boolean value that is the one that represents the same truth value as the JavaScript Boolean value x.
|
||
return f"{value_name}.to_boolean()"
|
||
|
||
|
||
# https://webidl.spec.whatwg.org/#abstract-opdef-converttoint
|
||
def convert_to_int(
|
||
cpp_type: str,
|
||
value_name: str,
|
||
includes: GeneratedIncludes,
|
||
extended_attributes: dict[str, str],
|
||
) -> str:
|
||
includes.add("LibWeb/WebIDL/Types.h")
|
||
includes.add("LibWeb/WebIDL/AbstractOperations.h")
|
||
|
||
enforce_range = "Yes" if "EnforceRange" in extended_attributes else "No"
|
||
clamp = "Yes" if "Clamp" in extended_attributes else "No"
|
||
return (
|
||
f"WebIDL::convert_to_int<{cpp_type}>(vm, {value_name}, "
|
||
f"WebIDL::EnforceRange::{enforce_range}, WebIDL::Clamp::{clamp})"
|
||
)
|
||
|
||
|
||
# 3.2.4.1. byte, https://webidl.spec.whatwg.org/#js-byte
|
||
def byte_to_idl_value(value_name: str, includes: GeneratedIncludes, extended_attributes: dict[str, str]) -> str:
|
||
# 1. Let x be ? ConvertToInt(V, 8, "signed").
|
||
# 2. Return the IDL byte value that represents the same numeric value as x.
|
||
return convert_to_int("WebIDL::Byte", value_name, includes, extended_attributes)
|
||
|
||
|
||
# 3.2.4.2. octet, https://webidl.spec.whatwg.org/#js-octet
|
||
def octet_to_idl_value(value_name: str, includes: GeneratedIncludes, extended_attributes: dict[str, str]) -> str:
|
||
# 1. Let x be ? ConvertToInt(V, 8, "unsigned").
|
||
# 2. Return the IDL octet value that represents the same numeric value as x.
|
||
return convert_to_int("WebIDL::Octet", value_name, includes, extended_attributes)
|
||
|
||
|
||
# 3.2.4.3. short, https://webidl.spec.whatwg.org/#js-short
|
||
def short_to_idl_value(value_name: str, includes: GeneratedIncludes, extended_attributes: dict[str, str]) -> str:
|
||
# 1. Let x be ? ConvertToInt(V, 16, "signed").
|
||
# 2. Return the IDL short value that represents the same numeric value as x.
|
||
return convert_to_int("WebIDL::Short", value_name, includes, extended_attributes)
|
||
|
||
|
||
# 3.2.4.4. unsigned short, https://webidl.spec.whatwg.org/#js-unsigned-short
|
||
def unsigned_short_to_idl_value(
|
||
value_name: str, includes: GeneratedIncludes, extended_attributes: dict[str, str]
|
||
) -> str:
|
||
# 1. Let x be ? ConvertToInt(V, 16, "unsigned").
|
||
# 2. Return the IDL unsigned short value that represents the same numeric value as x.
|
||
return convert_to_int("WebIDL::UnsignedShort", value_name, includes, extended_attributes)
|
||
|
||
|
||
# 3.2.4.5. long, https://webidl.spec.whatwg.org/#js-long
|
||
def long_to_idl_value(value_name: str, includes: GeneratedIncludes, extended_attributes: dict[str, str]) -> str:
|
||
# 1. Let x be ? ConvertToInt(V, 32, "signed").
|
||
# 2. Return the IDL long value that represents the same numeric value as x.
|
||
return convert_to_int("WebIDL::Long", value_name, includes, extended_attributes)
|
||
|
||
|
||
# 3.2.4.6. unsigned long, https://webidl.spec.whatwg.org/#js-unsigned-long
|
||
def unsigned_long_to_idl_value(
|
||
value_name: str, includes: GeneratedIncludes, extended_attributes: dict[str, str]
|
||
) -> str:
|
||
# 1. Let x be ? ConvertToInt(V, 32, "unsigned").
|
||
# 2. Return the IDL unsigned long value that represents the same numeric value as x.
|
||
return convert_to_int("WebIDL::UnsignedLong", value_name, includes, extended_attributes)
|
||
|
||
|
||
# 3.2.4.7. long long, https://webidl.spec.whatwg.org/#js-long-long
|
||
def long_long_to_idl_value(value_name: str, includes: GeneratedIncludes, extended_attributes: dict[str, str]) -> str:
|
||
# 1. Let x be ? ConvertToInt(V, 64, "signed").
|
||
# 2. Return the IDL long long value that represents the same numeric value as x.
|
||
return convert_to_int("WebIDL::LongLong", value_name, includes, extended_attributes)
|
||
|
||
|
||
# 3.2.4.8. unsigned long long, https://webidl.spec.whatwg.org/#js-unsigned-long-long
|
||
def unsigned_long_long_to_idl_value(
|
||
value_name: str,
|
||
includes: GeneratedIncludes,
|
||
extended_attributes: dict[str, str],
|
||
) -> str:
|
||
# 1. Let x be ? ConvertToInt(V, 64, "unsigned").
|
||
# 2. Return the IDL unsigned long long value that represents the same numeric value as x.
|
||
return convert_to_int("WebIDL::UnsignedLongLong", value_name, includes, extended_attributes)
|
||
|
||
|
||
# 3.2.5. float, https://webidl.spec.whatwg.org/#js-float
|
||
def float_to_idl_value(value_name: str, includes: GeneratedIncludes, identifier: str) -> str:
|
||
includes.add("LibJS/Runtime/Error.h")
|
||
includes.add("LibJS/Runtime/ValueInlines.h")
|
||
|
||
# 1. Let x be ? ToNumber(V).
|
||
# 2. If x is NaN, +∞, or −∞, then throw a TypeError.
|
||
# 3. Let S be the set of finite IEEE 754 single-precision floating point values except −0, but with two special values added: 2^128 and −2^128.
|
||
# 4. Let y be the number in S that is closest to x, selecting the number with an even significand if there are two equally close values. (The two special values 2^128 and −2^128 are considered to have even significands for this purpose.)
|
||
# 5. If y is 2^128 or −2^128, then throw a TypeError.
|
||
# 6. If y is +0 and x is negative, return −0.
|
||
# 7. Return y.
|
||
# FIXME: Correctly implement steps 3-7.
|
||
return f"""[&]() -> JS::ThrowCompletionOr<float> {{
|
||
float x = TRY({value_name}.to_double(vm));
|
||
if (isinf(x) || isnan(x))
|
||
return vm.throw_completion<JS::TypeError>(JS::ErrorType::InvalidRestrictedFloatingPointParameter, "{identifier}");
|
||
return x;
|
||
}}()"""
|
||
|
||
|
||
# 3.2.6. unrestricted float, https://webidl.spec.whatwg.org/#js-unrestricted-float
|
||
def unrestricted_float_to_idl_value(value_name: str, includes: GeneratedIncludes) -> str:
|
||
includes.add("LibJS/Runtime/ValueInlines.h")
|
||
|
||
# 1. Let x be ? ToNumber(V).
|
||
# 2. If x is NaN, then return the IDL unrestricted float value that represents the IEEE 754 NaN value with the bit pattern 0x7fc00000 [IEEE-754].
|
||
# 3. Let S be the set of finite IEEE 754 single-precision floating point values except −0, but with two special values added: 2^128 and −2^128.
|
||
# 4. Let y be the number in S that is closest to x, selecting the number with an even significand if there are two equally close values. (The two special values 2^128 and −2^128 are considered to have even significands for this purpose.)
|
||
# 5. If y is 2^128, return +∞.
|
||
# 6. If y is −2^128, return −∞.
|
||
# 7. If y is +0 and x is negative, return −0.
|
||
# 8. Return y.
|
||
# FIXME: Correctly implement steps 2-8.
|
||
return f"""[&]() -> JS::ThrowCompletionOr<float> {{
|
||
return static_cast<float>(TRY({value_name}.to_double(vm)));
|
||
}}()"""
|
||
|
||
|
||
# 3.2.7. double, https://webidl.spec.whatwg.org/#js-double
|
||
def double_to_idl_value(value_name: str, includes: GeneratedIncludes, identifier: str) -> str:
|
||
includes.add("LibJS/Runtime/Error.h")
|
||
includes.add("LibJS/Runtime/ValueInlines.h")
|
||
|
||
# 1. Let x be ? ToNumber(V).
|
||
# 2. If x is NaN, +∞, or −∞, then throw a TypeError.
|
||
# 3. Return the IDL double value that represents the same numeric value as x.
|
||
return f"""[&]() -> JS::ThrowCompletionOr<double> {{
|
||
auto x = TRY({value_name}.to_double(vm));
|
||
if (isinf(x) || isnan(x))
|
||
return vm.throw_completion<JS::TypeError>(JS::ErrorType::InvalidRestrictedFloatingPointParameter, "{identifier}");
|
||
return x;
|
||
}}()"""
|
||
|
||
|
||
# 3.2.8. unrestricted double, https://webidl.spec.whatwg.org/#js-unrestricted-double
|
||
def unrestricted_double_to_idl_value(value_name: str, includes: GeneratedIncludes) -> str:
|
||
includes.add("LibJS/Runtime/ValueInlines.h")
|
||
# 1. Let x be ? ToNumber(V).
|
||
# 2. If x is NaN, then return the IDL unrestricted double value that represents the IEEE 754 NaN value with the bit pattern 0x7ff8000000000000 [IEEE-754].
|
||
# 3. Return the IDL unrestricted double value that represents the same numeric value as x.
|
||
# FIXME!
|
||
return f"{value_name}.to_double(vm)"
|
||
|
||
|
||
# 3.2.9. bigint, https://webidl.spec.whatwg.org/#js-bigint
|
||
def bigint_to_idl_value(value_name: str, includes: GeneratedIncludes) -> str:
|
||
includes.add("LibJS/Runtime/BigInt.h")
|
||
includes.add("LibJS/Runtime/Value.h")
|
||
includes.add("LibJS/Runtime/ValueInlines.h")
|
||
|
||
# 1. Let x be ? ToBigInt(V).
|
||
# 2. Return the IDL bigint value that represents the same numeric value as x.
|
||
return f"{value_name}.to_bigint(vm)"
|
||
|
||
|
||
# 3.2.10. DOMString, https://webidl.spec.whatwg.org/#js-DOMString
|
||
def dom_string_to_idl_value(
|
||
value_name: str,
|
||
includes: GeneratedIncludes,
|
||
extended_attributes: Optional[dict[str, str]] = None,
|
||
type_name: str = "DOMString",
|
||
) -> str:
|
||
includes.add("LibJS/Runtime/Value.h")
|
||
includes.add("LibWeb/WebIDL/AbstractOperations.h")
|
||
cpp_type_name = cpp_type_name_for_string(type_name, extended_attributes)
|
||
add_include_for_string_cpp_type(cpp_type_name, includes)
|
||
to_string = "to_utf16_string" if "Utf16" in type_name else "to_string"
|
||
|
||
# 1. If V is null and the conversion is to an IDL type associated with the [LegacyNullToEmptyString] extended attribute, then return the DOMString value that represents the empty string.
|
||
if extended_attributes is not None and "LegacyNullToEmptyString" in extended_attributes:
|
||
return f"""[&]() -> JS::ThrowCompletionOr<{cpp_type_name}> {{
|
||
{cpp_type_name} string;
|
||
if (!{value_name}.is_null())
|
||
string = TRY(WebIDL::{to_string}(vm, {value_name}));
|
||
return string;
|
||
}}()"""
|
||
# 2. Let x be ? ToString(V).
|
||
# 3. Return the IDL DOMString value that represents the same sequence of code units as the one the JavaScript String value x represents.
|
||
return f"""[&]() -> JS::ThrowCompletionOr<{cpp_type_name}> {{
|
||
return TRY(WebIDL::{to_string}(vm, {value_name}));
|
||
}}()"""
|
||
|
||
|
||
# 3.2.11. ByteString, https://webidl.spec.whatwg.org/#js-ByteString
|
||
def bytestring_to_idl_value(value_name: str, includes: GeneratedIncludes) -> str:
|
||
includes.add("LibJS/Runtime/Value.h")
|
||
includes.add("LibWeb/WebIDL/AbstractOperations.h")
|
||
cpp_type_name = cpp_type_name_for_string("ByteString")
|
||
add_include_for_string_cpp_type(cpp_type_name, includes)
|
||
# 1. Let x be ? ToString(V).
|
||
# 2. If the value of any element of x is greater than 255, then throw a TypeError.
|
||
# 3. Return an IDL ByteString value whose length is the length of x, and where the value of each element is the value of the corresponding element of x.
|
||
return f"""[&]() -> JS::ThrowCompletionOr<{cpp_type_name}> {{
|
||
return TRY(WebIDL::to_byte_string(vm, {value_name}));
|
||
}}()"""
|
||
|
||
|
||
# 3.2.12. USVString, https://webidl.spec.whatwg.org/#js-USVString
|
||
def usv_string_to_idl_value(
|
||
value_name: str,
|
||
includes: GeneratedIncludes,
|
||
extended_attributes: Optional[dict[str, str]] = None,
|
||
type_name: str = "USVString",
|
||
) -> str:
|
||
includes.add("LibJS/Runtime/Value.h")
|
||
includes.add("LibWeb/WebIDL/AbstractOperations.h")
|
||
cpp_type_name = cpp_type_name_for_string(type_name, extended_attributes)
|
||
add_include_for_string_cpp_type(cpp_type_name, includes)
|
||
to_string = "to_utf16_usv_string" if "Utf16" in type_name else "to_usv_string"
|
||
|
||
# 1. Let string be the result of converting V to a DOMString.
|
||
# 2. If x contains any lone surrogates, then throw a TypeError.
|
||
# 3. Return the IDL USVString value that represents the same sequence of code units as the one the JavaScript String value x represents.
|
||
return f"""[&]() -> JS::ThrowCompletionOr<{cpp_type_name}> {{
|
||
return TRY(WebIDL::{to_string}(vm, {value_name}));
|
||
}}()"""
|
||
|
||
|
||
# 3.2.13. object, https://webidl.spec.whatwg.org/#js-object
|
||
def object_to_idl_value(value_name: str, includes: GeneratedIncludes) -> str:
|
||
includes.add("LibJS/Runtime/Error.h")
|
||
includes.add("LibJS/Runtime/Object.h")
|
||
includes.add("LibJS/Runtime/Value.h")
|
||
includes.add("LibJS/Runtime/ValueInlines.h")
|
||
# 1. If V is not an Object, then throw a TypeError.
|
||
# 2. Return the IDL object value that is a reference to the same object as V.
|
||
return f"""[&]() -> JS::ThrowCompletionOr<GC::Ref<JS::Object>> {{
|
||
if (!{value_name}.is_object())
|
||
return vm.throw_completion<JS::TypeError>(JS::ErrorType::NotAnObject, {value_name});
|
||
return GC::Ref {{ {value_name}.as_object() }};
|
||
}}()"""
|
||
|
||
|
||
# 3.2.14. symbol, https://webidl.spec.whatwg.org/#js-symbol
|
||
def symbol_to_idl_value(value_name: str, includes: GeneratedIncludes) -> str:
|
||
includes.add("LibJS/Runtime/Value.h")
|
||
# 1. If V is not a Symbol, then throw a TypeError.
|
||
# 2. Return the result of converting V to an IDL symbol value.
|
||
raise RuntimeError("symbol to IDL value conversion is not yet implemented")
|
||
|
||
|
||
# 3.2.15. Interface types, https://webidl.spec.whatwg.org/#js-interface
|
||
def interface_to_idl_value(
|
||
value_name: str,
|
||
includes: GeneratedIncludes,
|
||
interface_like_type: InterfaceLikeType,
|
||
) -> str:
|
||
includes.add("LibJS/Runtime/Error.h")
|
||
includes.add("LibJS/Runtime/Value.h")
|
||
includes.add("LibJS/Runtime/ValueInlines.h")
|
||
includes.add(interface_like_type.implementation_header)
|
||
|
||
# 1. If V implements I, then return the IDL interface type value that represents a reference to that platform object.
|
||
# 2. Throw a TypeError.
|
||
return f"""[&]() -> JS::ThrowCompletionOr<GC::Ref<{interface_like_type.fully_qualified_name}>> {{
|
||
if (auto impl = {value_name}.as_if<{interface_like_type.fully_qualified_name}>())
|
||
return *impl;
|
||
return vm.throw_completion<JS::TypeError>(JS::ErrorType::NotAnObjectOfType, "{interface_like_type.name}");
|
||
}}()"""
|
||
|
||
|
||
# 3.2.16. Callback interface types, https://webidl.spec.whatwg.org/#js-callback-interface
|
||
def callback_interface_to_idl_value(
|
||
value_name: str,
|
||
includes: GeneratedIncludes,
|
||
interface: Interface,
|
||
) -> str:
|
||
includes.add("LibGC/Heap.h")
|
||
includes.add("LibJS/Runtime/Error.h")
|
||
includes.add("LibJS/Runtime/Value.h")
|
||
includes.add("LibJS/Runtime/ValueInlines.h")
|
||
includes.add("LibWeb/Bindings/ExceptionOrUtils.h")
|
||
includes.add("LibWeb/HTML/Scripting/Environments.h")
|
||
includes.add("LibWeb/WebIDL/CallbackType.h")
|
||
includes.add(implementation_header_for_interface(interface))
|
||
|
||
cpp_type = fully_qualified_name_for_interface(interface)
|
||
# 1. If V is not an Object, then throw a TypeError.
|
||
# 2. Return the IDL callback interface type value that represents a reference to V, with the incumbent settings object as the callback context.
|
||
return f"""[&]() -> JS::ThrowCompletionOr<GC::Ref<{cpp_type}>> {{
|
||
if (!{value_name}.is_object())
|
||
return vm.throw_completion<JS::TypeError>(JS::ErrorType::NotAnObject, {value_name});
|
||
|
||
auto callback_type = vm.heap().allocate<WebIDL::CallbackType>({value_name}.as_object(), HTML::incumbent_realm());
|
||
return TRY(throw_dom_exception_if_needed(vm, [&] {{ return {cpp_type}::create(realm, callback_type); }}));
|
||
}}()"""
|
||
|
||
|
||
# 3.2.17. Dictionary types, https://webidl.spec.whatwg.org/#js-dictionary
|
||
def dictionary_to_idl_value(
|
||
idl_type: IDLType,
|
||
value_name: str,
|
||
includes: GeneratedIncludes,
|
||
context: GenerationContext,
|
||
) -> str:
|
||
add_binding_include_for_type(idl_type, includes, context)
|
||
# The actual implementation of this conversion function is generated by write_dictionary_conversion().
|
||
return f"{converter_function_name(idl_type)}(vm, {value_name})"
|
||
|
||
|
||
# 3.2.18. Enumeration types, https://webidl.spec.whatwg.org/#js-enumeration
|
||
def enumeration_to_idl_value(
|
||
idl_type: IDLType,
|
||
value_name: str,
|
||
includes: GeneratedIncludes,
|
||
context: GenerationContext,
|
||
) -> str:
|
||
add_binding_include_for_type(idl_type, includes, context)
|
||
# The actual implementation of this conversion function is generated by write_enumeration_conversion().
|
||
return f"{converter_function_name(idl_type)}(vm, {value_name})"
|
||
|
||
|
||
# 3.2.19. Callback function types, https://webidl.spec.whatwg.org/#js-callback-function
|
||
def callback_function_to_idl_value(
|
||
return_cpp_type: str,
|
||
callback_function: CallbackFunction,
|
||
value_name: str,
|
||
includes: GeneratedIncludes,
|
||
) -> str:
|
||
includes.add("LibGC/Heap.h")
|
||
includes.add("LibJS/Runtime/Error.h")
|
||
includes.add("LibJS/Runtime/FunctionObject.h")
|
||
includes.add("LibWeb/HTML/Scripting/Environments.h")
|
||
includes.add("LibWeb/WebIDL/CallbackType.h")
|
||
|
||
operation_returns_promise = (
|
||
"WebIDL::OperationReturnsPromise::Yes"
|
||
if callback_function.return_type.name.split("<", 1)[0] == "Promise"
|
||
else "WebIDL::OperationReturnsPromise::No"
|
||
)
|
||
|
||
legacy_treat_non_object_as_null = "LegacyTreatNonObjectAsNull" in callback_function.extended_attributes
|
||
return_cpp_type = "GC::Ptr<WebIDL::CallbackType>" if legacy_treat_non_object_as_null else return_cpp_type
|
||
|
||
conversion = f"""[&]() -> JS::ThrowCompletionOr<{return_cpp_type}> {{
|
||
"""
|
||
|
||
# 1. If the result of calling IsCallable(V) is false and the conversion to an IDL value is not being
|
||
# performed due to V being assigned to an attribute whose type is a nullable callback function that
|
||
# is annotated with [LegacyTreatNonObjectAsNull], then throw a TypeError.
|
||
if not legacy_treat_non_object_as_null:
|
||
conversion += f"""
|
||
if (!{value_name}.is_function())
|
||
return vm.throw_completion<JS::TypeError>(JS::ErrorType::NotAFunction, {value_name});
|
||
"""
|
||
|
||
# 2. Return the IDL callback function type value that represents a reference to the same object that V
|
||
# represents, with the incumbent settings object as the callback context.
|
||
conversion += f"""
|
||
return vm.heap().allocate<WebIDL::CallbackType>({value_name}.as_object(), HTML::incumbent_realm(), {operation_returns_promise});
|
||
}}()"""
|
||
return conversion
|
||
|
||
|
||
# 3.2.20. Nullable types — T?, https://webidl.spec.whatwg.org/#js-nullable-type
|
||
def nullable_to_idl_value(
|
||
idl_type: IDLType,
|
||
identifier: str,
|
||
extended_attributes: dict[str, str],
|
||
return_cpp_type: str,
|
||
value_name: str,
|
||
includes: GeneratedIncludes,
|
||
context: GenerationContext,
|
||
) -> str:
|
||
inner_type = idl_type.clone_with_nullable(False)
|
||
cpp_type = cpp_type_for_idl_type_details(
|
||
idl_type,
|
||
context,
|
||
extended_attributes=extended_attributes,
|
||
)
|
||
conversion = f"""[&]() -> JS::ThrowCompletionOr<{return_cpp_type}> {{
|
||
{cpp_type.name} value;
|
||
"""
|
||
|
||
# 1. If V is not an Object, and the conversion to an IDL value is being performed due to V being assigned to an
|
||
# attribute whose type is a nullable callback function that is annotated with [LegacyTreatNonObjectAsNull],
|
||
# then return the IDL nullable type T? value null.
|
||
callback_function = context.callback_function(inner_type)
|
||
legacy_treat_non_object_as_null = (
|
||
callback_function is not None and "LegacyTreatNonObjectAsNull" in callback_function.extended_attributes
|
||
)
|
||
if legacy_treat_non_object_as_null:
|
||
conversion += f" if ({value_name}.is_object()) {{\n"
|
||
|
||
inner_conversion = to_idl_value_from_type(
|
||
inner_type, identifier, extended_attributes, value_name, includes, context
|
||
)
|
||
inner_return = (
|
||
f"TRY({inner_conversion})" if idl_value_conversion_is_throwing(inner_type, context) else inner_conversion
|
||
)
|
||
inner_type_includes_undefined = inner_type.includes_undefined()
|
||
|
||
# 2. Otherwise, if V is undefined, and T includes undefined, return the unique undefined value.
|
||
if inner_type_includes_undefined:
|
||
conversion += f"""
|
||
if ({value_name}.is_undefined()) {{
|
||
value = Empty {{}};
|
||
}}
|
||
"""
|
||
# 3. Otherwise, if V is null or undefined, then return the IDL nullable type T? value null.
|
||
conversion += f"""
|
||
else if (!{value_name}.is_nullish()) {{
|
||
"""
|
||
else:
|
||
# 3. Otherwise, if V is null or undefined, then return the IDL nullable type T? value null.
|
||
conversion += f"""
|
||
if (!{value_name}.is_nullish()) {{
|
||
"""
|
||
|
||
# 4. Otherwise, return the result of converting V using the rules for the inner IDL type T.
|
||
conversion += f"""
|
||
value = {inner_return};
|
||
}}
|
||
"""
|
||
|
||
if legacy_treat_non_object_as_null:
|
||
conversion += " }\n"
|
||
|
||
conversion += """ return value;
|
||
}()"""
|
||
return conversion
|
||
|
||
|
||
# 3.2.21. Sequences — sequence<T>, https://webidl.spec.whatwg.org/#js-sequence
|
||
def sequence_to_idl_value(
|
||
sequence_type: IDLParameterizedType,
|
||
identifier: str,
|
||
value_name: str,
|
||
includes: GeneratedIncludes,
|
||
context: GenerationContext,
|
||
) -> str:
|
||
element_type = sequence_type.parameters[0]
|
||
element_cpp_type = cpp_type_for_idl_type_details(element_type, context)
|
||
storage_type_name = element_cpp_type.contained_storage_type.value
|
||
|
||
# 1. If V is not an Object, throw a TypeError.
|
||
# 2. Let method be ? GetMethod(V, %Symbol.iterator%).
|
||
# 3. If method is undefined, throw a TypeError.
|
||
# 4. Return the result of creating a sequence from V and method.
|
||
return f"""[&]() -> JS::ThrowCompletionOr<{storage_type_name}<{element_cpp_type.name}>> {{
|
||
if (!{value_name}.is_object())
|
||
return vm.throw_completion<JS::TypeError>(JS::ErrorType::NotAnObject, {value_name});
|
||
|
||
auto method = TRY({value_name}.get_method(vm, vm.well_known_symbol_iterator()));
|
||
if (!method)
|
||
return vm.throw_completion<JS::TypeError>(JS::ErrorType::NotIterable, {value_name});
|
||
|
||
return TRY({create_sequence_from_iterable(sequence_type, identifier, value_name, "method", includes, context)});
|
||
}}()"""
|
||
|
||
|
||
# 3.2.21.1. Creating a sequence from an iterable, https://webidl.spec.whatwg.org/#create-sequence-from-iterable
|
||
def create_sequence_from_iterable(
|
||
sequence_type: IDLParameterizedType,
|
||
identifier: str,
|
||
value_name: str,
|
||
iterator_method_name: str,
|
||
includes: GeneratedIncludes,
|
||
context: GenerationContext,
|
||
) -> str:
|
||
includes.add("LibJS/Runtime/Error.h")
|
||
includes.add("LibJS/Runtime/Iterator.h")
|
||
includes.add("LibJS/Runtime/Value.h")
|
||
includes.add("LibJS/Runtime/ValueInlines.h")
|
||
includes.add("LibWeb/Bindings/ExceptionOrUtils.h")
|
||
|
||
element_type = sequence_type.parameters[0]
|
||
element_cpp_type = cpp_type_for_idl_type_details(element_type, context)
|
||
storage_type_name = element_cpp_type.contained_storage_type.value
|
||
|
||
return f"""[&]() -> JS::ThrowCompletionOr<{storage_type_name}<{element_cpp_type.name}>> {{
|
||
// To create an IDL value of type sequence<T> given an iterable iterable and an iterator getter method, perform the following steps:
|
||
// 1. Let iteratorRecord be ? GetIteratorFromMethod(iterable, method).
|
||
auto iterator = TRY(JS::get_iterator_from_method(vm, {value_name}, *{iterator_method_name}));
|
||
|
||
{storage_type_name}<{element_cpp_type.name}> sequence;
|
||
|
||
// 2. Initialize i to be 0.
|
||
// 3. Repeat
|
||
for (;;) {{
|
||
// 1. Let next be ? IteratorStepValue(iteratorRecord).
|
||
auto next = TRY(JS::iterator_step_value(vm, iterator));
|
||
|
||
// 2. If next is done, then return an IDL sequence value of type sequence<T> of length i, where the value of the element at index j is Sj.
|
||
if (!next.has_value())
|
||
break;
|
||
|
||
// 3. Initialize Si to the result of converting next to an IDL value of type T.
|
||
auto next_value = next.release_value();
|
||
auto sequence_item = TRY(throw_dom_exception_if_needed(vm, [&] {{ return {to_idl_value_from_type(element_type, identifier, {}, "next_value", includes, context)}; }}));
|
||
|
||
// 4. Set i to i + 1.
|
||
sequence.append(sequence_item);
|
||
}}
|
||
|
||
return sequence;
|
||
}}()"""
|
||
|
||
|
||
# 3.2.22. Async sequences — async_sequence<T>, https://webidl.spec.whatwg.org/#js-async-iterable
|
||
def async_sequence_to_idl_value() -> NoReturn:
|
||
raise RuntimeError("async sequence to IDL value conversion is not yet implemented")
|
||
|
||
|
||
# 3.2.23. Records — record<K, V>, https://webidl.spec.whatwg.org/#js-record
|
||
def record_to_idl_value(
|
||
record_type: IDLParameterizedType,
|
||
identifier: str,
|
||
value_name: str,
|
||
includes: GeneratedIncludes,
|
||
context: GenerationContext,
|
||
) -> str:
|
||
if len(record_type.parameters) != 2:
|
||
raise RuntimeError("Record type must have exactly two parameters")
|
||
|
||
key_type = record_type.parameters[0]
|
||
value_type = record_type.parameters[1]
|
||
if not is_string_type(key_type.name):
|
||
raise RuntimeError("Record key type must be a string type")
|
||
|
||
includes.add("LibJS/Runtime/Error.h")
|
||
includes.add("LibJS/Runtime/PropertyKey.h")
|
||
includes.add("LibJS/Runtime/Value.h")
|
||
includes.add("LibJS/Runtime/ValueInlines.h")
|
||
|
||
record_cpp_type = cpp_type_for_idl_type(record_type, context)
|
||
|
||
return f"""[&]() -> JS::ThrowCompletionOr<{record_cpp_type}> {{
|
||
// An ECMAScript value O is converted to an IDL record<K, V> value as follows:
|
||
// 1. If Type(O) is not Object, throw a TypeError.
|
||
if (!{value_name}.is_object())
|
||
return vm.throw_completion<JS::TypeError>(JS::ErrorType::NotAnObject, {value_name});
|
||
|
||
auto& record_object0 = {value_name}.as_object();
|
||
|
||
// 2. Let result be a new empty instance of record<K, V>.
|
||
{record_cpp_type} record;
|
||
|
||
// 3. Let keys be ? O.[[OwnPropertyKeys]]().
|
||
auto keys = TRY(record_object0.internal_own_property_keys());
|
||
|
||
// 4. For each key of keys:
|
||
for (auto& key : keys) {{
|
||
auto property_key = MUST(JS::PropertyKey::from_value(vm, key));
|
||
|
||
// 1. Let desc be ? O.[[GetOwnProperty]](key).
|
||
auto desc = TRY(record_object0.internal_get_own_property(property_key));
|
||
|
||
// 2. If desc is not undefined and desc.[[Enumerable]] is true:
|
||
if (!desc.has_value() || !desc->enumerable.has_value() || !desc->enumerable.value())
|
||
continue;
|
||
|
||
// 1. Let typedKey be key converted to an IDL value of type K.
|
||
auto typed_key = TRY({to_idl_value_from_type(key_type, identifier, {}, "key", includes, context)});
|
||
|
||
// 2. Let value be ? Get(O, key).
|
||
auto value = TRY(record_object0.get(property_key));
|
||
|
||
// 3. Let typedValue be value converted to an IDL value of type V.
|
||
auto typed_value = TRY({to_idl_value_from_type(value_type, identifier, {}, "value", includes, context)});
|
||
|
||
// 4. Set result[typedKey] to typedValue.
|
||
record.set(typed_key, typed_value);
|
||
}}
|
||
|
||
// 5. Return result.
|
||
return record;
|
||
}}()"""
|
||
|
||
|
||
# 3.2.24. Promise types — Promise<T>, https://webidl.spec.whatwg.org/#js-promise
|
||
def promise_to_idl_value(
|
||
value_name: str,
|
||
includes: GeneratedIncludes,
|
||
) -> str:
|
||
includes.add("LibJS/Runtime/AbstractOperations.h")
|
||
includes.add("LibJS/Runtime/PromiseConstructor.h")
|
||
|
||
# An ECMAScript value V is converted to an IDL Promise<T> value as follows:
|
||
return f"""[&]() -> JS::ThrowCompletionOr<GC::Ref<WebIDL::Promise>> {{
|
||
auto& realm = *vm.current_realm();
|
||
|
||
// 1. Let promiseCapability be ? NewPromiseCapability(%Promise%).
|
||
auto promise_capability0 = TRY(JS::new_promise_capability(vm, realm.intrinsics().promise_constructor()));
|
||
|
||
// 2. Perform ? Call(promiseCapability.[[Resolve]], undefined, « V »).
|
||
TRY(JS::call(vm, *promise_capability0->resolve(), JS::js_undefined(), {value_name}));
|
||
|
||
// 3. Return promiseCapability.
|
||
return promise_capability0;
|
||
}}()"""
|
||
|
||
|
||
# 3.2.25. Union types, https://webidl.spec.whatwg.org/#js-union
|
||
def union_to_idl_value(
|
||
union_type: IDLUnionType,
|
||
identifier: str,
|
||
extended_attributes: dict[str, str],
|
||
value_name: str,
|
||
includes: GeneratedIncludes,
|
||
context: GenerationContext,
|
||
) -> str:
|
||
# https://webidl.spec.whatwg.org/#es-union
|
||
includes.add("AK/Variant.h")
|
||
|
||
variant_type = union_type_to_variant(union_type, context)
|
||
types = collect_union_types(union_type.flattened_member_types(), context)
|
||
sections: list[str] = []
|
||
|
||
def conversion_for(member_type: IDLType, name: str = value_name) -> str:
|
||
# Null is handled at the union level and stored as Empty in the generated Variant.
|
||
return to_idl_value_from_type(
|
||
member_type.clone_with_nullable(False),
|
||
identifier,
|
||
extended_attributes,
|
||
name,
|
||
includes,
|
||
context,
|
||
)
|
||
|
||
def append(section: str) -> None:
|
||
if section:
|
||
sections.append(section)
|
||
|
||
# 1. If the union type includes undefined and V is undefined, then return the unique undefined value.
|
||
if union_type.includes_undefined():
|
||
includes.add("AK/Types.h")
|
||
append(f"""
|
||
if ({value_name}.is_undefined()) {{
|
||
return Empty {{}};
|
||
}}
|
||
""")
|
||
|
||
# 2. If the union type includes a nullable type and V is null or undefined, then return the IDL value null.
|
||
if union_type.includes_nullable_type():
|
||
includes.add("AK/Types.h")
|
||
append(f"""
|
||
if ({value_name}.is_nullish()) {{
|
||
return Empty {{}};
|
||
}}
|
||
""")
|
||
|
||
# 4. If V is null or undefined, then:
|
||
# 1. If types includes a dictionary type, then return the result of converting V to that dictionary type.
|
||
if not union_type.includes_nullable_type() and types.dictionary_type is not None:
|
||
append(f"""
|
||
if ({value_name}.is_nullish()) {{
|
||
return {variant_type} {{ TRY({dictionary_to_idl_value(types.dictionary_type, value_name, includes, context)}) }};
|
||
}}
|
||
""")
|
||
|
||
# NB: Doing this here simplifies logic below.
|
||
append(f"""
|
||
if ({value_name}.is_object()) {{
|
||
[[maybe_unused]] auto& object = {value_name}.as_object();
|
||
""")
|
||
|
||
# 5. If V is a platform object, then:
|
||
if types.interface_types:
|
||
includes.add("LibWeb/Bindings/PlatformObject.h")
|
||
append("""
|
||
if (is<PlatformObject>(object)) {
|
||
""")
|
||
for interface_type in types.interface_types:
|
||
interface_like_type = interface_like_type_for_idl_type(interface_type, context)
|
||
assert interface_like_type is not None
|
||
platform_object_cpp_type = interface_like_type.fully_qualified_name
|
||
includes.add(interface_like_type.implementation_header)
|
||
# 1. If types includes an interface type that V implements, then return the IDL value that is a reference to the object V.
|
||
append(f"""
|
||
if (auto* result = as_if<{platform_object_cpp_type}>(object))
|
||
return {variant_type} {{ GC::Ref {{ *result }} }};
|
||
""")
|
||
# 2. If types includes object, then return the IDL value that is a reference to the object V.
|
||
if types.includes_object_type:
|
||
append(f""" return {variant_type} {{ GC::Ref {{ object }} }};
|
||
""")
|
||
append(""" }
|
||
""")
|
||
|
||
# 6. If Type(V) is Object, V has an [[ArrayBufferData]] internal slot, and IsSharedArrayBuffer(V) is false, then:
|
||
if types.array_buffer_type is not None:
|
||
includes.add("LibJS/Runtime/ArrayBuffer.h")
|
||
array_buffer_conversion = conversion_for(types.array_buffer_type)
|
||
# 1. If types includes ArrayBuffer, then return the result of converting V to ArrayBuffer.
|
||
# 2. If types includes object, then return the IDL value that is a reference to the object V.
|
||
append(f"""
|
||
if (auto* array_buffer = as_if<JS::ArrayBuffer>(object)) {{
|
||
if (!array_buffer->is_shared_array_buffer()) {{
|
||
// 1. If types includes ArrayBuffer, then return the result of converting V to ArrayBuffer.
|
||
auto array_buffer_union_type = TRY({array_buffer_conversion});
|
||
return {variant_type} {{ array_buffer_union_type }};
|
||
}}
|
||
}}
|
||
""")
|
||
# FIXME: 7. If V is an Object, V, has an [[ArrayBufferData]] internal slot, and IsSharedArrayBuffer(V) is true, then:
|
||
|
||
# 8. If V is an Object and V has a [[DataView]] internal slot, then:
|
||
if types.data_view_type is not None:
|
||
includes.add("LibJS/Runtime/DataView.h")
|
||
data_view_conversion = conversion_for(types.data_view_type)
|
||
# 1. If types includes DataView, then return the result of converting V to DataView.
|
||
# 2. If types includes object, then return the IDL value that is a reference to the object V.
|
||
append(f"""
|
||
if (as_if<JS::DataView>(object)) {{
|
||
// 1. If types includes DataView, then return the result of converting V to DataView.
|
||
auto data_view_union_type = TRY({data_view_conversion});
|
||
return {variant_type} {{ data_view_union_type }};
|
||
}}
|
||
""")
|
||
|
||
# 9. If V is an Object and V has a [[TypedArrayName]] internal slot, then:
|
||
if types.typed_array_types:
|
||
includes.add("LibJS/Runtime/TypedArray.h")
|
||
for typed_array_type in types.typed_array_types:
|
||
typed_array_conversion = conversion_for(typed_array_type)
|
||
typed_array_cpp_name = make_name_acceptable_cpp(title_case_to_snake_case(typed_array_type.name))
|
||
# 1. If types includes a typed array type whose name is the value of V’s [[TypedArrayName]] internal slot, then return the result of converting V to that type.
|
||
# 2. If types includes object, then return the IDL value that is a reference to the object V.
|
||
append(f"""
|
||
if (as_if<JS::{typed_array_type.name}>(object)) {{
|
||
auto {typed_array_cpp_name}_union_type = TRY({typed_array_conversion});
|
||
return {variant_type} {{ {typed_array_cpp_name}_union_type }};
|
||
}}
|
||
""")
|
||
|
||
# 1. If IsCallable(V) is true, then:
|
||
if types.callback_function_types:
|
||
callback_function_type = types.callback_function_types[0]
|
||
callback_function = context.callback_function(callback_function_type)
|
||
assert callback_function is not None
|
||
# 1. If types includes a callback function type, then return the result of converting V to that callback function type.
|
||
# 2. If types includes object, then return the IDL value that is a reference to the object V.
|
||
callback_function_conversion = callback_function_to_idl_value(
|
||
cpp_type_for_idl_type(callback_function_type, context, extended_attributes=extended_attributes),
|
||
callback_function,
|
||
value_name,
|
||
includes,
|
||
)
|
||
append(f"""
|
||
if (object.is_function()) {{
|
||
auto callback_function_union_type = TRY({callback_function_conversion});
|
||
return {variant_type} {{ callback_function_union_type }};
|
||
}}
|
||
|
||
""")
|
||
|
||
# 11. If V is an Object, then:
|
||
# FIXME: 11.1 If types includes an async sequence type, then
|
||
# 11.2. If types includes a sequence type, then
|
||
if types.sequence_type is not None:
|
||
append(f"""
|
||
// 1. Let method be ? GetMethod(V, @@iterator).
|
||
auto method = TRY({value_name}.get_method(vm, vm.well_known_symbol_iterator()));
|
||
|
||
// 2. If method is not undefined, return the result of creating a sequence of that type from V and method.
|
||
if (method) {{
|
||
auto sequence_union_type = TRY({create_sequence_from_iterable(types.sequence_type, identifier, value_name, "method", includes, context)});
|
||
return {variant_type} {{ sequence_union_type }};
|
||
}}
|
||
|
||
""")
|
||
# 11.3. If types includes a frozen array type, then
|
||
if types.frozen_array_type is not None:
|
||
append(f"""
|
||
// 1. Let method be ? GetMethod(V, @@iterator).
|
||
auto frozen_array_method = TRY({value_name}.get_method(vm, vm.well_known_symbol_iterator()));
|
||
|
||
// 2. If method is not undefined, return the result of creating a frozen array of that type from V and method.
|
||
if (frozen_array_method) {{
|
||
auto frozen_array_union_type = TRY({create_sequence_from_iterable(types.frozen_array_type, identifier, value_name, "frozen_array_method", includes, context)});
|
||
return {variant_type} {{ frozen_array_union_type }};
|
||
}}
|
||
""")
|
||
|
||
# 11.4. If types includes a dictionary type, then return the result of converting V to that dictionary type.
|
||
if types.dictionary_type is not None:
|
||
dictionary_conversion = dictionary_to_idl_value(types.dictionary_type, value_name, includes, context)
|
||
append(f" return {variant_type} {{ TRY({dictionary_conversion}) }};\n")
|
||
|
||
# 11.5. If types includes a record type, then return the result of converting V to that record type.
|
||
if types.record_type is not None:
|
||
record_conversion = record_to_idl_value(types.record_type, identifier, value_name, includes, context)
|
||
append(f"""
|
||
auto record_union_type = TRY({record_conversion});
|
||
return {variant_type} {{ record_union_type }};
|
||
""")
|
||
|
||
# 11.6. If types includes a callback interface type, then return the result of converting V to that callback interface type.
|
||
if types.callback_interface_types:
|
||
callback_interface_type = types.callback_interface_types[0]
|
||
callback_interface = context.interface(callback_interface_type)
|
||
assert callback_interface is not None
|
||
callback_interface_conversion = callback_interface_to_idl_value(
|
||
value_name,
|
||
includes,
|
||
callback_interface,
|
||
)
|
||
append(f"""
|
||
auto callback_interface_union_type = TRY({callback_interface_conversion});
|
||
return {variant_type} {{ callback_interface_union_type }};
|
||
""")
|
||
|
||
# 11.7. If types includes object, then return the IDL value that is a reference to the object V.
|
||
if types.includes_object_type:
|
||
append(f"""
|
||
return {variant_type} {{ GC::Ref {{ object }} }};
|
||
""")
|
||
|
||
append(" }\n")
|
||
|
||
# 12. If Type(V) is Boolean, then:
|
||
if types.boolean_type is not None:
|
||
# 1. If types includes boolean, then return the result of converting V to boolean.
|
||
append(f"""
|
||
if ({value_name}.is_boolean())
|
||
return {variant_type} {{ {value_name}.as_bool() }};
|
||
""")
|
||
|
||
# 13. If V is a Number, then:
|
||
if types.numeric_type is not None:
|
||
# 1. If types includes a numeric type, then return the result of converting V to that numeric type.
|
||
numeric_value_name = f"{make_name_acceptable_cpp(identifier)}_number"
|
||
numeric_conversion_expression = conversion_for(types.numeric_type)
|
||
append(f"""
|
||
if ({value_name}.is_number()) {{
|
||
auto {numeric_value_name} = TRY({numeric_conversion_expression});
|
||
return {variant_type} {{ {numeric_value_name} }};
|
||
}}
|
||
""")
|
||
|
||
# 14. If V is a BigInt, then:
|
||
if types.bigint_type is not None:
|
||
includes.add("LibJS/Runtime/BigInt.h")
|
||
# 1. If types includes bigint, then return the result of converting V to bigint
|
||
append(f"""
|
||
if ({value_name}.is_bigint())
|
||
return {variant_type} {{ GC::Ref {{ {value_name}.as_bigint() }} }};
|
||
""")
|
||
|
||
# 15. If types includes a string type, then return the result of converting V to that type.
|
||
# NB: We need to special case enumurations since we don't pass those through as strings.
|
||
# FIXME: Can we use a helper here?
|
||
if types.enumeration_types:
|
||
includes.add("AK/StringView.h")
|
||
includes.add("LibJS/Runtime/ValueInlines.h")
|
||
enumeration_conversion = f"""
|
||
if ({value_name}.is_string()) {{
|
||
auto enumeration_string = TRY({value_name}.to_utf16_string(vm));
|
||
"""
|
||
for enumeration_type in types.enumeration_types:
|
||
enumeration = context.enumeration(enumeration_type)
|
||
assert enumeration is not None
|
||
for enumeration_value in enumeration.values:
|
||
enumeration_conversion += f""" if (enumeration_string == "{enumeration_value}"sv)
|
||
return {variant_type} {{ {enumeration.name}::{string_to_cpp_enum_name(enumeration_value)} }};
|
||
"""
|
||
enumeration_conversion += """ }
|
||
|
||
"""
|
||
append(enumeration_conversion)
|
||
|
||
# 15. If types includes a string type, then return the result of converting V to that type.
|
||
if types.string_type is not None:
|
||
string_value_name = f"{make_name_acceptable_cpp(identifier)}_string"
|
||
string_conversion_expression = conversion_for(types.string_type)
|
||
append(f"""
|
||
auto {string_value_name} = TRY({string_conversion_expression});
|
||
return {variant_type} {{ {string_value_name} }};
|
||
""")
|
||
|
||
# 16. If types includes a numeric type and bigint, then return the result of converting V to either that numeric type or bigint.
|
||
if types.numeric_type is not None and types.bigint_type is not None:
|
||
includes.add("LibJS/Runtime/BigInt.h")
|
||
includes.add("LibJS/Runtime/ValueInlines.h")
|
||
numeric_value_name = f"{make_name_acceptable_cpp(identifier)}_number"
|
||
numeric_conversion_expression = conversion_for(types.numeric_type, "x")
|
||
# https://webidl.spec.whatwg.org/#js-bigint
|
||
append(f"""
|
||
// An ECMAScript value V is converted to an IDL numeric type T or bigint value by running the following algorithm:
|
||
// 1. Let x be ? ToNumeric(V).
|
||
auto x = TRY({value_name}.to_numeric(vm));
|
||
|
||
// 2. If Type(x) is BigInt, then:
|
||
if (x.is_bigint())
|
||
// 1. Return the IDL bigint value that represents the same numeric value as x.
|
||
return {variant_type} {{ GC::Ref {{ x.as_bigint() }} }};
|
||
|
||
// 3. Assert: Type(x) is Number.
|
||
VERIFY(x.is_number());
|
||
|
||
// 4. Return the result of converting x to T.
|
||
auto {numeric_value_name}_fallback = TRY({numeric_conversion_expression});
|
||
return {variant_type} {{ {numeric_value_name}_fallback }};
|
||
""")
|
||
|
||
# 17. If types includes a numeric type, then return the result of converting V to that numeric type.
|
||
if types.numeric_type is not None:
|
||
numeric_value_name = f"{make_name_acceptable_cpp(identifier)}_number"
|
||
numeric_conversion_expression = conversion_for(types.numeric_type)
|
||
append(f"""
|
||
auto {numeric_value_name}_fallback = TRY({numeric_conversion_expression});
|
||
return {variant_type} {{ {numeric_value_name}_fallback }};
|
||
""")
|
||
|
||
# 18. If types includes boolean, then return the result of converting V to boolean.
|
||
if types.boolean_type is not None:
|
||
append(f"""
|
||
return {variant_type} {{ {value_name}.to_boolean() }};
|
||
""")
|
||
|
||
# 19. If types includes bigint, then return the result of converting V to bigint.
|
||
if types.bigint_type is not None:
|
||
append(
|
||
f"""
|
||
auto bigint_fallback = TRY({bigint_to_idl_value(value_name, includes)});
|
||
return {variant_type} {{ bigint_fallback }};
|
||
"""
|
||
)
|
||
|
||
# 20. Throw a TypeError.
|
||
includes.add("LibJS/Runtime/Error.h")
|
||
append("""
|
||
return vm.throw_completion<JS::TypeError>("No union types matched"_utf16);
|
||
""")
|
||
|
||
return f"""[&]() -> JS::ThrowCompletionOr<{variant_type}> {{
|
||
{"".join(sections)} }}()"""
|
||
|
||
|
||
def buffer_source_to_idl_value(
|
||
idl_type: IDLType,
|
||
extended_attributes: dict[str, str],
|
||
value_cpp_type_name: str,
|
||
value_name: str,
|
||
includes: GeneratedIncludes,
|
||
context: GenerationContext,
|
||
) -> str:
|
||
type_name = idl_type.name
|
||
|
||
includes.add("AK/TypeCasts.h")
|
||
includes.add("LibJS/Runtime/Error.h")
|
||
includes.add("LibJS/Runtime/Value.h")
|
||
add_buffer_source_type_include(idl_type, includes)
|
||
|
||
def array_buffer_checks(buffer_name: str) -> str:
|
||
allow_shared_check = ""
|
||
if "AllowShared" not in extended_attributes:
|
||
allow_shared_check = f""" // 2. If IsSharedArrayBuffer(V) is true, then throw a TypeError.
|
||
if ({buffer_name}->is_shared_array_buffer())
|
||
return vm.throw_completion<JS::TypeError>(JS::ErrorType::SharedArrayBuffer);
|
||
|
||
"""
|
||
|
||
allow_resizable_check = ""
|
||
if "AllowResizable" not in extended_attributes:
|
||
allow_resizable_check = f""" // 3. If the conversion is not to an IDL type associated with the [AllowResizable] extended attribute, and
|
||
// IsFixedLengthArrayBuffer(V) is false, then throw a TypeError.
|
||
if (!{buffer_name}->is_fixed_length())
|
||
return vm.throw_completion<JS::TypeError>(JS::ErrorType::NotAnObjectOfType, "fixed-length {type_name}");
|
||
|
||
"""
|
||
|
||
return f"{allow_shared_check}{allow_resizable_check}"
|
||
|
||
def array_buffer_view_checks(buffer_name: str) -> str:
|
||
allow_shared_check = ""
|
||
if "AllowShared" not in extended_attributes:
|
||
allow_shared_check = f""" // 2. If the conversion is not to an IDL type associated with the [AllowShared] extended attribute, and
|
||
// IsSharedArrayBuffer(V.[[ViewedArrayBuffer]]) is true, then throw a TypeError.
|
||
if ({buffer_name}.is_shared_array_buffer())
|
||
return vm.throw_completion<JS::TypeError>(JS::ErrorType::SharedArrayBuffer);
|
||
|
||
"""
|
||
|
||
allow_resizable_check = ""
|
||
if "AllowResizable" not in extended_attributes:
|
||
allow_resizable_check = f""" // 3. If the conversion is not to an IDL type associated with the [AllowResizable] extended attribute, and
|
||
// IsFixedLengthArrayBuffer(V.[[ViewedArrayBuffer]]) is false, then throw a TypeError.
|
||
if (!{buffer_name}.is_fixed_length())
|
||
return vm.throw_completion<JS::TypeError>(JS::ErrorType::NotAnObjectOfType, "fixed-length {type_name}");
|
||
|
||
"""
|
||
|
||
return f"{allow_shared_check}{allow_resizable_check}"
|
||
|
||
if type_name == "ArrayBuffer":
|
||
return f"""[&]() -> JS::ThrowCompletionOr<{value_cpp_type_name}> {{
|
||
// A JavaScript value V is converted to an IDL ArrayBuffer value by running the following algorithm:
|
||
// 1. If V is not an Object, or V does not have an [[ArrayBufferData]] internal slot, then throw a TypeError.
|
||
auto builtin_buffer = {value_name}.as_if<JS::ArrayBuffer>();
|
||
if (!builtin_buffer)
|
||
return vm.throw_completion<JS::TypeError>(JS::ErrorType::NotAnObjectOfType, "{type_name}");
|
||
|
||
{array_buffer_checks("builtin_buffer")} // 4. Return the IDL ArrayBuffer value that is a reference to the same object as V.
|
||
return GC::Ref {{ *builtin_buffer }};
|
||
}}()"""
|
||
|
||
if type_name == "DataView":
|
||
includes.add("LibJS/Runtime/DataView.h")
|
||
return f"""[&]() -> JS::ThrowCompletionOr<{value_cpp_type_name}> {{
|
||
// A JavaScript value V is converted to an IDL DataView value by running the following algorithm:
|
||
// 1. If V is not an Object, or V does not have a [[DataView]] internal slot, then throw a TypeError.
|
||
auto builtin_buffer = {value_name}.as_if<JS::DataView>();
|
||
if (!builtin_buffer)
|
||
return vm.throw_completion<JS::TypeError>(JS::ErrorType::NotAnObjectOfType, "{type_name}");
|
||
|
||
auto& viewed_array_buffer = *builtin_buffer->viewed_array_buffer();
|
||
|
||
{array_buffer_view_checks("viewed_array_buffer")} // 4. Return the IDL DataView value that is a reference to the same object as V.
|
||
return GC::Ref {{ *builtin_buffer }};
|
||
}}()"""
|
||
|
||
if is_typed_array_type(idl_type):
|
||
includes.add("LibJS/Runtime/TypedArray.h")
|
||
return f"""[&]() -> JS::ThrowCompletionOr<{value_cpp_type_name}> {{
|
||
// A JavaScript value V is converted to an IDL typed array value by running the following algorithm:
|
||
// 1. Let T be the IDL type V is being converted to.
|
||
// 2. If V is not an Object, or V does not have a [[TypedArrayName]] internal slot with a value equal to T's name,
|
||
// then throw a TypeError.
|
||
auto builtin_buffer = {value_name}.as_if<JS::{type_name}>();
|
||
if (!builtin_buffer)
|
||
return vm.throw_completion<JS::TypeError>(JS::ErrorType::NotAnObjectOfType, "{type_name}");
|
||
|
||
auto& viewed_array_buffer = *builtin_buffer->viewed_array_buffer();
|
||
|
||
{array_buffer_view_checks("viewed_array_buffer")} // 5. Return the IDL value of type T that is a reference to the same object as V.
|
||
return GC::Ref {{ *builtin_buffer }};
|
||
}}()"""
|
||
|
||
return unsupported_to_idl_value(idl_type)
|
||
|
||
|
||
def to_idl_value_from_type(
|
||
idl_type: IDLType,
|
||
identifier: str,
|
||
extended_attributes: dict[str, str],
|
||
value_name: str,
|
||
includes: GeneratedIncludes,
|
||
context: GenerationContext,
|
||
return_cpp_type: Optional[str] = None,
|
||
value_cpp_type_name: Optional[str] = None,
|
||
) -> str:
|
||
type_name = idl_type.name
|
||
return_cpp_type = return_cpp_type or cpp_type_for_idl_type(
|
||
idl_type, context, extended_attributes=extended_attributes
|
||
)
|
||
value_cpp_type_name = value_cpp_type_name or return_cpp_type
|
||
|
||
if idl_type.nullable:
|
||
return nullable_to_idl_value(
|
||
idl_type, identifier, extended_attributes, return_cpp_type, value_name, includes, context
|
||
)
|
||
|
||
if type_name == "any":
|
||
return any_to_idl_value(value_name, includes)
|
||
if type_name == "boolean":
|
||
return boolean_to_idl_value(value_name, includes)
|
||
if type_name == "byte":
|
||
return byte_to_idl_value(value_name, includes, extended_attributes)
|
||
if type_name == "octet":
|
||
return octet_to_idl_value(value_name, includes, extended_attributes)
|
||
if type_name == "short":
|
||
return short_to_idl_value(value_name, includes, extended_attributes)
|
||
if type_name == "unsigned short":
|
||
return unsigned_short_to_idl_value(value_name, includes, extended_attributes)
|
||
if type_name == "long":
|
||
return long_to_idl_value(value_name, includes, extended_attributes)
|
||
if type_name == "unsigned long":
|
||
return unsigned_long_to_idl_value(value_name, includes, extended_attributes)
|
||
if type_name == "long long":
|
||
return long_long_to_idl_value(value_name, includes, extended_attributes)
|
||
if type_name == "unsigned long long":
|
||
return unsigned_long_long_to_idl_value(value_name, includes, extended_attributes)
|
||
if type_name == "float":
|
||
return float_to_idl_value(value_name, includes, identifier)
|
||
if type_name == "unrestricted float":
|
||
return unrestricted_float_to_idl_value(value_name, includes)
|
||
if type_name == "double":
|
||
return double_to_idl_value(value_name, includes, identifier)
|
||
if type_name == "unrestricted double":
|
||
return unrestricted_double_to_idl_value(value_name, includes)
|
||
if type_name == "bigint":
|
||
return bigint_to_idl_value(value_name, includes)
|
||
if type_name in ("DOMString", "Utf16DOMString"):
|
||
return dom_string_to_idl_value(
|
||
value_name,
|
||
includes,
|
||
extended_attributes,
|
||
type_name,
|
||
)
|
||
if type_name == "ByteString":
|
||
return bytestring_to_idl_value(value_name, includes)
|
||
if type_name in ("USVString", "Utf16USVString"):
|
||
return usv_string_to_idl_value(
|
||
value_name,
|
||
includes,
|
||
extended_attributes,
|
||
type_name,
|
||
)
|
||
if type_name == "object":
|
||
return object_to_idl_value(value_name, includes)
|
||
if type_name == "symbol":
|
||
return symbol_to_idl_value(value_name, includes)
|
||
|
||
interface_like_type = interface_like_type_for_idl_type(idl_type, context)
|
||
if interface_like_type is not None:
|
||
return interface_to_idl_value(value_name, includes, interface_like_type)
|
||
|
||
interface = context.interface(idl_type)
|
||
if interface is not None and interface.is_callback_interface:
|
||
return callback_interface_to_idl_value(value_name, includes, interface)
|
||
|
||
if context.dictionary(idl_type) is not None:
|
||
return dictionary_to_idl_value(idl_type, value_name, includes, context)
|
||
|
||
if context.enumeration(idl_type) is not None:
|
||
return enumeration_to_idl_value(idl_type, value_name, includes, context)
|
||
|
||
callback_function = context.callback_function(idl_type)
|
||
if callback_function is not None:
|
||
return callback_function_to_idl_value(return_cpp_type, callback_function, value_name, includes)
|
||
|
||
if isinstance(idl_type, IDLParameterizedType) and type_name == "sequence":
|
||
return sequence_to_idl_value(idl_type, identifier, value_name, includes, context)
|
||
|
||
if isinstance(idl_type, IDLParameterizedType) and type_name == "async_sequence":
|
||
return async_sequence_to_idl_value()
|
||
|
||
if isinstance(idl_type, IDLParameterizedType) and type_name == "record":
|
||
return record_to_idl_value(idl_type, identifier, value_name, includes, context)
|
||
|
||
if isinstance(idl_type, IDLParameterizedType) and type_name == "Promise":
|
||
return promise_to_idl_value(value_name, includes)
|
||
|
||
if isinstance(idl_type, IDLUnionType):
|
||
return union_to_idl_value(idl_type, identifier, extended_attributes, value_name, includes, context)
|
||
|
||
if is_buffer_source_type(idl_type):
|
||
return buffer_source_to_idl_value(
|
||
idl_type, extended_attributes, value_cpp_type_name, value_name, includes, context
|
||
)
|
||
|
||
if isinstance(idl_type, IDLParameterizedType) and type_name == "FrozenArray":
|
||
return sequence_to_idl_value(idl_type, identifier, value_name, includes, context)
|
||
|
||
return unsupported_to_idl_value(idl_type)
|
||
|
||
|
||
def to_idl_value(
|
||
member: DictionaryMemberOrAttribute,
|
||
value_name: str,
|
||
includes: GeneratedIncludes,
|
||
context: GenerationContext,
|
||
) -> str:
|
||
return to_idl_value_from_type(
|
||
member.type,
|
||
member.name,
|
||
getattr(member, "extended_attributes", {}),
|
||
value_name,
|
||
includes,
|
||
context,
|
||
return_cpp_type=cpp_type(member, context),
|
||
value_cpp_type_name=cpp_value_type(member, context),
|
||
)
|
||
|
||
|
||
def type_check_idl_value(
|
||
idl_type: IDLType,
|
||
value_name: str,
|
||
includes: GeneratedIncludes,
|
||
context: GenerationContext,
|
||
interface_name: str,
|
||
) -> str:
|
||
if is_string_type(idl_type.name):
|
||
return f""" if (!{value_name}.is_string())
|
||
return vm.throw_completion<JS::TypeError>(JS::ErrorType::NotAnObjectOfType, "String");
|
||
"""
|
||
|
||
interface_like_type = interface_like_type_for_idl_type(idl_type, context)
|
||
if interface_like_type is None:
|
||
raise RuntimeError(f"Unsupported IDL value type '{idl_type.name}' on '{interface_name}'")
|
||
|
||
includes.add("AK/TypeCasts.h")
|
||
includes.add(interface_like_type.implementation_header)
|
||
return f""" if (!{value_name}.is<{interface_like_type.fully_qualified_name}>())
|
||
return vm.throw_completion<JS::TypeError>(JS::ErrorType::NotAnObjectOfType, "{interface_like_type.fully_qualified_name}");
|
||
"""
|