| # 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 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_blink_to_v8_value |
| from .blink_v8_bridge import make_default_value_expr |
| from .blink_v8_bridge import make_v8_to_blink_value |
| from .blink_v8_bridge import native_value_tag |
| from .code_node import Likeliness |
| from .code_node import ListNode |
| from .code_node import SequenceNode |
| from .code_node import SymbolNode |
| from .code_node import SymbolScopeNode |
| 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 CxxIfElseNode |
| from .code_node_cxx import CxxLikelyIfNode |
| from .code_node_cxx import CxxNamespaceNode |
| from .codegen_accumulator import CodeGenAccumulator |
| from .codegen_context import CodeGenContext |
| from .codegen_expr import expr_from_exposure |
| from .codegen_format import format_template as _format |
| 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 |
| |
| |
| _DICT_MEMBER_PRESENCE_PREDICATES = { |
| "ScriptValue": "!{}.IsEmpty()", |
| "ScriptPromise": "!{}.IsEmpty()", |
| } |
| |
| |
| def _blink_member_name(member): |
| assert isinstance(member, web_idl.DictionaryMember) |
| |
| class BlinkMemberName(object): |
| def __init__(self, member): |
| blink_name = (member.code_generator_info.property_implemented_as |
| or member.identifier) |
| self.get_api = name_style.api_func(blink_name) |
| self.get_or_api = name_style.api_func(blink_name, "or") |
| self.set_api = name_style.api_func("set", blink_name) |
| self.has_api = name_style.api_func("has", blink_name) |
| # C++ data member that shows the presence of the IDL member. |
| self.presence_var = name_style.member_var("has", blink_name) |
| # C++ data member that holds the value of the IDL member. |
| self.value_var = name_style.member_var_f("member_{}", blink_name) |
| # Migration Adapters |
| self.get_non_null_api = name_style.api_func(blink_name, "non_null") |
| self.has_non_null_api = name_style.api_func( |
| "has", blink_name, "non_null") |
| |
| return BlinkMemberName(member) |
| |
| |
| def _is_member_always_present(member): |
| assert isinstance(member, web_idl.DictionaryMember) |
| return member.is_required or member.default_value is not None |
| |
| |
| def _does_use_presence_flag(member): |
| assert isinstance(member, web_idl.DictionaryMember) |
| return (not _is_member_always_present(member) and blink_type_info( |
| member.idl_type).member_t not in _DICT_MEMBER_PRESENCE_PREDICATES) |
| |
| |
| def _member_presence_expr(member): |
| assert isinstance(member, web_idl.DictionaryMember) |
| if _is_member_always_present(member): |
| return "true" |
| if _does_use_presence_flag(member): |
| return _blink_member_name(member).presence_var |
| blink_type = blink_type_info(member.idl_type).member_t |
| assert blink_type in _DICT_MEMBER_PRESENCE_PREDICATES |
| _1 = _blink_member_name(member).value_var |
| return _format(_DICT_MEMBER_PRESENCE_PREDICATES[blink_type], _1) |
| |
| |
| def bind_member_iteration_local_vars(code_node): |
| local_vars = [ |
| SymbolNode( |
| "current_context", "v8::Local<v8::Context> ${current_context} = " |
| "${isolate}->GetCurrentContext();"), |
| SymbolNode( |
| "v8_member_names", "const auto* ${v8_member_names} = " |
| "GetV8MemberNames(${isolate}).data();"), |
| SymbolNode( |
| "is_in_secure_context", "const bool ${is_in_secure_context} = " |
| "${execution_context}->IsSecureContext();"), |
| ] |
| |
| # Execution context |
| node = SymbolNode( |
| "execution_context", "ExecutionContext* ${execution_context} = " |
| "ToExecutionContext(${current_context});") |
| node.accumulate( |
| CodeGenAccumulator.require_include_headers([ |
| "third_party/blink/renderer/core/execution_context/execution_context.h" |
| ])) |
| local_vars.append(node) |
| |
| code_node.register_code_symbols(local_vars) |
| |
| |
| def _is_default_ctor_available(dictionary): |
| for member in dictionary.members: |
| if member.default_value is None: |
| continue |
| default_expr = make_default_value_expr(member.idl_type, |
| member.default_value) |
| if default_expr.initializer_deps: |
| return False |
| return True |
| |
| |
| def make_create_dict_funcs(cg_context): |
| assert isinstance(cg_context, CodeGenContext) |
| |
| dictionary = cg_context.dictionary |
| name = "Create" |
| return_type = "${class_name}*" |
| |
| decls = ListNode() |
| defs = ListNode() |
| |
| if _is_default_ctor_available(dictionary): |
| func_def = CxxFuncDefNode(name=name, |
| arg_decls=[], |
| return_type=return_type, |
| static=True) |
| decls.append(func_def) |
| func_def.set_base_template_vars(cg_context.template_bindings()) |
| func_def.body.append( |
| TextNode("return MakeGarbageCollected<${class_name}>();")) |
| |
| func_def = CxxFuncDefNode(name=name, |
| arg_decls=["v8::Isolate* isolate"], |
| return_type=return_type, |
| static=True) |
| decls.append(func_def) |
| func_def.set_base_template_vars(cg_context.template_bindings()) |
| func_def.body.append( |
| TextNode("return MakeGarbageCollected<${class_name}>(isolate);")) |
| |
| arg_decls = [ |
| "v8::Isolate* isolate", |
| "v8::Local<v8::Value> v8_value", |
| "ExceptionState& exception_state", |
| ] |
| func_decl = CxxFuncDeclNode(name=name, |
| arg_decls=arg_decls, |
| return_type=return_type, |
| static=True) |
| decls.append(func_decl) |
| func_def = CxxFuncDefNode(name=name, |
| class_name=cg_context.class_name, |
| arg_decls=arg_decls, |
| return_type=return_type) |
| defs.append(func_def) |
| func_def.set_base_template_vars(cg_context.template_bindings()) |
| |
| func_def.body.append( |
| TextNode("""\ |
| DCHECK(!v8_value.IsEmpty()); |
| |
| ${class_name}* dictionary = Create(isolate); |
| dictionary->FillMembers(isolate, v8_value, exception_state); |
| if (exception_state.HadException()) { |
| return nullptr; |
| } |
| return dictionary;""")) |
| |
| return decls, defs |
| |
| |
| def make_dict_constructors(cg_context): |
| decls = ListNode() |
| defs = ListNode() |
| |
| dictionary = cg_context.dictionary |
| class_name = blink_class_name(dictionary) |
| |
| member_initializer_list = [] |
| should_construct_in_source = False |
| for member in dictionary.own_members: |
| if member.default_value is None: |
| continue |
| # In order to avoid cyclic header inclusion of IDL dictionaries, do not |
| # put dictionary member's initialization in the class definition. |
| does_initialize_with_dict = member.default_value.idl_type.is_object |
| if does_initialize_with_dict: |
| should_construct_in_source = True |
| |
| default_expr = make_default_value_expr(member.idl_type, |
| member.default_value) |
| if (default_expr.initializer_deps == ["isolate"] |
| or does_initialize_with_dict): |
| _1 = _blink_member_name(member).value_var |
| _2 = default_expr.initializer_expr |
| member_initializer_list.append(_format("{_1}({_2})", _1=_1, _2=_2)) |
| |
| if _is_default_ctor_available(dictionary): |
| name = class_name |
| arg_decls = [] |
| return_type = "" |
| # In order to avoid cyclic header inclusion of IDL dictionaries, do not |
| # put dictionary member's initialization in the class definition. |
| if should_construct_in_source: |
| ctor_decl = CxxFuncDeclNode(name=name, |
| arg_decls=arg_decls, |
| return_type=return_type) |
| decls.append(ctor_decl) |
| ctor_def = CxxFuncDefNode( |
| name=name, |
| class_name=class_name, |
| arg_decls=arg_decls, |
| return_type=return_type, |
| member_initializer_list=member_initializer_list) |
| defs.append(ctor_def) |
| else: |
| ctor_decl = CxxFuncDeclNode(name=name, |
| arg_decls=arg_decls, |
| return_type=return_type, |
| default=True) |
| decls.append(ctor_decl) |
| |
| ctor_decl = CxxFuncDeclNode(name=class_name, |
| arg_decls=["v8::Isolate* isolate"], |
| return_type="", |
| explicit=True) |
| decls.append(ctor_decl) |
| ctor_decl.set_base_template_vars(cg_context.template_bindings()) |
| |
| member_initializer_list.insert(0, "BaseClass(${isolate})") |
| ctor_def = CxxFuncDefNode(name=class_name, |
| class_name=class_name, |
| arg_decls=["v8::Isolate* isolate"], |
| return_type="", |
| member_initializer_list=member_initializer_list) |
| defs.append(ctor_def) |
| ctor_def.set_base_template_vars(cg_context.template_bindings()) |
| ctor_def.add_template_var("isolate", "isolate") |
| |
| return decls, defs |
| |
| |
| def make_dict_member_get(cg_context): |
| assert isinstance(cg_context, CodeGenContext) |
| |
| member = cg_context.dict_member |
| blink_member_name = _blink_member_name(member) |
| name = blink_member_name.get_api |
| idl_type = member.idl_type.unwrap(typedef=True) |
| blink_type = blink_type_info(idl_type) |
| const_ref_t = blink_type.const_ref_t |
| ref_t = blink_type.ref_t |
| |
| # Since Blink conventionally prefers non-const references to const |
| # references for the certain types, makes const member's getters return a |
| # non-const reference. For example, "Node* foo() const;" is preferable to |
| # "const Node* foo() const;". |
| if (idl_type.unwrap().is_interface |
| or idl_type.unwrap().is_callback_interface |
| or idl_type.unwrap().is_callback_function |
| or idl_type.unwrap().is_buffer_source_type): |
| const_ref_t = ref_t |
| |
| decls = ListNode() |
| defs = ListNode() |
| |
| func_def = CxxFuncDefNode(name=name, |
| arg_decls=[], |
| return_type=const_ref_t, |
| const=True) |
| decls.append(func_def) |
| func_def.set_base_template_vars(cg_context.template_bindings()) |
| func_def.body.extend([ |
| TextNode(_format("DCHECK({}());", blink_member_name.has_api)), |
| TextNode(_format("return {};", blink_member_name.value_var)), |
| ]) |
| |
| if ref_t != const_ref_t: |
| func_def = CxxFuncDefNode(name=name, arg_decls=[], return_type=ref_t) |
| decls.append(func_def) |
| func_def.set_base_template_vars(cg_context.template_bindings()) |
| func_def.body.extend([ |
| TextNode(_format("DCHECK({}());", blink_member_name.has_api)), |
| TextNode(_format("return {};", blink_member_name.value_var)), |
| ]) |
| |
| if idl_type.is_numeric or idl_type.unwrap().is_string: |
| arg_type = blink_type.const_ref_t |
| return_type = blink_type.value_t |
| elif idl_type.unwrap().is_enumeration: |
| arg_type = ("base::nullopt_t" |
| if idl_type.is_nullable else blink_type.value_t) |
| return_type = blink_type.value_t |
| elif idl_type.unwrap().is_dictionary: |
| arg_type = "nullptr_t" |
| return_type = blink_type.const_ref_t |
| elif idl_type.unwrap().type_definition_object: |
| arg_type = "nullptr_t" |
| return_type = blink_type.ref_t |
| elif idl_type.is_nullable and (idl_type.unwrap().is_sequence |
| or idl_type.unwrap().is_frozen_array |
| or idl_type.unwrap().is_record): |
| arg_type = "base::nullopt_t" |
| return_type = blink_type.value_t |
| else: |
| arg_type = None |
| return_type = None |
| |
| if arg_type is not None or return_type is not None: |
| assert arg_type is not None and return_type is not None |
| |
| name = blink_member_name.get_or_api |
| arg_decls = [_format("{} fallback_value", arg_type)] |
| |
| def should_be_defined_in_source(member): |
| # sequence<Dictionary> and record<String, Dictionary> are |
| # implemented with HeapVector and Dictionary's definition is |
| # required while promise<Dictionary> doesn't require it. |
| def element_or_value_of(idl_type): |
| return (idl_type.unwrap().element_type |
| or idl_type.unwrap().value_type) |
| |
| idl_type = element_or_value_of(member.idl_type) |
| while idl_type and element_or_value_of(idl_type): |
| idl_type = element_or_value_of(idl_type) |
| return idl_type and idl_type.unwrap().is_dictionary |
| |
| # In order to avoid cyclic header inclusion of IDL dictionaries, |
| # whenever the function needs a dictionary's definition, do not put the |
| # function definition in the header file. |
| if should_be_defined_in_source(member): |
| func_decl = CxxFuncDeclNode(name=name, |
| arg_decls=arg_decls, |
| return_type=return_type, |
| const=True) |
| decls.append(func_decl) |
| func_def = CxxFuncDefNode(name=name, |
| class_name=cg_context.class_name, |
| arg_decls=arg_decls, |
| return_type=return_type, |
| const=True) |
| defs.append(func_def) |
| func_def.set_base_template_vars(cg_context.template_bindings()) |
| else: |
| func_def = CxxFuncDefNode(name=name, |
| arg_decls=arg_decls, |
| return_type=return_type, |
| const=True) |
| decls.append(func_def) |
| func_def.set_base_template_vars(cg_context.template_bindings()) |
| |
| body_node = TextNode( |
| _format("""\ |
| if ({has}()) {{ |
| return {get}(); |
| }} |
| return fallback_value;""", |
| has=blink_member_name.has_api, |
| get=blink_member_name.get_api)) |
| func_def.body.append(body_node) |
| |
| return decls, defs |
| |
| |
| def make_dict_member_has(cg_context): |
| assert isinstance(cg_context, CodeGenContext) |
| |
| member = cg_context.dict_member |
| |
| decls = ListNode() |
| defs = ListNode() |
| |
| func_def = CxxFuncDefNode( |
| name=_blink_member_name(member).has_api, |
| arg_decls=[], |
| return_type="bool", |
| const=True) |
| decls.append(func_def) |
| func_def.set_base_template_vars(cg_context.template_bindings()) |
| body = func_def.body |
| |
| _1 = _member_presence_expr(member) |
| body.append(TextNode(_format("return {_1};", _1=_1))) |
| |
| return decls, defs |
| |
| |
| def make_dict_member_set(cg_context): |
| assert isinstance(cg_context, CodeGenContext) |
| |
| T = TextNode |
| |
| member = cg_context.dict_member |
| blink_member_name = _blink_member_name(member) |
| real_type = member.idl_type.unwrap(typedef=True) |
| type_info = blink_type_info(real_type) |
| |
| decls = ListNode() |
| defs = ListNode() |
| |
| template_func_def = CxxFuncDefNode( |
| name=blink_member_name.set_api, |
| arg_decls=["T&& value"], |
| return_type="void", |
| template_params=["typename T"]) |
| decls.append(template_func_def) |
| |
| # This setter with the explicit type declaration makes it possible to set |
| # the dictionary member with uniform initialization (especially aggregate |
| # initialization), e.g. setIntVector({3, 1, 4, 1, 5}). |
| move_func_decl = CxxFuncDeclNode( |
| name=blink_member_name.set_api, |
| arg_decls=[_format("{}&&", type_info.member_t)], |
| return_type="void") |
| decls.append(move_func_decl) |
| |
| move_func_def = CxxFuncDefNode( |
| name=blink_member_name.set_api, |
| arg_decls=[_format("{}&& value", type_info.member_t)], |
| return_type="void", |
| class_name=cg_context.class_name) |
| defs.append(move_func_def) |
| |
| _1 = blink_member_name.value_var |
| template_func_def.body.append( |
| T(_format("{_1} = std::forward<T>(value);", _1=_1))) |
| move_func_def.body.append(T(_format("{_1} = value;", _1=_1))) |
| |
| if _does_use_presence_flag(member): |
| set_presense_expr = _format("{} = true;", |
| blink_member_name.presence_var) |
| template_func_def.body.append(T(set_presense_expr)) |
| move_func_def.body.append(T(set_presense_expr)) |
| |
| # Migration Adapter |
| if (real_type.is_nullable and |
| blink_type_info(real_type).typename.startswith("base::Optional")): |
| to_null_func_def = CxxFuncDefNode( |
| name=_format("{}ToNull", blink_member_name.set_api), |
| arg_decls=[], |
| return_type="void") |
| decls.append(to_null_func_def) |
| to_null_func_def.set_base_template_vars(cg_context.template_bindings()) |
| to_null_func_def.body.append( |
| T(_format("{}(base::nullopt);", blink_member_name.set_api))) |
| |
| return decls, defs |
| |
| |
| def make_dict_member_vars(cg_context): |
| assert isinstance(cg_context, CodeGenContext) |
| |
| member = cg_context.dict_member |
| |
| default_value_initializer = "" |
| if member.default_value: |
| default_expr = make_default_value_expr(member.idl_type, |
| member.default_value) |
| # In order to avoid cyclic header inclusion of IDL dictionaries, do not |
| # put dictionary member's initialization in the class definition. |
| if (default_expr.initializer_expr is not None |
| and not default_expr.initializer_deps |
| and not member.default_value.idl_type.is_object): |
| default_value_initializer = _format("{{{}}}", |
| default_expr.initializer_expr) |
| |
| _1 = blink_type_info(member.idl_type).member_t |
| _2 = _blink_member_name(member).value_var |
| _3 = default_value_initializer |
| value_var_def = TextNode(_format("{_1} {_2}{_3};", _1=_1, _2=_2, _3=_3)) |
| |
| if _does_use_presence_flag(member): |
| _1 = _blink_member_name(member).presence_var |
| presense_var_def = TextNode(_format("bool {_1} = false;", _1=_1)) |
| else: |
| presense_var_def = None |
| |
| return value_var_def, presense_var_def |
| |
| |
| def make_dict_member_migration_adapters(cg_context): |
| assert isinstance(cg_context, CodeGenContext) |
| |
| member = cg_context.dict_member |
| blink_member_name = _blink_member_name(member) |
| idl_type = member.idl_type |
| blink_type = blink_type_info(idl_type) |
| real_type = idl_type.unwrap(typedef=True) |
| |
| if (not real_type.is_nullable |
| or blink_type_info(real_type.inner_type).has_null_value): |
| return None, None |
| |
| decls = ListNode([TextNode("// Migration Adapters")]) |
| defs = ListNode() |
| |
| # Accessors for non-null values, if the usual getter returns |
| # base::Optional<T>. |
| blink_inner_type = blink_type_info(real_type.inner_type) |
| get_api = blink_member_name.get_api |
| has_api = blink_member_name.has_api |
| get_non_null_api = blink_member_name.get_non_null_api |
| has_non_null_api = blink_member_name.has_non_null_api |
| |
| func_def = CxxFuncDefNode(name=get_non_null_api, |
| arg_decls=[], |
| return_type=blink_inner_type.const_ref_t, |
| const=True) |
| decls.extend([ |
| TextNode( |
| _format( |
| """\ |
| // Returns the value if this member has a non-null value. Call |
| // |{}| in advance to check the condition.""", has_non_null_api)), |
| func_def, |
| ]) |
| func_def.set_base_template_vars(cg_context.template_bindings()) |
| func_def.body.extend([ |
| TextNode(_format("DCHECK({}());", has_non_null_api)), |
| TextNode(_format("return {}().value();", get_api)), |
| ]) |
| |
| if blink_inner_type.ref_t != blink_inner_type.const_ref_t: |
| func_def = CxxFuncDefNode(name=get_non_null_api, |
| arg_decls=[], |
| return_type=blink_inner_type.ref_t) |
| decls.append(func_def) |
| func_def.set_base_template_vars(cg_context.template_bindings()) |
| func_def.body.extend([ |
| TextNode(_format("DCHECK({}());", has_non_null_api)), |
| TextNode(_format("return {}().value();", get_api)), |
| ]) |
| |
| func_def = CxxFuncDefNode(name=has_non_null_api, |
| arg_decls=[], |
| return_type="bool", |
| const=True) |
| decls.extend([ |
| TextNode("""\ |
| // Returns true iff this member has a non-null value. Returns false if the |
| // value is missing or the null value."""), |
| func_def, |
| ]) |
| func_def.set_base_template_vars(cg_context.template_bindings()) |
| func_def.body.append( |
| TextNode(_format("return {}() && {}().has_value();", has_api, |
| get_api))) |
| |
| return decls, defs |
| |
| |
| def make_get_v8_dict_member_names_func(cg_context): |
| assert isinstance(cg_context, CodeGenContext) |
| |
| dictionary = cg_context.dictionary |
| name = "GetV8MemberNames" |
| arg_decls = ["v8::Isolate* isolate"] |
| return_type = "const base::span<const v8::Eternal<v8::Name>>" |
| |
| func_decl = CxxFuncDeclNode( |
| name=name, arg_decls=arg_decls, return_type=return_type, static=True) |
| func_def = CxxFuncDefNode( |
| name=name, |
| class_name=cg_context.class_name, |
| arg_decls=arg_decls, |
| return_type=return_type) |
| func_def.set_base_template_vars(cg_context.template_bindings()) |
| body = func_def.body |
| |
| if dictionary.own_members: |
| pattern = "static const char* const kKeyStrings[] = {{{_1}}};" |
| _1 = ", ".join( |
| _format("\"{}\"", member.identifier) |
| for member in dictionary.own_members) |
| body.extend([ |
| TextNode(_format(pattern, _1=_1)), |
| TextNode("return V8PerIsolateData::From(isolate)" |
| "->FindOrCreateEternalNameCache(kKeyStrings, " |
| "kKeyStrings);"), |
| ]) |
| else: |
| body.append(TextNode("return {};")) |
| |
| return func_decl, func_def |
| |
| |
| def make_fill_with_dict_members_func(cg_context): |
| assert isinstance(cg_context, CodeGenContext) |
| |
| dictionary = cg_context.dictionary |
| name = "FillWithMembers" |
| arg_decls = [ |
| "v8::Isolate* isolate", |
| "v8::Local<v8::Object> creation_context", |
| "v8::Local<v8::Object> v8_dictionary", |
| ] |
| return_type = "bool" |
| |
| func_decl = CxxFuncDeclNode( |
| name=name, |
| arg_decls=arg_decls, |
| return_type=return_type, |
| const=True, |
| override=True) |
| func_def = CxxFuncDefNode( |
| name=name, |
| class_name=cg_context.class_name, |
| arg_decls=arg_decls, |
| return_type=return_type, |
| const=True) |
| func_def.set_base_template_vars(cg_context.template_bindings()) |
| body = func_def.body |
| |
| if dictionary.inherited: |
| text = """\ |
| if (!BaseClass::FillWithMembers(isolate, creation_context, v8_dictionary)) { |
| return false; |
| }""" |
| body.append(TextNode(text)) |
| |
| body.append( |
| TextNode("return FillWithOwnMembers(" |
| "isolate, creation_context, v8_dictionary);")) |
| |
| return func_decl, func_def |
| |
| |
| def make_fill_with_own_dict_members_func(cg_context): |
| assert isinstance(cg_context, CodeGenContext) |
| |
| dictionary = cg_context.dictionary |
| own_members = dictionary.own_members |
| name = "FillWithOwnMembers" |
| arg_decls = [ |
| "v8::Isolate* isolate", |
| "v8::Local<v8::Object> creation_context", |
| "v8::Local<v8::Object> v8_dictionary", |
| ] |
| return_type = "bool" |
| |
| func_decl = CxxFuncDeclNode( |
| name=name, arg_decls=arg_decls, return_type=return_type, const=True) |
| func_def = CxxFuncDefNode( |
| name=name, |
| class_name=cg_context.class_name, |
| arg_decls=arg_decls, |
| return_type=return_type, |
| const=True) |
| func_def.set_base_template_vars(cg_context.template_bindings()) |
| body = func_def.body |
| body.add_template_var("isolate", "isolate") |
| body.add_template_var("creation_context", "creation_context") |
| body.add_template_var("v8_dictionary", "v8_dictionary") |
| bind_member_iteration_local_vars(body) |
| |
| for index, member in enumerate(own_members): |
| member_name = _blink_member_name(member) |
| v8_member_name = name_style.local_var_f("v8_member_{}", |
| member.identifier) |
| body.register_code_symbol( |
| make_blink_to_v8_value(v8_member_name, |
| "{}()".format(member_name.get_api), |
| member.idl_type, "${creation_context}")) |
| node = CxxLikelyIfNode( |
| cond="{}()".format(member_name.has_api), |
| body=TextNode( |
| _format( |
| "${v8_dictionary}->CreateDataProperty(" |
| "${current_context}, " |
| "${v8_member_names}[{index}].Get(${isolate}), " |
| "${{{v8_member_name}}}" |
| ").ToChecked();", |
| index=index, |
| v8_member_name=v8_member_name))) |
| conditional = expr_from_exposure(member.exposure) |
| if not conditional.is_always_true: |
| node = CxxLikelyIfNode(cond=conditional, body=node) |
| body.append(node) |
| |
| body.append(TextNode("return true;")) |
| |
| return func_decl, func_def |
| |
| |
| def make_fill_dict_members_func(cg_context): |
| assert isinstance(cg_context, CodeGenContext) |
| |
| T = TextNode |
| |
| dictionary = cg_context.dictionary |
| own_members = dictionary.own_members |
| required_own_members = list( |
| member for member in own_members if member.is_required) |
| name = "FillMembers" |
| arg_decls = [ |
| "v8::Isolate* isolate", |
| "v8::Local<v8::Value> v8_value", |
| "ExceptionState& exception_state", |
| ] |
| return_type = "void" |
| |
| func_decl = CxxFuncDeclNode( |
| name=name, arg_decls=arg_decls, return_type=return_type) |
| func_def = CxxFuncDefNode( |
| name=name, |
| class_name=cg_context.class_name, |
| arg_decls=arg_decls, |
| return_type=return_type) |
| func_def.set_base_template_vars(cg_context.template_bindings()) |
| |
| if required_own_members: |
| check_required_members_node = T("""\ |
| if (v8_value->IsNullOrUndefined()) { |
| exception_state.ThrowTypeError(ExceptionMessages::FailedToConstruct( |
| "${dictionary.identifier}", |
| "has required members, but null/undefined was passed.")); |
| return; |
| }""") |
| else: |
| check_required_members_node = T("""\ |
| if (v8_value->IsNullOrUndefined()) { |
| return; |
| }""") |
| |
| # [PermissiveDictionaryConversion] |
| if "PermissiveDictionaryConversion" in dictionary.extended_attributes: |
| permissive_conversion_node = T("""\ |
| if (!v8_value->IsObject()) { |
| // [PermissiveDictionaryConversion] |
| return; |
| }""") |
| else: |
| permissive_conversion_node = T("""\ |
| if (!v8_value->IsObject()) { |
| exception_state.ThrowTypeError( |
| ExceptionMessages::FailedToConstruct( |
| "${dictionary.identifier}", "The value is not of type Object")); |
| return; |
| }""") |
| |
| call_internal_func_node = T("""\ |
| FillMembersInternal(isolate, v8_value.As<v8::Object>(), exception_state);""") |
| |
| func_def.body.extend([ |
| check_required_members_node, |
| permissive_conversion_node, |
| call_internal_func_node, |
| ]) |
| |
| return func_decl, func_def |
| |
| |
| def make_fill_dict_members_internal_func(cg_context): |
| assert isinstance(cg_context, CodeGenContext) |
| |
| T = TextNode |
| |
| dictionary = cg_context.dictionary |
| own_members = dictionary.own_members |
| name = "FillMembersInternal" |
| arg_decls = [ |
| "v8::Isolate* isolate", |
| "v8::Local<v8::Object> v8_dictionary", |
| "ExceptionState& exception_state", |
| ] |
| return_type = "void" |
| func_decl = CxxFuncDeclNode( |
| name=name, arg_decls=arg_decls, return_type=return_type) |
| func_def = CxxFuncDefNode( |
| name=name, |
| class_name=cg_context.class_name, |
| arg_decls=arg_decls, |
| return_type=return_type) |
| func_def.set_base_template_vars(cg_context.template_bindings()) |
| body = func_def.body |
| body.add_template_var("isolate", "isolate") |
| body.add_template_var("exception_state", "exception_state") |
| bind_member_iteration_local_vars(body) |
| body.register_code_symbols([ |
| SymbolNode("try_block", "v8::TryCatch ${try_block}(${isolate});"), |
| SymbolNode("v8_value", "v8::Local<v8::Value> ${v8_value};"), |
| SymbolNode("unused_presence_var", "bool ${unused_presence_var};"), |
| ]) |
| |
| if dictionary.inherited: |
| text = """\ |
| BaseClass::FillMembersInternal(${isolate}, v8_dictionary, ${exception_state}); |
| if (${exception_state}.HadException()) { |
| return; |
| } |
| """ |
| body.append(T(text)) |
| |
| for key_index, member in enumerate(own_members): |
| body.append(make_fill_own_dict_member(key_index, member)) |
| |
| return func_decl, func_def |
| |
| |
| def make_fill_own_dict_member(key_index, member): |
| assert isinstance(key_index, int) |
| assert isinstance(member, web_idl.DictionaryMember) |
| |
| pattern = """\ |
| if (!bindings::ConvertDictionaryMember<{nvt_tag}, {is_required}>( |
| ${isolate}, |
| ${current_context}, |
| v8_dictionary, |
| ${v8_member_names}[{key_index}].Get(${isolate}), |
| "${{dictionary.identifier}}", |
| "{member_name}", |
| {value_var}, |
| {presence_var}, |
| ${try_block}, |
| ${exception_state})) {{ |
| return; |
| }}""" |
| if _does_use_presence_flag(member): |
| presence_var = _blink_member_name(member).presence_var |
| else: |
| presence_var = "${unused_presence_var}" |
| node = TextNode( |
| _format(pattern, |
| nvt_tag=native_value_tag(member.idl_type), |
| is_required="true" if member.is_required else "false", |
| key_index=key_index, |
| member_name=member.identifier, |
| value_var=_blink_member_name(member).value_var, |
| presence_var=presence_var)) |
| |
| conditional = expr_from_exposure(member.exposure) |
| if not conditional.is_always_true: |
| node = CxxLikelyIfNode(cond=conditional, body=node) |
| |
| return node |
| |
| |
| def make_dict_trace_func(cg_context): |
| assert isinstance(cg_context, CodeGenContext) |
| |
| T = TextNode |
| |
| dictionary = cg_context.dictionary |
| own_members = dictionary.own_members |
| name = "Trace" |
| arg_decls = ["Visitor* visitor"] |
| return_type = "void" |
| |
| func_decl = CxxFuncDeclNode(name=name, |
| arg_decls=arg_decls, |
| return_type=return_type, |
| const=True, |
| override=True) |
| func_def = CxxFuncDefNode(name=name, |
| class_name=cg_context.class_name, |
| arg_decls=arg_decls, |
| return_type=return_type, |
| const=True) |
| func_def.set_base_template_vars(cg_context.template_bindings()) |
| body = func_def.body |
| |
| def make_trace_member_node(member): |
| pattern = "TraceIfNeeded<{_1}>::Trace(visitor, {_2});" |
| _1 = blink_type_info(member.idl_type).member_t |
| _2 = _blink_member_name(member).value_var |
| return TextNode(_format(pattern, _1=_1, _2=_2)) |
| |
| body.extend(list(map(make_trace_member_node, own_members))) |
| body.append(TextNode("BaseClass::Trace(visitor);")) |
| |
| return func_decl, func_def |
| |
| |
| def generate_dictionary(dictionary_identifier): |
| assert isinstance(dictionary_identifier, web_idl.Identifier) |
| |
| web_idl_database = package_initializer().web_idl_database() |
| dictionary = web_idl_database.find(dictionary_identifier) |
| |
| path_manager = PathManager(dictionary) |
| assert path_manager.api_component == path_manager.impl_component, ( |
| "We don't support partial dictionaries across components yet.") |
| api_component = path_manager.api_component |
| for_testing = dictionary.code_generator_info.for_testing |
| |
| path_manager = PathManager(dictionary) |
| class_name = name_style.class_(blink_class_name(dictionary)) |
| if dictionary.inherited: |
| base_class_name = blink_class_name(dictionary.inherited) |
| else: |
| base_class_name = "bindings::DictionaryBase" |
| |
| cg_context = CodeGenContext( |
| dictionary=dictionary, |
| class_name=class_name, |
| base_class_name=base_class_name) |
| |
| # 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 definitions |
| class_def = CxxClassDefNode(cg_context.class_name, |
| base_class_names=[cg_context.base_class_name], |
| export=component_export( |
| api_component, for_testing)) |
| class_def.set_base_template_vars(cg_context.template_bindings()) |
| class_def.top_section.append( |
| TextNode("using BaseClass = ${base_class_name};")) |
| |
| # Create functions |
| create_decl, create_def = make_create_dict_funcs(cg_context) |
| |
| # Constructor and destructor |
| constructor_decls, constructor_defs = make_dict_constructors(cg_context) |
| destructor_decl = CxxFuncDeclNode( |
| name="~${class_name}", arg_decls=[], return_type="", default=True) |
| |
| # Fill with members (Blink -> V8 conversion) |
| (fill_with_members_decl, |
| fill_with_members_def) = make_fill_with_dict_members_func(cg_context) |
| (fill_with_own_members_decl, fill_with_own_members_def |
| ) = make_fill_with_own_dict_members_func(cg_context) |
| |
| # Fill members (V8 -> Blink conversion) |
| (fill_members_decl, |
| fill_members_def) = make_fill_dict_members_func(cg_context) |
| (fill_members_internal_decl, fill_members_internal_def |
| ) = make_fill_dict_members_internal_func(cg_context) |
| |
| # Misc. functions |
| (get_v8_member_names_decl, |
| get_v8_member_names_def) = make_get_v8_dict_member_names_func(cg_context) |
| trace_decl, trace_def = make_dict_trace_func(cg_context) |
| |
| member_accessor_decls = ListNode() |
| member_accessor_defs = ListNode() |
| member_value_var_defs = ListNode() |
| member_presense_var_defs = ListNode() |
| for member in cg_context.dictionary.own_members: |
| member_context = cg_context.make_copy(dict_member=member) |
| get_decls, get_defs = make_dict_member_get(member_context) |
| has_decls, has_defs = make_dict_member_has(member_context) |
| set_decls, set_defs = make_dict_member_set(member_context) |
| value_var_def, presense_var_def = make_dict_member_vars(member_context) |
| (migration_adapter_decls, migration_adapter_defs |
| ) = make_dict_member_migration_adapters(member_context) |
| member_accessor_decls.extend([ |
| TextNode(""), |
| get_decls, |
| has_decls, |
| set_decls, |
| migration_adapter_decls, |
| ]) |
| member_accessor_defs.extend([ |
| TextNode(""), |
| get_defs, |
| has_defs, |
| set_defs, |
| migration_adapter_defs, |
| ]) |
| member_value_var_defs.append(value_var_def) |
| member_presense_var_defs.append(presense_var_def) |
| |
| # Header part (copyright, include directives, and forward declarations) |
| header_node.extend([ |
| make_copyright_header(), |
| TextNode(""), |
| enclose_with_header_guard( |
| ListNode([ |
| make_header_include_directives(header_node.accumulator), |
| TextNode(""), |
| header_blink_ns, |
| ]), name_style.header_guard(header_path)), |
| ]) |
| header_blink_ns.body.extend([ |
| make_forward_declarations(header_node.accumulator), |
| TextNode(""), |
| ]) |
| source_node.extend([ |
| make_copyright_header(), |
| TextNode(""), |
| TextNode("#include \"{}\"".format(header_path)), |
| TextNode(""), |
| make_header_include_directives(source_node.accumulator), |
| TextNode(""), |
| source_blink_ns, |
| ]) |
| source_blink_ns.body.extend([ |
| make_forward_declarations(source_node.accumulator), |
| TextNode(""), |
| ]) |
| |
| # Assemble the parts. |
| header_node.accumulator.add_class_decls(["ExceptionState"]) |
| header_node.accumulator.add_include_headers([ |
| (PathManager(dictionary.inherited).api_path(ext="h") |
| if dictionary.inherited else |
| "third_party/blink/renderer/platform/bindings/dictionary_base.h"), |
| component_export_header(api_component, for_testing), |
| "v8/include/v8.h", |
| ]) |
| 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/platform/bindings/exception_messages.h", |
| "third_party/blink/renderer/platform/bindings/exception_state.h", |
| "third_party/blink/renderer/platform/bindings/v8_per_isolate_data.h", |
| ]) |
| (header_forward_decls, header_include_headers, source_forward_decls, |
| source_include_headers) = collect_forward_decls_and_include_headers( |
| map(lambda member: member.idl_type, dictionary.own_members)) |
| 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) |
| class_def.public_section.extend([ |
| create_decl, |
| constructor_decls, |
| destructor_decl, |
| TextNode(""), |
| trace_decl, |
| TextNode(""), |
| member_accessor_decls, |
| ]) |
| class_def.protected_section.extend([ |
| fill_with_members_decl, |
| TextNode(""), |
| fill_members_internal_decl, |
| ]) |
| class_def.private_section.extend([ |
| get_v8_member_names_decl, |
| TextNode(""), |
| fill_with_own_members_decl, |
| TextNode(""), |
| fill_members_decl, |
| TextNode(""), |
| member_value_var_defs, |
| TextNode(""), |
| member_presense_var_defs, |
| ]) |
| source_blink_ns.body.extend([ |
| constructor_defs, |
| TextNode(""), |
| get_v8_member_names_def, |
| TextNode(""), |
| create_def, |
| TextNode(""), |
| fill_with_members_def, |
| TextNode(""), |
| fill_with_own_members_def, |
| TextNode(""), |
| fill_members_def, |
| TextNode(""), |
| fill_members_internal_def, |
| TextNode(""), |
| member_accessor_defs, |
| TextNode(""), |
| trace_def, |
| ]) |
| |
| # 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_dictionaries(task_queue): |
| assert isinstance(task_queue, TaskQueue) |
| |
| web_idl_database = package_initializer().web_idl_database() |
| |
| for dictionary in web_idl_database.dictionaries: |
| task_queue.post_task(generate_dictionary, dictionary.identifier) |