| # Copyright 2019 The Chromium Authors. All rights reserved. |
| # Use of this source code is governed by a BSD-style license that can be |
| # found in the LICENSE file. |
| |
| import itertools |
| |
| import web_idl |
| |
| from . import name_style |
| from .blink_v8_bridge import blink_class_name |
| from .blink_v8_bridge import blink_type_info |
| from .blink_v8_bridge import make_v8_to_blink_value |
| from .blink_v8_bridge import make_v8_to_blink_value_variadic |
| from .blink_v8_bridge import v8_bridge_class_name |
| from .code_node import EmptyNode |
| from .code_node import ListNode |
| from .code_node import SequenceNode |
| from .code_node import SymbolDefinitionNode |
| from .code_node import SymbolNode |
| from .code_node import SymbolScopeNode |
| from .code_node import TextNode |
| from .code_node import WeakDependencyNode |
| from .code_node_cxx import CxxBlockNode |
| from .code_node_cxx import CxxBreakableBlockNode |
| from .code_node_cxx import CxxClassDefNode |
| from .code_node_cxx import CxxFuncDeclNode |
| from .code_node_cxx import CxxFuncDefNode |
| from .code_node_cxx import CxxLikelyIfNode |
| from .code_node_cxx import CxxMultiBranchesNode |
| from .code_node_cxx import CxxNamespaceNode |
| from .code_node_cxx import CxxSwitchNode |
| from .code_node_cxx import CxxUnlikelyIfNode |
| from .codegen_accumulator import CodeGenAccumulator |
| from .codegen_context import CodeGenContext |
| from .codegen_expr import CodeGenExpr |
| from .codegen_expr import expr_and |
| from .codegen_expr import expr_from_exposure |
| from .codegen_expr import expr_or |
| from .codegen_format import format_template as _format |
| from .codegen_utils import component_export |
| from .codegen_utils import component_export_header |
| from .codegen_utils import enclose_with_header_guard |
| from .codegen_utils import make_copyright_header |
| from .codegen_utils import make_forward_declarations |
| from .codegen_utils import make_header_include_directives |
| from .codegen_utils import write_code_node_to_file |
| from .mako_renderer import MakoRenderer |
| from .package_initializer import package_initializer |
| from .path_manager import PathManager |
| from .task_queue import TaskQueue |
| |
| |
| def _is_none_or_str(arg): |
| return arg is None or isinstance(arg, str) |
| |
| |
| def backward_compatible_api_func(cg_context): |
| """ |
| Returns the Blink function name compatible with the old bindings generator. |
| """ |
| assert isinstance(cg_context, CodeGenContext) |
| |
| name = (cg_context.member_like.code_generator_info.property_implemented_as |
| or cg_context.member_like.identifier |
| or cg_context.property_.identifier) |
| |
| if cg_context.attribute_get: |
| # modules/webaudio/biquad_filter_node.idl has readonly attribute "Q" |
| # and somehow it's implemented as "q" in Blink. |
| if name == "Q": |
| name = "q" |
| |
| if cg_context.attribute_set: |
| tokens = name_style.raw.tokenize(name) |
| if tokens[0] in ("IDL", "css", "xml"): |
| tokens[0] = tokens[0].upper() |
| else: |
| tokens[0] = tokens[0].capitalize() |
| tokens.insert(0, "set") |
| name = "".join(tokens) |
| |
| if cg_context.indexed_property_getter and not name: |
| name = "AnonymousIndexedGetter" |
| if cg_context.indexed_property_setter and not name: |
| name = "AnonymousIndexedSetter" |
| if cg_context.named_property_getter and not name: |
| name = "AnonymousNamedGetter" |
| if cg_context.named_property_setter and not name: |
| name = "AnonymousNamedSetter" |
| if cg_context.named_property_deleter and not name: |
| name = "AnonymousNamedDeleter" |
| |
| return name |
| |
| |
| def callback_function_name(cg_context, |
| overload_index=None, |
| for_cross_origin=False, |
| no_alloc_direct_call=False): |
| assert isinstance(cg_context, CodeGenContext) |
| |
| def _cxx_name(name): |
| """ |
| Returns a property name that the bindings generator can use in |
| generated code. |
| |
| Note that Web IDL allows '-' (hyphen-minus) and '_' (low line) in |
| identifiers but C++ does not allow or recommend them. This function |
| encodes these characters. |
| """ |
| # In Python3, we can use str.maketrans and str.translate. |
| # |
| # We're optimistic about name conflict. It's highly unlikely that |
| # these replacements will cause a conflict. |
| assert "Dec45" not in name |
| assert "Dec95" not in name |
| name = name.replace("-", "Dec45") |
| name = name.replace("_", "Dec95") |
| return name |
| |
| if cg_context.constant: |
| property_name = cg_context.property_.identifier |
| else: |
| property_name = _cxx_name(cg_context.property_.identifier) |
| |
| if cg_context.attribute_get: |
| kind = "AttributeGet" |
| elif cg_context.attribute_set: |
| kind = "AttributeSet" |
| elif cg_context.constant: |
| kind = "Constant" |
| elif cg_context.constructor_group: |
| if cg_context.is_named_constructor: |
| kind = "NamedConstructor" |
| else: |
| property_name = "" |
| kind = "Constructor" |
| elif cg_context.exposed_construct: |
| if cg_context.is_named_constructor: |
| kind = "NamedConstructorProperty" |
| elif cg_context.legacy_window_alias: |
| kind = "LegacyWindowAlias" |
| else: |
| kind = "ExposedConstruct" |
| elif cg_context.operation_group: |
| kind = "Operation" |
| elif cg_context.stringifier: |
| kind = "Operation" |
| |
| if for_cross_origin: |
| suffix = "CrossOrigin" |
| elif overload_index is not None: |
| suffix = "Overload{}".format(overload_index + 1) |
| elif no_alloc_direct_call: |
| suffix = "NoAllocDirectCallback" |
| else: |
| suffix = "Callback" |
| |
| if cg_context.for_world == CodeGenContext.MAIN_WORLD: |
| world_suffix = "ForMainWorld" |
| elif cg_context.for_world == CodeGenContext.NON_MAIN_WORLDS: |
| world_suffix = "ForNonMainWorlds" |
| elif cg_context.for_world == CodeGenContext.ALL_WORLDS: |
| world_suffix = "" |
| |
| return name_style.func(property_name, kind, suffix, world_suffix) |
| |
| |
| def constant_name(cg_context): |
| assert isinstance(cg_context, CodeGenContext) |
| assert cg_context.constant |
| |
| property_name = cg_context.property_.identifier.lower() |
| |
| return name_style.constant(property_name) |
| |
| |
| def custom_function_name(cg_context): |
| assert isinstance(cg_context, CodeGenContext) |
| |
| if cg_context.named_property_getter: |
| return "NamedPropertyGetterCustom" |
| if cg_context.named_property_setter: |
| return "NamedPropertySetterCustom" |
| if cg_context.named_property_deleter: |
| return "NamedPropertyDeleterCustom" |
| |
| if cg_context.attribute_get: |
| suffix = "AttributeGetterCustom" |
| elif cg_context.attribute_set: |
| suffix = "AttributeSetterCustom" |
| elif cg_context.operation_group: |
| suffix = "MethodCustom" |
| else: |
| assert False |
| |
| return name_style.func(cg_context.property_.identifier, suffix) |
| |
| |
| # ---------------------------------------------------------------------------- |
| # Callback functions |
| # ---------------------------------------------------------------------------- |
| |
| |
| def bind_blink_api_arguments(code_node, cg_context): |
| assert isinstance(code_node, SymbolScopeNode) |
| assert isinstance(cg_context, CodeGenContext) |
| |
| if cg_context.attribute_get: |
| return |
| |
| if cg_context.attribute_set: |
| real_type = cg_context.attribute.idl_type.unwrap(typedef=True) |
| if real_type.is_enumeration: |
| pattern = """\ |
| // https://heycam.github.io/webidl/#dfn-attribute-setter |
| // step 4.6.1. Let S be ? ToString(V). |
| const auto&& arg1_value_string = |
| NativeValueTraits<IDLStringV2>::NativeValue( |
| ${isolate}, ${v8_property_value}, ${exception_state}); |
| if (${exception_state}.HadException()) |
| return; |
| // step 4.6.2. If S is not one of the enumeration's values, then return |
| // undefined. |
| const auto arg1_value_maybe_enum = {enum_type}::Create(arg1_value_string); |
| if (!arg1_value_maybe_enum) {{ |
| bindings::ReportInvalidEnumSetToAttribute( |
| ${isolate}, arg1_value_string, "{enum_type_name}", ${exception_state}); |
| return; // Return undefined. |
| }} |
| const auto ${arg1_value} = arg1_value_maybe_enum.value(); |
| """ |
| text = _format(pattern, |
| enum_type=blink_class_name( |
| real_type.type_definition_object), |
| enum_type_name=real_type.identifier) |
| code_node.register_code_symbol(SymbolNode("arg1_value", text)) |
| return |
| |
| name = "arg1_value" |
| v8_value = "${v8_property_value}" |
| code_node.register_code_symbol( |
| make_v8_to_blink_value(name, v8_value, |
| cg_context.attribute.idl_type)) |
| return |
| |
| for argument in cg_context.function_like.arguments: |
| name = name_style.arg_f("arg{}_{}", argument.index + 1, |
| argument.identifier) |
| if argument.is_variadic: |
| code_node.register_code_symbol( |
| make_v8_to_blink_value_variadic(name, "${info}", |
| argument.index, |
| argument.idl_type)) |
| else: |
| v8_value = "${{info}}[{}]".format(argument.index) |
| code_node.register_code_symbol( |
| make_v8_to_blink_value(name, |
| v8_value, |
| argument.idl_type, |
| argument=argument, |
| cg_context=cg_context)) |
| |
| |
| def bind_callback_local_vars(code_node, cg_context): |
| assert isinstance(code_node, SymbolScopeNode) |
| assert isinstance(cg_context, CodeGenContext) |
| |
| S = SymbolNode |
| T = TextNode |
| |
| local_vars = [] |
| template_vars = {} |
| |
| local_vars.extend([ |
| S("blink_property_name", |
| ("const AtomicString& ${blink_property_name} = " |
| "ToCoreAtomicString(${v8_property_name}.As<v8::String>());")), |
| S("class_like_name", ("const char* const ${class_like_name} = " |
| "\"${class_like.identifier}\";")), |
| S("current_context", ("v8::Local<v8::Context> ${current_context} = " |
| "${isolate}->GetCurrentContext();")), |
| S("current_script_state", ("ScriptState* ${current_script_state} = " |
| "ScriptState::From(${current_context});")), |
| S("isolate", "v8::Isolate* ${isolate} = ${info}.GetIsolate();"), |
| S("non_undefined_argument_length", |
| ("const int ${non_undefined_argument_length} = " |
| "bindings::NonUndefinedArgumentLength(${info});")), |
| S("per_context_data", ("V8PerContextData* ${per_context_data} = " |
| "${script_state}->PerContextData();")), |
| S("per_isolate_data", ("V8PerIsolateData* ${per_isolate_data} = " |
| "V8PerIsolateData::From(${isolate});")), |
| S("property_name", |
| "const char* const ${property_name} = \"${property.identifier}\";"), |
| S("receiver_context", ("v8::Local<v8::Context> ${receiver_context} = " |
| "${v8_receiver}->CreationContext();")), |
| S("receiver_script_state", |
| ("ScriptState* ${receiver_script_state} = " |
| "ScriptState::From(${receiver_context});")), |
| ]) |
| |
| is_receiver_context = not ( |
| (cg_context.member_like and cg_context.member_like.is_static) |
| or cg_context.constructor) |
| |
| # creation_context |
| pattern = "const v8::Local<v8::Context>& ${creation_context} = {_1};" |
| _1 = "${receiver_context}" if is_receiver_context else "${current_context}" |
| local_vars.append(S("creation_context", _format(pattern, _1=_1))) |
| |
| # creation_context_object |
| text = ("${v8_receiver}" |
| if is_receiver_context else "${current_context}->Global()") |
| template_vars["creation_context_object"] = T(text) |
| |
| # script_state |
| pattern = "ScriptState* ${script_state} = {_1};" |
| _1 = ("${receiver_script_state}" |
| if is_receiver_context else "${current_script_state}") |
| local_vars.append(S("script_state", _format(pattern, _1=_1))) |
| |
| # execution_context |
| pattern = "ExecutionContext* ${execution_context} = {_1};" |
| _1 = ("${receiver_execution_context}" |
| if is_receiver_context else "${current_execution_context}") |
| local_vars.append(S("execution_context", _format(pattern, _1=_1))) |
| node = S("current_execution_context", |
| ("ExecutionContext* ${current_execution_context} = " |
| "ExecutionContext::From(${current_context});")) |
| node.accumulate( |
| CodeGenAccumulator.require_include_headers([ |
| "third_party/blink/renderer/core/execution_context/execution_context.h" |
| ])) |
| local_vars.append(node) |
| node = S("receiver_execution_context", |
| ("ExecutionContext* ${receiver_execution_context} = " |
| "ExecutionContext::From(${receiver_context});")) |
| node.accumulate( |
| CodeGenAccumulator.require_include_headers([ |
| "third_party/blink/renderer/core/execution_context/execution_context.h" |
| ])) |
| local_vars.append(node) |
| |
| # execution_context_of_document_tree |
| pattern = "ExecutionContext* ${execution_context_of_document_tree} = {_1};" |
| if is_receiver_context: |
| _1 = "bindings::ExecutionContextFromV8Wrappable(${blink_receiver})" |
| else: |
| _1 = "${current_execution_context}" |
| text = _format(pattern, _1=_1) |
| local_vars.append(S("execution_context_of_document_tree", text)) |
| |
| # exception_state_context_type |
| pattern = ( |
| "const ExceptionState::ContextType ${exception_state_context_type} = " |
| "{_1};") |
| if cg_context.attribute_get: |
| _1 = "ExceptionState::kGetterContext" |
| elif cg_context.attribute_set: |
| _1 = "ExceptionState::kSetterContext" |
| elif cg_context.constructor_group: |
| _1 = "ExceptionState::kConstructionContext" |
| elif cg_context.indexed_property_getter: |
| _1 = "ExceptionState::kIndexedGetterContext" |
| elif cg_context.indexed_property_setter: |
| _1 = "ExceptionState::kIndexedSetterContext" |
| elif cg_context.named_property_getter: |
| _1 = "ExceptionState::kNamedGetterContext" |
| elif cg_context.named_property_setter: |
| _1 = "ExceptionState::kNamedSetterContext" |
| elif cg_context.named_property_deleter: |
| _1 = "ExceptionState::kNamedDeletionContext" |
| else: |
| _1 = "ExceptionState::kExecutionContext" |
| local_vars.append( |
| S("exception_state_context_type", _format(pattern, _1=_1))) |
| |
| # exception_state |
| pattern = "ExceptionState ${exception_state}({_1});{_2}" |
| _1 = ["${isolate}", "${exception_state_context_type}"] |
| if cg_context.is_named_constructor: |
| _1.append("\"{}\"".format(cg_context.property_.identifier)) |
| else: |
| _1.append("${class_like_name}") |
| if (cg_context.property_ and cg_context.property_.identifier |
| and not cg_context.constructor_group): |
| _1.append("${property_name}") |
| _2 = "" |
| if cg_context.is_return_type_promise_type: |
| _2 = ("\n" |
| "ExceptionToRejectPromiseScope reject_promise_scope" |
| "(${info}, ${exception_state});") |
| local_vars.append( |
| S("exception_state", _format(pattern, _1=", ".join(_1), _2=_2))) |
| |
| # blink_receiver |
| if cg_context.class_like.identifier == "Window": |
| # TODO(yukishiino): Window interface should be |
| # [ImplementedAs=LocalDOMWindow] instead of [ImplementedAs=DOMWindow], |
| # and [CrossOrigin] properties should be implemented specifically with |
| # DOMWindow class. Then, we'll have less hacks. |
| if (not cg_context.member_like or |
| "CrossOrigin" in cg_context.member_like.extended_attributes): |
| text = ("DOMWindow* ${blink_receiver} = " |
| "${class_name}::ToWrappableUnsafe(${v8_receiver});") |
| else: |
| text = ("LocalDOMWindow* ${blink_receiver} = To<LocalDOMWindow>(" |
| "${class_name}::ToWrappableUnsafe(${v8_receiver}));") |
| else: |
| pattern = ("{_1}* ${blink_receiver} = " |
| "${class_name}::ToWrappableUnsafe(${v8_receiver});") |
| _1 = blink_class_name(cg_context.class_like) |
| text = _format(pattern, _1=_1) |
| local_vars.append(S("blink_receiver", text)) |
| |
| # v8_property_value |
| if cg_context.v8_callback_type == CodeGenContext.V8_FUNCTION_CALLBACK: |
| # In case of V8_ACCESSOR_NAME_SETTER_CALLBACK, |v8_property_value| is |
| # defined as an argument. In case of V8_FUNCTION_CALLBACK (of IDL |
| # attribute set function), |info[0]| is the value to be set. |
| local_vars.append( |
| S("v8_property_value", |
| "v8::Local<v8::Value> ${v8_property_value} = ${info}[0];")) |
| |
| # v8_receiver |
| if cg_context.v8_callback_type == CodeGenContext.V8_FUNCTION_CALLBACK: |
| # In case of v8::FunctionCallbackInfo, This() is the receiver object. |
| local_vars.append( |
| S("v8_receiver", |
| "v8::Local<v8::Object> ${v8_receiver} = ${info}.This();")) |
| else: |
| # In case of v8::PropertyCallbackInfo, Holder() is the object that has |
| # the property being processed. |
| local_vars.append( |
| S("v8_receiver", |
| "v8::Local<v8::Object> ${v8_receiver} = ${info}.Holder();")) |
| |
| # throw_security_error |
| template_vars["throw_security_error"] = T( |
| "BindingSecurity::FailedAccessCheckFor(" |
| "${info}.GetIsolate(), " |
| "${class_name}::GetWrapperTypeInfo(), " |
| "${info}.Holder());") |
| |
| code_node.register_code_symbols(local_vars) |
| code_node.add_template_vars(template_vars) |
| |
| |
| def _make_reflect_content_attribute_key(code_node, cg_context): |
| assert isinstance(code_node, SymbolScopeNode) |
| assert isinstance(cg_context, CodeGenContext) |
| |
| name = (cg_context.attribute.extended_attributes.value_of("Reflect") |
| or cg_context.attribute.identifier.lower()) |
| if cg_context.attribute_get and name in ("class", "id", "name"): |
| return None |
| |
| if cg_context.class_like.identifier.startswith("SVG"): |
| namespace = "svg_names" |
| code_node.accumulate( |
| CodeGenAccumulator.require_include_headers( |
| ["third_party/blink/renderer/core/svg_names.h"])) |
| else: |
| namespace = "html_names" |
| code_node.accumulate( |
| CodeGenAccumulator.require_include_headers( |
| ["third_party/blink/renderer/core/html_names.h"])) |
| return "{}::{}".format(namespace, name_style.constant(name, "attr")) |
| |
| |
| def _make_reflect_accessor_func_name(cg_context): |
| assert isinstance(cg_context, CodeGenContext) |
| assert cg_context.attribute_get or cg_context.attribute_set |
| |
| if cg_context.attribute_get: |
| name = (cg_context.attribute.extended_attributes.value_of("Reflect") |
| or cg_context.attribute.identifier.lower()) |
| if name in ("class", "id", "name"): |
| return name_style.func("get", name, "attribute") |
| |
| if "URL" in cg_context.attribute.extended_attributes: |
| return "GetURLAttribute" |
| |
| FAST_ACCESSORS = { |
| "boolean": ("FastHasAttribute", "SetBooleanAttribute"), |
| "long": ("GetIntegralAttribute", "SetIntegralAttribute"), |
| "unsigned long": ("GetUnsignedIntegralAttribute", |
| "SetUnsignedIntegralAttribute"), |
| } |
| idl_type = cg_context.attribute.idl_type.unwrap() |
| accessors = FAST_ACCESSORS.get(idl_type.keyword_typename) |
| if accessors: |
| return accessors[0 if cg_context.attribute_get else 1] |
| |
| if (idl_type.is_interface |
| and idl_type.type_definition_object.does_implement("Element")): |
| if cg_context.attribute_get: |
| return "GetElementAttribute" |
| else: |
| return "SetElementAttribute" |
| |
| if idl_type.element_type: |
| element_type = idl_type.element_type.unwrap() |
| if (element_type.is_interface and |
| element_type.type_definition_object.does_implement("Element")): |
| if cg_context.attribute_get: |
| return "GetElementArrayAttribute" |
| else: |
| return "SetElementArrayAttribute" |
| |
| if cg_context.attribute_get: |
| return "FastGetAttribute" |
| else: |
| return "setAttribute" |
| |
| |
| def _make_reflect_process_keyword_state(cg_context): |
| # https://html.spec.whatwg.org/C/#keywords-and-enumerated-attributes |
| |
| assert isinstance(cg_context, CodeGenContext) |
| assert cg_context.attribute_get or cg_context.attribute_set |
| |
| T = TextNode |
| F = lambda *args, **kwargs: T(_format(*args, **kwargs)) |
| |
| if not cg_context.attribute_get: |
| return None |
| |
| ext_attrs = cg_context.attribute.extended_attributes |
| keywords = ext_attrs.values_of("ReflectOnly") |
| missing_default = ext_attrs.value_of("ReflectMissing") |
| empty_default = ext_attrs.value_of("ReflectEmpty") |
| invalid_default = ext_attrs.value_of("ReflectInvalid") |
| |
| def constant(keyword): |
| if not keyword: |
| return "g_empty_atom" |
| return "keywords::{}".format(name_style.constant(keyword)) |
| |
| branches = CxxMultiBranchesNode() |
| branches.accumulate( |
| CodeGenAccumulator.require_include_headers( |
| ["third_party/blink/renderer/core/keywords.h"])) |
| nodes = [ |
| T("// [ReflectOnly]"), |
| T("const AtomicString reflect_value(${return_value}.LowerASCII());"), |
| branches, |
| ] |
| |
| if missing_default is not None: |
| branches.append( |
| cond="reflect_value.IsNull()", |
| body=F("${return_value} = {};", constant(missing_default))) |
| elif cg_context.return_type.unwrap(nullable=False).is_nullable: |
| branches.append( |
| cond="reflect_value.IsNull()", |
| body=T("// Null string to IDL null.")) |
| |
| if empty_default is not None: |
| branches.append( |
| cond="reflect_value.IsEmpty()", |
| body=F("${return_value} = {};", constant(empty_default))) |
| |
| expr = " || ".join( |
| map(lambda keyword: "reflect_value == {}".format(constant(keyword)), |
| keywords)) |
| branches.append(cond=expr, body=T("${return_value} = reflect_value;")) |
| |
| if invalid_default is not None: |
| branches.append( |
| cond=True, |
| body=F("${return_value} = {};", constant(invalid_default))) |
| else: |
| branches.append( |
| cond=True, body=F("${return_value} = {};", constant(""))) |
| |
| return SequenceNode(nodes) |
| |
| |
| def _make_blink_api_call(code_node, |
| cg_context, |
| num_of_args=None, |
| overriding_args=None): |
| assert isinstance(code_node, SymbolScopeNode) |
| assert isinstance(cg_context, CodeGenContext) |
| assert num_of_args is None or isinstance(num_of_args, int) |
| assert (overriding_args is None |
| or (isinstance(overriding_args, (list, tuple)) |
| and all(isinstance(arg, str) for arg in overriding_args))) |
| |
| arguments = [] |
| ext_attrs = cg_context.member_like.extended_attributes |
| |
| values = ext_attrs.values_of("CallWith") + ( |
| ext_attrs.values_of("GetterCallWith") if cg_context.attribute_get else |
| ext_attrs.values_of("SetterCallWith") if cg_context.attribute_set else |
| ()) |
| if "Isolate" in values: |
| arguments.append("${isolate}") |
| if "ScriptState" in values: |
| arguments.append("${script_state}") |
| if "ExecutionContext" in values: |
| arguments.append("${execution_context}") |
| if "Document" in values: |
| arguments.append( |
| "*bindings::ToDocumentFromExecutionContext(${execution_context})") |
| if "ThisValue" in values: |
| arguments.append("ScriptValue(${isolate}, ${v8_receiver})") |
| |
| code_generator_info = cg_context.member_like.code_generator_info |
| is_partial = code_generator_info.defined_in_partial |
| if (is_partial and |
| not (cg_context.constructor or cg_context.member_like.is_static)): |
| arguments.append("*${blink_receiver}") |
| |
| if "Reflect" in ext_attrs: # [Reflect] |
| key = _make_reflect_content_attribute_key(code_node, cg_context) |
| if key: |
| arguments.append(key) |
| |
| if overriding_args is not None: |
| arguments.extend(overriding_args) |
| elif cg_context.attribute_get: |
| pass |
| elif cg_context.attribute_set: |
| arguments.append("${arg1_value}") |
| else: |
| for index, argument in enumerate(cg_context.function_like.arguments): |
| if num_of_args is not None and index == num_of_args: |
| break |
| name = name_style.arg_f("arg{}_{}", index + 1, argument.identifier) |
| arguments.append(_format("${{{}}}", name)) |
| |
| if cg_context.is_return_by_argument: |
| arguments.append("${return_value}") |
| |
| if cg_context.may_throw_exception: |
| arguments.append("${exception_state}") |
| |
| func_name = backward_compatible_api_func(cg_context) |
| if cg_context.constructor: |
| if cg_context.is_named_constructor: |
| func_name = "CreateForJSConstructor" |
| else: |
| func_name = "Create" |
| if "Reflect" in ext_attrs: # [Reflect] |
| func_name = _make_reflect_accessor_func_name(cg_context) |
| |
| if (cg_context.constructor or cg_context.member_like.is_static |
| or is_partial): |
| class_like = cg_context.member_like.owner_mixin or cg_context.class_like |
| class_name = (code_generator_info.receiver_implemented_as |
| or name_style.class_(class_like.identifier)) |
| func_designator = "{}::{}".format(class_name, func_name) |
| else: |
| func_designator = _format("${blink_receiver}->{}", func_name) |
| |
| return _format("{_1}({_2})", _1=func_designator, _2=", ".join(arguments)) |
| |
| |
| def bind_return_value(code_node, cg_context, overriding_args=None): |
| assert isinstance(code_node, SymbolScopeNode) |
| assert isinstance(cg_context, CodeGenContext) |
| assert (overriding_args is None |
| or (isinstance(overriding_args, (list, tuple)) |
| and all(isinstance(arg, str) for arg in overriding_args))) |
| |
| T = TextNode |
| F = lambda *args, **kwargs: T(_format(*args, **kwargs)) |
| |
| def create_definition(symbol_node): |
| api_calls = [] # Pairs of (num_of_args, api_call_text) |
| if overriding_args is None: |
| arguments = (cg_context.function_like.arguments |
| if cg_context.function_like else []) |
| for index, arg in enumerate(arguments): |
| if arg.is_optional and not arg.default_value: |
| api_calls.append((index, |
| _make_blink_api_call( |
| code_node, cg_context, index))) |
| api_calls.append((None, _make_blink_api_call( |
| code_node, cg_context))) |
| else: |
| api_calls.append((None, |
| _make_blink_api_call( |
| code_node, |
| cg_context, |
| overriding_args=overriding_args))) |
| |
| nodes = [] |
| is_return_type_void = ((not cg_context.return_type |
| or cg_context.return_type.unwrap().is_void) and |
| not cg_context.does_override_idl_return_type) |
| if not (is_return_type_void |
| or cg_context.does_override_idl_return_type): |
| return_type = blink_type_info(cg_context.return_type).value_t |
| if len(api_calls) == 1: |
| _, api_call = api_calls[0] |
| if is_return_type_void: |
| nodes.append(F("{};", api_call)) |
| elif cg_context.is_return_by_argument: |
| nodes.append(F("{} ${return_value};", return_type)) |
| nodes.append(F("{};", api_call)) |
| elif "ReflectOnly" in cg_context.member_like.extended_attributes: |
| # [ReflectOnly] |
| nodes.append(F("auto ${return_value} = {};", api_call)) |
| else: |
| nodes.append(F("auto&& ${return_value} = {};", api_call)) |
| else: |
| branches = SequenceNode() |
| for index, api_call in api_calls: |
| if is_return_type_void or cg_context.is_return_by_argument: |
| assignment = "{};".format(api_call) |
| else: |
| assignment = _format("${return_value} = {};", api_call) |
| if index is not None: |
| branches.append( |
| CxxLikelyIfNode( |
| cond=_format( |
| "${non_undefined_argument_length} <= {}", |
| index), |
| body=[ |
| T(assignment), |
| T("break;"), |
| ])) |
| else: |
| branches.append(T(assignment)) |
| |
| if not is_return_type_void: |
| nodes.append(F("{} ${return_value};", return_type)) |
| nodes.append(CxxBreakableBlockNode(branches)) |
| |
| if cg_context.may_throw_exception: |
| nodes.append( |
| CxxUnlikelyIfNode( |
| cond="${exception_state}.HadException()", |
| body=T("return;"))) |
| |
| if "ReflectOnly" in cg_context.member_like.extended_attributes: |
| # [ReflectOnly] |
| node = _make_reflect_process_keyword_state(cg_context) |
| if node: |
| nodes.append(EmptyNode()) |
| nodes.append(node) |
| |
| return SymbolDefinitionNode(symbol_node, nodes) |
| |
| code_node.register_code_symbol( |
| SymbolNode("return_value", definition_constructor=create_definition)) |
| |
| |
| def make_bindings_trace_event(cg_context): |
| assert isinstance(cg_context, CodeGenContext) |
| |
| event_name = "{}.{}".format(cg_context.class_like.identifier, |
| cg_context.property_.identifier) |
| if cg_context.attribute_get: |
| event_name = "{}.{}".format(event_name, "get") |
| elif cg_context.attribute_set: |
| event_name = "{}.{}".format(event_name, "set") |
| elif cg_context.constructor_group and not cg_context.is_named_constructor: |
| event_name = "{}.{}".format(cg_context.class_like.identifier, |
| "constructor") |
| |
| return TextNode("BLINK_BINDINGS_TRACE_EVENT(\"{}\");".format(event_name)) |
| |
| |
| def make_check_argument_length(cg_context): |
| assert isinstance(cg_context, CodeGenContext) |
| |
| T = TextNode |
| F = lambda *args, **kwargs: T(_format(*args, **kwargs)) |
| |
| if cg_context.v8_callback_type != CodeGenContext.V8_FUNCTION_CALLBACK: |
| return None |
| |
| if cg_context.attribute_get: |
| num_of_required_args = 0 |
| elif cg_context.attribute_set: |
| idl_type = cg_context.attribute.idl_type |
| if not (idl_type.does_include_nullable_or_dict |
| or idl_type.unwrap().is_any or "LegacyTreatNonObjectAsNull" in |
| idl_type.unwrap().extended_attributes |
| or "PutForwards" in cg_context.attribute.extended_attributes |
| or "Replaceable" in cg_context.attribute.extended_attributes): |
| # ES undefined in ${v8_property_value} will cause a TypeError |
| # anyway, so omit the check against the number of arguments. |
| return None |
| num_of_required_args = 1 |
| elif cg_context.function_like: |
| num_of_required_args = ( |
| cg_context.function_like.num_of_required_arguments) |
| elif isinstance(cg_context.property_, web_idl.OverloadGroup): |
| num_of_required_args = ( |
| cg_context.property_.min_num_of_required_arguments) |
| else: |
| assert False |
| |
| if num_of_required_args == 0: |
| return None |
| |
| return CxxUnlikelyIfNode( |
| cond=_format("UNLIKELY(${info}.Length() < {})", num_of_required_args), |
| body=[ |
| F(("${exception_state}.ThrowTypeError(" |
| "ExceptionMessages::NotEnoughArguments" |
| "({}, ${info}.Length()));"), num_of_required_args), |
| T("return;"), |
| ]) |
| |
| |
| def make_check_constructor_call(cg_context): |
| assert isinstance(cg_context, CodeGenContext) |
| |
| T = TextNode |
| |
| node = SequenceNode([ |
| CxxUnlikelyIfNode( |
| cond="!${info}.IsConstructCall()", |
| body=T("${exception_state}.ThrowTypeError(" |
| "ExceptionMessages::ConstructorCalledAsFunction());\n" |
| "return;")), |
| ]) |
| if not cg_context.is_named_constructor: |
| node.append( |
| CxxLikelyIfNode( |
| cond=("ConstructorMode::Current(${isolate}) == " |
| "ConstructorMode::kWrapExistingObject"), |
| body=T("bindings::V8SetReturnValue(${info}, ${v8_receiver});\n" |
| "return;"))) |
| node.accumulate( |
| CodeGenAccumulator.require_include_headers([ |
| "third_party/blink/renderer/platform/bindings/v8_object_constructor.h" |
| ])) |
| return node |
| |
| |
| def make_check_receiver(cg_context): |
| assert isinstance(cg_context, CodeGenContext) |
| |
| T = TextNode |
| |
| if cg_context.member_like.is_static: |
| return None |
| |
| if (cg_context.attribute and |
| "LegacyLenientThis" in cg_context.attribute.extended_attributes): |
| return SequenceNode([ |
| T("// [LegacyLenientThis]"), |
| CxxUnlikelyIfNode( |
| cond="!${class_name}::HasInstance(${isolate}, ${v8_receiver})", |
| body=T("return;")), |
| ]) |
| |
| if cg_context.is_return_type_promise_type: |
| return SequenceNode([ |
| T("// Promise returning function: " |
| "Convert a TypeError to a reject promise."), |
| CxxUnlikelyIfNode( |
| cond="!${class_name}::HasInstance(${isolate}, ${v8_receiver})", |
| body=[ |
| T("${exception_state}.ThrowTypeError(" |
| "\"Illegal invocation\");"), |
| T("return;"), |
| ]) |
| ]) |
| |
| return None |
| |
| |
| def make_check_security_of_return_value(cg_context): |
| assert isinstance(cg_context, CodeGenContext) |
| |
| T = TextNode |
| |
| check_security = cg_context.member_like.extended_attributes.value_of( |
| "CheckSecurity") |
| if check_security != "ReturnValue": |
| return None |
| |
| web_feature = _format( |
| "WebFeature::{}", |
| name_style.constant("CrossOrigin", cg_context.class_like.identifier, |
| cg_context.property_.identifier)) |
| use_counter = _format( |
| "UseCounter::Count(${current_execution_context}, {});", web_feature) |
| cond = T("!BindingSecurity::ShouldAllowAccessTo(" |
| "ToLocalDOMWindow(${current_context}), ${return_value}, " |
| "BindingSecurity::ErrorReportOption::kDoNotReport)") |
| body = [ |
| T(use_counter), |
| T("bindings::V8SetReturnValue(${info}, nullptr);\n" |
| "return;"), |
| ] |
| node = SequenceNode([ |
| T("// [CheckSecurity=ReturnValue]"), |
| CxxUnlikelyIfNode(cond=cond, body=body), |
| ]) |
| node.accumulate( |
| CodeGenAccumulator.require_include_headers([ |
| "third_party/blink/renderer/bindings/core/v8/binding_security.h", |
| "third_party/blink/renderer/core/frame/web_feature.h", |
| "third_party/blink/renderer/platform/instrumentation/use_counter.h", |
| ])) |
| return node |
| |
| |
| def make_cooperative_scheduling_safepoint(cg_context): |
| assert isinstance(cg_context, CodeGenContext) |
| |
| node = TextNode("scheduler::CooperativeSchedulingManager::Instance()" |
| "->Safepoint();") |
| node.accumulate( |
| CodeGenAccumulator.require_include_headers([ |
| "third_party/blink/renderer/platform/scheduler/public/cooperative_scheduling_manager.h" |
| ])) |
| return node |
| |
| |
| def make_log_activity(cg_context): |
| assert isinstance(cg_context, CodeGenContext) |
| |
| target = cg_context.member_like or cg_context.property_ |
| ext_attrs = target.extended_attributes |
| if "LogActivity" not in ext_attrs: |
| return None |
| target = ext_attrs.value_of("LogActivity") |
| if target: |
| assert target in ("GetterOnly", "SetterOnly") |
| if ((target == "GetterOnly" and not cg_context.attribute_get) |
| or (target == "SetterOnly" and not cg_context.attribute_set)): |
| return None |
| if (cg_context.for_world == cg_context.MAIN_WORLD |
| and "LogAllWorlds" not in ext_attrs): |
| return None |
| |
| pattern = "{_1}${per_context_data} && ${per_context_data}->ActivityLogger()" |
| _1 = "" |
| if (cg_context.attribute and "PerWorldBindings" not in ext_attrs |
| and "LogAllWorlds" not in ext_attrs): |
| _1 = "${script_state}->World().IsIsolatedWorld() && " |
| cond = _format(pattern, _1=_1) |
| |
| pattern = "${per_context_data}->ActivityLogger()->{_1}(\"{_2}.{_3}\"{_4});" |
| _2 = cg_context.class_like.identifier |
| _3 = cg_context.property_.identifier |
| if cg_context.attribute_get: |
| _1 = "LogGetter" |
| _4 = "" |
| elif cg_context.attribute_set: |
| _1 = "LogSetter" |
| _4 = ", ${v8_property_value}" |
| elif cg_context.operation_group: |
| _1 = "LogMethod" |
| _4 = ", ${info}" |
| body = _format(pattern, _1=_1, _2=_2, _3=_3, _4=_4) |
| |
| pattern = ("// [LogActivity], [LogAllWorlds]\n" "if ({_1}) {{ {_2} }}") |
| node = TextNode(_format(pattern, _1=cond, _2=body)) |
| node.accumulate( |
| CodeGenAccumulator.require_include_headers([ |
| "third_party/blink/renderer/platform/bindings/v8_dom_activity_logger.h", |
| "third_party/blink/renderer/platform/bindings/v8_per_context_data.h", |
| ])) |
| return node |
| |
| |
| def _make_overload_dispatcher_per_arg_size(cg_context, items): |
| """ |
| https://heycam.github.io/webidl/#dfn-overload-resolution-algorithm |
| |
| Args: |
| items: Partial list of an "effective overload set" with the same |
| type list size. |
| |
| Returns: |
| A pair of a resulting CodeNode and a boolean flag that is True if there |
| exists a case that overload resolution will fail, i.e. a bailout that |
| throws a TypeError is necessary. |
| """ |
| assert isinstance(cg_context, CodeGenContext) |
| assert isinstance(items, (list, tuple)) |
| assert all( |
| isinstance(item, web_idl.OverloadGroup.EffectiveOverloadItem) |
| for item in items) |
| |
| # Variables shared with nested functions |
| if len(items) > 1: |
| arg_index = web_idl.OverloadGroup.distinguishing_argument_index(items) |
| else: |
| arg_index = None |
| func_like = None |
| dispatcher_nodes = SequenceNode() |
| |
| # True if there exists a case that overload resolution will fail. |
| can_fail = True |
| |
| def find_test(item, test): |
| # |test| is a callable that takes (t, u) where: |
| # t = the idl_type (in the original form) |
| # u = the unwrapped version of t |
| idl_type = item.type_list[arg_index] |
| t = idl_type |
| u = idl_type.unwrap() |
| return test(t, u) or (u.is_union and any( |
| [test(m, m.unwrap()) for m in u.flattened_member_types])) |
| |
| def find(test): |
| for item in items: |
| if find_test(item, test): |
| return item.function_like |
| return None |
| |
| def find_all_interfaces(): |
| result = [] # [(func_like, idl_type), ...] |
| for item in items: |
| idl_type = item.type_list[arg_index].unwrap() |
| if idl_type.is_interface: |
| result.append((item.function_like, idl_type)) |
| if idl_type.is_union: |
| for member_type in idl_type.flattened_member_types: |
| if member_type.unwrap().is_interface: |
| result.append((item.function_like, |
| member_type.unwrap())) |
| return result |
| |
| def make_node(pattern): |
| value = _format("${info}[{}]", arg_index) |
| func_name = callback_function_name(cg_context, |
| func_like.overload_index) |
| return TextNode(_format(pattern, value=value, func_name=func_name)) |
| |
| def dispatch_if(expr): |
| if expr is True: |
| pattern = "return {func_name}(${info});" |
| else: |
| pattern = ("if (" + expr + ") {{\n" |
| " return {func_name}(${info});\n" |
| "}}") |
| node = make_node(pattern) |
| conditional = expr_from_exposure(func_like.exposure) |
| if not conditional.is_always_true: |
| node = CxxUnlikelyIfNode(cond=conditional, body=node) |
| dispatcher_nodes.append(node) |
| return expr is True and conditional.is_always_true |
| |
| if len(items) == 1: |
| func_like = items[0].function_like |
| can_fail = False |
| return make_node("return {func_name}(${info});"), can_fail |
| |
| # 12.2. If V is undefined, ... |
| func_like = find(lambda t, u: t.is_optional) |
| if func_like: |
| dispatch_if("{value}->IsUndefined()") |
| |
| # 12.3. if V is null or undefined, ... |
| func_like = find(lambda t, u: t.does_include_nullable_or_dict) |
| if func_like: |
| dispatch_if("{value}->IsNullOrUndefined()") |
| |
| # 12.4. if V is a platform object, ... |
| def inheritance_length(func_and_type): |
| return (len(func_and_type[1].type_definition_object. |
| inclusive_inherited_interfaces), |
| func_and_type[1].type_definition_object.identifier) |
| |
| # Attempt to match from most derived to least derived. |
| for func_like, idl_type in sorted( |
| find_all_interfaces(), key=inheritance_length, reverse=True): |
| v8_bridge_name = v8_bridge_class_name( |
| idl_type.unwrap().type_definition_object) |
| dispatch_if( |
| _format("{}::HasInstance(${isolate}, {value})", v8_bridge_name)) |
| |
| # V8 specific optimization: BufferSource = ArrayBufferView or ArrayBuffer |
| is_typedef_name = lambda t, name: t.is_typedef and t.identifier == name |
| func_like = find( |
| lambda t, u: is_typedef_name(t.unwrap(typedef=False), "BufferSource")) |
| if func_like: |
| dispatch_if("{value}->IsArrayBufferView() || " |
| "{value}->IsArrayBuffer() || " |
| "{value}->IsSharedArrayBuffer()") |
| else: |
| # 12.5. if Type(V) is Object, V has an [[ArrayBufferData]] internal |
| # slot, ... |
| func_like = find(lambda t, u: u.is_array_buffer) |
| if func_like: |
| dispatch_if("{value}->IsArrayBuffer() || " |
| "{value}->IsSharedArrayBuffer()") |
| |
| # V8 specific optimization: ArrayBufferView |
| func_like = find(lambda t, u: u.is_array_buffer_view) |
| if func_like: |
| dispatch_if("{value}->IsArrayBufferView()") |
| |
| # 12.6. if Type(V) is Object, V has a [[DataView]] internal slot, ... |
| func_like = find(lambda t, u: u.is_data_view) |
| if func_like: |
| dispatch_if("{value}->IsDataView()") |
| |
| # 12.7. if Type(V) is Object, V has a [[TypedArrayName]] internal slot, ... |
| typed_array_types = ("Int8Array", "Int16Array", "Int32Array", "Uint8Array", |
| "Uint16Array", "Uint32Array", "Uint8ClampedArray", |
| "Float32Array", "Float64Array") |
| for typed_array_type in typed_array_types: |
| func_like = find(lambda t, u: u.keyword_typename == typed_array_type) |
| if func_like: |
| dispatch_if(_format("{value}->Is{}()", typed_array_type)) |
| |
| # 12.8. if IsCallable(V) is true, ... |
| func_like = find(lambda t, u: u.is_callback_function) |
| if func_like: |
| dispatch_if("{value}->IsFunction()") |
| |
| # 12.9. if Type(V) is Object and ... @@iterator ... |
| func_like = find(lambda t, u: u.is_sequence or u.is_frozen_array) |
| if func_like: |
| dispatch_if("{value}->IsArray() || " # Excessive optimization |
| "bindings::IsEsIterableObject" |
| "(${isolate}, {value}, ${exception_state})") |
| dispatcher_nodes.append( |
| CxxUnlikelyIfNode(cond="${exception_state}.HadException()", |
| body=TextNode("return;"))) |
| |
| # 12.10. if Type(V) is Object and ... |
| func_like = find(lambda t, u: u.is_callback_interface or u.is_dictionary or |
| u.is_record or u.is_object) |
| if func_like: |
| dispatch_if("{value}->IsObject()") |
| |
| # 12.11. if Type(V) is Boolean and ... |
| func_like = find(lambda t, u: u.is_boolean) |
| if func_like: |
| dispatch_if("{value}->IsBoolean()") |
| |
| # 12.12. if Type(V) is Number and ... |
| func_like = find(lambda t, u: u.is_numeric) |
| if func_like: |
| dispatch_if("{value}->IsNumber()") |
| |
| # 12.13. if there is an entry in S that has ... a string type ... |
| # 12.14. if there is an entry in S that has ... a numeric type ... |
| # 12.15. if there is an entry in S that has ... boolean ... |
| # 12.16. if there is an entry in S that has any ... |
| func_likes = [ |
| find(lambda t, u: u.is_enumeration), |
| find(lambda t, u: u.is_string), |
| find(lambda t, u: u.is_numeric), |
| find(lambda t, u: u.is_boolean), |
| find(lambda t, u: u.is_any), |
| ] |
| for func_like in func_likes: |
| if func_like: |
| if dispatch_if(True): |
| can_fail = False |
| break |
| |
| return dispatcher_nodes, can_fail |
| |
| |
| def make_overload_dispatcher(cg_context): |
| # https://heycam.github.io/webidl/#dfn-overload-resolution-algorithm |
| |
| assert isinstance(cg_context, CodeGenContext) |
| |
| T = TextNode |
| F = lambda *args, **kwargs: T(_format(*args, **kwargs)) |
| |
| overload_group = cg_context.property_ |
| items = overload_group.effective_overload_set() |
| args_size = lambda item: len(item.type_list) |
| items_grouped_by_arg_size = itertools.groupby( |
| sorted(items, key=args_size, reverse=True), key=args_size) |
| |
| # TODO(yukishiino): Runtime-enabled features should be taken into account |
| # when calculating the max argument size. |
| max_arg_size = max(map(args_size, items)) |
| arg_count_def = F("const int arg_count = std::min(${info}.Length(), {});", |
| max_arg_size) |
| |
| branches = SequenceNode() |
| did_use_break = False |
| for arg_size, items in items_grouped_by_arg_size: |
| items = list(items) |
| |
| node, can_fail = _make_overload_dispatcher_per_arg_size( |
| cg_context, items) |
| |
| if arg_size > 0: |
| node = CxxLikelyIfNode( |
| cond="arg_count == {}".format(arg_size), |
| body=[node, T("break;") if can_fail else None]) |
| did_use_break = did_use_break or can_fail |
| |
| conditional = expr_or( |
| list( |
| map( |
| lambda item: expr_from_exposure(item.function_like.exposure |
| ), items))) |
| if not conditional.is_always_true: |
| node = CxxUnlikelyIfNode(cond=conditional, body=node) |
| |
| branches.append(node) |
| |
| if did_use_break: |
| branches = CxxBreakableBlockNode(branches) |
| branches = SequenceNode([ |
| arg_count_def, |
| branches, |
| ]) |
| |
| if not did_use_break and arg_size == 0 and conditional.is_always_true: |
| return branches |
| |
| return SequenceNode([ |
| branches, |
| EmptyNode(), |
| make_check_argument_length(cg_context), |
| T("${exception_state}.ThrowTypeError" |
| "(\"Overload resolution failed.\");\n" |
| "return;"), |
| ]) |
| |
| |
| def make_report_coop_access(cg_context): |
| assert isinstance(cg_context, CodeGenContext) |
| |
| if cg_context.class_like.identifier != "Window": |
| return None |
| |
| ext_attrs = cg_context.member_like.extended_attributes |
| if "CrossOrigin" not in ext_attrs: |
| return None |
| |
| values = ext_attrs.values_of("CrossOrigin") |
| if (cg_context.attribute_get and not (not values or "Getter" in values)): |
| return None |
| elif (cg_context.attribute_set and not ("Setter" in values)): |
| return None |
| |
| return TextNode("${blink_receiver}->ReportCoopAccess(${property_name});") |
| |
| |
| def make_report_deprecate_as(cg_context): |
| assert isinstance(cg_context, CodeGenContext) |
| |
| target = cg_context.member_like or cg_context.property_ |
| name = target.extended_attributes.value_of("DeprecateAs") |
| if not name: |
| return None |
| |
| pattern = ("// [DeprecateAs]\n" |
| "Deprecation::CountDeprecation(" |
| "${current_execution_context}, WebFeature::k{_1});") |
| _1 = name |
| node = TextNode(_format(pattern, _1=_1)) |
| node.accumulate( |
| CodeGenAccumulator.require_include_headers( |
| ["third_party/blink/renderer/core/frame/deprecation.h"])) |
| return node |
| |
| |
| def _make_measure_web_feature_constant(cg_context): |
| assert isinstance(cg_context, CodeGenContext) |
| |
| target = cg_context.member_like or cg_context.property_ |
| ext_attrs = target.extended_attributes |
| |
| suffix = "" |
| if cg_context.attribute_get: |
| suffix = "_AttributeGetter" |
| elif cg_context.attribute_set: |
| suffix = "_AttributeSetter" |
| elif cg_context.constructor: |
| suffix = "_Constructor" |
| elif cg_context.exposed_construct: |
| suffix = "_ConstructorGetter" |
| elif cg_context.operation: |
| suffix = "_Method" |
| |
| name = ext_attrs.value_of("MeasureAs") or ext_attrs.value_of("Measure") |
| if name: |
| name = "k{}".format(name) |
| elif cg_context.constructor: |
| name = "kV8{}{}".format(cg_context.class_like.identifier, suffix) |
| else: |
| name = "kV8{}_{}{}".format( |
| cg_context.class_like.identifier, |
| name_style.raw.upper_camel_case(cg_context.property_.identifier), |
| suffix) |
| |
| return "WebFeature::{}".format(name) |
| |
| |
| def make_report_high_entropy(cg_context): |
| assert isinstance(cg_context, CodeGenContext) |
| |
| target = cg_context.member_like or cg_context.property_ |
| ext_attrs = target.extended_attributes |
| if cg_context.attribute_set or "HighEntropy" not in ext_attrs: |
| return None |
| assert "Measure" in ext_attrs or "MeasureAs" in ext_attrs, "{}: {}".format( |
| cg_context.idl_location_and_name, |
| "[HighEntropy] must be specified with either [Measure] or " |
| "[MeasureAs].") |
| |
| if ext_attrs.value_of("HighEntropy") == "Direct": |
| text = _format( |
| "// [HighEntropy=Direct]\n" |
| "Dactyloscoper::RecordDirectSurface(" |
| "${current_execution_context}, {measure_constant}, " |
| "${return_value});", |
| measure_constant=_make_measure_web_feature_constant(cg_context)) |
| else: |
| text = _format( |
| "// [HighEntropy]\n" |
| "Dactyloscoper::Record(" |
| "${current_execution_context}, {measure_constant});", |
| measure_constant=_make_measure_web_feature_constant(cg_context)) |
| node = TextNode(text) |
| node.accumulate( |
| CodeGenAccumulator.require_include_headers( |
| ["third_party/blink/renderer/core/frame/dactyloscoper.h"])) |
| return node |
| |
| |
| def make_report_measure_as(cg_context): |
| assert isinstance(cg_context, CodeGenContext) |
| |
| target = cg_context.member_like or cg_context.property_ |
| ext_attrs = target.extended_attributes |
| if not ("Measure" in ext_attrs or "MeasureAs" in ext_attrs): |
| return None |
| |
| text = _format( |
| "// [Measure], [MeasureAs]\n" |
| "UseCounter::Count(${current_execution_context}, {measure_constant});", |
| measure_constant=_make_measure_web_feature_constant(cg_context)) |
| node = TextNode(text) |
| node.accumulate( |
| CodeGenAccumulator.require_include_headers([ |
| "third_party/blink/renderer/core/frame/web_feature.h", |
| "third_party/blink/renderer/platform/instrumentation/use_counter.h", |
| ])) |
| return node |
| |
| |
| def make_return_value_cache_return_early(cg_context): |
| assert isinstance(cg_context, CodeGenContext) |
| |
| pred = cg_context.member_like.extended_attributes.value_of( |
| "CachedAttribute") |
| if pred: |
| return TextNode("""\ |
| // [CachedAttribute] |
| static const V8PrivateProperty::SymbolKey kPrivatePropertyCachedAttribute; |
| auto&& v8_private_cached_attribute = |
| V8PrivateProperty::GetSymbol(${isolate}, kPrivatePropertyCachedAttribute); |
| if (!${blink_receiver}->""" + pred + """()) { |
| v8::Local<v8::Value> v8_value; |
| if (!v8_private_cached_attribute.GetOrUndefined(${v8_receiver}) |
| .ToLocal(&v8_value)) { |
| return; |
| } |
| if (!v8_value->IsUndefined()) { |
| bindings::V8SetReturnValue(${info}, v8_value); |
| return; |
| } |
| }""") |
| |
| if "SaveSameObject" in cg_context.member_like.extended_attributes: |
| return TextNode("""\ |
| // [SaveSameObject] |
| static const V8PrivateProperty::SymbolKey kPrivatePropertySaveSameObject; |
| auto&& v8_private_save_same_object = |
| V8PrivateProperty::GetSymbol(${isolate}, kPrivatePropertySaveSameObject); |
| { |
| v8::Local<v8::Value> v8_value; |
| if (!v8_private_save_same_object.GetOrUndefined(${v8_receiver}) |
| .ToLocal(&v8_value)) { |
| return; |
| } |
| if (!v8_value->IsUndefined()) { |
| bindings::V8SetReturnValue(${info}, v8_value); |
| return; |
| } |
| }""") |
| |
| |
| def make_return_value_cache_update_value(cg_context): |
| assert isinstance(cg_context, CodeGenContext) |
| |
| if "CachedAttribute" in cg_context.member_like.extended_attributes: |
| return TextNode("// [CachedAttribute]\n" |
| "v8_private_cached_attribute.Set" |
| "(${v8_receiver}, ${info}.GetReturnValue().Get());") |
| |
| if "SaveSameObject" in cg_context.member_like.extended_attributes: |
| return TextNode("// [SaveSameObject]\n" |
| "v8_private_save_same_object.Set" |
| "(${v8_receiver}, ${info}.GetReturnValue().Get());") |
| |
| |
| def make_runtime_call_timer_scope(cg_context, overriding_name=None): |
| assert isinstance(cg_context, CodeGenContext) |
| assert _is_none_or_str(overriding_name) |
| |
| target = cg_context.member_like or cg_context.property_ |
| |
| suffix = "" |
| if cg_context.attribute_get: |
| suffix = "_Getter" |
| elif cg_context.attribute_set: |
| suffix = "_Setter" |
| elif cg_context.exposed_construct: |
| suffix = "_ConstructorGetterCallback" |
| |
| counter = (target and |
| target.extended_attributes.value_of("RuntimeCallStatsCounter")) |
| if counter: |
| macro_name = "RUNTIME_CALL_TIMER_SCOPE" |
| counter_name = "RuntimeCallStats::CounterId::k{}{}".format( |
| counter, suffix) |
| else: |
| macro_name = "RUNTIME_CALL_TIMER_SCOPE_DISABLED_BY_DEFAULT" |
| counter_name = "\"Blink_{}_{}{}\"".format( |
| blink_class_name(cg_context.class_like), overriding_name |
| or target.identifier, suffix) |
| |
| return TextNode( |
| _format( |
| "{macro_name}(${info}.GetIsolate(), {counter_name});", |
| macro_name=macro_name, |
| counter_name=counter_name)) |
| |
| |
| def make_steps_of_ce_reactions(cg_context): |
| assert isinstance(cg_context, CodeGenContext) |
| assert (cg_context.attribute_set or cg_context.operation |
| or cg_context.indexed_property_setter |
| or cg_context.named_property_setter |
| or cg_context.named_property_deleter) |
| |
| if "CEReactions" not in cg_context.member_like.extended_attributes: |
| return None |
| |
| nodes = [ |
| TextNode("// [CEReactions]"), |
| TextNode("CEReactionsScope ce_reactions_scope;"), |
| ] |
| |
| nodes[-1].accumulate( |
| CodeGenAccumulator.require_include_headers([ |
| "third_party/blink/renderer/core/html/custom/ce_reactions_scope.h" |
| ])) |
| |
| # CEReactions scope is not tolerant of V8 exception, so it's necessary to |
| # invoke custom element reactions before throwing an exception. Thus, put |
| # an ExceptionState before CEReactions scope. |
| nodes.insert(0, WeakDependencyNode(dep_syms=["exception_state"])) |
| |
| return SequenceNode(nodes) |
| |
| |
| def make_steps_of_put_forwards(cg_context): |
| assert isinstance(cg_context, CodeGenContext) |
| |
| T = TextNode |
| |
| return SequenceNode([ |
| T("// [PutForwards]"), |
| T("v8::Local<v8::Value> target;"), |
| T("if (!${v8_receiver}->Get(${current_context}, " |
| "V8AtomicString(${isolate}, ${property_name}))" |
| ".ToLocal(&target)) {\n" |
| " return;\n" |
| "}"), |
| CxxUnlikelyIfNode( |
| cond="!target->IsObject()", |
| body=[ |
| T("${exception_state}.ThrowTypeError(" |
| "\"The attribute value is not an object\");"), |
| T("return;"), |
| ]), |
| T("bool did_set;"), |
| T("if (!target.As<v8::Object>()->Set(${current_context}, " |
| "V8AtomicString(${isolate}, " |
| "\"${attribute.extended_attributes.value_of(\"PutForwards\")}\"" |
| "), ${v8_property_value}).To(&did_set)) {{\n" |
| " return;\n" |
| "}}"), |
| ]) |
| |
| |
| def make_steps_of_replaceable(cg_context): |
| assert isinstance(cg_context, CodeGenContext) |
| |
| T = TextNode |
| |
| return SequenceNode([ |
| T("// [Replaceable]"), |
| T("bool did_create;"), |
| T("if (!${v8_receiver}->CreateDataProperty(${current_context}, " |
| "V8AtomicString(${isolate}, ${property_name}), " |
| "${v8_property_value}).To(&did_create)) {\n" |
| " return;\n" |
| "}"), |
| ]) |
| |
| |
| def make_v8_set_return_value(cg_context): |
| assert isinstance(cg_context, CodeGenContext) |
| |
| T = TextNode |
| F = lambda *args, **kwargs: T(_format(*args, **kwargs)) |
| |
| if cg_context.does_override_idl_return_type: |
| return T("bindings::V8SetReturnValue(${info}, ${return_value});") |
| |
| if not cg_context.return_type or cg_context.return_type.unwrap().is_void: |
| # Request a SymbolNode |return_value| to define itself without |
| # rendering any text. |
| return T("<% return_value.request_symbol_definition() %>") |
| |
| operation = cg_context.operation |
| if operation and (operation.is_setter or operation.is_deleter): |
| # Blink implementation returns in a type different from the IDL type. |
| # Namely, IndexedPropertySetterResult, NamedPropertySetterResult, and |
| # NamedPropertyDeleterResult are returned ignoring the operation's |
| # return type. |
| return T("bindings::V8SetReturnValue(${info}, ${return_value});") |
| |
| return_type = cg_context.return_type |
| if return_type.is_typedef: |
| if return_type.identifier in ("EventHandler", |
| "OnBeforeUnloadEventHandler", |
| "OnErrorEventHandler"): |
| return T("bindings::V8SetReturnValue(${info}, ${return_value}, " |
| "${isolate}, ${blink_receiver});") |
| |
| # [CheckSecurity=ReturnValue] |
| # |
| # The returned object must be wrapped in its own realm instead of the |
| # receiver object's relevant realm or the current realm. |
| # |
| # [CheckSecurity=ReturnValue] is used only for 'contentDocument' attribute |
| # and 'getSVGDocument' operation of HTML{IFrame,Frame,Object,Embed}Element |
| # interfaces, and Window.frameElement attribute, so far. |
| # |
| # All the interfaces above except for Window support 'contentWindow' |
| # attribute and that's the global object of the creation context of the |
| # returned V8 wrapper. Window.frameElement is implemented with [Custom] |
| # for now and there is no need to support it. |
| # |
| # Note that the global object has its own context and there is no need to |
| # pass the creation context to ToV8. |
| if (cg_context.member_like.extended_attributes.value_of("CheckSecurity") == |
| "ReturnValue"): |
| return T("""\ |
| // [CheckSecurity=ReturnValue] |
| bindings::V8SetReturnValue( |
| ${info}, |
| ToV8(${return_value}, |
| ToV8(${blink_receiver}->contentWindow(), |
| v8::Local<v8::Object>(), |
| ${isolate}).As<v8::Object>(), |
| ${isolate}));\ |
| """) |
| |
| return_type = return_type.unwrap(typedef=True) |
| return_type_body = return_type.unwrap() |
| |
| PRIMITIVE_TYPE_TO_CXX_TYPE = { |
| "boolean": "bool", |
| "byte": "int8_t", |
| "octet": "uint8_t", |
| "short": "int16_t", |
| "unsigned short": "uint16_t", |
| "long": "int32_t", |
| "unsigned long": "uint32_t", |
| "long long": "int64_t", |
| "unsigned long long": "uint64_t", |
| "float": "float", |
| "unrestricted float": "float", |
| "double": "double", |
| "unrestricted double": "double", |
| } |
| cxx_type = PRIMITIVE_TYPE_TO_CXX_TYPE.get( |
| return_type_body.keyword_typename) |
| if cxx_type: |
| return F( |
| "bindings::V8SetReturnValue(${info}, ${return_value}, " |
| "bindings::V8ReturnValue::PrimitiveType<{cxx_type}>());", |
| cxx_type=cxx_type) |
| |
| # TODO(yukishiino): Remove |return_type_body.is_enumeration| below once |
| # the migration from String to V8Enum type is done. |
| if return_type_body.is_string or return_type_body.is_enumeration: |
| args = ["${info}", "${return_value}", "${isolate}"] |
| if return_type.is_nullable: |
| args.append("bindings::V8ReturnValue::kNullable") |
| else: |
| args.append("bindings::V8ReturnValue::kNonNullable") |
| return T("bindings::V8SetReturnValue({});".format(", ".join(args))) |
| |
| if return_type_body.is_interface: |
| args = ["${info}", "${return_value}"] |
| if cg_context.for_world == cg_context.MAIN_WORLD: |
| args.append("bindings::V8ReturnValue::kMainWorld") |
| elif cg_context.constructor or cg_context.member_like.is_static: |
| args.append("${creation_context}") |
| else: |
| args.append("${blink_receiver}") |
| return T("bindings::V8SetReturnValue({});".format(", ".join(args))) |
| |
| if return_type.is_frozen_array: |
| return T( |
| "bindings::V8SetReturnValue(" |
| "${info}, " |
| "ToV8(${return_value}, ${creation_context_object}, ${isolate}), " |
| "bindings::V8ReturnValue::kFrozen);") |
| |
| if return_type.is_promise: |
| return T("bindings::V8SetReturnValue" |
| "(${info}, ${return_value}.V8Value());") |
| |
| return T("bindings::V8SetReturnValue(${info}, " |
| "ToV8(${return_value}, ${creation_context_object}, ${isolate}));") |
| |
| |
| def _make_empty_callback_def(cg_context, function_name): |
| assert isinstance(cg_context, CodeGenContext) |
| assert isinstance(function_name, str) |
| |
| if cg_context.v8_callback_type == CodeGenContext.V8_FUNCTION_CALLBACK: |
| arg_decls = ["const v8::FunctionCallbackInfo<v8::Value>& info"] |
| arg_names = ["info"] |
| elif (cg_context.v8_callback_type == CodeGenContext. |
| V8_ACCESSOR_NAME_GETTER_CALLBACK): |
| arg_decls = [ |
| "v8::Local<v8::Name> v8_property_name", |
| "const v8::PropertyCallbackInfo<v8::Value>& info", |
| ] |
| arg_names = ["v8_property_name", "info"] |
| elif (cg_context.v8_callback_type == CodeGenContext. |
| V8_ACCESSOR_NAME_SETTER_CALLBACK): |
| arg_decls = [ |
| "v8::Local<v8::Name> v8_property_name", |
| "v8::Local<v8::Value> v8_property_value", |
| "const v8::PropertyCallbackInfo<void>& info", |
| ] |
| arg_names = ["v8_property_name", "v8_property_value", "info"] |
| elif (cg_context.v8_callback_type == CodeGenContext. |
| V8_GENERIC_NAMED_PROPERTY_SETTER_CALLBACK): |
| arg_decls = [ |
| "v8::Local<v8::Name> v8_property_name", |
| "v8::Local<v8::Value> v8_property_value", |
| "const v8::PropertyCallbackInfo<v8::Value>& info", |
| ] |
| arg_names = ["v8_property_name", "v8_property_value", "info"] |
| |
| func_def = CxxFuncDefNode( |
| name=function_name, arg_decls=arg_decls, return_type="void") |
| func_def.set_base_template_vars(cg_context.template_bindings()) |
| body = func_def.body |
| |
| for arg_name in arg_names: |
| body.add_template_var(arg_name, arg_name) |
| |
| bind_callback_local_vars(body, cg_context) |
| if cg_context.attribute or cg_context.function_like: |
| bind_blink_api_arguments(body, cg_context) |
| bind_return_value(body, cg_context) |
| |
| return func_def |
| |
| |
| def make_attribute_get_callback_def(cg_context, function_name): |
| assert isinstance(cg_context, CodeGenContext) |
| assert isinstance(function_name, str) |
| |
| func_def = _make_empty_callback_def(cg_context, function_name) |
| body = func_def.body |
| |
| body.extend([ |
| make_check_receiver(cg_context), |
| EmptyNode(), |
| make_runtime_call_timer_scope(cg_context), |
| make_bindings_trace_event(cg_context), |
| make_report_coop_access(cg_context), |
| make_report_deprecate_as(cg_context), |
| make_report_measure_as(cg_context), |
| make_log_activity(cg_context), |
| EmptyNode(), |
| ]) |
| |
| if "Getter" in cg_context.property_.extended_attributes.values_of( |
| "Custom"): |
| text = _format("${class_name}::{}(${info});", |
| custom_function_name(cg_context)) |
| body.append(TextNode(text)) |
| return func_def |
| |
| body.extend([ |
| make_return_value_cache_return_early(cg_context), |
| EmptyNode(), |
| make_check_security_of_return_value(cg_context), |
| make_v8_set_return_value(cg_context), |
| make_report_high_entropy(cg_context), |
| make_return_value_cache_update_value(cg_context), |
| ]) |
| |
| return func_def |
| |
| |
| def make_attribute_set_callback_def(cg_context, function_name): |
| assert isinstance(cg_context, CodeGenContext) |
| assert isinstance(function_name, str) |
| |
| ext_attrs = cg_context.attribute.extended_attributes |
| if cg_context.attribute.is_readonly and not any( |
| ext_attr in ext_attrs |
| for ext_attr in ("LegacyLenientSetter", "PutForwards", |
| "Replaceable")): |
| return None |
| |
| func_def = _make_empty_callback_def(cg_context, function_name) |
| body = func_def.body |
| |
| if "LegacyLenientSetter" in ext_attrs: |
| body.append(TextNode("// [LegacyLenientSetter]")) |
| return func_def |
| |
| body.extend([ |
| make_check_receiver(cg_context), |
| EmptyNode(), |
| make_runtime_call_timer_scope(cg_context), |
| make_bindings_trace_event(cg_context), |
| make_report_deprecate_as(cg_context), |
| make_report_measure_as(cg_context), |
| make_log_activity(cg_context), |
| EmptyNode(), |
| ]) |
| |
| if "Setter" in cg_context.property_.extended_attributes.values_of( |
| "Custom"): |
| text = _format("${class_name}::{}(${v8_property_value}, ${info});", |
| custom_function_name(cg_context)) |
| body.append(TextNode(text)) |
| return func_def |
| |
| # Binary size reduction hack |
| # 1. Drop the check of argument length although this is a violation of |
| # Web IDL. |
| # 2. Leverage the nature of [LegacyTreatNonObjectAsNull] (ES to IDL |
| # conversion never fails). |
| if (cg_context.attribute.idl_type.is_typedef |
| and (cg_context.attribute.idl_type.identifier in ( |
| "EventHandler", "OnBeforeUnloadEventHandler", |
| "OnErrorEventHandler"))): |
| body.extend([ |
| TextNode("""\ |
| EventListener* event_handler = JSEventHandler::CreateOrNull( |
| ${v8_property_value}, |
| JSEventHandler::HandlerType::k${attribute.idl_type.identifier});\ |
| """), |
| ]) |
| code_generator_info = cg_context.attribute.code_generator_info |
| func_name = name_style.api_func("set", cg_context.attribute.identifier) |
| if code_generator_info.defined_in_partial: |
| class_name = (code_generator_info.receiver_implemented_as |
| or name_style.class_( |
| cg_context.attribute.owner_mixin.identifier)) |
| text = _format( |
| "{class_name}::{func_name}" |
| "(*${blink_receiver}, event_handler);", |
| class_name=class_name, |
| func_name=func_name) |
| else: |
| text = _format("${blink_receiver}->{func_name}(event_handler);", |
| func_name=func_name) |
| body.append(TextNode(text)) |
| return func_def |
| |
| # Binary size reduction hack |
| # When the following conditions are met, the implementation is shared. |
| # 1. The attribute is annotated with [CEReactions, Reflect] and not |
| # annotated with other extended attributes having side effect. |
| # 2. The interface is implementing Element. |
| def optimize_element_cereactions_reflect(): |
| has_cereactions = False |
| has_reflect = False |
| for key in ext_attrs.keys(): |
| if key == "CEReactions": |
| has_cereactions = True |
| elif key == "Reflect": |
| has_reflect = True |
| elif key in ("Affects", "DeprecateAs", "Exposed", "LogActivity", |
| "LogAllWorlds", "Measure", "MeasureAs", |
| "ReflectEmpty", "ReflectInvalid", "ReflectMissing", |
| "ReflectOnly", "RuntimeCallStatsCounter", |
| "RuntimeEnabled", "SecureContext", "URL", |
| "Unscopable"): |
| pass |
| else: |
| return None |
| if not (has_cereactions and has_reflect): |
| return None |
| if not cg_context.interface.does_implement("Element"): |
| return None |
| content_attribute = _make_reflect_content_attribute_key( |
| body, cg_context) |
| idl_type = cg_context.attribute.idl_type.unwrap(typedef=True) |
| if idl_type.is_boolean: |
| func_name = "PerformAttributeSetCEReactionsReflectTypeBoolean" |
| elif idl_type.type_name == "String": |
| func_name = "PerformAttributeSetCEReactionsReflectTypeString" |
| elif idl_type.type_name == "StringTreatNullAs": |
| func_name = ("PerformAttributeSetCEReactionsReflect" |
| "TypeStringLegacyNullToEmptyString") |
| elif idl_type.type_name == "StringOrNull": |
| func_name = "PerformAttributeSetCEReactionsReflectTypeStringOrNull" |
| else: |
| return None |
| text = _format( |
| "bindings::{func_name}" |
| "(${info}, {content_attribute}, " |
| "${class_like_name}, ${property_name});", |
| func_name=func_name, |
| content_attribute=content_attribute) |
| return TextNode(text) |
| |
| node = optimize_element_cereactions_reflect() |
| if node: |
| body.append(node) |
| return func_def |
| |
| body.extend([ |
| make_check_argument_length(cg_context), |
| EmptyNode(), |
| ]) |
| |
| if "PutForwards" in ext_attrs: |
| body.append(make_steps_of_put_forwards(cg_context)) |
| return func_def |
| |
| if "Replaceable" in ext_attrs: |
| body.append(make_steps_of_replaceable(cg_context)) |
| return func_def |
| |
| body.extend([ |
| make_steps_of_ce_reactions(cg_context), |
| EmptyNode(), |
| make_v8_set_return_value(cg_context), |
| ]) |
| |
| return func_def |
| |
| |
| def make_constant_callback_def(cg_context, function_name): |
| assert isinstance(cg_context, CodeGenContext) |
| assert isinstance(function_name, str) |
| |
| logging_nodes = SequenceNode([ |
| make_report_deprecate_as(cg_context), |
| make_report_measure_as(cg_context), |
| make_log_activity(cg_context), |
| ]) |
| if not logging_nodes: |
| return None |
| |
| func_def = _make_empty_callback_def(cg_context, function_name) |
| body = func_def.body |
| |
| v8_set_return_value = _format( |
| "bindings::V8SetReturnValue(${info}, ${class_name}::Constant::{});", |
| constant_name(cg_context)) |
| body.extend([ |
| make_runtime_call_timer_scope(cg_context), |
| make_bindings_trace_event(cg_context), |
| logging_nodes, |
| EmptyNode(), |
| TextNode(v8_set_return_value), |
| make_report_high_entropy(cg_context), |
| ]) |
| |
| return func_def |
| |
| |
| def make_constant_constant_def(cg_context, constant_name): |
| # IDL constant's C++ constant definition |
| assert isinstance(cg_context, CodeGenContext) |
| assert isinstance(constant_name, str) |
| |
| constant_type = blink_type_info(cg_context.constant.idl_type).value_t |
| return TextNode("static constexpr {type} {name} = {value};".format( |
| type=constant_type, |
| name=constant_name, |
| value=cg_context.constant.value.literal)) |
| |
| |
| def make_overload_dispatcher_function_def(cg_context, function_name): |
| assert isinstance(cg_context, CodeGenContext) |
| assert isinstance(function_name, str) |
| |
| func_def = _make_empty_callback_def(cg_context, function_name) |
| body = func_def.body |
| |
| if cg_context.operation_group: |
| body.append(make_operation_entry(cg_context)) |
| body.append(EmptyNode()) |
| body.append(make_cooperative_scheduling_safepoint(cg_context)) |
| body.append(EmptyNode()) |
| |
| if cg_context.constructor_group: |
| body.append(make_constructor_entry(cg_context)) |
| body.append(EmptyNode()) |
| |
| body.append(make_overload_dispatcher(cg_context)) |
| |
| return func_def |
| |
| |
| def make_constructor_entry(cg_context): |
| assert isinstance(cg_context, CodeGenContext) |
| |
| return SequenceNode([ |
| make_runtime_call_timer_scope(cg_context), |
| make_bindings_trace_event(cg_context), |
| EmptyNode(), |
| make_check_constructor_call(cg_context), |
| ]) |
| |
| |
| def make_constructor_function_def(cg_context, function_name): |
| assert isinstance(cg_context, CodeGenContext) |
| assert isinstance(function_name, str) |
| |
| T = TextNode |
| |
| func_def = _make_empty_callback_def(cg_context, function_name) |
| body = func_def.body |
| |
| if len(cg_context.constructor_group) == 1: |
| body.append(make_constructor_entry(cg_context)) |
| body.append(EmptyNode()) |
| |
| body.extend([ |
| make_report_deprecate_as(cg_context), |
| make_report_measure_as(cg_context), |
| make_log_activity(cg_context), |
| EmptyNode(), |
| make_check_argument_length(cg_context), |
| EmptyNode(), |
| ]) |
| |
| if "HTMLConstructor" in cg_context.constructor.extended_attributes: |
| body.append(T("// [HTMLConstructor]")) |
| text = _format( |
| "V8HTMLConstructor::HtmlConstructor(" |
| "${info}, *${class_name}::GetWrapperTypeInfo(), " |
| "HTMLElementType::{});", |
| name_style.constant(cg_context.class_like.identifier)) |
| body.append(T(text)) |
| body.accumulate( |
| CodeGenAccumulator.require_include_headers([ |
| "third_party/blink/renderer/bindings/core/v8/v8_html_constructor.h" |
| ])) |
| else: |
| body.append( |
| T("v8::Local<v8::Object> v8_wrapper = " |
| "${return_value}->AssociateWithWrapper(${isolate}, " |
| "${class_name}::GetWrapperTypeInfo(), ${v8_receiver});")) |
| body.append(T("bindings::V8SetReturnValue(${info}, v8_wrapper);")) |
| |
| return func_def |
| |
| |
| def make_constructor_callback_def(cg_context, function_name): |
| assert isinstance(cg_context, CodeGenContext) |
| assert isinstance(function_name, str) |
| |
| constructor_group = cg_context.constructor_group |
| |
| if len(constructor_group) == 1: |
| return make_constructor_function_def( |
| cg_context.make_copy(constructor=constructor_group[0]), |
| function_name) |
| |
| node = SequenceNode() |
| for constructor in constructor_group: |
| cgc = cg_context.make_copy(constructor=constructor) |
| node.extend([ |
| make_constructor_function_def( |
| cgc, callback_function_name(cgc, constructor.overload_index)), |
| EmptyNode(), |
| ]) |
| node.append( |
| make_overload_dispatcher_function_def(cg_context, function_name)) |
| return node |
| |
| |
| def make_exposed_construct_callback_def(cg_context, function_name): |
| assert isinstance(cg_context, CodeGenContext) |
| assert isinstance(function_name, str) |
| |
| func_def = _make_empty_callback_def(cg_context, function_name) |
| body = func_def.body |
| |
| if (cg_context.exposed_construct.is_interface |
| or cg_context.exposed_construct.is_callback_interface): |
| tag = "bindings::V8ReturnValue::kInterfaceObject" |
| elif cg_context.exposed_construct.is_namespace: |
| tag = "bindings::V8ReturnValue::kNamespaceObject" |
| else: |
| assert False |
| v8_set_return_value = _format( |
| "bindings::V8SetReturnValue" |
| "(${info}, {bridge}::GetWrapperTypeInfo(), {tag});", |
| bridge=v8_bridge_class_name(cg_context.exposed_construct), |
| tag=tag) |
| body.extend([ |
| make_runtime_call_timer_scope(cg_context), |
| make_bindings_trace_event(cg_context), |
| make_report_deprecate_as(cg_context), |
| make_report_measure_as(cg_context), |
| make_log_activity(cg_context), |
| EmptyNode(), |
| TextNode(v8_set_return_value), |
| ]) |
| |
| return func_def |
| |
| |
| def make_named_constructor_property_callback_def(cg_context, function_name): |
| assert isinstance(cg_context, CodeGenContext) |
| assert isinstance(function_name, str) |
| |
| func_def = _make_empty_callback_def(cg_context, function_name) |
| body = func_def.body |
| |
| body.extend([ |
| make_runtime_call_timer_scope(cg_context), |
| make_bindings_trace_event(cg_context), |
| make_report_deprecate_as(cg_context), |
| make_report_measure_as(cg_context), |
| make_log_activity(cg_context), |
| EmptyNode(), |
| ]) |
| |
| constructor_group = cg_context.exposed_construct |
| assert isinstance(constructor_group, web_idl.ConstructorGroup) |
| assert isinstance(constructor_group.owner, web_idl.Interface) |
| named_ctor_v8_bridge = v8_bridge_class_name(constructor_group.owner) |
| cgc = CodeGenContext( |
| interface=constructor_group.owner, |
| constructor_group=constructor_group, |
| is_named_constructor=True, |
| class_name=named_ctor_v8_bridge) |
| named_ctor_name = callback_function_name(cgc) |
| named_ctor_def = make_constructor_callback_def(cgc, named_ctor_name) |
| |
| return_value_cache_return_early = """\ |
| static const V8PrivateProperty::SymbolKey kPrivatePropertyNamedConstructor; |
| auto&& v8_private_named_constructor = |
| V8PrivateProperty::GetSymbol(${isolate}, kPrivatePropertyNamedConstructor); |
| v8::Local<v8::Value> v8_named_constructor; |
| if (!v8_private_named_constructor.GetOrUndefined(${v8_receiver}) |
| .ToLocal(&v8_named_constructor)) { |
| return; |
| } |
| if (!v8_named_constructor->IsUndefined()) { |
| bindings::V8SetReturnValue(${info}, v8_named_constructor); |
| return; |
| } |
| """ |
| |
| pattern = """\ |
| v8::Local<v8::Value> v8_value; |
| if (!bindings::CreateNamedConstructorFunction( |
| ${script_state}, |
| {callback}, |
| "{func_name}", |
| {func_length}, |
| {v8_bridge}::GetWrapperTypeInfo()) |
| .ToLocal(&v8_value)) { |
| return; |
| } |
| bindings::V8SetReturnValue(${info}, v8_value); |
| """ |
| create_named_constructor_function = _format( |
| pattern, |
| callback=named_ctor_name, |
| func_name=constructor_group.identifier, |
| func_length=constructor_group.min_num_of_required_arguments, |
| v8_bridge=named_ctor_v8_bridge) |
| |
| return_value_cache_update_value = """\ |
| v8_private_named_constructor.Set(${v8_receiver}, v8_value); |
| """ |
| |
| body.extend([ |
| TextNode(return_value_cache_return_early), |
| TextNode(create_named_constructor_function), |
| TextNode(return_value_cache_update_value), |
| ]) |
| |
| return SequenceNode([named_ctor_def, EmptyNode(), func_def]) |
| |
| |
| def make_no_alloc_direct_call_callback_def(cg_context, function_name): |
| assert isinstance(cg_context, CodeGenContext) |
| assert isinstance(function_name, str) |
| |
| assert cg_context.operation_group and len(cg_context.operation_group) == 1 |
| |
| func_like = cg_context.operation_group[0] |
| |
| return_type = ("void" if func_like.return_type.is_void else |
| blink_type_info(func_like.return_type).value_t) |
| arg_type_and_names = [(blink_type_info(arg.idl_type).value_t, |
| name_style.arg_f("arg{}_{}", index + 1, |
| arg.identifier)) |
| for index, arg in enumerate(func_like.arguments)] |
| arg_decls = ["v8::ApiObject arg0_receiver"] + [ |
| "{} {}".format(arg_type, arg_name) |
| for arg_type, arg_name in arg_type_and_names |
| ] |
| arg_decls.append("v8::FastApiCallbackOptions& arg_callback_options") |
| func_def = CxxFuncDefNode(name=function_name, |
| arg_decls=arg_decls, |
| return_type=return_type) |
| body = func_def.body |
| |
| pattern = """\ |
| ThreadState::NoAllocationScope no_alloc_scope(ThreadState::Current()); |
| v8::Object* v8_receiver = reinterpret_cast<v8::Object*>(&arg0_receiver); |
| v8::Isolate* isolate = v8_receiver->GetIsolate(); |
| v8::Isolate::DisallowJavascriptExecutionScope no_js_exec_scope( |
| isolate, |
| v8::Isolate::DisallowJavascriptExecutionScope::CRASH_ON_FAILURE); |
| {blink_class}* blink_receiver = |
| ToScriptWrappable(v8_receiver)->ToImpl<{blink_class}>(); |
| return blink_receiver->{member_func}({blink_arguments}, arg_callback_options);\ |
| """ |
| blink_class = blink_class_name(cg_context.interface) |
| member_func = backward_compatible_api_func(cg_context) |
| blink_arguments = ", ".join( |
| [arg_name for arg_type, arg_name in arg_type_and_names]) |
| body.append( |
| TextNode( |
| _format(pattern, |
| blink_class=blink_class, |
| member_func=member_func, |
| blink_arguments=blink_arguments))) |
| |
| return func_def |
| |
| |
| def make_operation_entry(cg_context): |
| assert isinstance(cg_context, CodeGenContext) |
| |
| return SequenceNode([ |
| make_runtime_call_timer_scope(cg_context), |
| make_bindings_trace_event(cg_context), |
| ]) |
| |
| |
| def make_operation_function_def(cg_context, function_name): |
| assert isinstance(cg_context, CodeGenContext) |
| assert isinstance(function_name, str) |
| |
| func_def = _make_empty_callback_def(cg_context, function_name) |
| body = func_def.body |
| |
| if not cg_context.operation_group or len(cg_context.operation_group) == 1: |
| body.append(make_operation_entry(cg_context)) |
| body.append(EmptyNode()) |
| |
| body.extend([ |
| make_check_receiver(cg_context), |
| EmptyNode(), |
| make_report_coop_access(cg_context), |
| make_report_deprecate_as(cg_context), |
| make_report_measure_as(cg_context), |
| make_log_activity(cg_context), |
| EmptyNode(), |
| ]) |
| |
| if "Custom" in cg_context.property_.extended_attributes: |
| text = _format("${class_name}::{}(${info});", |
| custom_function_name(cg_context)) |
| body.append(TextNode(text)) |
| return func_def |
| |
| body.extend([ |
| make_check_argument_length(cg_context), |
| EmptyNode(), |
| make_steps_of_ce_reactions(cg_context), |
| EmptyNode(), |
| make_check_security_of_return_value(cg_context), |
| make_v8_set_return_value(cg_context), |
| make_report_high_entropy(cg_context), |
| ]) |
| |
| return func_def |
| |
| |
| def make_operation_callback_def(cg_context, |
| function_name, |
| no_alloc_direct_callback_name=None): |
| assert isinstance(cg_context, CodeGenContext) |
| assert isinstance(function_name, str) |
| |
| operation_group = cg_context.operation_group |
| |
| assert (not ("Custom" in operation_group.extended_attributes) |
| or len(operation_group) == 1) |
| assert (not ("NoAllocDirectCall" in operation_group.extended_attributes) |
| or len(operation_group) == 1) |
| |
| if "NoAllocDirectCall" in operation_group.extended_attributes: |
| return ListNode([ |
| make_operation_function_def( |
| cg_context.make_copy(operation=operation_group[0]), |
| function_name), |
| EmptyNode(), |
| make_no_alloc_direct_call_callback_def( |
| cg_context.make_copy(operation=operation_group[0]), |
| no_alloc_direct_callback_name), |
| ]) |
| |
| if len(operation_group) == 1: |
| return make_operation_function_def( |
| cg_context.make_copy(operation=operation_group[0]), function_name) |
| |
| node = SequenceNode() |
| for operation in operation_group: |
| cgc = cg_context.make_copy(operation=operation) |
| node.extend([ |
| make_operation_function_def( |
| cgc, callback_function_name(cgc, operation.overload_index)), |
| EmptyNode(), |
| ]) |
| node.append( |
| make_overload_dispatcher_function_def(cg_context, function_name)) |
| return node |
| |
| |
| def make_stringifier_callback_def(cg_context, function_name): |
| assert isinstance(cg_context, CodeGenContext) |
| assert isinstance(function_name, str) |
| |
| if cg_context.stringifier.attribute: |
| return make_attribute_get_callback_def( |
| cg_context.make_copy( |
| attribute=cg_context.stringifier.attribute, |
| attribute_get=True), function_name) |
| elif cg_context.stringifier.operation: |
| return make_operation_function_def( |
| cg_context.make_copy(operation=cg_context.stringifier.operation), |
| function_name) |
| assert False |
| |
| |
| # ---------------------------------------------------------------------------- |
| # Callback functions of indexed and named interceptors |
| # ---------------------------------------------------------------------------- |
| |
| |
| def _make_interceptor_callback(cg_context, function_name, arg_decls, arg_names, |
| class_name, runtime_call_timer_name): |
| assert isinstance(cg_context, CodeGenContext) |
| assert isinstance(function_name, str) |
| assert isinstance(arg_decls, (list, tuple)) |
| assert all(isinstance(arg_decl, str) for arg_decl in arg_decls) |
| assert isinstance(arg_names, (list, tuple)) |
| assert all(isinstance(arg_name, str) for arg_name in arg_names) |
| assert _is_none_or_str(class_name) |
| assert isinstance(runtime_call_timer_name, str) |
| |
| func_decl = CxxFuncDeclNode( |
| name=function_name, |
| arg_decls=arg_decls, |
| return_type="void", |
| static=True) |
| |
| func_def = _make_interceptor_callback_def(cg_context, function_name, |
| arg_decls, arg_names, class_name, |
| runtime_call_timer_name) |
| |
| return func_decl, func_def |
| |
| |
| def _make_interceptor_callback_def(cg_context, function_name, arg_decls, |
| arg_names, class_name, |
| runtime_call_timer_name): |
| assert isinstance(cg_context, CodeGenContext) |
| assert isinstance(function_name, str) |
| assert isinstance(arg_decls, (list, tuple)) |
| assert all(isinstance(arg_decl, str) for arg_decl in arg_decls) |
| assert isinstance(arg_names, (list, tuple)) |
| assert all(isinstance(arg_name, str) for arg_name in arg_names) |
| assert _is_none_or_str(class_name) |
| assert isinstance(runtime_call_timer_name, str) |
| |
| func_def = CxxFuncDefNode( |
| name=function_name, |
| arg_decls=arg_decls, |
| return_type="void", |
| class_name=class_name) |
| func_def.set_base_template_vars(cg_context.template_bindings()) |
| body = func_def.body |
| for arg_name in arg_names: |
| body.add_template_var(arg_name, arg_name) |
| bind_callback_local_vars(body, cg_context) |
| |
| body.extend([ |
| make_runtime_call_timer_scope(cg_context, runtime_call_timer_name), |
| EmptyNode(), |
| ]) |
| |
| return func_def |
| |
| |
| def make_indexed_property_getter_callback(cg_context, function_name): |
| assert isinstance(cg_context, CodeGenContext) |
| assert isinstance(function_name, str) |
| |
| arg_decls = [ |
| "uint32_t index", |
| "const v8::PropertyCallbackInfo<v8::Value>& info", |
| ] |
| arg_names = ["index", "info"] |
| |
| func_decl, func_def = _make_interceptor_callback( |
| cg_context, function_name, arg_decls, arg_names, cg_context.class_name, |
| "IndexedPropertyGetter") |
| body = func_def.body |
| |
| if not cg_context.interface.indexed_and_named_properties.indexed_getter: |
| body.append( |
| TextNode("""\ |
| v8::Local<v8::String> property_name = |
| V8AtomicString(${isolate}, AtomicString::Number(${index})); |
| ${class_name}::NamedPropertyGetterCallback(property_name, ${info}); |
| """)) |
| return func_decl, func_def |
| |
| bind_return_value(body, cg_context, overriding_args=["${index}"]) |
| |
| body.extend([ |
| TextNode("""\ |
| // LegacyPlatformObjectGetOwnProperty |
| // https://heycam.github.io/webidl/#LegacyPlatformObjectGetOwnProperty |
| // step 1.2. If index is a supported property index, then: |
| // step 3. Return OrdinaryGetOwnProperty(O, P). |
| if (${index} >= ${blink_receiver}->length()) |
| return; // Do not intercept. Fallback to OrdinaryGetOwnProperty. |
| """), |
| make_v8_set_return_value(cg_context), |
| ]) |
| |
| return func_decl, func_def |
| |
| |
| def make_indexed_property_setter_callback(cg_context, function_name): |
| assert isinstance(cg_context, CodeGenContext) |
| assert isinstance(function_name, str) |
| |
| arg_decls = [ |
| "uint32_t index", |
| "v8::Local<v8::Value> v8_property_value", |
| "const v8::PropertyCallbackInfo<v8::Value>& info", |
| ] |
| arg_names = ["index", "v8_property_value", "info"] |
| |
| func_decl, func_def = _make_interceptor_callback( |
| cg_context, function_name, arg_decls, arg_names, cg_context.class_name, |
| "IndexedPropertySetter") |
| body = func_def.body |
| |
| if not cg_context.interface.indexed_and_named_properties.indexed_getter: |
| body.append( |
| TextNode("""\ |
| v8::Local<v8::String> property_name = |
| V8AtomicString(${isolate}, AtomicString::Number(${index})); |
| ${class_name}::NamedPropertySetterCallback( |
| property_name, ${v8_property_value}, ${info}); |
| """)) |
| return func_decl, func_def |
| |
| if not cg_context.indexed_property_setter: |
| body.append( |
| TextNode("""\ |
| // 3.9.2. [[Set]] |
| // https://heycam.github.io/webidl/#legacy-platform-object-set |
| // OrdinarySetWithOwnDescriptor will end up calling DefineOwnProperty, |
| // which will fail when the receiver object is this legacy platform |
| // object. |
| bindings::V8SetReturnValue(${info}, nullptr); |
| if (${info}.ShouldThrowOnError()) { |
| ExceptionState exception_state(${info}.GetIsolate(), |
| ExceptionState::kIndexedSetterContext, |
| "${interface.identifier}"); |
| exception_state.ThrowTypeError( |
| "Indexed property setter is not supported."); |
| } |
| """)) |
| return func_decl, func_def |
| |
| bind_return_value( |
| body, |
| cg_context, |
| overriding_args=["${index}", "${blink_property_value}"]) |
| body.register_code_symbol( |
| make_v8_to_blink_value( |
| "blink_property_value", |
| "${v8_property_value}", |
| cg_context.indexed_property_setter.arguments[1].idl_type, |
| argument=cg_context.indexed_property_setter.arguments[1])) |
| |
| body.extend([ |
| TextNode("""\ |
| // 3.9.2. [[Set]] |
| // https://heycam.github.io/webidl/#legacy-platform-object-set |
| // step 1. If O and Receiver are the same object, then:\ |
| """), |
| CxxLikelyIfNode(cond="${info}.Holder() == ${info}.This()", |
| body=[ |
| TextNode("""\ |
| // step 1.1.1. Invoke the indexed property setter with P and V.\ |
| """), |
| make_steps_of_ce_reactions(cg_context), |
| EmptyNode(), |
| make_v8_set_return_value(cg_context), |
| TextNode("""\ |
| bindings::V8SetReturnValue(${info}, nullptr); |
| return;"""), |
| ]), |
| EmptyNode(), |
| TextNode("""\ |
| // Do not intercept. Fallback to OrdinarySetWithOwnDescriptor. |
| """), |
| ]) |
| |
| return func_decl, func_def |
| |
| |
| def make_indexed_property_deleter_callback(cg_context, function_name): |
| assert isinstance(cg_context, CodeGenContext) |
| assert isinstance(function_name, str) |
| |
| arg_decls = [ |
| "uint32_t index", |
| "const v8::PropertyCallbackInfo<v8::Boolean>& info", |
| ] |
| arg_names = ["index", "info"] |
| |
| func_decl, func_def = _make_interceptor_callback( |
| cg_context, function_name, arg_decls, arg_names, cg_context.class_name, |
| "IndexedPropertyDeleter") |
| body = func_def.body |
| |
| if not cg_context.interface.indexed_and_named_properties.indexed_getter: |
| body.append( |
| TextNode("""\ |
| v8::Local<v8::String> property_name = |
| V8AtomicString(${isolate}, AtomicString::Number(${index})); |
| ${class_name}::NamedPropertyDeleterCallback(property_name, ${info}); |
| """)) |
| return func_decl, func_def |
| |
| body.append( |
| TextNode("""\ |
| // 3.9.4. [[Delete]] |
| // https://heycam.github.io/webidl/#legacy-platform-object-delete |
| // step 1.2. If index is not a supported property index, then return true. |
| // step 1.3. Return false. |
| const bool is_supported = ${index} < ${blink_receiver}->length(); |
| bindings::V8SetReturnValue(${info}, !is_supported); |
| if (is_supported and ${info}.ShouldThrowOnError()) { |
| ExceptionState exception_state(${info}.GetIsolate(), |
| ExceptionState::kIndexedDeletionContext, |
| "${interface.identifier}"); |
| exception_state.ThrowTypeError("Index property deleter is not supported."); |
| } |
| """)) |
| |
| return func_decl, func_def |
| |
| |
| def make_indexed_property_definer_callback(cg_context, function_name): |
| assert isinstance(cg_context, CodeGenContext) |
| assert isinstance(function_name, str) |
| |
| arg_decls = [ |
| "uint32_t index", |
| "const v8::PropertyDescriptor& v8_property_desc", |
| "const v8::PropertyCallbackInfo<v8::Value>& info", |
| ] |
| arg_names = ["index", "v8_property_desc", "info"] |
| |
| func_decl, func_def = _make_interceptor_callback( |
| cg_context, function_name, arg_decls, arg_names, cg_context.class_name, |
| "IndexedPropertyDefiner") |
| body = func_def.body |
| |
| if not cg_context.interface.indexed_and_named_properties.indexed_getter: |
| body.append( |
| TextNode("""\ |
| v8::Local<v8::String> property_name = |
| V8AtomicString(${isolate}, AtomicString::Number(${index})); |
| ${class_name}::NamedPropertyDefinerCallback( |
| property_name, ${v8_property_desc}, ${info}); |
| """)) |
| return func_decl, func_def |
| |
| body.append( |
| TextNode("""\ |
| // 3.9.3. [[DefineOwnProperty]] |
| // https://heycam.github.io/webidl/#legacy-platform-object-defineownproperty |
| // step 1.1. If the result of calling IsDataDescriptor(Desc) is false, then |
| // return false. |
| if (v8_property_desc.has_get() || v8_property_desc.has_set()) { |
| bindings::V8SetReturnValue(${info}, nullptr); |
| if (${info}.ShouldThrowOnError()) { |
| ExceptionState exception_state(${info}.GetIsolate(), |
| ExceptionState::kIndexedSetterContext, |
| "${interface.identifier}"); |
| exception_state.ThrowTypeError("Accessor properties are not allowed."); |
| } |
| return; |
| } |
| """)) |
| |
| if not cg_context.interface.indexed_and_named_properties.indexed_setter: |
| body.append( |
| TextNode("""\ |
| // step 1.2. If O does not implement an interface with an indexed property |
| // setter, then return false. |
| bindings::V8SetReturnValue(${info}, nullptr); |
| if (${info}.ShouldThrowOnError()) { |
| ExceptionState exception_state(${info}.GetIsolate(), |
| ExceptionState::kIndexedSetterContext, |
| "${interface.identifier}"); |
| exception_state.ThrowTypeError("Index property setter is not supported."); |
| } |
| """)) |
| else: |
| body.append( |
| TextNode("""\ |
| // step 1.3. Invoke the indexed property setter with P and Desc.[[Value]]. |
| ${class_name}::IndexedPropertySetterCallback( |
| ${index}, ${v8_property_desc}.value(), ${info}); |
| """)) |
| |
| return func_decl, func_def |
| |
| |
| def make_indexed_property_descriptor_callback(cg_context, function_name): |
| assert isinstance(cg_context, CodeGenContext) |
| assert isinstance(function_name, str) |
| |
| arg_decls = [ |
| "uint32_t index", |
| "const v8::PropertyCallbackInfo<v8::Value>& info", |
| ] |
| arg_names = ["index", "info"] |
| |
| func_decl, func_def = _make_interceptor_callback( |
| cg_context, function_name, arg_decls, arg_names, cg_context.class_name, |
| "IndexedPropertyDescriptor") |
| body = func_def.body |
| |
| if not cg_context.interface.indexed_and_named_properties.indexed_getter: |
| body.append( |
| TextNode("""\ |
| v8::Local<v8::String> property_name = |
| V8AtomicString(${isolate}, AtomicString::Number(${index})); |
| ${class_name}::NamedPropertyDescriptorCallback(property_name, ${info}); |
| """)) |
| return func_decl, func_def |
| |
| pattern = """\ |
| // LegacyPlatformObjectGetOwnProperty |
| // https://heycam.github.io/webidl/#LegacyPlatformObjectGetOwnProperty |
| // step 1.2.3. If operation was defined without an identifier, then set |
| // value to the result of performing the steps listed in the interface |
| // description to determine the value of an indexed property with index |
| // as the index. |
| // step 1.2.4. Otherwise, operation was defined with an identifier. Set |
| // value to the result of performing the steps listed in the description |
| // of operation with index as the only argument value. |
| ${class_name}::IndexedPropertyGetterCallback(${index}, ${info}); |
| v8::Local<v8::Value> v8_value = ${info}.GetReturnValue().Get(); |
| // step 1.2. If index is a supported property index, then: |
| // step 3. Return OrdinaryGetOwnProperty(O, P). |
| if (v8_value->IsUndefined()) |
| return; // Do not intercept. Fallback to OrdinaryGetOwnProperty. |
| |
| // step 1.2.6. Set desc.[[Value]] to the result of converting value to an |
| // ECMAScript value. |
| // step 1.2.7. If O implements an interface with an indexed property setter, |
| // then set desc.[[Writable]] to true, otherwise set it to false. |
| // step 1.2.8. Set desc.[[Enumerable]] and desc.[[Configurable]] to true. |
| v8::PropertyDescriptor desc(v8_value, /*writable=*/{cxx_writable}); |
| desc.set_enumerable(true); |
| desc.set_configurable(true); |
| bindings::V8SetReturnValue(${info}, desc);""" |
| writable = bool( |
| cg_context.interface.indexed_and_named_properties.indexed_setter) |
| cxx_writable = "true" if writable else "false" |
| body.append(TextNode(_format(pattern, cxx_writable=cxx_writable))) |
| |
| return func_decl, func_def |
| |
| |
| def make_indexed_property_enumerator_callback(cg_context, function_name): |
| assert isinstance(cg_context, CodeGenContext) |
| assert isinstance(function_name, str) |
| |
| if not cg_context.interface.indexed_and_named_properties.indexed_getter: |
| return None, None |
| |
| arg_decls = ["const v8::PropertyCallbackInfo<v8::Array>& info"] |
| arg_names = ["info"] |
| |
| func_decl, func_def = _make_interceptor_callback( |
| cg_context, function_name, arg_decls, arg_names, cg_context.class_name, |
| "IndexedPropertyEnumerator") |
| body = func_def.body |
| |
| body.append( |
| TextNode("""\ |
| // 3.9.6. [[OwnPropertyKeys]] |
| // https://heycam.github.io/webidl/#legacy-platform-object-ownpropertykeys |
| // step 2. If O supports indexed properties, then for each index of O's |
| // supported property indices, in ascending numerical order, append |
| // ! ToString(index) to keys. |
| uint32_t length = ${blink_receiver}->length(); |
| v8::Local<v8::Array> array = |
| bindings::EnumerateIndexedProperties(${isolate}, length); |
| bindings::V8SetReturnValue(${info}, array); |
| """)) |
| body.accumulate( |
| CodeGenAccumulator.require_include_headers([ |
| "third_party/blink/renderer/bindings/core/v8/generated_code_helper.h" |
| ])) |
| |
| return func_decl, func_def |
| |
| |
| def make_named_property_getter_callback(cg_context, function_name): |
| assert isinstance(cg_context, CodeGenContext) |
| assert isinstance(function_name, str) |
| |
| arg_decls = [ |
| "v8::Local<v8::Name> v8_property_name", |
| "const v8::PropertyCallbackInfo<v8::Value>& info", |
| ] |
| arg_names = ["v8_property_name", "info"] |
| |
| func_decl, func_def = _make_interceptor_callback( |
| cg_context, function_name, arg_decls, arg_names, cg_context.class_name, |
| "NamedPropertyGetter") |
| body = func_def.body |
| |
| bind_return_value( |
| body, cg_context, overriding_args=["${blink_property_name}"]) |
| |
| if "Custom" in cg_context.named_property_getter.extended_attributes: |
| text = _format("${class_name}::{}(${blink_property_name}, ${info});", |
| custom_function_name(cg_context)) |
| body.append(TextNode(text)) |
| return func_decl, func_def |
| |
| # The named property getter's implementation of Blink is not designed to |
| # represent the property existence, and we have to determine the property |
| # existence by heuristics. |
| type = cg_context.return_type.unwrap() |
| if type.is_any or type.is_object: |
| not_found_expr = "${return_value}.IsEmpty()" |
| elif type.is_string: |
| not_found_expr = "${return_value}.IsNull()" |
| elif type.is_interface: |
| not_found_expr = "!${return_value}" |
| elif type.is_union: |
| not_found_expr = "${return_value}.IsNull()" |
| else: |
| assert False |
| |
| body.extend([ |
| TextNode("""\ |
| // LegacyPlatformObjectGetOwnProperty |
| // https://heycam.github.io/webidl/#LegacyPlatformObjectGetOwnProperty |
| // step 2.1. If the result of running the named property visibility |
| // algorithm with property name P and object O is true, then:\ |
| """), |
| CxxUnlikelyIfNode( |
| cond=not_found_expr, |
| body=[ |
| TextNode("// step 3. Return OrdinaryGetOwnProperty(O, P)."), |
| TextNode("return; // Do not intercept."), |
| ]), |
| TextNode("""\ |
| // step 2.1.3. If operation was defined without an identifier, then set |
| // value to the result of performing the steps listed in the interface |
| // description to determine the value of a named property with P as the |
| // name. |
| // step 2.1.4. Otherwise, operation was defined with an identifier. Set |
| // value to the result of performing the steps listed in the description |
| // of operation with P as the only argument value.\ |
| """), |
| make_v8_set_return_value(cg_context), |
| ]) |
| |
| return func_decl, func_def |
| |
| |
| def make_named_property_setter_callback(cg_context, function_name): |
| assert isinstance(cg_context, CodeGenContext) |
| assert isinstance(function_name, str) |
| |
| arg_decls = [ |
| "v8::Local<v8::Name> v8_property_name", |
| "v8::Local<v8::Value> v8_property_value", |
| "const v8::PropertyCallbackInfo<v8::Value>& info", |
| ] |
| arg_names = ["v8_property_name", "v8_property_value", "info"] |
| |
| func_decl, func_def = _make_interceptor_callback( |
| cg_context, function_name, arg_decls, arg_names, cg_context.class_name, |
| "NamedPropertySetter") |
| body = func_def.body |
| |
| if not cg_context.named_property_setter: |
| body.append( |
| TextNode("""\ |
| // 3.9.2. [[Set]] |
| // https://heycam.github.io/webidl/#legacy-platform-object-set |
| // step 3. Perform ? OrdinarySetWithOwnDescriptor(O, P, V, Receiver, ownDesc).\ |
| """)) |
| if ("LegacyOverrideBuiltIns" in |
| cg_context.interface.extended_attributes): |
| body.append( |
| TextNode("""\ |
| // [LegacyOverrideBuiltIns] |
| if (${info}.Holder()->GetRealNamedPropertyAttributesInPrototypeChain( |
| ${current_context}, ${v8_property_name}).IsJust()) { |
| return; // Fallback to the existing property. |
| } |
| """)) |
| body.append( |
| TextNode("""\ |
| ${class_name}::NamedPropertyGetterCallback(${v8_property_name}, ${info}); |
| const bool is_creating = ${info}.GetReturnValue().Get()->IsUndefined(); |
| if (!is_creating) { |
| bindings::V8SetReturnValue(${info}, nullptr); |
| if (${info}.ShouldThrowOnError()) { |
| ExceptionState exception_state(${info}.GetIsolate(), |
| ExceptionState::kNamedSetterContext, |
| "${interface.identifier}"); |
| exception_state.ThrowTypeError( |
| "Named property setter is not supported."); |
| } |
| return; |
| } |
| |
| // Do not intercept. Fallback and let it define a new own property. |
| """)) |
| return func_decl, func_def |
| |
| bind_return_value( |
| body, |
| cg_context, |
| overriding_args=["${blink_property_name}", "${blink_property_value}"]) |
| body.register_code_symbol( |
| make_v8_to_blink_value( |
| "blink_property_value", |
| "${v8_property_value}", |
| cg_context.named_property_setter.arguments[1].idl_type, |
| argument=cg_context.named_property_setter.arguments[1])) |
| |
| if "Custom" in cg_context.named_property_setter.extended_attributes: |
| text = _format( |
| "${class_name}::{}" |
| "(${blink_property_name}, ${v8_property_value}, ${info});", |
| custom_function_name(cg_context)) |
| body.append(TextNode(text)) |
| return func_decl, func_def |
| |
| body.extend([ |
| TextNode("""\ |
| // 3.9.2. [[Set]] |
| // https://heycam.github.io/webidl/#legacy-platform-object-set |
| // step 1. If O and Receiver are the same object, then:\ |
| """), |
| CxxLikelyIfNode(cond="${info}.Holder() == ${info}.This()", |
| body=[ |
| TextNode("""\ |
| // step 1.2.1. Invoke the named property setter with P and V.\ |
| """), |
| make_steps_of_ce_reactions(cg_context), |
| EmptyNode(), |
| make_v8_set_return_value(cg_context), |
| TextNode("""\ |
| % if interface.identifier == "CSSStyleDeclaration": |
| // CSSStyleDeclaration is abusing named properties. |
| // Do not intercept if the property is not found. |
| % else: |
| bindings::V8SetReturnValue(${info}, nullptr); |
| % endif |
| return;"""), |
| ]), |
| EmptyNode(), |
| TextNode("""\ |
| // Do not intercept. Fallback to OrdinarySetWithOwnDescriptor. |
| """), |
| ]) |
| |
| return func_decl, func_def |
| |
| |
| def make_named_property_deleter_callback(cg_context, function_name): |
| assert isinstance(cg_context, CodeGenContext) |
| assert isinstance(function_name, str) |
| |
| arg_decls = [ |
| "v8::Local<v8::Name> v8_property_name", |
| "const v8::PropertyCallbackInfo<v8::Boolean>& info", |
| ] |
| arg_names = ["v8_property_name", "info"] |
| |
| func_decl, func_def = _make_interceptor_callback( |
| cg_context, function_name, arg_decls, arg_names, cg_context.class_name, |
| "NamedPropertyDeleter") |
| body = func_def.body |
| |
| props = cg_context.interface.indexed_and_named_properties |
| if (not cg_context.named_property_deleter |
| and "NotEnumerable" in props.named_getter.extended_attributes): |
| body.append( |
| TextNode("""\ |
| // 3.9.4. [[Delete]] |
| // https://heycam.github.io/webidl/#legacy-platform-object-delete |
| // step 2. If O supports named properties, O does not implement an interface |
| // with the [Global] extended attribute and the result of calling the |
| // named property visibility algorithm with property name P and object O |
| // is true, then: |
| // |
| // There is no easy way to determine whether the named property is visible |
| // or not. Just do not intercept and fallback to the default behavior. |
| """)) |
| return func_decl, func_def |
| |
| if not cg_context.named_property_deleter: |
| body.append( |
| TextNode("""\ |
| // 3.9.4. [[Delete]] |
| // https://heycam.github.io/webidl/#legacy-platform-object-delete |
| // step 2. If O supports named properties, O does not implement an interface |
| // with the [Global] extended attribute and the result of calling the |
| // named property visibility algorithm with property name P and object O |
| // is true, then: |
| // step 2.1. If O does not implement an interface with a named property |
| // deleter, then return false. |
| ExceptionState exception_state(${info}.GetIsolate(), |
| ExceptionState::kNamedDeletionContext, |
| "${interface.identifier}"); |
| bool does_exist = ${blink_receiver}->NamedPropertyQuery( |
| ${blink_property_name}, exception_state); |
| if (exception_state.HadException()) |
| return; |
| if (does_exist) { |
| bindings::V8SetReturnValue(${info}, false); |
| if (${info}.ShouldThrowOnError()) { |
| exception_state.ThrowTypeError("Named property deleter is not supported."); |
| } |
| return; |
| } |
| |
| // Do not intercept. |
| """)) |
| return func_decl, func_def |
| |
| bind_return_value( |
| body, cg_context, overriding_args=["${blink_property_name}"]) |
| |
| if "Custom" in cg_context.named_property_deleter.extended_attributes: |
| text = _format("${class_name}::{}(${blink_property_name}, ${info});", |
| custom_function_name(cg_context)) |
| body.append(TextNode(text)) |
| return func_decl, func_def |
| |
| body.extend([ |
| TextNode("""\ |
| // 3.9.4. [[Delete]] |
| // https://heycam.github.io/webidl/#legacy-platform-object-delete\ |
| """), |
| make_steps_of_ce_reactions(cg_context), |
| EmptyNode(), |
| make_v8_set_return_value(cg_context), |
| TextNode("""\ |
| if (${return_value} == NamedPropertyDeleterResult::kDidNotDelete) { |
| if (${info}.ShouldThrowOnError()) { |
| ExceptionState exception_state(${info}.GetIsolate(), |
| ExceptionState::kNamedDeletionContext, |
| "${interface.identifier}"); |
| exception_state.ThrowTypeError("Failed to delete a property."); |
| } |
| return; |
| }"""), |
| ]) |
| |
| return func_decl, func_def |
| |
| |
| def make_named_property_definer_callback(cg_context, function_name): |
| assert isinstance(cg_context, CodeGenContext) |
| assert isinstance(function_name, str) |
| |
| arg_decls = [ |
| "v8::Local<v8::Name> v8_property_name", |
| "const v8::PropertyDescriptor& v8_property_desc", |
| "const v8::PropertyCallbackInfo<v8::Value>& info", |
| ] |
| arg_names = ["v8_property_name", "v8_property_desc", "info"] |
| |
| func_decl, func_def = _make_interceptor_callback( |
| cg_context, function_name, arg_decls, arg_names, cg_context.class_name, |
| "NamedPropertyDefiner") |
| body = func_def.body |
| |
| if cg_context.interface.identifier == "CSSStyleDeclaration": |
| body.append( |
| TextNode("""\ |
| // CSSStyleDeclaration is abusing named properties. |
| // Do not intercept. Fallback to OrdinaryDefineOwnProperty. |
| """)) |
| elif cg_context.interface.identifier in ("HTMLEmbedElement", |
| "HTMLObjectElement"): |
| body.append( |
| TextNode("""\ |
| // HTMLEmbedElement and HTMLObjectElement are abusing named properties. |
| // Do not intercept. Fallback to OrdinaryDefineOwnProperty. |
| """)) |
| elif not cg_context.interface.indexed_and_named_properties.named_setter: |
| body.append( |
| TextNode("""\ |
| // 3.9.3. [[DefineOwnProperty]] |
| // https://heycam.github.io/webidl/#legacy-platform-object-defineownproperty |
| // step 2.1. Let creating be true if P is not a supported property name, and |
| // false otherwise. |
| // step 2.2.1. If creating is false and O does not implement an interface |
| // with a named property setter, then return false. |
| ${class_name}::NamedPropertyGetterCallback(${v8_property_name}, ${info}); |
| const bool is_creating = ${info}.GetReturnValue().Get()->IsUndefined(); |
| if (!is_creating) { |
| bindings::V8SetReturnValue(${info}, nullptr); |
| if (${info}.ShouldThrowOnError()) { |
| ExceptionState exception_state(${info}.GetIsolate(), |
| ExceptionState::kNamedSetterContext, |
| "${interface.identifier}"); |
| exception_state.ThrowTypeError("Named property setter is not supported."); |
| } |
| return; |
| } |
| |
| // Do not intercept. Fallback to OrdinaryDefineOwnProperty. |
| """)) |
| else: |
| body.append( |
| TextNode("""\ |
| // 3.9.3. [[DefineOwnProperty]] |
| // https://heycam.github.io/webidl/#legacy-platform-object-defineownproperty |
| // step 2.2.2. If O implements an interface with a named property setter, |
| // then: |
| // step 2.2.2.1. If the result of calling IsDataDescriptor(Desc) is false, |
| // then return false. |
| if (v8_property_desc.has_get() || v8_property_desc.has_set()) { |
| bindings::V8SetReturnValue(${info}, nullptr); |
| if (${info}.ShouldThrowOnError()) { |
| ExceptionState exception_state(${info}.GetIsolate(), |
| ExceptionState::kNamedSetterContext, |
| "${interface.identifier}"); |
| exception_state.ThrowTypeError("Accessor properties are not allowed."); |
| } |
| return; |
| } |
| |
| // step 2.2.2.2. Invoke the named property setter with P and Desc.[[Value]]. |
| ${class_name}::NamedPropertySetterCallback( |
| ${v8_property_name}, ${v8_property_desc}.value(), ${info}); |
| bindings::V8SetReturnValue(${info}, nullptr); |
| """)) |
| |
| return func_decl, func_def |
| |
| |
| def make_named_property_descriptor_callback(cg_context, function_name): |
| assert isinstance(cg_context, CodeGenContext) |
| assert isinstance(function_name, str) |
| |
| arg_decls = [ |
| "v8::Local<v8::Name> v8_property_name", |
| "const v8::PropertyCallbackInfo<v8::Value>& info", |
| ] |
| arg_names = ["v8_property_name", "info"] |
| |
| func_decl, func_def = _make_interceptor_callback( |
| cg_context, function_name, arg_decls, arg_names, cg_context.class_name, |
| "NamedPropertyDescriptor") |
| body = func_def.body |
| |
| body.append( |
| TextNode("""\ |
| // LegacyPlatformObjectGetOwnProperty |
| // https://heycam.github.io/webidl/#LegacyPlatformObjectGetOwnProperty\ |
| """)) |
| |
| if ("LegacyOverrideBuiltIns" not in |
| cg_context.interface.extended_attributes): |
| body.append( |
| TextNode("""\ |
| // step 2.1. If the result of running the named property visibility algorithm |
| // with property name P and object O is true, then: |
| if (${v8_receiver}->GetRealNamedPropertyAttributesInPrototypeChain( |
| ${current_context}, ${v8_property_name}).IsJust()) { |
| return; // Do not intercept. Fallback to OrdinaryGetOwnProperty. |
| } |
| """)) |
| |
| pattern = """\ |
| // step 2.1.3. If operation was defined without an identifier, then set |
| // value to the result of performing the steps listed in the interface |
| // description to determine the value of a named property with P as the |
| // name. |
| // step 2.1.4. Otherwise, operation was defined with an identifier. Set |
| // value to the result of performing the steps listed in the description |
| // of operation with P as the only argument value. |
| ${class_name}::NamedPropertyGetterCallback(${v8_property_name}, ${info}); |
| v8::Local<v8::Value> v8_value = ${info}.GetReturnValue().Get(); |
| // step 2.1. If the result of running the named property visibility |
| // algorithm with property name P and object O is true, then: |
| // step 3. Return OrdinaryGetOwnProperty(O, P). |
| if (v8_value->IsUndefined()) |
| return; // Do not intercept. Fallback to OrdinaryGetOwnProperty. |
| |
| // step 2.1.6. Set desc.[[Value]] to the result of converting value to an |
| // ECMAScript value. |
| // step 2.1.7. If O implements an interface with a named property setter, |
| // then set desc.[[Writable]] to true, otherwise set it to false. |
| // step 2.1.8. If O implements an interface with the |
| // [LegacyUnenumerableNamedProperties] extended attribute, then set |
| // desc.[[Enumerable]] to false, otherwise set it to true. |
| // step 2.1.9. Set desc.[[Configurable]] to true. |
| v8::PropertyDescriptor desc(v8_value, /*writable=*/{cxx_writable}); |
| desc.set_enumerable({cxx_enumerable}); |
| desc.set_configurable(true); |
| bindings::V8SetReturnValue(${info}, desc); |
| """ |
| props = cg_context.interface.indexed_and_named_properties |
| writable = bool(props.named_setter) |
| cxx_writable = "true" if writable else "false" |
| enumerable = props.is_named_property_enumerable |
| cxx_enumerable = "true" if enumerable else "false" |
| body.append( |
| TextNode( |
| _format( |
| pattern, |
| cxx_writable=cxx_writable, |
| cxx_enumerable=cxx_enumerable))) |
| |
| return func_decl, func_def |
| |
| |
| def make_named_property_query_callback(cg_context, function_name): |
| assert isinstance(cg_context, CodeGenContext) |
| assert isinstance(function_name, str) |
| |
| props = cg_context.interface.indexed_and_named_properties |
| if "NotEnumerable" in props.named_getter.extended_attributes: |
| return None, None |
| |
| arg_decls = [ |
| "v8::Local<v8::Name> v8_property_name", |
| "const v8::PropertyCallbackInfo<v8::Integer>& info", |
| ] |
| arg_names = ["v8_property_name", "info"] |
| |
| func_decl, func_def = _make_interceptor_callback( |
| cg_context, function_name, arg_decls, arg_names, cg_context.class_name, |
| "NamedPropertyQuery") |
| body = func_def.body |
| |
| flags = [] |
| if not props.named_setter: |
| flags.append("v8::ReadOnly") |
| if not props.is_named_property_enumerable: |
| flags.append("v8::DontEnum") |
| if not flags: |
| flags.append("v8::None") |
| if len(flags) == 1: |
| property_attribute = flags[0] |
| else: |
| property_attribute = " | ".join(flags) |
| |
| body.extend([ |
| TextNode("""\ |
| ExceptionState exception_state(${isolate}, |
| ExceptionState::kNamedGetterContext, |
| "${interface.identifier}"); |
| bool does_exist = ${blink_receiver}->NamedPropertyQuery( |
| ${blink_property_name}, exception_state); |
| if (!does_exist) |
| return; // Do not intercept. |
| """), |
| TextNode( |
| _format( |
| "bindings::V8SetReturnValue" |
| "(${info}, uint32_t({property_attribute}));", |
| property_attribute= |