| # Copyright 2020 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 web_idl |
| |
| from . import name_style |
| from .blink_v8_bridge import blink_class_name |
| from .code_node import EmptyNode |
| from .code_node import ListNode |
| from .code_node import SequenceNode |
| from .code_node import TextNode |
| from .code_node_cxx import CxxClassDefNode |
| from .code_node_cxx import CxxFuncDeclNode |
| from .code_node_cxx import CxxFuncDefNode |
| from .code_node_cxx import CxxNamespaceNode |
| from .codegen_accumulator import CodeGenAccumulator |
| from .codegen_context import CodeGenContext |
| from .codegen_utils import collect_forward_decls_and_include_headers |
| 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 |
| |
| # IDL callback interface and IDL callback function share a lot by their nature, |
| # so this module uses some implementations from IDL callback function. |
| from .callback_function import make_callback_invocation_function |
| from .callback_function import make_invoke_and_report_function |
| from .callback_function import make_is_runnable_or_throw_exception |
| from .callback_function import make_nameclient_implementation |
| |
| # IDL legacy callback interface is mostly the same as IDL interface, so this |
| # module uses some implementations from IDL interface |
| from .interface import FN_INSTALL_INTERFACE_TEMPLATE |
| from .interface import FN_INSTALL_UNCONDITIONAL_PROPS |
| from .interface import PropInstallMode |
| from .interface import constant_name |
| from .interface import make_constant_constant_def |
| from .interface import make_install_interface_template |
| from .interface import make_install_properties |
| from .interface import make_property_entries_and_callback_defs |
| from .interface import make_wrapper_type_info |
| |
| |
| def make_factory_methods(cg_context): |
| assert isinstance(cg_context, CodeGenContext) |
| |
| func_def = CxxFuncDefNode( |
| name="Create", |
| arg_decls=["v8::Local<v8::Object> callback_object"], |
| return_type="${class_name}*", |
| static=True) |
| func_def.set_base_template_vars(cg_context.template_bindings()) |
| |
| func_def.body.append( |
| TextNode("return MakeGarbageCollected<${class_name}>(" |
| "callback_object);")) |
| |
| return func_def, None |
| |
| |
| def make_constructors(cg_context): |
| assert isinstance(cg_context, CodeGenContext) |
| |
| decls = ListNode([ |
| CxxFuncDefNode( |
| name=cg_context.class_name, |
| arg_decls=["v8::Local<v8::Object> callback_object"], |
| return_type="", |
| explicit=True, |
| member_initializer_list=[ |
| "${base_class_name}(callback_object, kSingleOperation)", |
| ]), |
| CxxFuncDeclNode(name="~${class_name}", |
| arg_decls=[], |
| return_type="", |
| override=True, |
| default=True), |
| ]) |
| |
| return decls, None |
| |
| |
| def generate_callback_interface(callback_interface_identifier): |
| assert isinstance(callback_interface_identifier, web_idl.Identifier) |
| |
| web_idl_database = package_initializer().web_idl_database() |
| callback_interface = web_idl_database.find(callback_interface_identifier) |
| |
| path_manager = PathManager(callback_interface) |
| assert path_manager.api_component == path_manager.impl_component |
| api_component = path_manager.api_component |
| for_testing = callback_interface.code_generator_info.for_testing |
| |
| # Class names |
| class_name = blink_class_name(callback_interface) |
| |
| cg_context = CodeGenContext(callback_interface=callback_interface, |
| class_name=class_name, |
| base_class_name="CallbackInterfaceBase") |
| |
| # Filepaths |
| header_path = path_manager.api_path(ext="h") |
| source_path = path_manager.api_path(ext="cc") |
| |
| # 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")) |
| |
| # Class definition |
| class_def = CxxClassDefNode(cg_context.class_name, |
| base_class_names=["CallbackInterfaceBase"], |
| final=True, |
| export=component_export( |
| api_component, for_testing)) |
| class_def.set_base_template_vars(cg_context.template_bindings()) |
| |
| # Constants |
| constants_def = None |
| if callback_interface.constants: |
| constants_def = CxxClassDefNode(name="Constant", final=True) |
| constants_def.top_section.append(TextNode("STATIC_ONLY(Constant);")) |
| for constant in callback_interface.constants: |
| cgc = cg_context.make_copy(constant=constant) |
| constants_def.public_section.append( |
| make_constant_constant_def(cgc, constant_name(cgc))) |
| backward_compatible_constants_def = ListNode() |
| backward_compatible_constants_def.append( |
| TextNode("// Migration adapters")) |
| for constant in callback_interface.constants: |
| cgc = cg_context.make_copy(constant=constant) |
| backward_compatible_constants_def.append( |
| make_constant_constant_def( |
| cgc, name_style.macro(constant.identifier))) |
| |
| # 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) |
| assert not attribute_entries |
| assert not constructor_entries |
| assert not exposed_construct_entries |
| assert not operation_entries |
| |
| # Installer functions |
| is_unconditional = lambda entry: entry.exposure_conditional.is_always_true |
| assert all(is_unconditional(entry) for entry in constant_entries) |
| (install_unconditional_props_decl, install_unconditional_props_def, |
| install_unconditional_props_trampoline) = make_install_properties( |
| cg_context, |
| FN_INSTALL_UNCONDITIONAL_PROPS, |
| class_name=class_name, |
| prop_install_mode=PropInstallMode.UNCONDITIONAL, |
| trampoline_var_name=None, |
| attribute_entries=[], |
| constant_entries=list(filter(is_unconditional, constant_entries)), |
| exposed_construct_entries=[], |
| 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=class_name, |
| trampoline_var_name=None, |
| constructor_entries=[], |
| supplemental_install_node=SequenceNode(), |
| install_unconditional_func_name=(install_unconditional_props_def |
| and FN_INSTALL_UNCONDITIONAL_PROPS), |
| install_context_independent_func_name=None) |
| installer_function_decls = ListNode([ |
| install_interface_template_decl, |
| install_unconditional_props_decl, |
| ]) |
| installer_function_defs = ListNode([ |
| install_interface_template_def, |
| EmptyNode(), |
| install_unconditional_props_def, |
| ]) |
| installer_function_defs.accumulate( |
| CodeGenAccumulator.require_include_headers([ |
| "third_party/blink/renderer/platform/bindings/idl_member_installer.h", |
| ])) |
| |
| # 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=False) |
| |
| # Implementation parts |
| factory_decls, factory_defs = make_factory_methods(cg_context) |
| ctor_decls, ctor_defs = make_constructors(cg_context) |
| nameclient_decls, nameclient_defs = make_nameclient_implementation( |
| cg_context) |
| |
| assert len(callback_interface.operation_groups) == 1 |
| operation_group = callback_interface.operation_groups[0] |
| assert len(operation_group) == 1 |
| operation = operation_group[0] |
| cgc = cg_context.make_copy(operation_group=operation_group, |
| operation=operation) |
| |
| operation_decls, operation_defs = make_callback_invocation_function( |
| cgc, name_style.api_func(operation.identifier)) |
| |
| (invoke_and_report_decls, |
| invoke_and_report_defs) = make_invoke_and_report_function( |
| cgc, name_style.func("InvokeAndReportException"), |
| name_style.api_func(operation.identifier)) |
| |
| event_listener_decls, event_listener_defs = None, None |
| if callback_interface.identifier == "EventListener": |
| event_listener_decls = SequenceNode() |
| event_listener_defs = SequenceNode() |
| (decls, defs) = make_is_runnable_or_throw_exception( |
| cgc, name_style.func("IsRunnableOrThrowException")) |
| event_listener_decls.append(decls) |
| event_listener_defs.append(defs) |
| event_listener_decls.append(EmptyNode()) |
| event_listener_defs.append(EmptyNode()) |
| (decls, defs) = make_callback_invocation_function( |
| cgc, |
| name_style.func("InvokeWithoutRunnabilityCheck"), |
| skip_runnability_check=True) |
| event_listener_decls.append(decls) |
| event_listener_defs.append(defs) |
| |
| # Header part (copyright, include directives, and forward declarations) |
| 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)), |
| ]) |
| header_blink_ns.body.extend([ |
| make_forward_declarations(header_node.accumulator), |
| EmptyNode(), |
| ]) |
| source_node.extend([ |
| make_copyright_header(), |
| EmptyNode(), |
| TextNode("#include \"{}\"".format(header_path)), |
| EmptyNode(), |
| make_header_include_directives(source_node.accumulator), |
| EmptyNode(), |
| source_blink_ns, |
| ]) |
| source_blink_ns.body.extend([ |
| make_forward_declarations(source_node.accumulator), |
| EmptyNode(), |
| ]) |
| |
| # Assemble the parts. |
| header_node.accumulator.add_include_headers([ |
| component_export_header(api_component, for_testing), |
| "third_party/blink/renderer/platform/bindings/callback_interface_base.h", |
| "third_party/blink/renderer/platform/bindings/v8_value_or_script_wrappable_adapter.h", |
| ]) |
| source_node.accumulator.add_include_headers([ |
| "third_party/blink/renderer/bindings/core/v8/callback_invoke_helper.h", |
| "third_party/blink/renderer/bindings/core/v8/generated_code_helper.h", |
| "third_party/blink/renderer/bindings/core/v8/to_v8_traits.h", |
| ]) |
| (header_forward_decls, header_include_headers, source_forward_decls, |
| source_include_headers) = collect_forward_decls_and_include_headers( |
| [callback_interface.operation_groups[0][0].return_type] + list( |
| map(lambda argument: argument.idl_type, |
| callback_interface.operation_groups[0][0].arguments))) |
| header_node.accumulator.add_class_decls(header_forward_decls) |
| header_node.accumulator.add_include_headers(header_include_headers) |
| source_node.accumulator.add_class_decls(source_forward_decls) |
| source_node.accumulator.add_include_headers(source_include_headers) |
| |
| header_blink_ns.body.append(class_def) |
| header_blink_ns.body.append(EmptyNode()) |
| |
| if constants_def: |
| # Legacy callback interface |
| class_def.public_section.extend([ |
| TextNode("// Constants"), |
| constants_def, |
| EmptyNode(), |
| backward_compatible_constants_def, |
| EmptyNode(), |
| ]) |
| |
| class_def.public_section.append(get_wrapper_type_info_def) |
| class_def.public_section.append(EmptyNode()) |
| class_def.private_section.append(wrapper_type_info_var_def) |
| class_def.private_section.append(EmptyNode()) |
| source_blink_ns.body.extend([ |
| wrapper_type_info_init, |
| EmptyNode(), |
| ]) |
| |
| class_def.public_section.append(installer_function_decls) |
| class_def.public_section.append(EmptyNode()) |
| if callback_defs: |
| source_blink_ns.body.extend([ |
| CxxNamespaceNode(name="", body=callback_defs), |
| EmptyNode(), |
| ]) |
| source_blink_ns.body.extend([ |
| installer_function_defs, |
| EmptyNode(), |
| ]) |
| |
| class_def.public_section.append(factory_decls) |
| class_def.public_section.append(EmptyNode()) |
| source_blink_ns.body.append(factory_defs) |
| source_blink_ns.body.append(EmptyNode()) |
| |
| class_def.public_section.append(ctor_decls) |
| class_def.public_section.append(EmptyNode()) |
| source_blink_ns.body.append(ctor_defs) |
| source_blink_ns.body.append(EmptyNode()) |
| |
| class_def.public_section.append(TextNode("// NameClient overrides:")) |
| class_def.public_section.append(nameclient_decls) |
| class_def.public_section.append(EmptyNode()) |
| source_blink_ns.body.append(nameclient_defs) |
| source_blink_ns.body.append(EmptyNode()) |
| |
| class_def.public_section.append(operation_decls) |
| class_def.public_section.append(EmptyNode()) |
| source_blink_ns.body.append(operation_defs) |
| source_blink_ns.body.append(EmptyNode()) |
| |
| class_def.public_section.append(invoke_and_report_decls) |
| class_def.public_section.append(EmptyNode()) |
| source_blink_ns.body.append(invoke_and_report_defs) |
| source_blink_ns.body.append(EmptyNode()) |
| |
| class_def.public_section.append(event_listener_decls) |
| class_def.public_section.append(EmptyNode()) |
| source_blink_ns.body.append(event_listener_defs) |
| source_blink_ns.body.append(EmptyNode()) |
| |
| # 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_callback_interfaces(task_queue): |
| assert isinstance(task_queue, TaskQueue) |
| |
| web_idl_database = package_initializer().web_idl_database() |
| |
| for callback_interface in web_idl_database.callback_interfaces: |
| task_queue.post_task(generate_callback_interface, |
| callback_interface.identifier) |