# Copyright 2020 The Chromium Authors
# 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 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 IncludeDefinition
from .codegen_accumulator import CodeGenAccumulator
from .codegen_context import CodeGenContext
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 make_factory_methods(cg_context):
assert isinstance(cg_context, CodeGenContext)
T = TextNode
decls = ListNode()
defs = ListNode()
func_decl = CxxFuncDeclNode(name="Create",
arg_decls=[
"v8::Isolate* isolate",
"v8::Local<v8::Value> value",
"ExceptionState& exception_state",
],
return_type="${class_name}",
static=True)
func_def = CxxFuncDefNode(name="Create",
arg_decls=[
"v8::Isolate* isolate",
"v8::Local<v8::Value> value",
"ExceptionState& exception_state",
],
return_type="${class_name}",
class_name=cg_context.class_name)
func_def.set_base_template_vars(cg_context.template_bindings())
decls.append(func_decl)
defs.append(func_def)
func_def.body.extend([
T("const auto& result = bindings::FindIndexInEnumStringTable("
"isolate, value, string_table_, \"${enumeration.identifier}\", "
"exception_state);"),
T("return result.has_value() ? "
"${class_name}(static_cast<Enum>(result.value())) : "
"${class_name}();"),
])
func_decl = CxxFuncDeclNode(name="Create",
arg_decls=["const String& value"],
return_type="std::optional<${class_name}>",
static=True)
func_def = CxxFuncDefNode(name="Create",
arg_decls=["const String& value"],
return_type="std::optional<${class_name}>",
class_name=cg_context.class_name)
func_def.set_base_template_vars(cg_context.template_bindings())
decls.append(func_decl)
defs.append(EmptyNode())
defs.append(func_def)
func_def.body.extend([
T("const auto& result = bindings::FindIndexInEnumStringTable"
"(value, string_table_);"),
T("if (!result)\n"
" return std::nullopt;"),
T("return ${class_name}(static_cast<Enum>(result.value()));"),
])
return decls, defs
def make_default_constructor(cg_context):
assert isinstance(cg_context, CodeGenContext)
func_decl = CxxFuncDeclNode(name=cg_context.class_name,
arg_decls=[],
return_type="",
constexpr=True,
default=True)
return func_decl, None
def make_constructors(cg_context):
assert isinstance(cg_context, CodeGenContext)
T = TextNode
class_name = cg_context.class_name
decls = ListNode([
CxxFuncDefNode(
name=class_name,
arg_decls=["Enum value"],
return_type="",
explicit=True,
constexpr=True,
member_initializer_list=[
"${base_class_name}("
"static_cast<enum_int_t>(value), "
"string_table_[static_cast<enum_int_t>(value)])"
]),
CxxFuncDeclNode(
name=class_name,
arg_decls=["const ${class_name}&"],
return_type="",
constexpr=True,
default=True),
CxxFuncDeclNode(
name=class_name,
arg_decls=["${class_name}&&"],
return_type="",
constexpr=True,
default=True),
CxxFuncDeclNode(
name="~${class_name}", arg_decls=[], return_type="", default=True),
])
defs = ListNode([
T("static_assert("
"std::is_trivially_copyable<${class_name}>::value, \"\");"),
])
defs.set_base_template_vars(cg_context.template_bindings())
return decls, defs
def make_assignment_operators(cg_context):
assert isinstance(cg_context, CodeGenContext)
decls = ListNode([
CxxFuncDeclNode(
name="operator=",
arg_decls=["const ${class_name}&"],
return_type="${class_name}&",
default=True),
CxxFuncDeclNode(
name="operator=",
arg_decls=["${class_name}&&"],
return_type="${class_name}&",
default=True),
])
defs = ListNode()
# Migration adapter
func_decl = CxxFuncDeclNode(
name="operator=",
arg_decls=["const String&"],
return_type="${class_name}&")
func_def = CxxFuncDefNode(
name="operator=",
arg_decls=["const String& str_value"],
return_type="${class_name}&",
class_name=cg_context.class_name)
decls.append(func_decl)
defs.append(func_def)
func_def.set_base_template_vars(cg_context.template_bindings())
func_def.body.append(
TextNode("""\
const auto& index =
bindings::FindIndexInEnumStringTable(str_value, string_table_);
CHECK(index.has_value());
return operator=(${class_name}(static_cast<Enum>(index.value())));
"""))
return decls, defs
def make_equality_operators(cg_context):
assert isinstance(cg_context, CodeGenContext)
func1_def = CxxFuncDefNode(
name="operator==",
arg_decls=["const ${class_name}& lhs", "${class_name}::Enum rhs"],
return_type="bool",
inline=True)
func1_def.set_base_template_vars(cg_context.template_bindings())
func1_def.body.append(TextNode("return lhs.AsEnum() == rhs;"))
func2_def = CxxFuncDefNode(
name="operator==",
arg_decls=["${class_name}::Enum lhs", "const ${class_name}& rhs"],
return_type="bool",
inline=True)
func2_def.set_base_template_vars(cg_context.template_bindings())
func2_def.body.append(TextNode("return lhs == rhs.AsEnum();"))
decls = ListNode([func1_def, EmptyNode(), func2_def])
return decls, None
def make_as_enum_function(cg_context):
assert isinstance(cg_context, CodeGenContext)
func_def = CxxFuncDefNode(
name="AsEnum", arg_decls=[], return_type="Enum", const=True)
func_def.body.append(TextNode("return static_cast<Enum>(GetEnumValue());"))
return func_def, None
def make_nested_enum_class_def(cg_context):
assert isinstance(cg_context, CodeGenContext)
enum_values = [
TextNode(name_style.constant(value))
for value in cg_context.enumeration.values
]
return ListNode([
TextNode("enum class Enum : enum_int_t {"),
ListNode(enum_values, separator=", "),
TextNode("};"),
TextNode("static constexpr size_t kEnumSize = {};".format(
len(enum_values))),
])
def make_enum_string_table(cg_context):
assert isinstance(cg_context, CodeGenContext)
decls = TextNode("static const char* const string_table_[];")
str_values = [
TextNode("\"{}\"".format(value))
for value in cg_context.enumeration.values
]
# Define the string table in *.cc so that there never exists a copy of
# the table (i.e. the strings in the table are interned strings in the
# scope of this IDL enumeration). This trick makes it possible to compare
# the strings by their address.
defs = ListNode([
TextNode("constexpr const char* const "
"${class_name}::string_table_[] = {"),
ListNode(str_values, separator=", "),
TextNode("};"),
])
defs.set_base_template_vars(cg_context.template_bindings())
return decls, defs
def generate_enumeration(enumeration_identifier):
assert isinstance(enumeration_identifier, web_idl.Identifier)
web_idl_database = package_initializer().web_idl_database()
enumeration = web_idl_database.find(enumeration_identifier)
path_manager = PathManager(enumeration)
assert path_manager.api_component == path_manager.impl_component
api_component = path_manager.api_component
for_testing = enumeration.code_generator_info.for_testing
# Class names
class_name = blink_class_name(enumeration)
cg_context = CodeGenContext(
enumeration=enumeration,
class_name=class_name,
base_class_name="bindings::EnumerationBase")
# 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=["bindings::EnumerationBase"],
final=True,
export=component_export(
api_component, for_testing))
class_def.set_base_template_vars(cg_context.template_bindings())
# Implementation parts
factory_decls, factory_defs = make_factory_methods(cg_context)
default_ctor_decls, default_ctor_defs = make_default_constructor(
cg_context)
ctor_decls, ctor_defs = make_constructors(cg_context)
assign_decls, assign_defs = make_assignment_operators(cg_context)
equal_decls, equal_defs = make_equality_operators(cg_context)
nested_enum_class_def = make_nested_enum_class_def(cg_context)
table_decls, table_defs = make_enum_string_table(cg_context)
as_enum_decl, as_enum_def = make_as_enum_function(cg_context)
# 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_class_decls([
"ExceptionState",
])
header_node.accumulator.add_stdcpp_include_headers([
"optional",
])
header_node.accumulator.add_include_headers([
component_export_header(api_component, for_testing),
IncludeDefinition(
"third_party/blink/renderer/platform/bindings/enumeration_base.h",
"IWYU pragma: export")
])
source_node.accumulator.add_include_headers([
"third_party/blink/renderer/bindings/core/v8/generated_code_helper.h",
])
header_blink_ns.body.append(class_def)
header_blink_ns.body.append(EmptyNode())
class_def.public_section.append(nested_enum_class_def)
class_def.public_section.append(EmptyNode())
class_def.private_section.append(table_decls)
class_def.private_section.append(EmptyNode())
source_blink_ns.body.append(table_defs)
source_blink_ns.body.append(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.private_section.append(default_ctor_decls)
class_def.private_section.append(EmptyNode())
source_blink_ns.body.append(default_ctor_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(assign_decls)
class_def.public_section.append(EmptyNode())
source_blink_ns.body.append(assign_defs)
source_blink_ns.body.append(EmptyNode())
class_def.public_section.append(as_enum_decl)
class_def.public_section.append(EmptyNode())
source_blink_ns.body.append(as_enum_def)
source_blink_ns.body.append(EmptyNode())
header_blink_ns.body.append(equal_decls)
header_blink_ns.body.append(EmptyNode())
source_blink_ns.body.append(equal_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_enumerations(task_queue):
assert isinstance(task_queue, TaskQueue)
web_idl_database = package_initializer().web_idl_database()
for enumeration in web_idl_database.enumerations:
task_queue.post_task(generate_enumeration, enumeration.identifier)