| # 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=property_attribute)), |
| ]) |
| |
| return func_decl, func_def |
| |
| |
| def make_named_property_enumerator_callback(cg_context, function_name): |
| assert isinstance(cg_context, CodeGenContext) |
| assert isinstance(function_name, str) |
| |
| props = cg_context.interface.indexed_and_named_properties |
| if "NotEnumerable" in props.named_getter.extended_attributes: |
| return None, None |
| |
| 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, |
| "NamedPropertyEnumerator") |
| body = func_def.body |
| |
| body.append( |
| TextNode("""\ |
| // 3.9.6. [[OwnPropertyKeys]] |
| // https://heycam.github.io/webidl/#legacy-platform-object-ownpropertykeys |
| // step 3. If O supports named properties, then for each P of O's supported |
| // property names that is visible according to the named property |
| // visibility algorithm, append P to keys. |
| Vector<String> blink_property_names; |
| ExceptionState exception_state(${info}.GetIsolate(), |
| ExceptionState::kEnumerationContext, |
| "${interface.identifier}"); |
| ${blink_receiver}->NamedPropertyEnumerator( |
| blink_property_names, exception_state); |
| if (exception_state.HadException()) |
| return; |
| bindings::V8SetReturnValue( |
| ${info}, |
| ToV8(blink_property_names, ${creation_context_object}, ${isolate})); |
| """)) |
| |
| return func_decl, func_def |
| |
| |
| # ---------------------------------------------------------------------------- |
| # Callback functions of interceptors on named properties object |
| # ---------------------------------------------------------------------------- |
| |
| |
| def make_named_props_obj_indexed_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_def = _make_interceptor_callback_def( |
| cg_context, function_name, arg_decls, arg_names, None, |
| "NamedPropertiesObject_IndexedPropertyGetter") |
| body = func_def.body |
| |
| body.append( |
| TextNode("""\ |
| v8::Local<v8::String> property_name = |
| V8AtomicString(${isolate}, AtomicString::Number(${index})); |
| NamedPropsObjNamedGetterCallback(property_name, ${info}); |
| """)) |
| |
| return func_def |
| |
| |
| def make_named_props_obj_indexed_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_def = _make_interceptor_callback_def( |
| cg_context, function_name, arg_decls, arg_names, None, |
| "NamedPropertiesObject_IndexedPropertyDeleter") |
| body = func_def.body |
| |
| body.append( |
| TextNode("""\ |
| bindings::V8SetReturnValue(${info}, false); |
| if (${info}.ShouldThrowOnError()) { |
| ExceptionState exception_state(${info}.GetIsolate(), |
| ExceptionState::kIndexedDeletionContext, |
| "${interface.identifier}"); |
| exception_state.ThrowTypeError("Named property deleter is not supported."); |
| } |
| """)) |
| |
| return func_def |
| |
| |
| def make_named_props_obj_indexed_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_def = _make_interceptor_callback_def( |
| cg_context, function_name, arg_decls, arg_names, None, |
| "NamedPropertiesObject_IndexedPropertyDefiner") |
| body = func_def.body |
| |
| body.append( |
| TextNode("""\ |
| // 3.6.4.2. [[DefineOwnProperty]] |
| // https://heycam.github.io/webidl/#named-properties-object-defineownproperty |
| bindings::V8SetReturnValue(${info}, nullptr); |
| if (${info}.ShouldThrowOnError()) { |
| ExceptionState exception_state(${info}.GetIsolate(), |
| ExceptionState::kIndexedSetterContext, |
| "${interface.identifier}"); |
| exception_state.ThrowTypeError("Named property deleter is not supported."); |
| } |
| """)) |
| |
| return func_def |
| |
| |
| def make_named_props_obj_indexed_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_def = _make_interceptor_callback_def( |
| cg_context, function_name, arg_decls, arg_names, None, |
| "NamedPropertiesObject_IndexedPropertyDescriptor") |
| body = func_def.body |
| |
| body.append( |
| TextNode("""\ |
| v8::Local<v8::String> property_name = |
| V8AtomicString(${isolate}, AtomicString::Number(${index})); |
| NamedPropsObjNamedDescriptorCallback(property_name, ${info}); |
| """)) |
| |
| return func_def |
| |
| |
| def make_named_props_obj_named_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_def = _make_interceptor_callback_def( |
| cg_context, function_name, arg_decls, arg_names, None, |
| "NamedPropertiesObject_NamedPropertyGetter") |
| body = func_def.body |
| |
| body.append( |
| TextNode("""\ |
| // 3.6.4.1. [[GetOwnProperty]] |
| // https://heycam.github.io/webidl/#named-properties-object-getownproperty |
| // |
| // TODO(yukishiino): Update the following hard-coded call to an appropriate |
| // one. |
| V8Window::NamedPropertyGetterCustom(${blink_property_name}, ${info}); |
| """)) |
| |
| return func_def |
| |
| |
| def make_named_props_obj_named_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_def = _make_interceptor_callback_def( |
| cg_context, function_name, arg_decls, arg_names, None, |
| "NamedPropertiesObject_NamedPropertyDeleter") |
| body = func_def.body |
| |
| body.append( |
| TextNode("""\ |
| // 3.6.4.3. [[Delete]] |
| // https://heycam.github.io/webidl/#named-properties-object-delete |
| bindings::V8SetReturnValue(${info}, false); |
| if (${info}.ShouldThrowOnError()) { |
| ExceptionState exception_state(${info}.GetIsolate(), |
| ExceptionState::kNamedDeletionContext, |
| "${interface.identifier}"); |
| exception_state.ThrowTypeError("Named property deleter is not supported."); |
| } |
| """)) |
| |
| return func_def |
| |
| |
| def make_named_props_obj_named_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_def = _make_interceptor_callback_def( |
| cg_context, function_name, arg_decls, arg_names, None, |
| "NamedPropertiesObject_NamedPropertyDefiner") |
| body = func_def.body |
| |
| body.append( |
| TextNode("""\ |
| // 3.6.4.2. [[DefineOwnProperty]] |
| // https://heycam.github.io/webidl/#named-properties-object-defineownproperty |
| 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 func_def |
| |
| |
| def make_named_props_obj_named_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_def = _make_interceptor_callback_def( |
| cg_context, function_name, arg_decls, arg_names, None, |
| "NamedPropertiesObject_NamedPropertyDescriptor") |
| body = func_def.body |
| |
| body.append( |
| TextNode("""\ |
| // 3.6.4.1. [[GetOwnProperty]] |
| // https://heycam.github.io/webidl/#named-properties-object-getownproperty |
| // step 4. If the result of running the named property visibility algorithm |
| // with property name P and object object is true, then: |
| if (${v8_receiver}->GetRealNamedPropertyAttributesInPrototypeChain( |
| ${current_context}, ${v8_property_name}).IsJust()) { |
| return; // Do not intercept. Fallback to OrdinaryGetOwnProperty. |
| } |
| |
| // TODO(yukishiino): Update the following hard-coded call to an appropriate |
| // one. |
| V8Window::NamedPropertyGetterCustom(${blink_property_name}, ${info}); |
| v8::Local<v8::Value> v8_value = ${info}.GetReturnValue().Get(); |
| if (v8_value->IsUndefined()) |
| return; // Do not intercept. Fallback to OrdinaryGetOwnProperty. |
| |
| // step 4.7. If A implements an interface with the |
| // [LegacyUnenumerableNamedProperties] extended attribute, then set |
| // desc.[[Enumerable]] to false, otherwise set it to true. |
| // step 4.8. Set desc.[[Writable]] to true and desc.[[Configurable]] to |
| // true. |
| v8::PropertyDescriptor desc(v8_value, /*writable=*/true); |
| desc.set_enumerable(false); |
| desc.set_configurable(true); |
| bindings::V8SetReturnValue(${info}, desc); |
| """)) |
| |
| return func_def |
| |
| |
| # ---------------------------------------------------------------------------- |
| # Callback functions of cross origin interceptors |
| # ---------------------------------------------------------------------------- |
| |
| |
| def make_cross_origin_access_check_callback(cg_context, function_name): |
| assert isinstance(cg_context, CodeGenContext) |
| assert isinstance(function_name, str) |
| |
| func_def = CxxFuncDefNode( |
| name=function_name, |
| arg_decls=[ |
| "v8::Local<v8::Context> accessing_context", |
| "v8::Local<v8::Object> accessed_object", |
| "v8::Local<v8::Value> unused_data", |
| ], |
| return_type="bool") |
| func_def.set_base_template_vars(cg_context.template_bindings()) |
| body = func_def.body |
| body.add_template_var("accessing_context", "accessing_context") |
| body.add_template_var("accessed_object", "accessed_object") |
| bind_callback_local_vars(body, cg_context) |
| |
| if cg_context.interface.identifier == "Window": |
| blink_class = "DOMWindow" |
| else: |
| blink_class = blink_class_name(cg_context.interface) |
| body.extend([ |
| TextNode( |
| _format( |
| "{blink_class}* blink_accessed_object = " |
| "${class_name}::ToWrappableUnsafe(${accessed_object});", |
| blink_class=blink_class)), |
| TextNode("return BindingSecurity::ShouldAllowAccessTo(" |
| "ToLocalDOMWindow(${accessing_context}), " |
| "blink_accessed_object, " |
| "BindingSecurity::ErrorReportOption::kDoNotReport);"), |
| ]) |
| |
| return func_def |
| |
| |
| def make_cross_origin_indexed_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_def = _make_interceptor_callback_def( |
| cg_context, function_name, arg_decls, arg_names, None, |
| "CrossOriginProperty_IndexedPropertyGetter") |
| body = func_def.body |
| |
| if cg_context.interface.identifier != "Window": |
| body.append(TextNode("${throw_security_error}")) |
| return func_def |
| |
| bind_return_value(body, cg_context, overriding_args=["${index}"]) |
| |
| body.extend([ |
| TextNode("""\ |
| if (${index} >= ${blink_receiver}->length()) { |
| ${throw_security_error} |
| return; |
| } |
| """), |
| make_v8_set_return_value(cg_context), |
| ]) |
| |
| return func_def |
| |
| |
| def make_cross_origin_indexed_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_def = _make_interceptor_callback_def( |
| cg_context, function_name, arg_decls, arg_names, None, |
| "CrossOriginProperty_IndexedPropertySetter") |
| body = func_def.body |
| |
| body.append(TextNode("${throw_security_error}")) |
| |
| return func_def |
| |
| |
| def make_cross_origin_indexed_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_def = _make_interceptor_callback_def( |
| cg_context, function_name, arg_decls, arg_names, None, |
| "CrossOriginProperty_IndexedPropertyDeleter") |
| body = func_def.body |
| |
| body.append(TextNode("${throw_security_error}")) |
| |
| return func_def |
| |
| |
| def make_cross_origin_indexed_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_def = _make_interceptor_callback_def( |
| cg_context, function_name, arg_decls, arg_names, None, |
| "CrossOriginProperty_IndexedPropertyDefiner") |
| body = func_def.body |
| |
| body.append(TextNode("${throw_security_error}")) |
| |
| return func_def |
| |
| |
| def make_cross_origin_indexed_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_def = _make_interceptor_callback_def( |
| cg_context, function_name, arg_decls, arg_names, None, |
| "CrossOriginProperty_IndexedPropertyDescriptor") |
| body = func_def.body |
| |
| if cg_context.interface.identifier != "Window": |
| body.append(TextNode("${throw_security_error}")) |
| return func_def |
| |
| body.append( |
| TextNode("""\ |
| CrossOriginIndexedGetterCallback(${index}, ${info}); |
| v8::Local<v8::Value> v8_value = ${info}.GetReturnValue().Get(); |
| if (v8_value->IsUndefined()) { |
| // Must have already thrown a SecurityError. |
| return; |
| } |
| |
| v8::PropertyDescriptor desc(v8_value, /*writable=*/false); |
| desc.set_enumerable(true); |
| desc.set_configurable(true); |
| bindings::V8SetReturnValue(${info}, desc); |
| """)) |
| |
| return func_def |
| |
| |
| def make_cross_origin_indexed_enumerator_callback(cg_context, function_name): |
| assert isinstance(cg_context, CodeGenContext) |
| assert isinstance(function_name, str) |
| |
| arg_decls = ["const v8::PropertyCallbackInfo<v8::Array>& info"] |
| arg_names = ["info"] |
| |
| func_def = _make_interceptor_callback_def( |
| cg_context, function_name, arg_decls, arg_names, None, |
| "CrossOriginProperty_IndexedPropertyEnumerator") |
| body = func_def.body |
| |
| if cg_context.interface.identifier != "Window": |
| return func_def |
| |
| body.append( |
| TextNode("""\ |
| uint32_t length = ${blink_receiver}->length(); |
| v8::Local<v8::Array> array = |
| bindings::EnumerateIndexedProperties(${isolate}, length); |
| bindings::V8SetReturnValue(${info}, array); |
| """)) |
| |
| return func_def |
| |
| |
| def make_cross_origin_named_getter_callback(cg_context, function_name): |
| assert isinstance(cg_context, CodeGenContext) |
| assert isinstance(function_name, str) |
| |
| arg_decls = [ |
| "v8::Local<v8::Name> v8_property_name", |
| "const v8::PropertyCallbackInfo<v8::Value>& info", |
| ] |
| arg_names = ["v8_property_name", "info"] |
| |
| func_def = _make_interceptor_callback_def( |
| cg_context, function_name, arg_decls, arg_names, None, |
| "CrossOriginProperty_NamedPropertyGetter") |
| body = func_def.body |
| |
| string_case_body = [] |
| string_case_body.append( |
| TextNode("""\ |
| for (const auto& attribute : kCrossOriginAttributeTable) { |
| if (${blink_property_name} != attribute.name) |
| continue; |
| if (UNLIKELY(!attribute.get_value)) { |
| ${throw_security_error} |
| return; |
| } |
| attribute.get_value(${v8_property_name}, ${info}); |
| return; |
| } |
| for (const auto& operation : kCrossOriginOperationTable) { |
| if (${blink_property_name} != operation.name) |
| continue; |
| v8::Local<v8::Function> function; |
| if (bindings::GetCrossOriginFunction( |
| ${info}.GetIsolate(), operation.callback, operation.func_length, |
| ${class_name}::GetWrapperTypeInfo()) |
| .ToLocal(&function)) { |
| bindings::V8SetReturnValue(${info}, function); |
| } |
| return; |
| } |
| """)) |
| if cg_context.interface.identifier == "Window": |
| string_case_body.append( |
| TextNode("""\ |
| // Window object's document-tree child browsing context name property set |
| // |
| // TODO(yukishiino): Update the following hard-coded call to an appropriate |
| // one. |
| V8Window::NamedPropertyGetterCustom(${blink_property_name}, ${info}); |
| if (!${info}.GetReturnValue().Get()->IsUndefined()) |
| return; |
| """)) |
| |
| body.extend([ |
| CxxLikelyIfNode( |
| cond="${v8_property_name}->IsString()", body=string_case_body), |
| EmptyNode(), |
| TextNode("""\ |
| // 7.2.3.2 CrossOriginPropertyFallback ( P ) |
| // https://html.spec.whatwg.org/C/#crossoriginpropertyfallback-(-p-) |
| if (bindings::IsSupportedInCrossOriginPropertyFallback( |
| ${info}.GetIsolate(), ${v8_property_name})) { |
| return ${info}.GetReturnValue().SetUndefined(); |
| } |
| ${throw_security_error} |
| """), |
| ]) |
| |
| return func_def |
| |
| |
| def make_cross_origin_named_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_def = _make_interceptor_callback_def( |
| cg_context, function_name, arg_decls, arg_names, None, |
| "CrossOriginProperty_NamedPropertySetter") |
| body = func_def.body |
| |
| string_case_body = [] |
| string_case_body.append( |
| TextNode("""\ |
| for (const auto& attribute : kCrossOriginAttributeTable) { |
| if (${blink_property_name} == attribute.name && attribute.set_value) { |
| attribute.set_value(${v8_property_name}, ${v8_property_value}, ${info}); |
| return; |
| } |
| } |
| """)) |
| |
| body.extend([ |
| CxxLikelyIfNode( |
| cond="${v8_property_name}->IsString()", body=string_case_body), |
| EmptyNode(), |
| TextNode("${throw_security_error}"), |
| ]) |
| |
| return func_def |
| |
| |
| def make_cross_origin_named_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_def = _make_interceptor_callback_def( |
| cg_context, function_name, arg_decls, arg_names, None, |
| "CrossOriginProperty_NamedPropertyDeleter") |
| body = func_def.body |
| |
| body.append(TextNode("${throw_security_error}")) |
| |
| return func_def |
| |
| |
| def make_cross_origin_named_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_def = _make_interceptor_callback_def( |
| cg_context, function_name, arg_decls, arg_names, None, |
| "CrossOriginProperty_NamedPropertyDefiner") |
| body = func_def.body |
| |
| body.append(TextNode("${throw_security_error}")) |
| |
| return func_def |
| |
| |
| def make_cross_origin_named_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_def = _make_interceptor_callback_def( |
| cg_context, function_name, arg_decls, arg_names, None, |
| "CrossOriginProperty_NamedPropertyDescriptor") |
| body = func_def.body |
| |
| string_case_body = [] |
| string_case_body.append( |
| TextNode("""\ |
| // 7.2.3.4 CrossOriginGetOwnPropertyHelper ( O, P ) |
| // https://html.spec.whatwg.org/C/#crossorigingetownpropertyhelper-(-o,-p-) |
| for (const auto& attribute : kCrossOriginAttributeTable) { |
| if (${blink_property_name} != attribute.name) |
| continue; |
| v8::Local<v8::Value> get; |
| v8::Local<v8::Value> set; |
| if (!bindings::GetCrossOriginFunctionOrUndefined( |
| ${info}.GetIsolate(), attribute.get_callback, 0, |
| ${class_name}::GetWrapperTypeInfo()) |
| .ToLocal(&get) || |
| !bindings::GetCrossOriginFunctionOrUndefined( |
| ${info}.GetIsolate(), attribute.set_callback, 1, |
| ${class_name}::GetWrapperTypeInfo()) |
| .ToLocal(&set)) { |
| return; |
| } |
| v8::PropertyDescriptor desc(get, set); |
| desc.set_enumerable(false); |
| desc.set_configurable(true); |
| bindings::V8SetReturnValue(${info}, desc); |
| return; |
| } |
| for (const auto& operation : kCrossOriginOperationTable) { |
| if (${blink_property_name} != operation.name) |
| continue; |
| v8::Local<v8::Function> function; |
| if (!bindings::GetCrossOriginFunction( |
| ${info}.GetIsolate(), operation.callback, operation.func_length, |
| ${class_name}::GetWrapperTypeInfo()) |
| .ToLocal(&function)) { |
| return; |
| } |
| v8::PropertyDescriptor desc(function, /*writable=*/false); |
| desc.set_enumerable(false); |
| desc.set_configurable(true); |
| bindings::V8SetReturnValue(${info}, desc); |
| return; |
| } |
| """)) |
| if cg_context.interface.identifier == "Window": |
| string_case_body.append( |
| TextNode("""\ |
| // Window object's document-tree child browsing context name property set |
| // |
| // TODO(yukishiino): Update the following hard-coded call to an appropriate |
| // one. |
| V8Window::NamedPropertyGetterCustom(${blink_property_name}, ${info}); |
| if (!${info}.GetReturnValue().Get()->IsUndefined()) { |
| v8::PropertyDescriptor desc(${info}.GetReturnValue().Get(), |
| /*writable=*/false); |
| desc.set_enumerable(false); |
| desc.set_configurable(true); |
| bindings::V8SetReturnValue(${info}, desc); |
| return; |
| } |
| """)) |
| |
| body.extend([ |
| CxxLikelyIfNode( |
| cond="${v8_property_name}->IsString()", body=string_case_body), |
| EmptyNode(), |
| TextNode("""\ |
| // 7.2.3.2 CrossOriginPropertyFallback ( P ) |
| // https://html.spec.whatwg.org/C/#crossoriginpropertyfallback-(-p-) |
| if (bindings::IsSupportedInCrossOriginPropertyFallback( |
| ${info}.GetIsolate(), ${v8_property_name})) { |
| v8::PropertyDescriptor desc(v8::Undefined(${info}.GetIsolate()), |
| /*writable=*/false); |
| desc.set_enumerable(false); |
| desc.set_configurable(true); |
| bindings::V8SetReturnValue(${info}, desc); |
| return; |
| } |
| ${throw_security_error} |
| """), |
| ]) |
| |
| return func_def |
| |
| |
| def make_cross_origin_named_query_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::Integer>& info", |
| ] |
| arg_names = ["v8_property_name", "info"] |
| |
| func_def = _make_interceptor_callback_def( |
| cg_context, function_name, arg_decls, arg_names, None, |
| "CrossOriginProperty_NamedPropertyQuery") |
| body = func_def.body |
| |
| string_case_body = [] |
| string_case_body.append( |
| TextNode("""\ |
| // 7.2.3.4 CrossOriginGetOwnPropertyHelper ( O, P ) |
| // https://html.spec.whatwg.org/C/#crossorigingetownpropertyhelper-(-o,-p-) |
| for (const auto& attribute : kCrossOriginAttributeTable) { |
| if (${blink_property_name} != attribute.name) |
| continue; |
| int32_t v8_property_attribute = v8::DontEnum; |
| if (!attribute.set_callback) |
| v8_property_attribute |= v8::ReadOnly; |
| bindings::V8SetReturnValue(${info}, v8_property_attribute); |
| return; |
| } |
| for (const auto& operation : kCrossOriginOperationTable) { |
| if (${blink_property_name} != operation.name) |
| continue; |
| int32_t v8_property_attribute = v8::DontEnum | v8::ReadOnly; |
| bindings::V8SetReturnValue(${info}, v8_property_attribute); |
| return; |
| } |
| """)) |
| |
| body.extend([ |
| CxxLikelyIfNode( |
| cond="${v8_property_name}->IsString()", body=string_case_body), |
| EmptyNode(), |
| TextNode("""\ |
| // 7.2.3.2 CrossOriginPropertyFallback ( P ) |
| // https://html.spec.whatwg.org/C/#crossoriginpropertyfallback-(-p-) |
| if (bindings::IsSupportedInCrossOriginPropertyFallback( |
| ${info}.GetIsolate(), ${v8_property_name})) { |
| int32_t v8_property_attribute = v8::DontEnum | v8::ReadOnly; |
| bindings::V8SetReturnValue(${info}, v8_property_attribute); |
| return; |
| } |
| """), |
| ]) |
| |
| return func_def |
| |
| |
| def make_cross_origin_named_enumerator_callback(cg_context, function_name): |
| assert isinstance(cg_context, CodeGenContext) |
| assert isinstance(function_name, str) |
| |
| arg_decls = ["const v8::PropertyCallbackInfo<v8::Array>& info"] |
| arg_names = ["info"] |
| |
| func_def = _make_interceptor_callback_def( |
| cg_context, function_name, arg_decls, arg_names, None, |
| "CrossOriginProperty_NamedPropertyEnumerator") |
| body = func_def.body |
| |
| body.append( |
| TextNode("""\ |
| bindings::V8SetReturnValue( |
| ${info}, |
| bindings::EnumerateCrossOriginProperties( |
| ${isolate}, |
| kCrossOriginAttributeTable, |
| kCrossOriginOperationTable)); |
| """)) |
| |
| return func_def |
| |
| |
| # ---------------------------------------------------------------------------- |
| # Callback functions of same origin interceptors |
| # ---------------------------------------------------------------------------- |
| |
| |
| def make_same_origin_indexed_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_def = _make_interceptor_callback_def( |
| cg_context, function_name, arg_decls, arg_names, None, |
| "SameOriginProperty_IndexedPropertyGetter") |
| body = func_def.body |
| |
| bind_return_value(body, cg_context, overriding_args=["${index}"]) |
| |
| body.extend([ |
| TextNode("""\ |
| if (${index} >= ${blink_receiver}->length()) { |
| return; |
| } |
| """), |
| make_v8_set_return_value(cg_context), |
| ]) |
| |
| return func_def |
| |
| |
| def make_same_origin_indexed_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_def = _make_interceptor_callback_def( |
| cg_context, function_name, arg_decls, arg_names, None, |
| "SameOriginProperty_IndexedPropertySetter") |
| body = func_def.body |
| |
| body.append( |
| TextNode("""\ |
| 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_def |
| |
| |
| def make_same_origin_indexed_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_def = _make_interceptor_callback_def( |
| cg_context, function_name, arg_decls, arg_names, None, |
| "SameOriginProperty_IndexedPropertyDeleter") |
| body = func_def.body |
| |
| body.append( |
| TextNode("""\ |
| // 7.4.9 [[Delete]] ( P ) |
| // https://html.spec.whatwg.org/C/#windowproxy-delete |
| 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_def |
| |
| |
| def make_same_origin_indexed_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_def = _make_interceptor_callback_def( |
| cg_context, function_name, arg_decls, arg_names, None, |
| "SameOriginProperty_IndexedPropertyDefiner") |
| body = func_def.body |
| |
| body.append( |
| TextNode("""\ |
| // 7.4.6 [[DefineOwnProperty]] ( P, Desc ) |
| // https://html.spec.whatwg.org/C/#windowproxy-defineownproperty |
| 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."); |
| } |
| """)) |
| |
| return func_def |
| |
| |
| def make_same_origin_indexed_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_def = _make_interceptor_callback_def( |
| cg_context, function_name, arg_decls, arg_names, None, |
| "SameOriginProperty_IndexedPropertyDescriptor") |
| body = func_def.body |
| |
| body.append( |
| TextNode("""\ |
| // 7.4.5 [[GetOwnProperty]] ( P ) |
| // https://html.spec.whatwg.org/C/#windowproxy-getownproperty |
| SameOriginIndexedGetterCallback(${index}, ${info}); |
| v8::Local<v8::Value> v8_value = ${info}.GetReturnValue().Get(); |
| if (v8_value->IsUndefined()) { |
| return; // Do not intercept. |
| } |
| |
| v8::PropertyDescriptor desc(v8_value, /*writable=*/false); |
| desc.set_enumerable(true); |
| desc.set_configurable(true); |
| bindings::V8SetReturnValue(${info}, desc); |
| """)) |
| |
| return func_def |
| |
| |
| def make_same_origin_indexed_enumerator_callback(cg_context, function_name): |
| assert isinstance(cg_context, CodeGenContext) |
| assert isinstance(function_name, str) |
| |
| arg_decls = ["const v8::PropertyCallbackInfo<v8::Array>& info"] |
| arg_names = ["info"] |
| |
| func_def = _make_interceptor_callback_def( |
| cg_context, function_name, arg_decls, arg_names, None, |
| "SameOriginProperty_IndexedPropertyEnumerator") |
| body = func_def.body |
| |
| body.append( |
| TextNode("""\ |
| uint32_t length = ${blink_receiver}->length(); |
| v8::Local<v8::Array> array = |
| bindings::EnumerateIndexedProperties(${isolate}, length); |
| bindings::V8SetReturnValue(${info}, array); |
| """)) |
| |
| return func_def |
| |
| |
| # ---------------------------------------------------------------------------- |
| # Installer functions |
| # ---------------------------------------------------------------------------- |
| |
| # FN = function name |
| FN_INSTALL_INTERFACE_TEMPLATE = name_style.func("InstallInterfaceTemplate") |
| FN_INSTALL_UNCONDITIONAL_PROPS = name_style.func( |
| "InstallUnconditionalProperties") |
| FN_INSTALL_CONTEXT_INDEPENDENT_PROPS = name_style.func( |
| "InstallContextIndependentProperties") |
| FN_INSTALL_CONTEXT_DEPENDENT_PROPS = name_style.func( |
| "InstallContextDependentProperties") |
| |
| # TP = trampoline name |
| TP_INSTALL_INTERFACE_TEMPLATE = name_style.member_var( |
| "install_interface_template_func") |
| TP_INSTALL_UNCONDITIONAL_PROPS = name_style.member_var( |
| "install_unconditional_props_func") |
| TP_INSTALL_CONTEXT_INDEPENDENT_PROPS = name_style.member_var( |
| "install_context_independent_props_func") |
| TP_INSTALL_CONTEXT_DEPENDENT_PROPS = name_style.member_var( |
| "install_context_dependent_props_func") |
| |
| |
| def bind_installer_local_vars(code_node, cg_context): |
| assert isinstance(code_node, SymbolScopeNode) |
| assert isinstance(cg_context, CodeGenContext) |
| |
| S = SymbolNode |
| |
| local_vars = [] |
| |
| local_vars.extend([ |
| S("is_in_secure_context", |
| ("const bool ${is_in_secure_context} = " |
| "${execution_context}->IsSecureContext();")), |
| S("isolate", "v8::Isolate* ${isolate} = ${v8_context}->GetIsolate();"), |
| S("script_state", |
| "ScriptState* ${script_state} = ScriptState::From(${v8_context});"), |
| S("wrapper_type_info", |
| ("const WrapperTypeInfo* const ${wrapper_type_info} = " |
| "${class_name}::GetWrapperTypeInfo();")), |
| ]) |
| |
| if cg_context.interface: |
| local_vars.extend([ |
| S("interface_function_template", |
| ("v8::Local<v8::FunctionTemplate> " |
| "${interface_function_template} = " |
| "${interface_template}.As<v8::FunctionTemplate>();")), |
| S("instance_object_template", |
| ("v8::Local<v8::ObjectTemplate> ${instance_object_template} = " |
| "${interface_function_template}->InstanceTemplate();")), |
| S("instance_template", |
| ("v8::Local<v8::Template> ${instance_template} = " |
| "${instance_object_template};")), |
| S("prototype_object_template", |
| ("v8::Local<v8::ObjectTemplate> ${prototype_object_template} = " |
| "${interface_function_template}->PrototypeTemplate();")), |
| S("prototype_template", |
| ("v8::Local<v8::Template> ${prototype_template} = " |
| "${prototype_object_template};")), |
| S("signature", ("v8::Local<v8::Signature> ${signature} = " |
| "v8::Signature::New(${isolate}, " |
| "${interface_function_template});")), |
| ]) |
| elif cg_context.namespace: |
| local_vars.extend([ |
| S("namespace_object_template", |
| ("v8::Local<v8::ObjectTemplate> " |
| "${namespace_object_template} = " |
| "${interface_template}.As<v8::ObjectTemplate>();")), |
| S("instance_template", |
| "v8::Local<v8::Template> ${instance_template};"), |
| S("prototype_template", |
| "v8::Local<v8::Template> ${prototype_template};"), |
| S("signature", "v8::Local<v8::Signature> ${signature};"), |
| ]) |
| elif cg_context.callback_interface: |
| local_vars.extend([ |
| S("interface_function_template", |
| ("v8::Local<v8::FunctionTemplate> " |
| "${interface_function_template} = " |
| "${interface_template}.As<v8::FunctionTemplate>();")), |
| S("instance_template", |
| "v8::Local<v8::Template> ${instance_template};"), |
| S("prototype_template", |
| "v8::Local<v8::Template> ${prototype_template};"), |
| S("signature", "v8::Local<v8::Signature> ${signature};"), |
| ]) |
| |
| # context_feature_settings |
| node = S("context_feature_settings", |
| ("const ContextFeatureSettings* ${context_feature_settings} = " |
| "ContextFeatureSettings::From(" |
| "${execution_context}, " |
| "ContextFeatureSettings::CreationMode::kDontCreateIfNotExists" |
| ");")) |
| node.accumulate( |
| CodeGenAccumulator.require_include_headers([ |
| "third_party/blink/renderer/core/context_features/context_feature_settings.h" |
| ])) |
| local_vars.append(node) |
| |
| # execution_context |
| node = S("execution_context", ("ExecutionContext* ${execution_context} = " |
| "ExecutionContext::From(${script_state});")) |
| node.accumulate( |
| CodeGenAccumulator.require_include_headers([ |
| "third_party/blink/renderer/core/execution_context/execution_context.h" |
| ])) |
| local_vars.append(node) |
| |
| # parent_interface_template |
| pattern = ( |
| "v8::Local<v8::FunctionTemplate> ${parent_interface_template}{_1};") |
| interface = cg_context.interface |
| if not interface: |
| _1 = "" |
| elif (interface and "Global" in interface.extended_attributes |
| and interface.indexed_and_named_properties |
| and interface.indexed_and_named_properties.has_named_properties): |
| # https://heycam.github.io/webidl/#named-properties-object |
| _1 = " = ${npo_interface_template}" # npo = named properties object |
| elif interface.inherited: |
| _1 = (" = ${wrapper_type_info}->parent_class" |
| "->GetV8ClassTemplate(${isolate}, ${world})" |
| ".As<v8::FunctionTemplate>()") |
| else: |
| _1 = "" |
| local_vars.append(S("parent_interface_template", _format(pattern, _1=_1))) |
| |
| # npo_interface_template |
| # npo = named properties object |
| text = """\ |
| // Named properties object |
| v8::Local<v8::FunctionTemplate> ${npo_interface_template} = |
| v8::FunctionTemplate::New(${isolate}); |
| v8::Local<v8::ObjectTemplate> ${npo_prototype_template} = |
| ${npo_interface_template}->PrototypeTemplate(); |
| ${npo_interface_template}->Inherit( |
| ${wrapper_type_info}->parent_class |
| ->GetV8ClassTemplate(${isolate}, ${world}).As<v8::FunctionTemplate>()); |
| ${npo_prototype_template}->SetImmutableProto(); |
| ${npo_prototype_template}->Set( |
| v8::Symbol::GetToStringTag(${isolate}), |
| V8AtomicString(${isolate}, "${interface.identifier}Properties"), |
| static_cast<v8::PropertyAttribute>(v8::ReadOnly | v8::DontEnum)); |
| // Make the named properties object look like the global object. Note that |
| // the named properties object is _not_ a prototype object, plus, we'd like |
| // the named properties object to behave just like the global object (= the |
| // wrapper object of the global object) from the point of view of named |
| // properties. |
| // https://heycam.github.io/webidl/#named-properties-object |
| ${npo_prototype_template}->SetInternalFieldCount( |
| kV8DefaultWrapperInternalFieldCount); |
| """ |
| local_vars.append(S("npo_interface_template", text)) |
| local_vars.append( |
| S("npo_prototype_template", |
| "<% npo_interface_template.request_symbol_definition() %>")) |
| |
| # Arguments have priority over local vars. |
| template_vars = code_node.template_vars |
| for symbol_node in local_vars: |
| if symbol_node.name not in template_vars: |
| code_node.register_code_symbol(symbol_node) |
| |
| |
| def _make_property_entry_cross_origin_check(property_, |
| is_get=False, |
| is_set=False): |
| constants = { |
| False: "unsigned(IDLMemberInstaller::FlagCrossOriginCheck::kCheck)", |
| True: |
| "unsigned(IDLMemberInstaller::FlagCrossOriginCheck::kDoNotCheck)", |
| } |
| if property_.is_static: |
| return constants[True] |
| if "CrossOrigin" not in property_.extended_attributes: |
| return constants[False] |
| values = property_.extended_attributes.values_of("CrossOrigin") |
| if is_get: |
| return constants[not values or "Getter" in values] |
| elif is_set: |
| return constants["Setter" in values] |
| else: |
| return constants[True] |
| |
| |
| def _make_property_entry_location(property_): |
| if hasattr(property_, "is_static") and property_.is_static: |
| return "unsigned(IDLMemberInstaller::FlagLocation::kInterface)" |
| if "Global" in property_.owner.extended_attributes: |
| return "unsigned(IDLMemberInstaller::FlagLocation::kInstance)" |
| if "LegacyUnforgeable" in property_.extended_attributes: |
| return "unsigned(IDLMemberInstaller::FlagLocation::kInstance)" |
| return "unsigned(IDLMemberInstaller::FlagLocation::kPrototype)" |
| |
| |
| def _make_property_entry_receiver_check(property_): |
| if ("LegacyLenientThis" in property_.extended_attributes |
| or property_.is_static |
| or (isinstance(property_, web_idl.Attribute) |
| and property_.idl_type.unwrap().is_promise) |
| or (isinstance(property_, web_idl.OverloadGroup) |
| and property_[0].return_type.unwrap().is_promise)): |
| return "unsigned(IDLMemberInstaller::FlagReceiverCheck::kDoNotCheck)" |
| else: |
| return "unsigned(IDLMemberInstaller::FlagReceiverCheck::kCheck)" |
| |
| |
| def _make_property_entry_v8_c_function(entry): |
| if entry.no_alloc_direct_callback_name is None: |
| return None |
| return "v8::CFunction::MakeWithFallbackSupport({})".format( |
| entry.no_alloc_direct_callback_name) |
| |
| |
| def _make_property_entry_v8_cached_accessor(property_): |
| return "unsigned(V8PrivateProperty::CachedAccessor::{})".format( |
| property_.extended_attributes.value_of("CachedAccessor") or "kNone") |
| |
| |
| def _make_property_entry_v8_property_attribute(property_): |
| values = [] |
| if "NotEnumerable" in property_.extended_attributes: |
| values.append("v8::DontEnum") |
| if "LegacyUnforgeable" in property_.extended_attributes: |
| if not isinstance(property_, web_idl.Attribute): |
| values.append("v8::ReadOnly") |
| values.append("v8::DontDelete") |
| if not values: |
| values.append("v8::None") |
| if len(values) == 1: |
| return "unsigned({})".format(values[0]) |
| else: |
| return "unsigned({})".format(" | ".join(values)) |
| |
| |
| def _make_property_entry_v8_side_effect(property_): |
| if property_.extended_attributes.value_of("Affects") == "Nothing": |
| return "unsigned(v8::SideEffectType::kHasNoSideEffect)" |
| else: |
| return "unsigned(v8::SideEffectType::kHasSideEffect)" |
| |
| |
| def _make_property_entry_world(world): |
| if world == CodeGenContext.MAIN_WORLD: |
| return "unsigned(IDLMemberInstaller::FlagWorld::kMainWorld)" |
| if world == CodeGenContext.NON_MAIN_WORLDS: |
| return "unsigned(IDLMemberInstaller::FlagWorld::kNonMainWorlds)" |
| if world == CodeGenContext.ALL_WORLDS: |
| return "unsigned(IDLMemberInstaller::FlagWorld::kAllWorlds)" |
| assert False |
| |
| |
| def _make_attribute_registration_table(table_name, attribute_entries): |
| assert isinstance(table_name, str) |
| assert isinstance(attribute_entries, (list, tuple)) |
| assert all( |
| isinstance(entry, _PropEntryAttribute) for entry in attribute_entries) |
| |
| T = TextNode |
| |
| entry_nodes = [] |
| for entry in attribute_entries: |
| pattern = ("{{" |
| "\"{property_name}\", " |
| "{attribute_get_callback}, " |
| "{attribute_set_callback}, " |
| "{v8_property_attribute}, " |
| "{location}, " |
| "{world}, " |
| "{receiver_check}, " |
| "{cross_origin_check_for_get}, " |
| "{cross_origin_check_for_set}, " |
| "{v8_side_effect}, " |
| "{v8_cached_accessor}" |
| "}},") |
| text = _format( |
| pattern, |
| property_name=entry.property_.identifier, |
| attribute_get_callback=entry.attr_get_callback_name, |
| attribute_set_callback=(entry.attr_set_callback_name or "nullptr"), |
| v8_property_attribute=_make_property_entry_v8_property_attribute( |
| entry.property_), |
| location=_make_property_entry_location(entry.property_), |
| world=_make_property_entry_world(entry.world), |
| receiver_check=_make_property_entry_receiver_check( |
| entry.property_), |
| cross_origin_check_for_get=( |
| _make_property_entry_cross_origin_check(entry.property_, |
| is_get=True)), |
| cross_origin_check_for_set=( |
| _make_property_entry_cross_origin_check(entry.property_, |
| is_set=True)), |
| v8_side_effect=_make_property_entry_v8_side_effect( |
| entry.property_), |
| v8_cached_accessor=_make_property_entry_v8_cached_accessor( |
| entry.property_)) |
| entry_nodes.append(T(text)) |
| |
| return ListNode([ |
| T("static const IDLMemberInstaller::AttributeConfig " + table_name + |
| "[] = {"), |
| ListNode(entry_nodes), |
| T("};"), |
| ]) |
| |
| |
| def _make_constant_callback_registration_table(table_name, constant_entries): |
| assert isinstance(table_name, str) |
| assert isinstance(constant_entries, (list, tuple)) |
| assert all( |
| isinstance(entry, _PropEntryConstant) |
| and isinstance(entry.const_callback_name, str) |
| for entry in constant_entries) |
| |
| T = TextNode |
| |
| entry_nodes = [] |
| for entry in constant_entries: |
| pattern = ("{{" "\"{property_name}\", " "{constant_callback}" "}},") |
| text = _format( |
| pattern, |
| property_name=entry.property_.identifier, |
| constant_callback=entry.const_callback_name) |
| entry_nodes.append(T(text)) |
| |
| return ListNode([ |
| T("static const IDLMemberInstaller::ConstantCallbackConfig " + |
| table_name + "[] = {"), |
| ListNode(entry_nodes), |
| T("};"), |
| ]) |
| |
| |
| def _make_constant_value_registration_table(table_name, constant_entries): |
| assert isinstance(table_name, str) |
| assert isinstance(constant_entries, (list, tuple)) |
| assert all( |
| isinstance(entry, _PropEntryConstant) |
| and entry.const_callback_name is None for entry in constant_entries) |
| |
| T = TextNode |
| |
| entry_nodes = [] |
| for entry in constant_entries: |
| pattern = ("{{" |
| "\"{property_name}\", " |
| "{constant_value}" |
| "}},") |
| text = _format(pattern, |
| property_name=entry.property_.identifier, |
| constant_value=entry.const_constant_name) |
| entry_nodes.append(T(text)) |
| |
| return ListNode([ |
| T("static const IDLMemberInstaller::ConstantValueConfig " + |
| table_name + "[] = {"), |
| ListNode(entry_nodes), |
| T("};"), |
| ]) |
| |
| |
| def _make_exposed_construct_registration_table(table_name, |
| exposed_construct_entries): |
| assert isinstance(table_name, str) |
| assert isinstance(exposed_construct_entries, (list, tuple)) |
| assert all( |
| isinstance(entry, _PropEntryExposedConstruct) |
| for entry in exposed_construct_entries) |
| |
| T = TextNode |
| |
| entry_nodes = [] |
| for entry in exposed_construct_entries: |
| pattern = ("{{" |
| "\"{property_name}\", " |
| "{exposed_construct_callback}" |
| "}}, ") |
| text = _format(pattern, |
| property_name=entry.property_.identifier, |
| exposed_construct_callback=entry.prop_callback_name) |
| entry_nodes.append(T(text)) |
| |
| return ListNode([ |
| T("static const IDLMemberInstaller::ExposedConstructConfig " + |
| table_name + "[] = {"), |
| ListNode(entry_nodes), |
| T("};"), |
| ]) |
| |
| |
| def _make_operation_registration_table(table_name, operation_entries): |
| assert isinstance(table_name, str) |
| assert isinstance(operation_entries, (list, tuple)) |
| assert all( |
| isinstance(entry, _PropEntryOperationGroup) |
| for entry in operation_entries) |
| |
| T = TextNode |
| |
| no_alloc_direct_call_count = 0 |
| for entry in operation_entries: |
| if entry.no_alloc_direct_callback_name: |
| no_alloc_direct_call_count += 1 |
| assert (no_alloc_direct_call_count == 0 |
| or no_alloc_direct_call_count == len(operation_entries)) |
| no_alloc_direct_call_enabled = no_alloc_direct_call_count > 0 |
| |
| entry_nodes = [] |
| for entry in operation_entries: |
| pattern = ("{{" |
| "\"{property_name}\", " |
| "{operation_callback}, " |
| "{function_length}, " |
| "{v8_property_attribute}, " |
| "{location}, " |
| "{world}, " |
| "{receiver_check}, " |
| "{cross_origin_check}, " |
| "{v8_side_effect}" |
| "}}, ") |
| if no_alloc_direct_call_enabled: |
| pattern = "{{" + pattern + "{v8_c_function}}}, " |
| text = _format( |
| pattern, |
| property_name=entry.property_.identifier, |
| operation_callback=entry.op_callback_name, |
| function_length=entry.op_func_length, |
| v8_property_attribute=_make_property_entry_v8_property_attribute( |
| entry.property_), |
| location=_make_property_entry_location(entry.property_), |
| world=_make_property_entry_world(entry.world), |
| receiver_check=_make_property_entry_receiver_check( |
| entry.property_), |
| cross_origin_check=_make_property_entry_cross_origin_check( |
| entry.property_), |
| v8_side_effect=_make_property_entry_v8_side_effect( |
| entry.property_), |
| v8_c_function=_make_property_entry_v8_c_function(entry)) |
| entry_nodes.append(T(text)) |
| |
| table_decl_before_name = ( |
| "static const IDLMemberInstaller::OperationConfig") |
| if no_alloc_direct_call_enabled: |
| table_decl_before_name = ( |
| "static const " |
| "IDLMemberInstaller::NoAllocDirectCallOperationConfig") |
| return ListNode([ |
| T(table_decl_before_name + " " + table_name + "[] = {"), |
| ListNode(entry_nodes), |
| T("};"), |
| ]) |
| |
| |
| class _PropEntryBase(object): |
| def __init__(self, is_context_dependent, exposure_conditional, world, |
| property_): |
| assert isinstance(is_context_dependent, bool) |
| assert isinstance(exposure_conditional, CodeGenExpr) |
| |
| self.is_context_dependent = is_context_dependent |
| self.exposure_conditional = exposure_conditional |
| self.world = world |
| self.property_ = property_ |
| |
| |
| class _PropEntryAttribute(_PropEntryBase): |
| def __init__(self, is_context_dependent, exposure_conditional, world, |
| attribute, attr_get_callback_name, attr_set_callback_name): |
| assert isinstance(attr_get_callback_name, str) |
| assert _is_none_or_str(attr_set_callback_name) |
| |
| _PropEntryBase.__init__(self, is_context_dependent, |
| exposure_conditional, world, attribute) |
| self.attr_get_callback_name = attr_get_callback_name |
| self.attr_set_callback_name = attr_set_callback_name |
| |
| |
| class _PropEntryConstant(_PropEntryBase): |
| def __init__(self, is_context_dependent, exposure_conditional, world, |
| constant, const_callback_name, const_constant_name): |
| assert _is_none_or_str(const_callback_name) |
| assert isinstance(const_constant_name, str) |
| |
| _PropEntryBase.__init__(self, is_context_dependent, |
| exposure_conditional, world, constant) |
| self.const_callback_name = const_callback_name |
| self.const_constant_name = const_constant_name |
| |
| |
| class _PropEntryConstructorGroup(_PropEntryBase): |
| def __init__(self, is_context_dependent, exposure_conditional, world, |
| constructor_group, ctor_callback_name, ctor_func_length): |
| assert isinstance(ctor_callback_name, str) |
| assert isinstance(ctor_func_length, int) |
| |
| _PropEntryBase.__init__(self, is_context_dependent, |
| exposure_conditional, world, constructor_group) |
| self.ctor_callback_name = ctor_callback_name |
| self.ctor_func_length = ctor_func_length |
| |
| |
| class _PropEntryExposedConstruct(_PropEntryBase): |
| def __init__(self, is_context_dependent, exposure_conditional, world, |
| exposed_construct, prop_callback_name): |
| assert isinstance(prop_callback_name, str) |
| |
| _PropEntryBase.__init__(self, is_context_dependent, |
| exposure_conditional, world, exposed_construct) |
| self.prop_callback_name = prop_callback_name |
| |
| |
| class _PropEntryOperationGroup(_PropEntryBase): |
| def __init__(self, |
| is_context_dependent, |
| exposure_conditional, |
| world, |
| operation_group, |
| op_callback_name, |
| op_func_length, |
| no_alloc_direct_callback_name=None): |
| assert isinstance(op_callback_name, str) |
| assert isinstance(op_func_length, int) |
| |
| _PropEntryBase.__init__(self, is_context_dependent, |
| exposure_conditional, world, operation_group) |
| self.op_callback_name = op_callback_name |
| self.op_func_length = op_func_length |
| self.no_alloc_direct_callback_name = no_alloc_direct_callback_name |
| |
| |
| def make_property_entries_and_callback_defs(cg_context, attribute_entries, |
| constant_entries, |
| constructor_entries, |
| exposed_construct_entries, |
| operation_entries): |
| """ |
| Creates intermediate objects to help property installation and also makes |
| code nodes of callback functions. |
| |
| Args: |
| attribute_entries: |
| constructor_entries: |
| exposed_construct_entries: |
| operation_entries: |
| Output parameters to store the intermediate objects. |
| """ |
| assert isinstance(cg_context, CodeGenContext) |
| assert isinstance(attribute_entries, list) |
| assert isinstance(constant_entries, list) |
| assert isinstance(constructor_entries, list) |
| assert isinstance(exposed_construct_entries, list) |
| assert isinstance(operation_entries, list) |
| |
| class_like = cg_context.class_like |
| interface = cg_context.interface |
| global_names = class_like.extended_attributes.values_of("Global") |
| |
| callback_def_nodes = ListNode() |
| |
| def iterate(members, callback): |
| for member in members: |
| is_context_dependent = member.exposure.is_context_dependent( |
| global_names) |
| exposure_conditional = expr_from_exposure( |
| member.exposure, |
| global_names=global_names, |
| may_use_feature_selector=True) |
| |
| if "PerWorldBindings" in member.extended_attributes: |
| worlds = (CodeGenContext.MAIN_WORLD, |
| CodeGenContext.NON_MAIN_WORLDS) |
| else: |
| worlds = (CodeGenContext.ALL_WORLDS, ) |
| |
| for world in worlds: |
| callback(member, is_context_dependent, exposure_conditional, |
| world) |
| |
| def process_attribute(attribute, is_context_dependent, |
| exposure_conditional, world): |
| if "CSSProperty" in attribute.extended_attributes: |
| return # [CSSProperty] will be installed in a special manner. |
| |
| cgc_attr = cg_context.make_copy(attribute=attribute, for_world=world) |
| cgc = cgc_attr.make_copy(attribute_get=True) |
| attr_get_callback_name = callback_function_name(cgc) |
| attr_get_callback_node = make_attribute_get_callback_def( |
| cgc, attr_get_callback_name) |
| cgc = cgc_attr.make_copy(attribute_set=True) |
| attr_set_callback_name = callback_function_name(cgc) |
| attr_set_callback_node = make_attribute_set_callback_def( |
| cgc, attr_set_callback_name) |
| if attr_set_callback_node is None: |
| attr_set_callback_name = None |
| |
| callback_def_nodes.extend([ |
| attr_get_callback_node, |
| EmptyNode(), |
| attr_set_callback_node, |
| EmptyNode(), |
| ]) |
| |
| attribute_entries.append( |
| _PropEntryAttribute( |
| is_context_dependent=is_context_dependent, |
| exposure_conditional=exposure_conditional, |
| world=world, |
| attribute=attribute, |
| attr_get_callback_name=attr_get_callback_name, |
| attr_set_callback_name=attr_set_callback_name)) |
| |
| def process_constant(constant, is_context_dependent, exposure_conditional, |
| world): |
| cgc = cg_context.make_copy( |
| constant=constant, |
| for_world=world, |
| v8_callback_type=CodeGenContext.V8_ACCESSOR_NAME_GETTER_CALLBACK) |
| const_callback_name = callback_function_name(cgc) |
| const_callback_node = make_constant_callback_def( |
| cgc, const_callback_name) |
| if const_callback_node is None: |
| const_callback_name = None |
| # IDL constant's C++ constant name |
| const_constant_name = _format("${class_name}::Constant::{}", |
| constant_name(cgc)) |
| |
| callback_def_nodes.extend([ |
| const_callback_node, |
| EmptyNode(), |
| ]) |
| |
| constant_entries.append( |
| _PropEntryConstant( |
| is_context_dependent=is_context_dependent, |
| exposure_conditional=exposure_conditional, |
| world=world, |
| constant=constant, |
| const_callback_name=const_callback_name, |
| const_constant_name=const_constant_name)) |
| |
| def process_constructor_group(constructor_group, is_context_dependent, |
| exposure_conditional, world): |
| cgc = cg_context.make_copy( |
| constructor_group=constructor_group, for_world=world) |
| ctor_callback_name = callback_function_name(cgc) |
| ctor_callback_node = make_constructor_callback_def( |
| cgc, ctor_callback_name) |
| |
| callback_def_nodes.extend([ |
| ctor_callback_node, |
| EmptyNode(), |
| ]) |
| |
| constructor_entries.append( |
| _PropEntryConstructorGroup( |
| is_context_dependent=is_context_dependent, |
| exposure_conditional=exposure_conditional, |
| world=world, |
| constructor_group=constructor_group, |
| ctor_callback_name=ctor_callback_name, |
| ctor_func_length=( |
| constructor_group.min_num_of_required_arguments))) |
| |
| def process_exposed_construct(exposed_construct, is_context_dependent, |
| exposure_conditional, world): |
| if isinstance(exposed_construct, web_idl.LegacyWindowAlias): |
| cgc = cg_context.make_copy( |
| exposed_construct=exposed_construct.original, |
| legacy_window_alias=exposed_construct, |
| for_world=world, |
| v8_callback_type=CodeGenContext. |
| V8_ACCESSOR_NAME_GETTER_CALLBACK) |
| elif ("LegacyNoInterfaceObject" in |
| exposed_construct.extended_attributes): |
| return # Skip due to [LegacyNoInterfaceObject]. |
| else: |
| cgc = cg_context.make_copy( |
| exposed_construct=exposed_construct, |
| for_world=world, |
| v8_callback_type=CodeGenContext. |
| V8_ACCESSOR_NAME_GETTER_CALLBACK) |
| prop_callback_name = callback_function_name(cgc) |
| prop_callback_node = make_exposed_construct_callback_def( |
| cgc, prop_callback_name) |
| |
| callback_def_nodes.extend([ |
| prop_callback_node, |
| EmptyNode(), |
| ]) |
| |
| exposed_construct_entries.append( |
| _PropEntryExposedConstruct( |
| is_context_dependent=is_context_dependent, |
| exposure_conditional=exposure_conditional, |
| world=world, |
| exposed_construct=exposed_construct, |
| prop_callback_name=prop_callback_name)) |
| |
| def process_named_constructor_group(named_constructor_group, |
| is_context_dependent, |
| exposure_conditional, world): |
| cgc = cg_context.make_copy( |
| exposed_construct=named_constructor_group, |
| is_named_constructor=True, |
| for_world=world, |
| v8_callback_type=CodeGenContext.V8_ACCESSOR_NAME_GETTER_CALLBACK) |
| prop_callback_name = callback_function_name(cgc) |
| prop_callback_node = make_named_constructor_property_callback_def( |
| cgc, prop_callback_name) |
| |
| callback_def_nodes.extend([ |
| prop_callback_node, |
| EmptyNode(), |
| ]) |
| |
| exposed_construct_entries.append( |
| _PropEntryExposedConstruct( |
| is_context_dependent=is_context_dependent, |
| exposure_conditional=exposure_conditional, |
| world=world, |
| exposed_construct=named_constructor_group, |
| prop_callback_name=prop_callback_name)) |
| |
| def process_operation_group(operation_group, is_context_dependent, |
| exposure_conditional, world): |
| cgc = cg_context.make_copy( |
| operation_group=operation_group, for_world=world) |
| op_callback_name = callback_function_name(cgc) |
| no_alloc_direct_callback_name = ( |
| callback_function_name(cgc, no_alloc_direct_call=True) |
| if "NoAllocDirectCall" in operation_group.extended_attributes else |
| None) |
| op_callback_node = make_operation_callback_def( |
| cgc, |
| op_callback_name, |
| no_alloc_direct_callback_name=no_alloc_direct_callback_name) |
| |
| callback_def_nodes.extend([ |
| op_callback_node, |
| EmptyNode(), |
| ]) |
| |
| operation_entries.append( |
| _PropEntryOperationGroup( |
| is_context_dependent=is_context_dependent, |
| exposure_conditional=exposure_conditional, |
| world=world, |
| operation_group=operation_group, |
| op_callback_name=op_callback_name, |
| op_func_length=operation_group.min_num_of_required_arguments, |
| no_alloc_direct_callback_name=no_alloc_direct_callback_name)) |
| |
| def process_stringifier(_, is_context_dependent, exposure_conditional, |
| world): |
| cgc = cg_context.make_copy( |
| stringifier=interface.stringifier, for_world=world) |
| op_callback_name = callback_function_name(cgc) |
| op_callback_node = make_stringifier_callback_def(cgc, op_callback_name) |
| |
| callback_def_nodes.extend([ |
| op_callback_node, |
| EmptyNode(), |
| ]) |
| |
| operation_entries.append( |
| _PropEntryOperationGroup( |
| is_context_dependent=is_context_dependent, |
| exposure_conditional=exposure_conditional, |
| world=world, |
| operation_group=cgc.property_, |
| op_callback_name=op_callback_name, |
| op_func_length=0)) |
| |
| iterate(class_like.attributes, process_attribute) |
| iterate(class_like.constants, process_constant) |
| if interface: |
| iterate(interface.constructor_groups, process_constructor_group) |
| iterate(interface.exposed_constructs, process_exposed_construct) |
| iterate(interface.legacy_window_aliases, process_exposed_construct) |
| named_constructor_groups = [ |
| group for construct in interface.exposed_constructs |
| for group in construct.named_constructor_groups |
| if construct.named_constructor_groups |
| ] |
| iterate(named_constructor_groups, process_named_constructor_group) |
| if not class_like.is_callback_interface: |
| iterate(class_like.operation_groups, process_operation_group) |
| if interface and interface.stringifier: |
| iterate([interface.stringifier.operation], process_stringifier) |
| collectionlike = interface and (interface.iterable or interface.maplike |
| or interface.setlike) |
| if collectionlike: |
| |
| def should_define(target): |
| if not target[0].is_optionally_defined: |
| return True |
| return all(target.identifier != member.identifier |
| for member in itertools.chain( |
| interface.attributes, interface.constants, |
| interface.operation_groups)) |
| |
| iterate(collectionlike.attributes, process_attribute) |
| iterate( |
| filter(should_define, collectionlike.operation_groups), |
| process_operation_group) |
| |
| return callback_def_nodes |
| |
| |
| def _make_install_prototype_object(cg_context): |
| assert isinstance(cg_context, CodeGenContext) |
| |
| nodes = [] |
| |
| class_like = cg_context.class_like |
| interface = cg_context.interface |
| |
| unscopables = [] |
| is_unscopable = lambda member: "Unscopable" in member.extended_attributes |
| unscopables.extend(filter(is_unscopable, class_like.attributes)) |
| unscopables.extend(filter(is_unscopable, class_like.operations)) |
| if unscopables: |
| nodes.extend([ |
| TextNode("""\ |
| // [Unscopable] |
| // 3.7.3. Interface prototype object |
| // https://heycam.github.io/webidl/#interface-prototype-object |
| // step 10. If interface has any member declared with the [Unscopable] |
| // extended attribute, then:\ |
| """), |
| ListNode([ |
| TextNode("static constexpr const char* " |
| "kUnscopablePropertyNames[] = {"), |
| ListNode([ |
| TextNode("\"{}\", ".format(member.identifier)) |
| for member in unscopables |
| ]), |
| TextNode("};"), |
| ]), |
| TextNode("""\ |
| bindings::InstallUnscopablePropertyNames( |
| ${isolate}, ${v8_context}, ${prototype_object}, kUnscopablePropertyNames); |
| """), |
| ]) |
| |
| if "LegacyNoInterfaceObject" in class_like.extended_attributes: |
| nodes.append( |
| TextNode("""\ |
| // [LegacyNoInterfaceObject] |
| // 3.7.3. Interface prototype object |
| // https://heycam.github.io/webidl/#interface-prototype-object |
| // step 13. If the [LegacyNoInterfaceObject] extended attribute was not |
| // specified on interface, then: |
| // |
| // V8 defines "constructor" property on the prototype object by default. |
| ${prototype_object}->Delete( |
| ${v8_context}, V8AtomicString(${isolate}, "constructor")).ToChecked(); |
| """)) |
| |
| collectionlike = interface and (interface.iterable or interface.maplike |
| or interface.setlike) |
| if collectionlike: |
| property_name = None |
| for operation_group in collectionlike.operation_groups: |
| if operation_group[0].is_iterator: |
| property_name = operation_group.identifier |
| break |
| if property_name: |
| pattern = """\ |
| // @@iterator == "{property_name}" |
| {{ |
| v8::Local<v8::Value> v8_value = ${prototype_object}->Get( |
| ${v8_context}, V8AtomicString(${isolate}, "{property_name}")) |
| .ToLocalChecked(); |
| ${prototype_object}->DefineOwnProperty( |
| ${v8_context}, v8::Symbol::GetIterator(${isolate}), v8_value, |
| v8::DontEnum).ToChecked(); |
| }} |
| """ |
| nodes.append( |
| TextNode(_format(pattern, property_name=property_name))) |
| |
| if class_like.identifier == "FileSystemDirectoryHandle": |
| pattern = """\ |
| // Temporary @@asyncIterator support for FileSystemDirectoryHandle |
| // TODO(https://crbug.com/1087157): Replace with proper bindings support. |
| // @@asyncIterator == "{property_name}" |
| {{ |
| v8::Local<v8::Value> v8_value = ${prototype_object}->Get( |
| ${v8_context}, V8AtomicString(${isolate}, "{property_name}")) |
| .ToLocalChecked(); |
| ${prototype_object}->DefineOwnProperty( |
| ${v8_context}, v8::Symbol::GetAsyncIterator(${isolate}), v8_value, |
| v8::DontEnum).ToChecked(); |
| }} |
| """ |
| nodes.append(TextNode(_format(pattern, property_name="entries"))) |
| |
| if ("Global" in class_like.extended_attributes |
| and class_like.indexed_and_named_properties |
| and class_like.indexed_and_named_properties.has_named_properties): |
| nodes.append( |
| TextNode("""\ |
| // https://heycam.github.io/webidl/#named-properties-object |
| // V8 defines "constructor" property on the prototype object by default. |
| // Named properties object is currently implemented as a prototype object |
| // (implemented with v8::FunctionTemplate::PrototypeTemplate()). |
| ${prototype_object}->GetPrototype().As<v8::Object>()->Delete( |
| ${v8_context}, V8AtomicString(${isolate}, "constructor")).ToChecked(); |
| """)) |
| |
| return SequenceNode(nodes) if nodes else None |
| |
| |
| def make_install_interface_template(cg_context, function_name, class_name, |
| trampoline_var_name, constructor_entries, |
| supplemental_install_node, |
| install_unconditional_func_name, |
| install_context_independent_func_name): |
| """ |
| Returns: |
| A triplet of CodeNode of: |
| - function declaration |
| - function definition |
| - trampoline function definition (from the API class to the |
| implementation class), which is supposed to be defined inline |
| """ |
| assert isinstance(cg_context, CodeGenContext) |
| assert isinstance(function_name, str) |
| assert _is_none_or_str(class_name) |
| assert _is_none_or_str(trampoline_var_name) |
| assert isinstance(constructor_entries, (list, tuple)) |
| assert all( |
| isinstance(entry, _PropEntryConstructorGroup) |
| for entry in constructor_entries) |
| assert isinstance(supplemental_install_node, SequenceNode) |
| assert _is_none_or_str(install_unconditional_func_name) |
| assert _is_none_or_str(install_context_independent_func_name) |
| |
| T = TextNode |
| |
| class_like = cg_context.class_like |
| interface = cg_context.interface |
| |
| arg_decls = [ |
| "v8::Isolate* isolate", |
| "const DOMWrapperWorld& world", |
| "v8::Local<v8::Template> interface_template", |
| ] |
| return_type = "void" |
| |
| if trampoline_var_name is None: |
| trampoline_def = None |
| else: |
| trampoline_def = CxxFuncDefNode( |
| name=function_name, |
| arg_decls=arg_decls, |
| return_type=return_type, |
| static=True) |
| trampoline_def.body.append( |
| TextNode( |
| _format("return {}(isolate, world, interface_template);", |
| trampoline_var_name))) |
| |
| func_decl = CxxFuncDeclNode( |
| name=function_name, |
| arg_decls=arg_decls, |
| return_type=return_type, |
| static=True) |
| |
| func_def = CxxFuncDefNode( |
| name=function_name, |
| arg_decls=arg_decls, |
| return_type=return_type, |
| class_name=class_name) |
| func_def.set_base_template_vars(cg_context.template_bindings()) |
| |
| body = func_def.body |
| body.add_template_vars({ |
| "isolate": "isolate", |
| "world": "world", |
| "interface_template": "interface_template", |
| }) |
| bind_installer_local_vars(body, cg_context) |
| |
| if cg_context.interface: |
| body.extend([ |
| T("bindings::SetupIDLInterfaceTemplate(" |
| "${isolate}, ${wrapper_type_info}, " |
| "${instance_object_template}, " |
| "${prototype_object_template}, " |
| "${interface_function_template}, " |
| "${parent_interface_template});"), |
| EmptyNode(), |
| ]) |
| elif cg_context.namespace: |
| body.extend([ |
| T("bindings::SetupIDLNamespaceTemplate(" |
| "${isolate}, ${wrapper_type_info}, " |
| "${namespace_object_template});"), |
| EmptyNode(), |
| ]) |
| elif cg_context.callback_interface: |
| body.extend([ |
| T("bindings::SetupIDLCallbackInterfaceTemplate(" |
| "${isolate}, ${wrapper_type_info}, " |
| "${interface_function_template});"), |
| EmptyNode(), |
| ]) |
| else: |
| assert False |
| |
| for entry in constructor_entries: |
| set_callback = _format( |
| "${interface_function_template}->SetCallHandler({});", |
| entry.ctor_callback_name) |
| set_length = _format("${interface_function_template}->SetLength({});", |
| entry.ctor_func_length) |
| if entry.world == CodeGenContext.MAIN_WORLD: |
| body.append( |
| CxxLikelyIfNode( |
| cond="${world}.IsMainWorld()", |
| body=[T(set_callback), T(set_length)])) |
| elif entry.world == CodeGenContext.NON_MAIN_WORLDS: |
| body.append( |
| CxxLikelyIfNode( |
| cond="!${world}.IsMainWorld()", |
| body=[T(set_callback), T(set_length)])) |
| elif entry.world == CodeGenContext.ALL_WORLDS: |
| body.extend([T(set_callback), T(set_length)]) |
| else: |
| assert False |
| body.append(EmptyNode()) |
| |
| body.extend([ |
| supplemental_install_node, |
| EmptyNode(), |
| ]) |
| |
| if class_like.identifier == "CSSStyleDeclaration": |
| css_properties = list( |
| filter(lambda attr: "CSSProperty" in attr.extended_attributes, |
| class_like.attributes)) |
| if css_properties: |
| prop_name_list = "".join( |
| map(lambda attr: "\"{}\", ".format(attr.identifier), |
| css_properties)) |
| body.append( |
| T("""\ |
| // CSSStyleDeclaration-specific settings |
| // [CSSProperty] |
| { |
| static constexpr const char* kCssProperties[] = { |
| """ + prop_name_list + """ |
| }; |
| bindings::InstallCSSPropertyAttributes( |
| ${isolate}, ${world}, |
| ${instance_template}, ${prototype_template}, ${interface_template}, |
| ${signature}, kCssProperties); |
| } |
| """)) |
| |
| if class_like.identifier == "DOMException": |
| body.append( |
| T("""\ |
| // DOMException-specific settings |
| // https://heycam.github.io/webidl/#es-DOMException-specialness |
| { |
| v8::Local<v8::FunctionTemplate> intrinsic_error_prototype_interface_template = |
| v8::FunctionTemplate::New(${isolate}, nullptr, v8::Local<v8::Value>(), |
| v8::Local<v8::Signature>(), 0, |
| v8::ConstructorBehavior::kThrow); |
| intrinsic_error_prototype_interface_template->SetIntrinsicDataProperty( |
| V8AtomicString(${isolate}, "prototype"), v8::kErrorPrototype); |
| ${interface_function_template}->Inherit( |
| intrinsic_error_prototype_interface_template); |
| } |
| """)) |
| |
| if class_like.identifier == "FileSystemDirectoryIterator": |
| body.append( |
| T("""\ |
| // Temporary @@asyncIterator support for FileSystemDirectoryHandle |
| // TODO(https://crbug.com/1087157): Replace with proper bindings support. |
| { |
| v8::Local<v8::FunctionTemplate> |
| intrinsic_iterator_prototype_interface_template = |
| v8::FunctionTemplate::New(${isolate}, nullptr, v8::Local<v8::Value>(), |
| v8::Local<v8::Signature>(), 0, |
| v8::ConstructorBehavior::kThrow); |
| intrinsic_iterator_prototype_interface_template->SetIntrinsicDataProperty( |
| V8AtomicString(${isolate}, "prototype"), v8::kAsyncIteratorPrototype); |
| ${interface_function_template}->Inherit( |
| intrinsic_iterator_prototype_interface_template); |
| } |
| """)) |
| |
| if class_like.identifier == "HTMLAllCollection": |
| body.append( |
| T("""\ |
| // HTMLAllCollection-specific settings |
| // https://html.spec.whatwg.org/C/#the-htmlallcollection-interface |
| ${instance_object_template}->SetCallAsFunctionHandler( |
| ${class_name}::LegacyCallCustom); |
| ${instance_object_template}->MarkAsUndetectable(); |
| """)) |
| |
| if class_like.identifier == "Iterator": |
| body.append( |
| T("""\ |
| // Iterator-specific settings |
| // https://heycam.github.io/webidl/#es-iterator-prototype-object |
| { |
| v8::Local<v8::FunctionTemplate> |
| intrinsic_iterator_prototype_interface_template = |
| v8::FunctionTemplate::New(${isolate}, nullptr, v8::Local<v8::Value>(), |
| v8::Local<v8::Signature>(), 0, |
| v8::ConstructorBehavior::kThrow); |
| intrinsic_iterator_prototype_interface_template->SetIntrinsicDataProperty( |
| V8AtomicString(${isolate}, "prototype"), v8::kIteratorPrototype); |
| ${interface_function_template}->Inherit( |
| intrinsic_iterator_prototype_interface_template); |
| } |
| """)) |
| |
| if class_like.identifier == "Location": |
| body.append( |
| T("""\ |
| // Location-specific settings |
| // https://html.spec.whatwg.org/C/#the-location-interface |
| // To create a Location object, run these steps: |
| // step 3. Let valueOf be location's relevant |
| // Realm.[[Intrinsics]].[[%ObjProto_valueOf%]]. |
| // step 3. Perform ! location.[[DefineOwnProperty]]("valueOf", |
| // { [[Value]]: valueOf, [[Writable]]: false, [[Enumerable]]: false, |
| // [[Configurable]]: false }). |
| ${instance_template}->SetIntrinsicDataProperty( |
| V8AtomicString(${isolate}, "valueOf"), |
| v8::kObjProto_valueOf, |
| static_cast<v8::PropertyAttribute>( |
| v8::ReadOnly | v8::DontEnum | v8::DontDelete)); |
| // step 4. Perform ! location.[[DefineOwnProperty]](@@toPrimitive, |
| // { [[Value]]: undefined, [[Writable]]: false, [[Enumerable]]: false, |
| // [[Configurable]]: false }). |
| ${instance_template}->Set( |
| v8::Symbol::GetToPrimitive(${isolate}), |
| v8::Undefined(${isolate}), |
| static_cast<v8::PropertyAttribute>( |
| v8::ReadOnly | v8::DontEnum | v8::DontDelete)); |
| // 7.7.4.2 [[SetPrototypeOf]] ( V ) |
| // https://html.spec.whatwg.org/C/#location-setprototypeof |
| ${instance_object_template}->SetImmutableProto(); |
| ${prototype_object_template}->SetImmutableProto(); |
| """)) |
| |
| if (interface and interface.indexed_and_named_properties |
| and interface.indexed_and_named_properties.indexed_getter |
| and "Global" not in interface.extended_attributes): |
| body.append( |
| T("""\ |
| // @@iterator for indexed properties |
| // https://heycam.github.io/webidl/#define-the-iteration-methods |
| ${prototype_template}->SetIntrinsicDataProperty( |
| v8::Symbol::GetIterator(${isolate}), v8::kArrayProto_values, v8::DontEnum); |
| """)) |
| if interface and interface.iterable and not interface.iterable.key_type: |
| body.append( |
| T("""\ |
| // Value iterator's properties |
| // https://heycam.github.io/webidl/#define-the-iteration-methods |
| ${prototype_template}->SetIntrinsicDataProperty( |
| V8AtomicString(${isolate}, "entries"), v8::kArrayProto_entries, v8::None); |
| ${prototype_template}->SetIntrinsicDataProperty( |
| V8AtomicString(${isolate}, "keys"), v8::kArrayProto_keys, v8::None); |
| ${prototype_template}->SetIntrinsicDataProperty( |
| V8AtomicString(${isolate}, "values"), v8::kArrayProto_values, v8::None); |
| ${prototype_template}->SetIntrinsicDataProperty( |
| V8AtomicString(${isolate}, "forEach"), v8::kArrayProto_forEach, v8::None); |
| """)) |
| |
| if interface and "IsCodeLike" in interface.extended_attributes: |
| body.append( |
| CxxUnlikelyIfNode( |
| cond="RuntimeEnabledFeatures::TrustedTypesUseCodeLikeEnabled()", |
| body=[ |
| TextNode("// [IsCodeLike]"), |
| TextNode("${instance_object_template}->SetCodeLike();"), |
| ])) |
| |
| if "Global" in class_like.extended_attributes: |
| body.append( |
| TextNode("""\ |
| // [Global] |
| // 3.7.1. [[SetPrototypeOf]] |
| // https://heycam.github.io/webidl/#platform-object-setprototypeof |
| ${instance_object_template}->SetImmutableProto(); |
| ${prototype_object_template}->SetImmutableProto(); |
| """)) |
| elif interface and any("Global" in derived.extended_attributes |
| for derived in interface.deriveds): |
| body.append( |
| TextNode("""\ |
| // [Global] - prototype object in the prototype chain of global objects |
| // 3.7.1. [[SetPrototypeOf]] |
| // https://heycam.github.io/webidl/#platform-object-setprototypeof |
| ${prototype_object_template}->SetImmutableProto(); |
| """)) |
| |
| func_call_pattern = ("{}(${isolate}, ${world}, ${instance_template}, " |
| "${prototype_template}, ${interface_template});") |
| if install_unconditional_func_name: |
| func_call = _format(func_call_pattern, install_unconditional_func_name) |
| body.append(T(func_call)) |
| if install_context_independent_func_name: |
| func_call = _format(func_call_pattern, |
| install_context_independent_func_name) |
| body.append(T(func_call)) |
| |
| return func_decl, func_def, trampoline_def |
| |
| |
| class PropInstallMode(object): |
| class Mode(int): |
| pass |
| |
| UNCONDITIONAL = Mode(0) |
| CONTEXT_INDEPENDENT = Mode(1) |
| CONTEXT_DEPENDENT = Mode(2) |
| V8_CONTEXT_SNAPSHOT = Mode(3) |
| |
| |
| def make_install_properties(cg_context, function_name, class_name, |
| prop_install_mode, trampoline_var_name, |
| attribute_entries, constant_entries, |
| exposed_construct_entries, operation_entries): |
| """ |
| Returns: |
| A triplet of CodeNode of: |
| - function declaration |
| - function definition |
| - trampoline function definition (from the API class to the |
| implementation class), which is supposed to be defined inline |
| """ |
| assert isinstance(cg_context, CodeGenContext) |
| assert isinstance(function_name, str) |
| assert _is_none_or_str(class_name) |
| assert isinstance(prop_install_mode, PropInstallMode.Mode) |
| assert _is_none_or_str(trampoline_var_name) |
| assert isinstance(attribute_entries, (list, tuple)) |
| assert all( |
| isinstance(entry, _PropEntryAttribute) for entry in attribute_entries) |
| assert isinstance(constant_entries, (list, tuple)) |
| assert all( |
| isinstance(entry, _PropEntryConstant) for entry in constant_entries) |
| assert isinstance(exposed_construct_entries, (list, tuple)) |
| assert all( |
| isinstance(entry, _PropEntryExposedConstruct) |
| for entry in exposed_construct_entries) |
| assert isinstance(operation_entries, (list, tuple)) |
| assert all( |
| isinstance(entry, _PropEntryOperationGroup) |
| for entry in operation_entries) |
| |
| if prop_install_mode == PropInstallMode.CONTEXT_DEPENDENT: |
| install_prototype_object_node = _make_install_prototype_object( |
| cg_context) |
| else: |
| install_prototype_object_node = None |
| |
| if not (attribute_entries or constant_entries or exposed_construct_entries |
| or operation_entries or install_prototype_object_node): |
| if prop_install_mode != PropInstallMode.V8_CONTEXT_SNAPSHOT: |
| return None, None, None |
| |
| if prop_install_mode in (PropInstallMode.UNCONDITIONAL, |
| PropInstallMode.CONTEXT_INDEPENDENT): |
| arg_decls = [ |
| "v8::Isolate* isolate", |
| "const DOMWrapperWorld& world", |
| "v8::Local<v8::Template> instance_template", |
| "v8::Local<v8::Template> prototype_template", |
| "v8::Local<v8::Template> interface_template", |
| ] |
| arg_names = [ |
| "isolate", |
| "world", |
| "instance_template", |
| "prototype_template", |
| "interface_template", |
| ] |
| elif prop_install_mode == PropInstallMode.CONTEXT_DEPENDENT: |
| arg_decls = [ |
| "v8::Local<v8::Context> context", |
| "const DOMWrapperWorld& world", |
| "v8::Local<v8::Object> instance_object", |
| "v8::Local<v8::Object> prototype_object", |
| "v8::Local<v8::Object> interface_object", |
| "v8::Local<v8::Template> interface_template", |
| "FeatureSelector feature_selector", |
| ] |
| arg_names = [ |
| "context", |
| "world", |
| "instance_object", |
| "prototype_object", |
| "interface_object", |
| "interface_template", |
| "feature_selector", |
| ] |
| elif prop_install_mode == PropInstallMode.V8_CONTEXT_SNAPSHOT: |
| arg_decls = [ |
| "v8::Local<v8::Context> context", |
| "const DOMWrapperWorld& world", |
| "v8::Local<v8::Object> instance_object", |
| "v8::Local<v8::Object> prototype_object", |
| "v8::Local<v8::Object> interface_object", |
| "v8::Local<v8::Template> interface_template", |
| ] |
| arg_names = [ |
| "context", |
| "world", |
| "instance_object", |
| "prototype_object", |
| "interface_object", |
| "interface_template", |
| ] |
| return_type = "void" |
| |
| is_per_context_install = ( |
| prop_install_mode in (PropInstallMode.CONTEXT_DEPENDENT, |
| PropInstallMode.V8_CONTEXT_SNAPSHOT)) |
| |
| if trampoline_var_name is None: |
| trampoline_def = None |
| else: |
| trampoline_def = CxxFuncDefNode( |
| name=function_name, |
| arg_decls=arg_decls, |
| return_type=return_type, |
| static=True) |
| text = _format( |
| "return {func}({args});", |
| func=trampoline_var_name, |
| args=", ".join(arg_names)) |
| trampoline_def.body.append(TextNode(text)) |
| |
| func_decl = CxxFuncDeclNode(name=function_name, |
| arg_decls=arg_decls, |
| return_type=return_type, |
| static=bool(class_name)) |
| |
| func_def = CxxFuncDefNode( |
| name=function_name, |
| arg_decls=arg_decls, |
| return_type=return_type, |
| class_name=class_name) |
| func_def.set_base_template_vars(cg_context.template_bindings()) |
| |
| body = func_def.body |
| for arg_name in arg_names: |
| if arg_name == "context": # 'context' is reserved by Mako. |
| body.add_template_var("v8_context", "context") |
| else: |
| body.add_template_var(arg_name, arg_name) |
| bind_installer_local_vars(body, cg_context) |
| |
| body.extend([ |
| TextNode("using bindings::IDLMemberInstaller;"), |
| EmptyNode(), |
| ]) |
| |
| if (is_per_context_install |
| and "Global" in cg_context.class_like.extended_attributes): |
| body.extend([ |
| CxxLikelyIfNode(cond="${instance_object}.IsEmpty()", |
| body=[ |
| TextNode("""\ |
| ${instance_object} = ${v8_context}->Global()->GetPrototype().As<v8::Object>();\ |
| """), |
| ]), |
| EmptyNode(), |
| ]) |
| |
| if install_prototype_object_node: |
| body.extend([ |
| CxxLikelyIfNode(cond="${feature_selector}.IsAll()", |
| body=[install_prototype_object_node]), |
| EmptyNode(), |
| ]) |
| |
| def group_by_condition(entries): |
| unconditional_entries = [] |
| conditional_to_entries = {} |
| for entry in entries: |
| if entry.exposure_conditional.is_always_true: |
| unconditional_entries.append(entry) |
| else: |
| conditional_to_entries.setdefault(entry.exposure_conditional, |
| []).append(entry) |
| return unconditional_entries, conditional_to_entries |
| |
| def install_properties(table_name, target_entries, make_table_func, |
| installer_call_text): |
| unconditional_entries, conditional_to_entries = group_by_condition( |
| target_entries) |
| if unconditional_entries: |
| body.append( |
| CxxBlockNode([ |
| make_table_func(table_name, unconditional_entries), |
| TextNode(installer_call_text), |
| ])) |
| body.append(EmptyNode()) |
| for conditional, entries in conditional_to_entries.items(): |
| body.append( |
| CxxUnlikelyIfNode( |
| cond=conditional, |
| body=[ |
| make_table_func(table_name, entries), |
| TextNode(installer_call_text), |
| ])) |
| body.append(EmptyNode()) |
| |
| if is_per_context_install: |
| pattern = ("{install_func}(" |
| "${isolate}, ${world}, " |
| "${instance_object}, " |
| "${prototype_object}, " |
| "${interface_object}, " |
| "${signature}, {table_name});") |
| else: |
| pattern = ("{install_func}(" |
| "${isolate}, ${world}, " |
| "${instance_template}, " |
| "${prototype_template}, " |
| "${interface_template}, " |
| "${signature}, {table_name});") |
| |
| table_name = "kAttributeTable" |
| installer_call_text = _format( |
| pattern, |
| install_func="IDLMemberInstaller::InstallAttributes", |
| table_name=table_name) |
| install_properties(table_name, attribute_entries, |
| _make_attribute_registration_table, installer_call_text) |
| |
| table_name = "kConstantCallbackTable" |
| installer_call_text = _format( |
| pattern, |
| install_func="IDLMemberInstaller::InstallConstants", |
| table_name=table_name) |
| constant_callback_entries = list( |
| filter(lambda entry: entry.const_callback_name, constant_entries)) |
| install_properties(table_name, constant_callback_entries, |
| _make_constant_callback_registration_table, |
| installer_call_text) |
| |
| table_name = "kConstantValueTable" |
| installer_call_text = _format( |
| pattern, |
| install_func="IDLMemberInstaller::InstallConstants", |
| table_name=table_name) |
| constant_value_entries = list( |
| filter(lambda entry: not entry.const_callback_name, constant_entries)) |
| install_properties(table_name, constant_value_entries, |
| _make_constant_value_registration_table, |
| installer_call_text) |
| |
| table_name = "kExposedConstructTable" |
| installer_call_text = _format( |
| pattern, |
| install_func="IDLMemberInstaller::InstallExposedConstructs", |
| table_name=table_name) |
| install_properties(table_name, exposed_construct_entries, |
| _make_exposed_construct_registration_table, |
| installer_call_text) |
| |
| table_name = "kOperationTable" |
| installer_call_text = _format( |
| pattern, |
| install_func="IDLMemberInstaller::InstallOperations", |
| table_name=table_name) |
| entries = list( |
| filter(lambda entry: not entry.no_alloc_direct_callback_name, |
| operation_entries)) |
| install_properties(table_name, entries, _make_operation_registration_table, |
| installer_call_text) |
| entries = list( |
| filter(lambda entry: entry.no_alloc_direct_callback_name, |
| operation_entries)) |
| install_properties(table_name, entries, _make_operation_registration_table, |
| installer_call_text) |
| |
| return func_decl, func_def, trampoline_def |
| |
| |
| def make_indexed_and_named_property_callbacks_and_install_node(cg_context): |
| """ |
| Implements non-ordinary internal methods of legacy platform objects. |
| https://heycam.github.io/webidl/#es-legacy-platform-objects |
| |
| Also implements the same origin case of indexed access to WindowProxy |
| objects just same as indexed properties of legacy platform objects. |
| https://html.spec.whatwg.org/C/#the-windowproxy-exotic-object |
| """ |
| |
| assert isinstance(cg_context, CodeGenContext) |
| |
| F = lambda *args, **kwargs: TextNode(_format(*args, **kwargs)) |
| |
| func_decls = ListNode() |
| func_defs = ListNode() |
| install_node = SequenceNode() |
| |
| interface = cg_context.interface |
| if not (interface and interface.indexed_and_named_properties |
| and "Global" not in interface.extended_attributes): |
| return func_decls, func_defs, install_node |
| props = interface.indexed_and_named_properties |
| |
| def add_callback(func_decl, func_def): |
| func_decls.append(func_decl) |
| if func_def: |
| func_defs.append(func_def) |
| func_defs.append(EmptyNode()) |
| |
| def most_derived_interface(*interfaces): |
| key = lambda interface: len(interface.inclusive_inherited_interfaces) |
| return sorted(filter(None, interfaces), key=key)[-1] |
| |
| cg_context = cg_context.make_copy( |
| v8_callback_type=CodeGenContext.V8_OTHER_CALLBACK) |
| |
| if props.own_named_getter: |
| add_callback(*make_named_property_getter_callback( |
| cg_context.make_copy(named_property_getter=props.named_getter), |
| "NamedPropertyGetterCallback")) |
| add_callback(*make_named_property_setter_callback( |
| cg_context.make_copy(named_property_setter=props.named_setter), |
| "NamedPropertySetterCallback")) |
| add_callback(*make_named_property_deleter_callback( |
| cg_context.make_copy(named_property_deleter=props.named_deleter), |
| "NamedPropertyDeleterCallback")) |
| add_callback(*make_named_property_definer_callback( |
| cg_context, "NamedPropertyDefinerCallback")) |
| add_callback(*make_named_property_descriptor_callback( |
| cg_context, "NamedPropertyDescriptorCallback")) |
| add_callback(*make_named_property_query_callback( |
| cg_context, "NamedPropertyQueryCallback")) |
| add_callback(*make_named_property_enumerator_callback( |
| cg_context, "NamedPropertyEnumeratorCallback")) |
| |
| if props.named_getter: |
| impl_bridge = v8_bridge_class_name( |
| most_derived_interface( |
| props.named_getter.owner, props.named_setter |
| and props.named_setter.owner, props.named_deleter |
| and props.named_deleter.owner)) |
| flags = ["v8::PropertyHandlerFlags::kOnlyInterceptStrings"] |
| if "LegacyOverrideBuiltIns" not in interface.extended_attributes: |
| flags.append("v8::PropertyHandlerFlags::kNonMasking") |
| if (props.named_getter.extended_attributes.value_of("Affects") == |
| "Nothing"): |
| flags.append("v8::PropertyHandlerFlags::kHasNoSideEffect") |
| property_handler_flags = ( |
| "static_cast<v8::PropertyHandlerFlags>({})".format(" | ".join( |
| map(lambda flag: "int32_t({})".format(flag), flags)))) |
| pattern = """\ |
| // Named interceptors |
| ${instance_object_template}->SetHandler( |
| v8::NamedPropertyHandlerConfiguration( |
| {impl_bridge}::NamedPropertyGetterCallback, |
| {impl_bridge}::NamedPropertySetterCallback, |
| % if "NotEnumerable" not in \ |
| interface.indexed_and_named_properties.named_getter.extended_attributes: |
| {impl_bridge}::NamedPropertyQueryCallback, |
| % else: |
| nullptr, // query |
| % endif |
| {impl_bridge}::NamedPropertyDeleterCallback, |
| % if "NotEnumerable" not in \ |
| interface.indexed_and_named_properties.named_getter.extended_attributes: |
| {impl_bridge}::NamedPropertyEnumeratorCallback, |
| % else: |
| nullptr, // enumerator |
| % endif |
| {impl_bridge}::NamedPropertyDefinerCallback, |
| {impl_bridge}::NamedPropertyDescriptorCallback, |
| v8::Local<v8::Value>(), |
| {property_handler_flags}));""" |
| install_node.append( |
| F(pattern, |
| impl_bridge=impl_bridge, |
| property_handler_flags=property_handler_flags)) |
| |
| if props.own_indexed_getter or props.own_named_getter: |
| add_callback(*make_indexed_property_getter_callback( |
| cg_context.make_copy(indexed_property_getter=props.indexed_getter), |
| "IndexedPropertyGetterCallback")) |
| add_callback(*make_indexed_property_setter_callback( |
| cg_context.make_copy(indexed_property_setter=props.indexed_setter), |
| "IndexedPropertySetterCallback")) |
| add_callback(*make_indexed_property_deleter_callback( |
| cg_context, "IndexedPropertyDeleterCallback")) |
| add_callback(*make_indexed_property_definer_callback( |
| cg_context, "IndexedPropertyDefinerCallback")) |
| add_callback(*make_indexed_property_descriptor_callback( |
| cg_context, "IndexedPropertyDescriptorCallback")) |
| add_callback(*make_indexed_property_enumerator_callback( |
| cg_context, "IndexedPropertyEnumeratorCallback")) |
| |
| if props.indexed_getter or props.named_getter: |
| impl_bridge = v8_bridge_class_name( |
| most_derived_interface( |
| props.indexed_getter and props.indexed_getter.owner, |
| props.indexed_setter and props.indexed_setter.owner, |
| props.named_getter and props.named_getter.owner, |
| props.named_setter and props.named_setter.owner, |
| props.named_deleter and props.named_deleter.owner)) |
| flags = [] |
| if (props.indexed_getter and props.indexed_getter.extended_attributes. |
| value_of("Affects") == "Nothing"): |
| flags.append("v8::PropertyHandlerFlags::kHasNoSideEffect") |
| else: |
| flags.append("v8::PropertyHandlerFlags::kNone") |
| property_handler_flags = flags[0] |
| pattern = """\ |
| // Indexed interceptors |
| ${instance_object_template}->SetHandler( |
| v8::IndexedPropertyHandlerConfiguration( |
| {impl_bridge}::IndexedPropertyGetterCallback, |
| {impl_bridge}::IndexedPropertySetterCallback, |
| nullptr, // query |
| {impl_bridge}::IndexedPropertyDeleterCallback, |
| % if interface.indexed_and_named_properties.indexed_getter: |
| {impl_bridge}::IndexedPropertyEnumeratorCallback, |
| % else: |
| nullptr, // enumerator |
| % endif |
| {impl_bridge}::IndexedPropertyDefinerCallback, |
| {impl_bridge}::IndexedPropertyDescriptorCallback, |
| v8::Local<v8::Value>(), |
| {property_handler_flags}));""" |
| install_node.append( |
| F(pattern, |
| impl_bridge=impl_bridge, |
| property_handler_flags=property_handler_flags)) |
| |
| func_defs.accumulate( |
| CodeGenAccumulator.require_include_headers([ |
| "third_party/blink/renderer/bindings/core/v8/v8_set_return_value_for_core.h" |
| ])) |
| |
| return func_decls, func_defs, install_node |
| |
| |
| def make_named_properties_object_callbacks_and_install_node(cg_context): |
| """ |
| Implements non-ordinary internal methods of named properties objects. |
| https://heycam.github.io/webidl/#named-properties-object |
| """ |
| |
| assert isinstance(cg_context, CodeGenContext) |
| |
| callback_defs = [] |
| install_node = SequenceNode() |
| |
| interface = cg_context.interface |
| if not (interface and interface.indexed_and_named_properties |
| and interface.indexed_and_named_properties.named_getter |
| and "Global" in interface.extended_attributes): |
| return callback_defs, install_node |
| |
| cg_context = cg_context.make_copy( |
| v8_callback_type=CodeGenContext.V8_OTHER_CALLBACK) |
| |
| func_defs = [ |
| make_named_props_obj_named_getter_callback( |
| cg_context, "NamedPropsObjNamedGetterCallback"), |
| make_named_props_obj_named_deleter_callback( |
| cg_context, "NamedPropsObjNamedDeleterCallback"), |
| make_named_props_obj_named_definer_callback( |
| cg_context, "NamedPropsObjNamedDefinerCallback"), |
| make_named_props_obj_named_descriptor_callback( |
| cg_context, "NamedPropsObjNamedDescriptorCallback"), |
| make_named_props_obj_indexed_getter_callback( |
| cg_context, "NamedPropsObjIndexedGetterCallback"), |
| make_named_props_obj_indexed_deleter_callback( |
| cg_context, "NamedPropsObjIndexedDeleterCallback"), |
| make_named_props_obj_indexed_definer_callback( |
| cg_context, "NamedPropsObjIndexedDefinerCallback"), |
| make_named_props_obj_indexed_descriptor_callback( |
| cg_context, "NamedPropsObjIndexedDescriptorCallback"), |
| ] |
| for func_def in func_defs: |
| callback_defs.append(func_def) |
| callback_defs.append(EmptyNode()) |
| |
| text = """\ |
| // Named interceptors |
| ${npo_prototype_template}->SetHandler( |
| v8::NamedPropertyHandlerConfiguration( |
| NamedPropsObjNamedGetterCallback, |
| nullptr, // setter |
| nullptr, // query |
| NamedPropsObjNamedDeleterCallback, |
| nullptr, // enumerator |
| NamedPropsObjNamedDefinerCallback, |
| NamedPropsObjNamedDescriptorCallback, |
| v8::Local<v8::Value>(), |
| static_cast<v8::PropertyHandlerFlags>( |
| int32_t(v8::PropertyHandlerFlags::kNonMasking) | |
| int32_t(v8::PropertyHandlerFlags::kOnlyInterceptStrings)))); |
| // Indexed interceptors |
| ${npo_prototype_template}->SetHandler( |
| v8::IndexedPropertyHandlerConfiguration( |
| NamedPropsObjIndexedGetterCallback, |
| nullptr, // setter |
| nullptr, // query |
| NamedPropsObjIndexedDeleterCallback, |
| nullptr, // enumerator |
| NamedPropsObjIndexedDefinerCallback, |
| NamedPropsObjIndexedDescriptorCallback, |
| v8::Local<v8::Value>(), |
| v8::PropertyHandlerFlags::kNone));""" |
| install_node.append(TextNode(text)) |
| |
| return callback_defs, install_node |
| |
| |
| def make_cross_origin_property_callbacks_and_install_node( |
| cg_context, attribute_entries, operation_entries): |
| """ |
| Implements non-ordinary internal methods of WindowProxy and Location |
| objects. |
| https://html.spec.whatwg.org/C/#the-windowproxy-exotic-object |
| https://html.spec.whatwg.org/C/#the-location-interface |
| """ |
| |
| assert isinstance(cg_context, CodeGenContext) |
| |
| callback_defs = [] |
| install_node = SequenceNode() |
| |
| CROSS_ORIGIN_INTERFACES = ("Window", "Location") |
| if cg_context.class_like.identifier not in CROSS_ORIGIN_INTERFACES: |
| return callback_defs, install_node |
| props = cg_context.interface.indexed_and_named_properties |
| |
| entry_nodes = [] |
| for entry in attribute_entries: |
| attribute = entry.property_ |
| if "CrossOrigin" not in attribute.extended_attributes: |
| continue |
| assert entry.world == CodeGenContext.ALL_WORLDS |
| values = attribute.extended_attributes.values_of("CrossOrigin") |
| get_func = "nullptr" |
| set_func = "nullptr" |
| get_value = "nullptr" |
| set_value = "nullptr" |
| if not values or "Getter" in values: |
| get_func = entry.attr_get_callback_name |
| cgc = cg_context.make_copy( |
| attribute=attribute, |
| attribute_get=True, |
| v8_callback_type=( |
| CodeGenContext.V8_ACCESSOR_NAME_GETTER_CALLBACK)) |
| get_value = callback_function_name(cgc, for_cross_origin=True) |
| func_def = make_attribute_get_callback_def(cgc, get_value) |
| callback_defs.extend([func_def, EmptyNode()]) |
| if values and "Setter" in values: |
| set_func = entry.attr_set_callback_name |
| cgc = cg_context.make_copy( |
| attribute=attribute, |
| attribute_set=True, |
| v8_callback_type=( |
| CodeGenContext.V8_GENERIC_NAMED_PROPERTY_SETTER_CALLBACK)) |
| set_value = callback_function_name(cgc, for_cross_origin=True) |
| func_def = make_attribute_set_callback_def(cgc, set_value) |
| callback_defs.extend([func_def, EmptyNode()]) |
| pattern = ("{{\"{property_name}\", " |
| "{get_func}, {set_func}, {get_value}, {set_value}}},") |
| entry_nodes.append( |
| TextNode( |
| _format( |
| pattern, |
| property_name=attribute.identifier, |
| get_func=get_func, |
| set_func=set_func, |
| get_value=get_value, |
| set_value=set_value))) |
| callback_defs.append( |
| ListNode([ |
| TextNode("constexpr bindings::CrossOriginAttributeTableEntry " |
| "kCrossOriginAttributeTable[] = {"), |
| ListNode(entry_nodes), |
| TextNode("};"), |
| EmptyNode(), |
| ])) |
| |
| entry_nodes = [] |
| for entry in operation_entries: |
| operation_group = entry.property_ |
| if "CrossOrigin" not in operation_group.extended_attributes: |
| continue |
| assert entry.world == CodeGenContext.ALL_WORLDS |
| entry_nodes.append( |
| TextNode( |
| _format( |
| "{{\"{property_name}\", {op_callback}, {op_func_length}}},", |
| property_name=operation_group.identifier, |
| op_callback=entry.op_callback_name, |
| op_func_length=entry.op_func_length))) |
| callback_defs.append( |
| ListNode([ |
| TextNode("constexpr bindings::CrossOriginOperationTableEntry " |
| "kCrossOriginOperationTable[] = {"), |
| ListNode(entry_nodes), |
| TextNode("};"), |
| EmptyNode(), |
| ])) |
| |
| cg_context = cg_context.make_copy( |
| v8_callback_type=CodeGenContext.V8_OTHER_CALLBACK) |
| |
| func_defs = [ |
| make_cross_origin_access_check_callback( |
| cg_context, "CrossOriginAccessCheckCallback"), |
| make_cross_origin_named_getter_callback( |
| cg_context, "CrossOriginNamedGetterCallback"), |
| make_cross_origin_named_setter_callback( |
| cg_context, "CrossOriginNamedSetterCallback"), |
| make_cross_origin_named_deleter_callback( |
| cg_context, "CrossOriginNamedDeleterCallback"), |
| make_cross_origin_named_definer_callback( |
| cg_context, "CrossOriginNamedDefinerCallback"), |
| make_cross_origin_named_descriptor_callback( |
| cg_context, "CrossOriginNamedDescriptorCallback"), |
| make_cross_origin_named_query_callback( |
| cg_context, "CrossOriginNamedQueryCallback"), |
| make_cross_origin_named_enumerator_callback( |
| cg_context, "CrossOriginNamedEnumeratorCallback"), |
| make_cross_origin_indexed_getter_callback( |
| cg_context.make_copy( |
| indexed_property_getter=(props and props.indexed_getter)), |
| "CrossOriginIndexedGetterCallback"), |
| make_cross_origin_indexed_setter_callback( |
| cg_context, "CrossOriginIndexedSetterCallback"), |
| make_cross_origin_indexed_deleter_callback( |
| cg_context, "CrossOriginIndexedDeleterCallback"), |
| make_cross_origin_indexed_definer_callback( |
| cg_context, "CrossOriginIndexedDefinerCallback"), |
| make_cross_origin_indexed_descriptor_callback( |
| cg_context, "CrossOriginIndexedDescriptorCallback"), |
| make_cross_origin_indexed_enumerator_callback( |
| cg_context, "CrossOriginIndexedEnumeratorCallback"), |
| ] |
| for func_def in func_defs: |
| callback_defs.append(func_def) |
| callback_defs.append(EmptyNode()) |
| |
| text = """\ |
| // Cross origin properties |
| ${instance_object_template}->SetAccessCheckCallbackAndHandler( |
| CrossOriginAccessCheckCallback, |
| v8::NamedPropertyHandlerConfiguration( |
| CrossOriginNamedGetterCallback, |
| CrossOriginNamedSetterCallback, |
| CrossOriginNamedQueryCallback, |
| CrossOriginNamedDeleterCallback, |
| CrossOriginNamedEnumeratorCallback, |
| CrossOriginNamedDefinerCallback, |
| CrossOriginNamedDescriptorCallback, |
| v8::Local<v8::Value>(), |
| v8::PropertyHandlerFlags::kNone), |
| v8::IndexedPropertyHandlerConfiguration( |
| CrossOriginIndexedGetterCallback, |
| CrossOriginIndexedSetterCallback, |
| nullptr, // query |
| CrossOriginIndexedDeleterCallback, |
| CrossOriginIndexedEnumeratorCallback, |
| CrossOriginIndexedDefinerCallback, |
| CrossOriginIndexedDescriptorCallback, |
| v8::Local<v8::Value>(), |
| v8::PropertyHandlerFlags::kNone), |
| v8::External::New( |
| ${isolate}, |
| const_cast<WrapperTypeInfo*>(${class_name}::GetWrapperTypeInfo()))); |
| """ |
| install_node.append(TextNode(text)) |
| install_node.accumulate( |
| CodeGenAccumulator.require_include_headers([ |
| "third_party/blink/renderer/bindings/core/v8/binding_security.h", |
| "third_party/blink/renderer/platform/bindings/v8_cross_origin_property_support.h", |
| ])) |
| |
| if cg_context.interface.identifier != "Window": |
| return callback_defs, install_node |
| |
| func_defs = [ |
| make_same_origin_indexed_getter_callback( |
| cg_context.make_copy( |
| indexed_property_getter=(props and props.indexed_getter)), |
| "SameOriginIndexedGetterCallback"), |
| make_same_origin_indexed_setter_callback( |
| cg_context, "SameOriginIndexedSetterCallback"), |
| make_same_origin_indexed_deleter_callback( |
| cg_context, "SameOriginIndexedDeleterCallback"), |
| make_same_origin_indexed_definer_callback( |
| cg_context, "SameOriginIndexedDefinerCallback"), |
| make_same_origin_indexed_descriptor_callback( |
| cg_context, "SameOriginIndexedDescriptorCallback"), |
| make_same_origin_indexed_enumerator_callback( |
| cg_context, "SameOriginIndexedEnumeratorCallback"), |
| ] |
| for func_def in func_defs: |
| callback_defs.append(func_def) |
| callback_defs.append(EmptyNode()) |
| |
| text = """\ |
| // Same origin interceptors |
| ${instance_object_template}->SetHandler( |
| v8::IndexedPropertyHandlerConfiguration( |
| SameOriginIndexedGetterCallback, |
| SameOriginIndexedSetterCallback, |
| nullptr, // query |
| SameOriginIndexedDeleterCallback, |
| SameOriginIndexedEnumeratorCallback, |
| SameOriginIndexedDefinerCallback, |
| SameOriginIndexedDescriptorCallback, |
| v8::Local<v8::Value>(), |
| v8::PropertyHandlerFlags::kNone)); |
| """ |
| install_node.append(TextNode(text)) |
| |
| return callback_defs, install_node |
| |
| |
| def make_cross_component_init( |
| cg_context, function_name, class_name, has_unconditional_props, |
| has_context_independent_props, has_context_dependent_props): |
| """ |
| Returns: |
| A triplet of CodeNode of: |
| - function declaration |
| - function definition |
| - trampoline member variable definitions |
| """ |
| assert isinstance(cg_context, CodeGenContext) |
| assert isinstance(function_name, str) |
| assert isinstance(class_name, str) |
| assert isinstance(has_unconditional_props, bool) |
| assert isinstance(has_context_independent_props, bool) |
| assert isinstance(has_context_dependent_props, bool) |
| |
| T = TextNode |
| F = lambda *args, **kwargs: T(_format(*args, **kwargs)) |
| |
| def filter_four_trampolines(nodes): |
| assert len(nodes) == 4 |
| flags = (True, has_unconditional_props, has_context_independent_props, |
| has_context_dependent_props) |
| return [node for node, flag in zip(nodes, flags) if flag] |
| |
| trampoline_var_decls = ListNode( |
| filter_four_trampolines([ |
| F("static InstallInterfaceTemplateFuncType {};", |
| TP_INSTALL_INTERFACE_TEMPLATE), |
| F("static InstallUnconditionalPropertiesFuncType {};", |
| TP_INSTALL_UNCONDITIONAL_PROPS), |
| F("static InstallContextIndependentPropertiesFuncType {};", |
| TP_INSTALL_CONTEXT_INDEPENDENT_PROPS), |
| F("static InstallContextDependentPropertiesFuncType {};", |
| TP_INSTALL_CONTEXT_DEPENDENT_PROPS), |
| ])) |
| |
| trampoline_var_defs = ListNode( |
| filter_four_trampolines([ |
| F(("${class_name}::InstallInterfaceTemplateFuncType " |
| "${class_name}::{} = nullptr;"), TP_INSTALL_INTERFACE_TEMPLATE), |
| F(("${class_name}::InstallUnconditionalPropertiesFuncType " |
| "${class_name}::{} = nullptr;"), |
| TP_INSTALL_UNCONDITIONAL_PROPS), |
| F(("${class_name}::InstallContextIndependentPropertiesFuncType " |
| "${class_name}::{} = nullptr;"), |
| TP_INSTALL_CONTEXT_INDEPENDENT_PROPS), |
| F(("${class_name}::InstallContextDependentPropertiesFuncType " |
| "${class_name}::{} = nullptr;"), |
| TP_INSTALL_CONTEXT_DEPENDENT_PROPS), |
| ])) |
| trampoline_var_defs.set_base_template_vars(cg_context.template_bindings()) |
| |
| func_decl = CxxFuncDeclNode( |
| name=function_name, arg_decls=[], return_type="void", static=True) |
| |
| func_def = CxxFuncDefNode( |
| name=function_name, |
| arg_decls=[], |
| return_type="void", |
| class_name=class_name) |
| func_def.set_base_template_vars(cg_context.template_bindings()) |
| |
| body = func_def.body |
| body.extend( |
| filter_four_trampolines([ |
| F("${class_name}::{} = {};", TP_INSTALL_INTERFACE_TEMPLATE, |
| FN_INSTALL_INTERFACE_TEMPLATE), |
| F("${class_name}::{} = {};", TP_INSTALL_UNCONDITIONAL_PROPS, |
| FN_INSTALL_UNCONDITIONAL_PROPS), |
| F("${class_name}::{} = {};", TP_INSTALL_CONTEXT_INDEPENDENT_PROPS, |
| FN_INSTALL_CONTEXT_INDEPENDENT_PROPS), |
| F("${class_name}::{} = {};", TP_INSTALL_CONTEXT_DEPENDENT_PROPS, |
| FN_INSTALL_CONTEXT_DEPENDENT_PROPS), |
| ])) |
| |
| return func_decl, func_def, trampoline_var_decls, trampoline_var_defs |
| |
| |
| # ---------------------------------------------------------------------------- |
| # WrapperTypeInfo |
| # ---------------------------------------------------------------------------- |
| |
| |
| def make_wrapper_type_info(cg_context, function_name, |
| has_context_dependent_props): |
| assert isinstance(cg_context, CodeGenContext) |
| assert function_name == "GetWrapperTypeInfo" |
| assert isinstance(has_context_dependent_props, bool) |
| |
| F = lambda *args, **kwargs: TextNode(_format(*args, **kwargs)) |
| |
| func_def = CxxFuncDefNode( |
| name=function_name, |
| arg_decls=[], |
| return_type="constexpr const WrapperTypeInfo*", |
| static=True) |
| func_def.set_base_template_vars(cg_context.template_bindings()) |
| func_def.body.append(TextNode("return &wrapper_type_info_;")) |
| |
| member_var_def = TextNode( |
| "static const WrapperTypeInfo wrapper_type_info_;") |
| |
| wrapper_type_info_def = ListNode() |
| wrapper_type_info_def.set_base_template_vars( |
| cg_context.template_bindings()) |
| |
| pattern = """\ |
| // Construction of WrapperTypeInfo may require non-trivial initialization due |
| // to cross-component address resolution in order to load the pointer to the |
| // parent interface's WrapperTypeInfo. We ignore this issue because the issue |
| // happens only on component builds and the official release builds |
| // (statically-linked builds) are never affected by this issue. |
| #if defined(COMPONENT_BUILD) && defined(WIN32) && defined(__clang__) |
| #pragma clang diagnostic push |
| #pragma clang diagnostic ignored "-Wglobal-constructors" |
| #endif |
| |
| const WrapperTypeInfo ${class_name}::wrapper_type_info_{{ |
| gin::kEmbedderBlink, |
| ${class_name}::{install_interface_template_func}, |
| {install_context_dependent_func}, |
| "${{class_like.identifier}}", |
| {wrapper_type_info_of_inherited}, |
| {wrapper_type_prototype}, |
| {wrapper_class_id}, |
| {active_script_wrappable_inheritance}, |
| {idl_definition_kind}, |
| }}; |
| |
| #if defined(COMPONENT_BUILD) && defined(WIN32) && defined(__clang__) |
| #pragma clang diagnostic pop |
| #endif |
| """ |
| class_like = cg_context.class_like |
| if has_context_dependent_props: |
| install_context_dependent_func = _format( |
| "${class_name}::{}", FN_INSTALL_CONTEXT_DEPENDENT_PROPS) |
| else: |
| install_context_dependent_func = "nullptr" |
| if class_like.is_interface and class_like.inherited: |
| wrapper_type_info_of_inherited = "{}::GetWrapperTypeInfo()".format( |
| v8_bridge_class_name(class_like.inherited)) |
| else: |
| wrapper_type_info_of_inherited = "nullptr" |
| if class_like.is_interface: |
| wrapper_type_prototype = "WrapperTypeInfo::kWrapperTypeObjectPrototype" |
| else: |
| wrapper_type_prototype = "WrapperTypeInfo::kWrapperTypeNoPrototype" |
| if class_like.is_interface and class_like.does_implement("Node"): |
| wrapper_class_id = "WrapperTypeInfo::kNodeClassId" |
| else: |
| wrapper_class_id = "WrapperTypeInfo::kObjectClassId" |
| if class_like.code_generator_info.is_active_script_wrappable: |
| active_script_wrappable_inheritance = ( |
| "WrapperTypeInfo::kInheritFromActiveScriptWrappable") |
| else: |
| active_script_wrappable_inheritance = ( |
| "WrapperTypeInfo::kNotInheritFromActiveScriptWrappable") |
| if class_like.is_interface: |
| idl_definition_kind = "WrapperTypeInfo::kIdlInterface" |
| elif class_like.is_namespace: |
| idl_definition_kind = "WrapperTypeInfo::kIdlNamespace" |
| elif class_like.is_callback_interface: |
| idl_definition_kind = "WrapperTypeInfo::kIdlCallbackInterface" |
| wrapper_type_info_def.append( |
| F(pattern, |
| install_interface_template_func=FN_INSTALL_INTERFACE_TEMPLATE, |
| install_context_dependent_func=install_context_dependent_func, |
| wrapper_type_info_of_inherited=wrapper_type_info_of_inherited, |
| wrapper_type_prototype=wrapper_type_prototype, |
| wrapper_class_id=wrapper_class_id, |
| active_script_wrappable_inheritance=( |
| active_script_wrappable_inheritance), |
| idl_definition_kind=idl_definition_kind)) |
| |
| if class_like.is_interface: |
| blink_class = blink_class_name(class_like) |
| pattern = """\ |
| const WrapperTypeInfo& {blink_class}::wrapper_type_info_ = |
| ${class_name}::wrapper_type_info_; |
| """ |
| wrapper_type_info_def.append(F(pattern, blink_class=blink_class)) |
| |
| if class_like.code_generator_info.is_active_script_wrappable: |
| pattern = """\ |
| // [ActiveScriptWrappable] |
| static_assert( |
| std::is_base_of<ActiveScriptWrappableBase, {blink_class}>::value, |
| "{blink_class} does not inherit from ActiveScriptWrappable<> despite " |
| "the IDL has [ActiveScriptWrappable] extended attribute."); |
| static_assert( |
| !std::is_same<decltype(&{blink_class}::HasPendingActivity), |
| decltype(&ScriptWrappable::HasPendingActivity)>::value, |
| "{blink_class} is not overriding hasPendingActivity() despite " |
| "the IDL has [ActiveScriptWrappable] extended attribute.");""" |
| else: |
| pattern = """\ |
| // non-[ActiveScriptWrappable] |
| static_assert( |
| !std::is_base_of<ActiveScriptWrappableBase, {blink_class}>::value, |
| "{blink_class} inherits from ActiveScriptWrappable<> without " |
| "[ActiveScriptWrappable] extended attribute."); |
| static_assert( |
| std::is_same<decltype(&{blink_class}::HasPendingActivity), |
| decltype(&ScriptWrappable::HasPendingActivity)>::value, |
| "{blink_class} is overriding hasPendingActivity() without " |
| "[ActiveScriptWrappable] extended attribute.");""" |
| if class_like.is_interface: |
| wrapper_type_info_def.append(F(pattern, blink_class=blink_class)) |
| |
| return func_def, member_var_def, wrapper_type_info_def |
| |
| |
| # ---------------------------------------------------------------------------- |
| # V8 Context Snapshot |
| # ---------------------------------------------------------------------------- |
| |
| |
| def make_v8_context_snapshot_api(cg_context, component, attribute_entries, |
| constant_entries, constructor_entries, |
| exposed_construct_entries, operation_entries, |
| named_properties_object_callback_defs, |
| cross_origin_property_callback_defs, |
| install_context_independent_func_name): |
| assert isinstance(cg_context, CodeGenContext) |
| assert isinstance(component, web_idl.Component) |
| |
| if not cg_context.interface: |
| return None, None |
| |
| derived_interfaces = cg_context.interface.deriveds |
| derived_names = list( |
| map(lambda interface: interface.identifier, derived_interfaces)) |
| derived_names.append(cg_context.interface.identifier) |
| if not ("Window" in derived_names or "HTMLDocument" in derived_names): |
| return None, None |
| |
| header_ns = CxxNamespaceNode(name_style.namespace("v8_context_snapshot")) |
| source_ns = CxxNamespaceNode(name_style.namespace("v8_context_snapshot")) |
| |
| export_text = component_export(component, False) |
| |
| def add_func(func_decl, func_def): |
| header_ns.body.extend([ |
| TextNode(export_text), |
| func_decl, |
| EmptyNode(), |
| ]) |
| source_ns.body.extend([ |
| func_def, |
| EmptyNode(), |
| ]) |
| |
| add_func(*_make_v8_context_snapshot_get_reference_table_function( |
| cg_context, name_style.func("GetRefTableOf", |
| cg_context.class_name), attribute_entries, |
| constant_entries, constructor_entries, exposed_construct_entries, |
| operation_entries, named_properties_object_callback_defs, |
| cross_origin_property_callback_defs)) |
| |
| add_func(*_make_v8_context_snapshot_install_props_per_context_function( |
| cg_context, name_style.func("InstallPropsOf", |
| cg_context.class_name), attribute_entries, |
| constant_entries, exposed_construct_entries, operation_entries)) |
| |
| add_func(*_make_v8_context_snapshot_install_props_per_isolate_function( |
| cg_context, name_style.func("InstallPropsOf", cg_context.class_name), |
| install_context_independent_func_name)) |
| |
| return header_ns, source_ns |
| |
| |
| def _make_v8_context_snapshot_get_reference_table_function( |
| cg_context, function_name, attribute_entries, constant_entries, |
| constructor_entries, exposed_construct_entries, operation_entries, |
| named_properties_object_callback_defs, |
| cross_origin_property_callback_defs): |
| callback_names = ["${class_name}::GetWrapperTypeInfo()"] |
| |
| for entry in attribute_entries: |
| if entry.exposure_conditional.is_always_true: |
| callback_names.append(entry.attr_get_callback_name) |
| callback_names.append(entry.attr_set_callback_name) |
| for entry in constant_entries: |
| if entry.exposure_conditional.is_always_true: |
| callback_names.append(entry.const_callback_name) |
| for entry in constructor_entries: |
| if entry.exposure_conditional.is_always_true: |
| callback_names.append(entry.ctor_callback_name) |
| for entry in exposed_construct_entries: |
| if entry.exposure_conditional.is_always_true: |
| callback_names.append(entry.prop_callback_name) |
| for entry in operation_entries: |
| if entry.exposure_conditional.is_always_true: |
| callback_names.append(entry.op_callback_name) |
| |
| def collect_callbacks(node): |
| if isinstance(node, CxxFuncDefNode): |
| callback_names.append(node.function_name) |
| elif hasattr(node, "__iter__"): |
| for child_node in node: |
| collect_callbacks(child_node) |
| |
| collect_callbacks(named_properties_object_callback_defs) |
| collect_callbacks(cross_origin_property_callback_defs) |
| |
| entry_nodes = list( |
| map( |
| lambda name: TextNode("reinterpret_cast<intptr_t>({}),".format(name |
| )), |
| filter(None, callback_names))) |
| table_node = ListNode([ |
| TextNode("static const intptr_t kReferenceTable[] = {"), |
| ListNode(entry_nodes), |
| TextNode("};"), |
| ]) |
| |
| func_decl = CxxFuncDeclNode(name=function_name, |
| arg_decls=[], |
| return_type="base::span<const intptr_t>") |
| |
| func_def = CxxFuncDefNode(name=function_name, |
| arg_decls=[], |
| return_type="base::span<const intptr_t>") |
| func_def.set_base_template_vars(cg_context.template_bindings()) |
| body = func_def.body |
| body.extend([table_node, TextNode("return kReferenceTable;")]) |
| |
| return func_decl, func_def |
| |
| |
| def _make_v8_context_snapshot_install_props_per_context_function( |
| cg_context, function_name, attribute_entries, constant_entries, |
| exposed_construct_entries, operation_entries): |
| def selector(entry): |
| if entry.exposure_conditional.is_always_true: |
| return False |
| if entry.is_context_dependent: |
| return False |
| return True |
| |
| func_decl, func_def, _ = make_install_properties( |
| cg_context, |
| function_name, |
| class_name=None, |
| prop_install_mode=PropInstallMode.V8_CONTEXT_SNAPSHOT, |
| trampoline_var_name=None, |
| attribute_entries=list(filter(selector, attribute_entries)), |
| constant_entries=list(filter(selector, constant_entries)), |
| exposed_construct_entries=list( |
| filter(selector, exposed_construct_entries)), |
| operation_entries=list(filter(selector, operation_entries))) |
| |
| return func_decl, func_def |
| |
| |
| def _make_v8_context_snapshot_install_props_per_isolate_function( |
| cg_context, function_name, install_context_independent_func_name): |
| arg_decls = [ |
| "v8::Isolate* isolate", |
| "const DOMWrapperWorld& world", |
| "v8::Local<v8::Template> instance_template", |
| "v8::Local<v8::Template> prototype_template", |
| "v8::Local<v8::Template> interface_template", |
| ] |
| arg_names = [ |
| "isolate", |
| "world", |
| "instance_template", |
| "prototype_template", |
| "interface_template", |
| ] |
| return_type = "void" |
| |
| func_decl = CxxFuncDeclNode(name=function_name, |
| arg_decls=arg_decls, |
| return_type=return_type) |
| func_def = CxxFuncDefNode(name=function_name, |
| arg_decls=arg_decls, |
| return_type=return_type) |
| |
| if not install_context_independent_func_name: |
| return func_decl, func_def |
| |
| func_def.set_base_template_vars(cg_context.template_bindings()) |
| body = func_def.body |
| for arg_name in arg_names: |
| body.add_template_var(arg_name, arg_name) |
| pattern = """\ |
| return ${class_name}::{func}( |
| ${isolate}, ${world}, |
| ${instance_template}, |
| ${prototype_template}, |
| ${interface_template});\ |
| """ |
| body.append( |
| TextNode(_format(pattern, func=install_context_independent_func_name))) |
| return func_decl, func_def |
| |
| |
| # ---------------------------------------------------------------------------- |
| # Main functions |
| # ---------------------------------------------------------------------------- |
| |
| |
| def _collect_include_headers(class_like): |
| assert isinstance(class_like, (web_idl.Interface, web_idl.Namespace)) |
| |
| headers = set(class_like.code_generator_info.blink_headers) |
| |
| def collect_from_idl_type(idl_type): |
| idl_type.apply_to_all_composing_elements(add_include_headers) |
| |
| def add_include_headers(idl_type): |
| # ScriptPromise doesn't require any header for the result type. |
| if idl_type.is_promise: |
| raise StopIteration(idl_type.syntactic_form) |
| |
| type_def_obj = idl_type.type_definition_object |
| if type_def_obj is not None: |
| if (type_def_obj.identifier in ( |
| "OnErrorEventHandlerNonNull", |
| "OnBeforeUnloadEventHandlerNonNull")): |
| raise StopIteration(idl_type.syntactic_form) |
| |
| headers.add(PathManager(type_def_obj).api_path(ext="h")) |
| if type_def_obj.is_interface or type_def_obj.is_namespace: |
| headers.add(PathManager(type_def_obj).blink_path(ext="h")) |
| raise StopIteration(idl_type.syntactic_form) |
| |
| union_def_obj = idl_type.union_definition_object |
| if union_def_obj is not None: |
| headers.add(PathManager(union_def_obj).api_path(ext="h")) |
| |
| for attribute in class_like.attributes: |
| collect_from_idl_type(attribute.idl_type) |
| for constructor in class_like.constructors: |
| for argument in constructor.arguments: |
| collect_from_idl_type(argument.idl_type) |
| for operation in class_like.operations: |
| collect_from_idl_type(operation.return_type) |
| for argument in operation.arguments: |
| collect_from_idl_type(argument.idl_type) |
| |
| if class_like.is_interface: |
| for exposed_construct in class_like.exposed_constructs: |
| headers.add(PathManager(exposed_construct).api_path(ext="h")) |
| for legacy_window_alias in class_like.legacy_window_aliases: |
| headers.add( |
| PathManager(legacy_window_alias.original).api_path(ext="h")) |
| |
| path_manager = PathManager(class_like) |
| headers.discard(path_manager.api_path(ext="h")) |
| headers.discard(path_manager.impl_path(ext="h")) |
| |
| # TODO(yukishiino): Window interface should be |
| # [ImplementedAs=LocalDOMWindow] instead of [ImplementedAs=DOMWindow], and |
| # [CrossOrigin] properties should be implemented specifically with |
| # DOMWindow class. Then, we'll have less hacks. |
| if class_like.identifier == "Window": |
| headers.add("third_party/blink/renderer/core/frame/local_dom_window.h") |
| |
| return headers |
| |
| |
| def generate_class_like(class_like): |
| assert isinstance(class_like, (web_idl.Interface, web_idl.Namespace)) |
| |
| path_manager = PathManager(class_like) |
| api_component = path_manager.api_component |
| impl_component = path_manager.impl_component |
| is_cross_components = path_manager.is_cross_components |
| for_testing = class_like.code_generator_info.for_testing |
| |
| # Class names |
| api_class_name = v8_bridge_class_name(class_like) |
| if is_cross_components: |
| impl_class_name = "{}::Impl".format(api_class_name) |
| else: |
| impl_class_name = api_class_name |
| |
| interface = None |
| namespace = None |
| if class_like.is_interface: |
| interface = class_like |
| cg_context = CodeGenContext(interface=interface, |
| class_name=api_class_name) |
| elif class_like.is_namespace: |
| namespace = class_like |
| cg_context = CodeGenContext(namespace=namespace, |
| class_name=api_class_name) |
| |
| # Filepaths |
| api_header_path = path_manager.api_path(ext="h") |
| api_source_path = path_manager.api_path(ext="cc") |
| if is_cross_components: |
| impl_header_path = path_manager.impl_path(ext="h") |
| impl_source_path = path_manager.impl_path(ext="cc") |
| |
| # Root nodes |
| api_header_node = ListNode(tail="\n") |
| api_header_node.set_accumulator(CodeGenAccumulator()) |
| api_header_node.set_renderer(MakoRenderer()) |
| api_source_node = ListNode(tail="\n") |
| api_source_node.set_accumulator(CodeGenAccumulator()) |
| api_source_node.set_renderer(MakoRenderer()) |
| if is_cross_components: |
| impl_header_node = ListNode(tail="\n") |
| impl_header_node.set_accumulator(CodeGenAccumulator()) |
| impl_header_node.set_renderer(MakoRenderer()) |
| impl_source_node = ListNode(tail="\n") |
| impl_source_node.set_accumulator(CodeGenAccumulator()) |
| impl_source_node.set_renderer(MakoRenderer()) |
| else: |
| impl_header_node = api_header_node |
| impl_source_node = api_source_node |
| |
| # Namespaces |
| api_header_blink_ns = CxxNamespaceNode(name_style.namespace("blink")) |
| api_source_blink_ns = CxxNamespaceNode(name_style.namespace("blink")) |
| if is_cross_components: |
| impl_header_blink_ns = CxxNamespaceNode(name_style.namespace("blink")) |
| impl_source_blink_ns = CxxNamespaceNode(name_style.namespace("blink")) |
| else: |
| impl_header_blink_ns = api_header_blink_ns |
| impl_source_blink_ns = api_source_blink_ns |
| |
| # Class definitions |
| api_class_def = CxxClassDefNode( |
| cg_context.class_name, |
| base_class_names=[ |
| _format("bindings::V8InterfaceBridge<${class_name}, {}>", |
| blink_class_name(class_like)), |
| ], |
| final=True, |
| export=component_export(api_component, for_testing)) |
| api_class_def.set_base_template_vars(cg_context.template_bindings()) |
| api_class_def.bottom_section.append( |
| TextNode("friend class {};".format(blink_class_name(class_like)))) |
| if is_cross_components: |
| impl_class_def = CxxClassDefNode(impl_class_name, |
| final=True, |
| export=component_export( |
| impl_component, for_testing)) |
| impl_class_def.set_base_template_vars(cg_context.template_bindings()) |
| api_class_def.public_section.extend([ |
| TextNode("// Cross-component implementation class"), |
| TextNode("class Impl;"), |
| EmptyNode(), |
| ]) |
| else: |
| impl_class_def = api_class_def |
| |
| # Constants |
| constants_def = None |
| if class_like.constants: |
| constants_def = CxxClassDefNode(name="Constant", final=True) |
| constants_def.top_section.append(TextNode("STATIC_ONLY(Constant);")) |
| for constant in class_like.constants: |
| cgc = cg_context.make_copy(constant=constant) |
| constants_def.public_section.append( |
| make_constant_constant_def(cgc, constant_name(cgc))) |
| |
| # Custom callback implementations |
| custom_callback_impl_decls = ListNode() |
| |
| def add_custom_callback_impl_decl(**params): |
| arg_decls = params.pop("arg_decls") |
| name = params.pop("name", None) |
| if name is None: |
| name = custom_function_name(cg_context.make_copy(**params)) |
| custom_callback_impl_decls.append( |
| CxxFuncDeclNode( |
| name=name, |
| arg_decls=arg_decls, |
| return_type="void", |
| static=True)) |
| |
| if class_like.identifier == "HTMLAllCollection": |
| add_custom_callback_impl_decl( |
| name=name_style.func("LegacyCallCustom"), |
| arg_decls=["const v8::FunctionCallbackInfo<v8::Value>&"]) |
| for attribute in class_like.attributes: |
| custom_values = attribute.extended_attributes.values_of("Custom") |
| is_cross_origin = "CrossOrigin" in attribute.extended_attributes |
| cross_origin_values = attribute.extended_attributes.values_of( |
| "CrossOrigin") |
| if "Getter" in custom_values: |
| add_custom_callback_impl_decl( |
| attribute=attribute, |
| attribute_get=True, |
| arg_decls=["const v8::FunctionCallbackInfo<v8::Value>&"]) |
| if is_cross_origin and (not cross_origin_values |
| or "Getter" in cross_origin_values): |
| add_custom_callback_impl_decl( |
| attribute=attribute, |
| attribute_get=True, |
| arg_decls=["const v8::PropertyCallbackInfo<v8::Value>&"]) |
| if "Setter" in custom_values: |
| add_custom_callback_impl_decl( |
| attribute=attribute, |
| attribute_set=True, |
| arg_decls=[ |
| "v8::Local<v8::Value>", |
| "const v8::FunctionCallbackInfo<v8::Value>&", |
| ]) |
| if is_cross_origin and (not cross_origin_values |
| or "Setter" in cross_origin_values): |
| add_custom_callback_impl_decl( |
| attribute=attribute, |
| attribute_set=True, |
| arg_decls=[ |
| "v8::Local<v8::Value>", |
| "const v8::PropertyCallbackInfo<void>&", |
| ]) |
| for operation_group in class_like.operation_groups: |
| if "Custom" in operation_group.extended_attributes: |
| add_custom_callback_impl_decl( |
| operation_group=operation_group, |
| arg_decls=["const v8::FunctionCallbackInfo<v8::Value>&"]) |
| if interface and interface.indexed_and_named_properties: |
| props = interface.indexed_and_named_properties |
| operation = props.own_named_getter |
| if operation and "Custom" in operation.extended_attributes: |
| add_custom_callback_impl_decl( |
| named_property_getter=operation, |
| arg_decls=[ |
| "const AtomicString& property_name", |
| "const v8::PropertyCallbackInfo<v8::Value>&", |
| ]) |
| operation = props.own_named_setter |
| if operation and "Custom" in operation.extended_attributes: |
| add_custom_callback_impl_decl( |
| named_property_setter=operation, |
| arg_decls=[ |
| "const AtomicString& property_name", |
| "v8::Local<v8::Value> v8_property_value", |
| "const v8::PropertyCallbackInfo<v8::Value>&", |
| ]) |
| operation = props.own_named_deleter |
| if operation and "Custom" in operation.extended_attributes: |
| add_custom_callback_impl_decl( |
| named_property_deleter=operation, |
| arg_decls=[ |
| "const AtomicString& property_name", |
| "const v8::PropertyCallbackInfo<v8::Value>&", |
| ]) |
| |
| # Cross-component trampolines |
| if is_cross_components: |
| # tp_ = trampoline name |
| tp_install_interface_template = TP_INSTALL_INTERFACE_TEMPLATE |
| tp_install_unconditional_props = TP_INSTALL_UNCONDITIONAL_PROPS |
| tp_install_context_independent_props = ( |
| TP_INSTALL_CONTEXT_INDEPENDENT_PROPS) |
| tp_install_context_dependent_props = TP_INSTALL_CONTEXT_DEPENDENT_PROPS |
| else: |
| tp_install_interface_template = None |
| tp_install_unconditional_props = None |
| tp_install_context_independent_props = None |
| tp_install_context_dependent_props = None |
| |
| # Callback functions |
| attribute_entries = [] |
| constant_entries = [] |
| constructor_entries = [] |
| exposed_construct_entries = [] |
| operation_entries = [] |
| callback_defs = make_property_entries_and_callback_defs( |
| cg_context, |
| attribute_entries=attribute_entries, |
| constant_entries=constant_entries, |
| constructor_entries=constructor_entries, |
| exposed_construct_entries=exposed_construct_entries, |
| operation_entries=operation_entries) |
| supplemental_install_node = SequenceNode() |
| |
| # Indexed and named properties |
| # Shorten a function name to mitigate a style check error. |
| f = make_indexed_and_named_property_callbacks_and_install_node |
| (indexed_and_named_property_decls, indexed_and_named_property_defs, |
| indexed_and_named_property_install_node) = f(cg_context) |
| supplemental_install_node.append(indexed_and_named_property_install_node) |
| supplemental_install_node.append(EmptyNode()) |
| |
| # Named properties object |
| (named_properties_object_callback_defs, |
| named_properties_object_install_node) = ( |
| make_named_properties_object_callbacks_and_install_node(cg_context)) |
| callback_defs.extend(named_properties_object_callback_defs) |
| supplemental_install_node.append(named_properties_object_install_node) |
| supplemental_install_node.append(EmptyNode()) |
| |
| # Cross origin properties |
| (cross_origin_property_callback_defs, |
| cross_origin_property_install_node) = ( |
| make_cross_origin_property_callbacks_and_install_node( |
| cg_context, attribute_entries, operation_entries)) |
| callback_defs.extend(cross_origin_property_callback_defs) |
| supplemental_install_node.append(cross_origin_property_install_node) |
| supplemental_install_node.append(EmptyNode()) |
| |
| # Installer functions |
| is_unconditional = lambda entry: entry.exposure_conditional.is_always_true |
| is_context_dependent = lambda entry: entry.is_context_dependent |
| is_context_independent = ( |
| lambda e: not is_context_dependent(e) and not is_unconditional(e)) |
| (install_unconditional_props_decl, install_unconditional_props_def, |
| install_unconditional_props_trampoline) = make_install_properties( |
| cg_context, |
| FN_INSTALL_UNCONDITIONAL_PROPS, |
| class_name=impl_class_name, |
| prop_install_mode=PropInstallMode.UNCONDITIONAL, |
| trampoline_var_name=tp_install_unconditional_props, |
| attribute_entries=list(filter(is_unconditional, attribute_entries)), |
| constant_entries=list(filter(is_unconditional, constant_entries)), |
| exposed_construct_entries=list( |
| filter(is_unconditional, exposed_construct_entries)), |
| operation_entries=list(filter(is_unconditional, operation_entries))) |
| (install_context_independent_props_decl, |
| install_context_independent_props_def, |
| install_context_independent_props_trampoline) = make_install_properties( |
| cg_context, |
| FN_INSTALL_CONTEXT_INDEPENDENT_PROPS, |
| class_name=impl_class_name, |
| prop_install_mode=PropInstallMode.CONTEXT_INDEPENDENT, |
| trampoline_var_name=tp_install_context_independent_props, |
| attribute_entries=list( |
| filter(is_context_independent, attribute_entries)), |
| constant_entries=list(filter(is_context_independent, |
| constant_entries)), |
| exposed_construct_entries=list( |
| filter(is_context_independent, exposed_construct_entries)), |
| operation_entries=list( |
| filter(is_context_independent, operation_entries))) |
| (install_context_dependent_props_decl, install_context_dependent_props_def, |
| install_context_dependent_props_trampoline) = make_install_properties( |
| cg_context, |
| FN_INSTALL_CONTEXT_DEPENDENT_PROPS, |
| class_name=impl_class_name, |
| prop_install_mode=PropInstallMode.CONTEXT_DEPENDENT, |
| trampoline_var_name=tp_install_context_dependent_props, |
| attribute_entries=list(filter(is_context_dependent, |
| attribute_entries)), |
| constant_entries=list(filter(is_context_dependent, constant_entries)), |
| exposed_construct_entries=list( |
| filter(is_context_dependent, exposed_construct_entries)), |
| operation_entries=list(filter(is_context_dependent, |
| operation_entries))) |
| (install_interface_template_decl, install_interface_template_def, |
| install_interface_template_trampoline) = make_install_interface_template( |
| cg_context, |
| FN_INSTALL_INTERFACE_TEMPLATE, |
| class_name=impl_class_name, |
| trampoline_var_name=tp_install_interface_template, |
| constructor_entries=constructor_entries, |
| supplemental_install_node=supplemental_install_node, |
| install_unconditional_func_name=(install_unconditional_props_def |
| and FN_INSTALL_UNCONDITIONAL_PROPS), |
| install_context_independent_func_name=( |
| install_context_independent_props_def |
| and FN_INSTALL_CONTEXT_INDEPENDENT_PROPS)) |
| installer_function_decls = ListNode([ |
| install_interface_template_decl, |
| install_unconditional_props_decl, |
| install_context_independent_props_decl, |
| install_context_dependent_props_decl, |
| ]) |
| installer_function_defs = ListNode([ |
| install_interface_template_def, |
| EmptyNode(), |
| install_unconditional_props_def, |
| EmptyNode(), |
| install_context_independent_props_def, |
| EmptyNode(), |
| install_context_dependent_props_def, |
| ]) |
| installer_function_trampolines = ListNode([ |
| install_interface_template_trampoline, |
| install_unconditional_props_trampoline, |
| install_context_independent_props_trampoline, |
| install_context_dependent_props_trampoline, |
| ]) |
| |
| # WrapperTypeInfo |
| (get_wrapper_type_info_def, wrapper_type_info_var_def, |
| wrapper_type_info_init) = make_wrapper_type_info( |
| cg_context, |
| "GetWrapperTypeInfo", |
| has_context_dependent_props=bool( |
| install_context_dependent_props_decl)) |
| |
| # Cross-component trampolines |
| if is_cross_components: |
| (cross_component_init_decl, cross_component_init_def, |
| trampoline_var_decls, |
| trampoline_var_defs) = make_cross_component_init( |
| cg_context, |
| "Init", |
| class_name=impl_class_name, |
| has_unconditional_props=bool(install_unconditional_props_decl), |
| has_context_independent_props=bool( |
| install_context_independent_props_decl), |
| has_context_dependent_props=bool( |
| install_context_dependent_props_decl)) |
| |
| # V8 Context Snapshot |
| (header_v8_context_snapshot_ns, |
| source_v8_context_snapshot_ns) = make_v8_context_snapshot_api( |
| cg_context, impl_component, attribute_entries, constant_entries, |
| constructor_entries, exposed_construct_entries, operation_entries, |
| named_properties_object_callback_defs, |
| cross_origin_property_callback_defs, |
| (install_context_independent_props_def |
| and FN_INSTALL_CONTEXT_INDEPENDENT_PROPS)) |
| |
| # Header part (copyright, include directives, and forward declarations) |
| api_header_node.extend([ |
| make_copyright_header(), |
| EmptyNode(), |
| enclose_with_header_guard( |
| ListNode([ |
| make_header_include_directives(api_header_node.accumulator), |
| EmptyNode(), |
| api_header_blink_ns, |
| ]), name_style.header_guard(api_header_path)), |
| ]) |
| api_header_blink_ns.body.extend([ |
| make_forward_declarations(api_header_node.accumulator), |
| EmptyNode(), |
| ]) |
| api_source_node.extend([ |
| make_copyright_header(), |
| EmptyNode(), |
| TextNode("#include \"{}\"".format(api_header_path)), |
| EmptyNode(), |
| make_header_include_directives(api_source_node.accumulator), |
| EmptyNode(), |
| api_source_blink_ns, |
| ]) |
| api_source_blink_ns.body.extend([ |
| make_forward_declarations(api_source_node.accumulator), |
| EmptyNode(), |
| ]) |
| if is_cross_components: |
| impl_header_node.extend([ |
| make_copyright_header(), |
| EmptyNode(), |
| enclose_with_header_guard( |
| ListNode([ |
| make_header_include_directives( |
| impl_header_node.accumulator), |
| EmptyNode(), |
| impl_header_blink_ns, |
| ]), name_style.header_guard(impl_header_path)), |
| ]) |
| impl_header_blink_ns.body.extend([ |
| make_forward_declarations(impl_header_node.accumulator), |
| EmptyNode(), |
| ]) |
| impl_source_node.extend([ |
| make_copyright_header(), |
| EmptyNode(), |
| TextNode("#include \"{}\"".format(impl_header_path)), |
| EmptyNode(), |
| make_header_include_directives(impl_source_node.accumulator), |
| EmptyNode(), |
| impl_source_blink_ns, |
| ]) |
| impl_source_blink_ns.body.extend([ |
| make_forward_declarations(impl_source_node.accumulator), |
| EmptyNode(), |
| ]) |
| api_header_node.accumulator.add_include_headers([ |
| class_like.code_generator_info.blink_headers[0], |
| component_export_header(api_component, for_testing), |
| "third_party/blink/renderer/platform/bindings/v8_interface_bridge.h", |
| ]) |
| if interface and interface.inherited: |
| api_source_node.accumulator.add_include_headers( |
| [PathManager(interface.inherited).api_path(ext="h")]) |
| if is_cross_components: |
| impl_header_node.accumulator.add_include_headers([ |
| api_header_path, |
| component_export_header(impl_component, for_testing), |
| ]) |
| impl_source_node.accumulator.add_include_headers([ |
| "third_party/blink/renderer/bindings/core/v8/generated_code_helper.h", |
| "third_party/blink/renderer/bindings/core/v8/native_value_traits_impl.h", |
| "third_party/blink/renderer/bindings/core/v8/v8_set_return_value_for_core.h", |
| "third_party/blink/renderer/platform/bindings/exception_messages.h", |
| "third_party/blink/renderer/platform/bindings/idl_member_installer.h", |
| "third_party/blink/renderer/platform/bindings/runtime_call_stats.h", |
| "third_party/blink/renderer/platform/bindings/v8_binding.h", |
| ]) |
| impl_source_node.accumulator.add_include_headers( |
| _collect_include_headers(class_like)) |
| |
| # Assemble the parts. |
| api_header_blink_ns.body.extend([ |
| api_class_def, |
| EmptyNode(), |
| ]) |
| if is_cross_components: |
| impl_header_blink_ns.body.extend([ |
| impl_class_def, |
| EmptyNode(), |
| ]) |
| |
| if constants_def: |
| api_class_def.public_section.extend([ |
| TextNode("// Constants"), |
| constants_def, |
| EmptyNode(), |
| ]) |
| |
| api_class_def.public_section.append(get_wrapper_type_info_def) |
| api_class_def.public_section.append(EmptyNode()) |
| api_class_def.private_section.append(wrapper_type_info_var_def) |
| api_class_def.private_section.append(EmptyNode()) |
| api_source_blink_ns.body.extend([ |
| wrapper_type_info_init, |
| EmptyNode(), |
| ]) |
| |
| if is_cross_components: |
| api_class_def.public_section.append(installer_function_trampolines) |
| api_class_def.public_section.append(EmptyNode()) |
| api_class_def.private_section.extend([ |
| TextNode("// Cross-component trampolines"), |
| trampoline_var_decls, |
| EmptyNode(), |
| ]) |
| api_source_blink_ns.body.extend([ |
| TextNode("// Cross-component trampolines"), |
| trampoline_var_defs, |
| EmptyNode(), |
| ]) |
| impl_class_def.public_section.append(cross_component_init_decl) |
| impl_class_def.private_section.append(installer_function_decls) |
| impl_source_blink_ns.body.extend([ |
| cross_component_init_def, |
| EmptyNode(), |
| ]) |
| else: |
| api_class_def.public_section.append(installer_function_decls) |
| api_class_def.public_section.append(EmptyNode()) |
| |
| if custom_callback_impl_decls: |
| api_class_def.public_section.extend([ |
| TextNode("// Custom callback implementations"), |
| custom_callback_impl_decls, |
| EmptyNode(), |
| ]) |
| |
| if indexed_and_named_property_decls: |
| api_class_def.public_section.extend([ |
| TextNode("// Indexed properties and named properties"), |
| indexed_and_named_property_decls, |
| EmptyNode(), |
| ]) |
| api_source_blink_ns.body.extend([ |
| indexed_and_named_property_defs, |
| EmptyNode(), |
| ]) |
| |
| impl_source_blink_ns.body.extend([ |
| CxxNamespaceNode(name="", body=callback_defs), |
| EmptyNode(), |
| installer_function_defs, |
| EmptyNode(), |
| ]) |
| |
| if header_v8_context_snapshot_ns: |
| impl_header_blink_ns.body.extend([ |
| CxxNamespaceNode(name=name_style.namespace("bindings"), |
| body=header_v8_context_snapshot_ns), |
| EmptyNode(), |
| ]) |
| impl_source_blink_ns.body.extend([ |
| CxxNamespaceNode(name=name_style.namespace("bindings"), |
| body=source_v8_context_snapshot_ns), |
| EmptyNode(), |
| ]) |
| |
| # Write down to the files. |
| write_code_node_to_file(api_header_node, |
| path_manager.gen_path_to(api_header_path)) |
| write_code_node_to_file(api_source_node, |
| path_manager.gen_path_to(api_source_path)) |
| if path_manager.is_cross_components: |
| write_code_node_to_file(impl_header_node, |
| path_manager.gen_path_to(impl_header_path)) |
| write_code_node_to_file(impl_source_node, |
| path_manager.gen_path_to(impl_source_path)) |
| |
| |
| def generate_interface(interface_identifier): |
| assert isinstance(interface_identifier, web_idl.Identifier) |
| |
| web_idl_database = package_initializer().web_idl_database() |
| interface = web_idl_database.find(interface_identifier) |
| |
| generate_class_like(interface) |
| |
| |
| def generate_install_properties_per_feature(function_name, |
| filepath_basename, |
| for_testing=False): |
| assert isinstance(function_name, str) |
| assert isinstance(filepath_basename, str) |
| assert isinstance(for_testing, bool) |
| |
| web_idl_database = package_initializer().web_idl_database() |
| |
| # Filepaths |
| header_path = PathManager.component_path("modules", |
| "{}.h".format(filepath_basename)) |
| source_path = PathManager.component_path("modules", |
| "{}.cc".format(filepath_basename)) |
| |
| # Root nodes |
| header_node = ListNode(tail="\n") |
| header_node.set_accumulator(CodeGenAccumulator()) |
| header_node.set_renderer(MakoRenderer()) |
| source_node = ListNode(tail="\n") |
| source_node.set_accumulator(CodeGenAccumulator()) |
| source_node.set_renderer(MakoRenderer()) |
| |
| # Namespaces |
| header_blink_ns = CxxNamespaceNode(name_style.namespace("blink")) |
| source_blink_ns = CxxNamespaceNode(name_style.namespace("blink")) |
| header_bindings_ns = CxxNamespaceNode(name_style.namespace("bindings")) |
| source_bindings_ns = CxxNamespaceNode(name_style.namespace("bindings")) |
| header_blink_ns.body.extend([ |
| make_forward_declarations(header_node.accumulator), |
| EmptyNode(), |
| header_bindings_ns, |
| ]) |
| source_blink_ns.body.append(source_bindings_ns) |
| |
| # Function nodes |
| arg_decls = [ |
| "ScriptState* script_state", |
| "OriginTrialFeature feature", |
| ] |
| func_decl = CxxFuncDeclNode( |
| name=function_name, arg_decls=arg_decls, return_type="void") |
| func_def = CxxFuncDefNode( |
| name=function_name, arg_decls=arg_decls, return_type="void") |
| func_def.body.add_template_vars({ |
| "script_state": "script_state", |
| "feature": "feature", |
| }) |
| helper_func_def = CxxFuncDefNode( |
| name="InstallPropertiesPerFeatureInternal", |
| arg_decls=[ |
| "ScriptState* script_state", |
| "OriginTrialFeature feature", |
| "base::span<const WrapperTypeInfo* const> wrapper_type_info_list", |
| ], |
| return_type="void") |
| |
| # Assemble the parts. |
| header_node.accumulator.add_class_decls(["ScriptState"]) |
| header_node.accumulator.add_include_headers([ |
| "third_party/blink/renderer/platform/runtime_enabled_features.h", |
| ]) |
| header_node.extend([ |
| make_copyright_header(), |
| EmptyNode(), |
| enclose_with_header_guard( |
| ListNode([ |
| make_header_include_directives(header_node.accumulator), |
| EmptyNode(), |
| header_blink_ns, |
| ]), name_style.header_guard(header_path)), |
| ]) |
| source_node.accumulator.add_include_headers([ |
| "base/containers/span.h", |
| "third_party/blink/renderer/platform/bindings/script_state.h", |
| "third_party/blink/renderer/platform/bindings/v8_per_context_data.h", |
| ]) |
| source_node.extend([ |
| make_copyright_header(), |
| EmptyNode(), |
| TextNode("#include \"{}\"".format(header_path)), |
| EmptyNode(), |
| make_header_include_directives(source_node.accumulator), |
| EmptyNode(), |
| source_blink_ns, |
| ]) |
| header_bindings_ns.body.extend([ |
| TextNode("""\ |
| // Install ES properties associated with the given origin trial feature.\ |
| """), |
| func_decl, |
| ]) |
| source_bindings_ns.body.extend([ |
| CxxNamespaceNode(name="", body=helper_func_def), |
| EmptyNode(), |
| func_def, |
| ]) |
| |
| # The public function |
| feature_to_class_likes = {} |
| set_of_class_likes = set() |
| for class_like in itertools.chain(web_idl_database.interfaces, |
| web_idl_database.namespaces): |
| if class_like.code_generator_info.for_testing != for_testing: |
| continue |
| |
| for member in itertools.chain(class_like.attributes, |
| class_like.constants, |
| class_like.operation_groups, |
| class_like.exposed_constructs): |
| features = list( |
| member.exposure.context_dependent_runtime_enabled_features) |
| for entry in member.exposure.global_names_and_features: |
| if entry.feature and entry.feature.is_context_dependent: |
| features.append(entry.feature) |
| for feature in features: |
| feature_to_class_likes.setdefault(feature, |
| set()).add(class_like) |
| if features: |
| set_of_class_likes.add(class_like) |
| |
| switch_node = CxxSwitchNode(cond="${feature}") |
| switch_node.append( |
| case=None, |
| body=[ |
| TextNode("// Ignore unknown, deprecated, and unused features."), |
| TextNode("return;"), |
| ], |
| should_add_break=False) |
| for feature, class_likes in sorted(feature_to_class_likes.items()): |
| entries = [ |
| TextNode("{}::GetWrapperTypeInfo(), ".format( |
| v8_bridge_class_name(class_like))) |
| for class_like in sorted(class_likes, key=lambda x: x.identifier) |
| ] |
| table_def = ListNode([ |
| TextNode("static const WrapperTypeInfo* const wti_list[] = {"), |
| ListNode(entries), |
| TextNode("};"), |
| ]) |
| switch_node.append( |
| case="OriginTrialFeature::k{}".format(feature), |
| body=[ |
| table_def, |
| TextNode("selected_wti_list = wti_list;"), |
| ]) |
| |
| func_def.body.extend([ |
| TextNode( |
| "base::span<const WrapperTypeInfo* const> selected_wti_list;"), |
| EmptyNode(), |
| switch_node, |
| EmptyNode(), |
| TextNode("InstallPropertiesPerFeatureInternal" |
| "(${script_state}, ${feature}, selected_wti_list);"), |
| ]) |
| |
| for class_like in set_of_class_likes: |
| path_manager = PathManager(class_like) |
| source_node.accumulator.add_include_headers( |
| [path_manager.api_path(ext="h")]) |
| |
| # The helper function |
| helper_func_def.body.append( |
| TextNode("""\ |
| V8PerContextData* per_context_data = script_state->PerContextData(); |
| v8::Isolate* isolate = script_state->GetIsolate(); |
| v8::Local<v8::Context> context = script_state->GetContext(); |
| const DOMWrapperWorld& world = script_state->World(); |
| V8InterfaceBridgeBase::FeatureSelector feature_selector(feature); |
| |
| for (const auto* wrapper_type_info : wrapper_type_info_list) { |
| v8::Local<v8::Object> instance_object; |
| v8::Local<v8::Object> prototype_object; |
| v8::Local<v8::Function> interface_object; |
| v8::Local<v8::Template> interface_template = |
| wrapper_type_info->GetV8ClassTemplate(isolate, world); |
| |
| switch (wrapper_type_info->idl_definition_kind) { |
| case WrapperTypeInfo::kIdlInterface: |
| if (!per_context_data->GetExistingConstructorAndPrototypeForType( |
| wrapper_type_info, &prototype_object, &interface_object)) { |
| continue; |
| } |
| break; |
| case WrapperTypeInfo::kIdlNamespace: |
| NOTIMPLEMENTED(); |
| break; |
| default: |
| NOTREACHED(); |
| } |
| |
| wrapper_type_info->install_context_dependent_props_func( |
| context, world, instance_object, prototype_object, interface_object, |
| interface_template, feature_selector); |
| }\ |
| """)) |
| |
| # Write down to the files. |
| write_code_node_to_file(header_node, path_manager.gen_path_to(header_path)) |
| write_code_node_to_file(source_node, path_manager.gen_path_to(source_path)) |
| |
| |
| def generate_init_idl_interfaces(function_name, |
| filepath_basename, |
| for_testing=False): |
| assert isinstance(function_name, str) |
| assert isinstance(filepath_basename, str) |
| assert isinstance(for_testing, bool) |
| |
| web_idl_database = package_initializer().web_idl_database() |
| |
| # Filepaths |
| header_path = PathManager.component_path("modules", |
| "{}.h".format(filepath_basename)) |
| source_path = PathManager.component_path("modules", |
| "{}.cc".format(filepath_basename)) |
| |
| # Root nodes |
| header_node = ListNode(tail="\n") |
| header_node.set_accumulator(CodeGenAccumulator()) |
| header_node.set_renderer(MakoRenderer()) |
| source_node = ListNode(tail="\n") |
| source_node.set_accumulator(CodeGenAccumulator()) |
| source_node.set_renderer(MakoRenderer()) |
| |
| # Namespaces |
| header_blink_ns = CxxNamespaceNode(name_style.namespace("blink")) |
| source_blink_ns = CxxNamespaceNode(name_style.namespace("blink")) |
| header_bindings_ns = CxxNamespaceNode(name_style.namespace("bindings")) |
| source_bindings_ns = CxxNamespaceNode(name_style.namespace("bindings")) |
| header_blink_ns.body.append(header_bindings_ns) |
| source_blink_ns.body.append(source_bindings_ns) |
| |
| # Function nodes |
| func_decl = CxxFuncDeclNode( |
| name=function_name, arg_decls=[], return_type="void") |
| func_def = CxxFuncDefNode( |
| name=function_name, arg_decls=[], return_type="void") |
| header_bindings_ns.body.extend([ |
| TextNode("""\ |
| // Initializes cross-component trampolines of IDL interface / namespace.\ |
| """), |
| func_decl, |
| ]) |
| source_bindings_ns.body.append(func_def) |
| |
| # Assemble the parts. |
| header_node.extend([ |
| make_copyright_header(), |
| EmptyNode(), |
| enclose_with_header_guard( |
| ListNode([ |
| make_header_include_directives(header_node.accumulator), |
| EmptyNode(), |
| header_blink_ns, |
| ]), name_style.header_guard(header_path)), |
| ]) |
| source_node.extend([ |
| make_copyright_header(), |
| EmptyNode(), |
| TextNode("#include \"{}\"".format(header_path)), |
| EmptyNode(), |
| make_header_include_directives(source_node.accumulator), |
| EmptyNode(), |
| source_blink_ns, |
| ]) |
| |
| init_calls = [] |
| for class_like in itertools.chain(web_idl_database.interfaces, |
| web_idl_database.namespaces): |
| if class_like.code_generator_info.for_testing != for_testing: |
| continue |
| |
| path_manager = PathManager(class_like) |
| if path_manager.is_cross_components: |
| source_node.accumulator.add_include_headers( |
| [path_manager.impl_path(ext="h")]) |
| |
| class_name = v8_bridge_class_name(class_like) |
| init_calls.append(_format("{}::Impl::Init();", class_name)) |
| for init_call in sorted(init_calls): |
| func_def.body.append(TextNode(init_call)) |
| |
| # Write down to the files. |
| write_code_node_to_file(header_node, path_manager.gen_path_to(header_path)) |
| write_code_node_to_file(source_node, path_manager.gen_path_to(source_path)) |
| |
| |
| def generate_interfaces(task_queue): |
| assert isinstance(task_queue, TaskQueue) |
| |
| web_idl_database = package_initializer().web_idl_database() |
| |
| for interface in web_idl_database.interfaces: |
| task_queue.post_task(generate_interface, interface.identifier) |
| |
| task_queue.post_task(generate_install_properties_per_feature, |
| "InstallPropertiesPerFeature", |
| "properties_per_feature_installer") |
| task_queue.post_task(generate_install_properties_per_feature, |
| "InstallPropertiesPerFeatureForTesting", |
| "properties_per_feature_installer_for_testing", |
| for_testing=True) |
| task_queue.post_task(generate_init_idl_interfaces, "InitIDLInterfaces", |
| "init_idl_interfaces") |
| task_queue.post_task(generate_init_idl_interfaces, |
| "InitIDLInterfacesForTesting", |
| "init_idl_interfaces_for_testing", |
| for_testing=True) |