blob: 0d721c25db85cb884a95d5e65b624d3a2751ed92 [file] [log] [blame]
# 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 os.path
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_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 CxxUnlikelyIfNode
from .codegen_accumulator import CodeGenAccumulator
from .codegen_context import CodeGenContext
from .codegen_expr import CodeGenExpr
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 collect_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 .path_manager import PathManager
def _is_none_or_str(arg):
return arg is None or isinstance(arg, str)
def callback_function_name(cg_context, overload_index=None):
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:
property_name = ""
kind = "Constructor"
elif cg_context.exposed_construct:
kind = "ExposedConstruct"
elif cg_context.operation_group:
kind = "Operation"
if overload_index is None:
callback_suffix = "Callback"
else:
callback_suffix = "Overload{}".format(overload_index + 1)
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, callback_suffix, world_suffix)
def constant_name(cg_context):
assert isinstance(cg_context, CodeGenContext)
assert cg_context.constant
property_name = cg_context.property_.identifier.lower()
kind = "Constant"
return name_style.constant(kind, property_name)
# ----------------------------------------------------------------------------
# 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:
name = "arg1_value"
v8_value = "${info}[0]"
code_node.register_code_symbol(
make_v8_to_blink_value(name, v8_value,
cg_context.attribute.idl_type))
return
for index, argument in enumerate(cg_context.function_like.arguments, 1):
name = name_style.arg_f("arg{}_{}", index, argument.identifier)
if argument.is_variadic:
code_node.register_code_symbol(
make_v8_to_blink_value_variadic(name, "${info}", index - 1,
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.default_value))
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("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_execution_context",
("ExecutionContext* ${current_execution_context} = "
"ExecutionContext::From(${current_script_state});")),
S("current_script_state", ("ScriptState* ${current_script_state} = "
"ScriptState::From(${current_context});")),
S("execution_context", ("ExecutionContext* ${execution_context} = "
"ExecutionContext::From(${script_state});")),
S("isolate", "v8::Isolate* ${isolate} = ${info}.GetIsolate();"),
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("v8_receiver",
"v8::Local<v8::Object> ${v8_receiver} = ${info}.This();"),
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 = (cg_context.member_like
and not cg_context.member_like.is_static)
# 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)))
# 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:
_1 = "ExceptionState::kConstructionContext"
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}", "${class_like_name}",
"${property_name}"
]
_2 = ""
if cg_context.return_type and cg_context.return_type.unwrap().is_promise:
_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}::ToBlinkUnsafe(${v8_receiver});")
else:
text = ("LocalDOMWindow* ${blink_receiver} = To<LocalDOMWindow>("
"${class_name}::ToBlinkUnsafe(${v8_receiver}));")
else:
pattern = ("{_1}* ${blink_receiver} = "
"${class_name}::ToBlinkUnsafe(${v8_receiver});")
_1 = blink_class_name(cg_context.class_like)
text = _format(pattern, _1=_1)
local_vars.append(S("blink_receiver", text))
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()
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):
assert isinstance(code_node, SymbolScopeNode)
assert isinstance(cg_context, CodeGenContext)
assert num_of_args is None or isinstance(num_of_args, (int, long))
arguments = []
ext_attrs = cg_context.member_like.extended_attributes
values = ext_attrs.values_of("CallWith") + (
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}")
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 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 = (code_generator_info.property_implemented_as
or cg_context.member_like.identifier)
if cg_context.attribute_set:
func_name = name_style.api_func("set", func_name)
if cg_context.constructor:
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):
assert isinstance(code_node, SymbolScopeNode)
assert isinstance(cg_context, CodeGenContext)
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)
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)))
nodes = []
is_return_type_void = (cg_context.attribute_set
or cg_context.return_type.unwrap().is_void)
if not is_return_type_void:
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 cg_context.is_return_value_mutable:
nodes.append(
F("{} ${return_value} = {};", return_type, api_call))
else:
nodes.append(F("const 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("${info}[{}]->IsUndefined()", 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_check_argument_length(cg_context):
assert isinstance(cg_context, CodeGenContext)
# Attribute getters don't need this check and operations have their own
# checks handling optional arguments.
assert cg_context.attribute_set
T = TextNode
idl_type = cg_context.attribute.idl_type
if not (idl_type.does_include_nullable_or_dict or idl_type.unwrap().is_any
or
"TreatNonObjectAsNull" in idl_type.unwrap().extended_attributes):
# ES undefined in ${info}[0] will cause a TypeError anyway, so omit the
# check against the number of arguments.
return None
return CxxUnlikelyIfNode(
cond="${info}.Length() == 0",
body=[
T("${exception_state}.ThrowTypeError("
"ExceptionMessages::NotEnoughArguments(1, ${info}.Length()));"),
T("return;"),
])
def make_check_constructor_call(cg_context):
assert isinstance(cg_context, CodeGenContext)
T = TextNode
return SequenceNode([
CxxUnlikelyIfNode(
cond="!${info}.IsConstructCall()",
body=T("${exception_state}.ThrowTypeError("
"ExceptionMessages::ConstructorNotCallableAsFunction("
"${class_like_name}));\n"
"return;")),
CxxLikelyIfNode(
cond=("ConstructorMode::Current(${isolate}) == "
"ConstructorMode::kWrapExistingObject"),
body=T("V8SetReturnValue(${info}, ${v8_receiver});\n"
"return;")),
])
def make_check_receiver(cg_context):
assert isinstance(cg_context, CodeGenContext)
T = TextNode
if (cg_context.attribute
and "LenientThis" in cg_context.attribute.extended_attributes):
return SequenceNode([
T("// [LenientThis]"),
CxxUnlikelyIfNode(
cond="!${class_name}::HasInstance(${v8_receiver}, ${isolate})",
body=T("return;")),
])
if cg_context.return_type and cg_context.return_type.unwrap().is_promise:
return SequenceNode([
T("// Promise returning function: "
"Convert a TypeError to a reject promise."),
CxxUnlikelyIfNode(
cond="!${class_name}::HasInstance(${v8_receiver}, ${isolate})",
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.member_like.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("V8SetReturnValueNull(${info});\n"
"return;"),
]
return SequenceNode([
T("// [CheckSecurity=ReturnValue]"),
CxxUnlikelyIfNode(cond=cond, body=body),
])
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 = ", ${info}[0]"
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",
]))
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)
# 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))
is_typedef_name = lambda t, name: t.is_typedef and t.identifier == name
func_like_a = find(
lambda t, u: is_typedef_name(t.unwrap(typedef=False),
"ArrayBufferView"))
func_like_b = find(
lambda t, u: is_typedef_name(t.unwrap(typedef=False), "BufferSource"))
if func_like_a or func_like_b:
# V8 specific optimization: ArrayBufferView
if func_like_a:
func_like = func_like_a
dispatch_if("{value}->IsArrayBufferView()")
if func_like_b:
func_like = func_like_b
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()")
# 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,
# ...
func_like = find(lambda t, u: u.is_typed_array_type)
if func_like:
dispatch_if("{value}->IsTypedArray()")
# 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(
TextNode("if (${exception_state}.HadException()) {\n"
" return;\n"
"}"))
# 12.10. if Type(V) is Object and ...
def is_es_object_type(t, u):
return (u.is_callback_interface or u.is_dictionary or u.is_record
or u.is_object)
func_like = find(is_es_object_type)
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_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
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)
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=_format("${info}.Length() >= {}", arg_size),
body=[node, T("break;") if can_fail else None])
did_use_break = did_use_break or can_fail
conditional = expr_or(
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)
if not did_use_break and arg_size == 0 and conditional.is_always_true:
return branches
return SequenceNode([
branches,
EmptyNode(),
T("${exception_state}.ThrowTypeError"
"(\"Overload resolution failed.\");\n"
"return;"),
])
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("
"${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_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):
assert "HighEntropy" not in ext_attrs, "{}: {}".format(
cg_context.idl_location_and_name,
"[HighEntropy] must be specified with either [Measure] or "
"[MeasureAs].")
return None
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 = target.extended_attributes.value_of("MeasureAs")
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(target.identifier), suffix)
node = SequenceNode()
pattern = ("// [Measure], [MeasureAs]\n"
"UseCounter::Count(${execution_context}, WebFeature::{_1});")
_1 = name
node.append(TextNode(_format(pattern, _1=_1)))
node.accumulate(
CodeGenAccumulator.require_include_headers([
"third_party/blink/renderer/core/frame/web_feature.h",
"third_party/blink/renderer/platform/instrumentation/use_counter.h",
]))
if "HighEntropy" not in ext_attrs or cg_context.attribute_set:
return node
pattern = (
"// [HighEntropy]\n"
"Dactyloscoper::Record(${execution_context}, WebFeature::{_1});")
_1 = name
node.append(TextNode(_format(pattern, _1=_1)))
node.accumulate(
CodeGenAccumulator.require_include_headers(
["third_party/blink/renderer/core/frame/dactyloscoper.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) && !v8_value->IsUndefined()) {
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) && !v8_value->IsUndefined()) {
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):
assert isinstance(cg_context, CodeGenContext)
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.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), 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
T = TextNode
nodes = []
ext_attrs = cg_context.member_like.extended_attributes
if "CustomElementCallbacks" in ext_attrs or "Reflect" in ext_attrs:
if "CustomElementCallbacks" in ext_attrs:
nodes.append(T("// [CustomElementCallbacks]"))
elif "Reflect" in ext_attrs:
nodes.append(T("// [Reflect]"))
nodes.append(
T("V0CustomElementProcessingStack::CallbackDeliveryScope "
"v0_custom_element_scope;"))
if "CEReactions" in ext_attrs:
nodes.append(T("// [CEReactions]"))
nodes.append(T("CEReactionsScope ce_reactions_scope;"))
if not nodes:
return None
nodes = SequenceNode(nodes)
nodes.accumulate(
CodeGenAccumulator.require_include_headers([
"third_party/blink/renderer/core/html/custom/ce_reactions_scope.h",
"third_party/blink/renderer/core/html/custom/"
"v0_custom_element_processing_stack.h"
]))
return 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("
"\"${attribute.extended_attributes.value_of(\"PutForwards\")}\""
"), ${info}[0]).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), "
"${info}[0]).To(&did_create)) {\n"
" return;\n"
"}"),
])
def make_v8_set_return_value(cg_context):
assert isinstance(cg_context, CodeGenContext)
T = TextNode
if cg_context.attribute_set 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() %>")
return_type = cg_context.return_type.unwrap(typedef=True)
return_type_body = return_type.unwrap()
if (cg_context.for_world == cg_context.MAIN_WORLD
and return_type_body.is_interface):
return T("V8SetReturnValueForMainWorld(${info}, ${return_value});")
if return_type_body.is_interface:
return T("V8SetReturnValue(${info}, ${return_value}, "
"${creation_context_object});")
if return_type_body.is_string:
if return_type.is_nullable:
return T("V8SetReturnValueStringOrNull"
"(${info}, ${return_value}, ${isolate});")
else:
return T("V8SetReturnValueString"
"(${info}, ${return_value}, ${isolate});")
if return_type.is_frozen_array:
return T("V8SetReturnValue(${info}, FreezeV8Object(ToV8("
"${return_value}, ${v8_receiver}, ${isolate}), ${isolate}));")
if return_type.is_promise:
return T("V8SetReturnValue(${info}, ${return_value}.V8Value());")
return T("V8SetReturnValue(${info}, ${return_value});")
def _make_empty_callback_def(cg_context, function_name, arg_decls=None):
assert isinstance(cg_context, CodeGenContext)
assert isinstance(function_name, str)
if arg_decls is None:
arg_decls = ["const v8::FunctionCallbackInfo<v8::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
body.add_template_var("info", "info")
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_runtime_call_timer_scope(cg_context),
make_report_deprecate_as(cg_context),
make_report_measure_as(cg_context),
make_log_activity(cg_context),
EmptyNode(),
make_check_receiver(cg_context),
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_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 ("LenientSetter", "PutForwards", "Replaceable")):
return None
func_def = _make_empty_callback_def(cg_context, function_name)
body = func_def.body
if "LenientSetter" in ext_attrs:
body.append(TextNode("// [LenientSetter]"))
return func_def
body.extend([
make_runtime_call_timer_scope(cg_context),
make_report_deprecate_as(cg_context),
make_report_measure_as(cg_context),
make_log_activity(cg_context),
EmptyNode(),
make_check_receiver(cg_context),
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,
arg_decls=[
"v8::Local<v8::Name> property",
"const v8::FunctionCallbackInfo<v8::Value>& info",
])
body = func_def.body
v8_set_return_value = _format(
"V8SetReturnValue(${info}, ${class_name}::{});",
constant_name(cg_context))
body.extend([
make_runtime_call_timer_scope(cg_context),
logging_nodes,
EmptyNode(),
TextNode(v8_set_return_value),
])
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_cooperative_scheduling_safepoint(cg_context))
body.append(EmptyNode())
if cg_context.constructor_group:
body.append(make_check_constructor_call(cg_context))
body.append(EmptyNode())
body.append(make_overload_dispatcher(cg_context))
return func_def
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
body.extend([
make_runtime_call_timer_scope(cg_context),
make_report_deprecate_as(cg_context),
make_report_measure_as(cg_context),
make_log_activity(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))
else:
body.append(
T("v8::Local<v8::Object> v8_wrapper = "
"${return_value}->AssociateWithWrapper(${isolate}, "
"${class_name}::GetWrapperTypeInfo(), ${v8_receiver});"))
body.append(T("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,
arg_decls=[
"v8::Local<v8::Name> property",
"const v8::PropertyCallbackInfo<v8::Value>& info",
])
body = func_def.body
v8_set_return_value = _format(
"V8SetReturnValueInterfaceObject(${info}, {}::GetWrapperTypeInfo());",
v8_bridge_class_name(cg_context.exposed_construct))
body.extend([
make_runtime_call_timer_scope(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_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
body.extend([
make_runtime_call_timer_scope(cg_context),
make_report_deprecate_as(cg_context),
make_report_measure_as(cg_context),
make_log_activity(cg_context),
EmptyNode(),
make_check_receiver(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),
])
return func_def
def make_operation_callback_def(cg_context, function_name):
assert isinstance(cg_context, CodeGenContext)
assert isinstance(function_name, str)
operation_group = cg_context.operation_group
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
# ----------------------------------------------------------------------------
# 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("execution_context", ("ExecutionContext* ${execution_context} = "
"ExecutionContext::From(${script_state});")),
S("instance_template",
("v8::Local<v8::ObjectTemplate> ${instance_template} = "
"${interface_template}->InstanceTemplate();")),
S("interface_template",
("v8::Local<v8::FunctionTemplate> ${interface_template} = "
"${wrapper_type_info}->DomTemplate(${isolate}, ${world});")),
S("is_in_secure_context",
("const bool ${is_in_secure_context} = "
"${execution_context}->IsSecureContext();")),
S("isolate", "v8::Isolate* ${isolate} = ${v8_context}->GetIsolate();"),
S("prototype_template",
("v8::Local<v8::ObjectTemplate> ${prototype_template} = "
"${interface_template}->PrototypeTemplate();")),
S("script_state",
"ScriptState* ${script_state} = ScriptState::From(${v8_context});"),
S("signature",
("v8::Local<v8::Signature> ${signature} = "
"v8::Signature::New(${isolate}, ${interface_template});")),
S("wrapper_type_info",
("const WrapperTypeInfo* const ${wrapper_type_info} = "
"${class_name}::GetWrapperTypeInfo();")),
])
pattern = (
"v8::Local<v8::FunctionTemplate> ${parent_interface_template}{_1};")
_1 = (" = ${wrapper_type_info}->parent_class->dom_template_function"
"(${isolate}, ${world})")
if not cg_context.class_like.inherited:
_1 = ""
local_vars.append(S("parent_interface_template", _format(pattern, _1=_1)))
# 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_v8_property_attribute(member):
values = []
if "NotEnumerable" in member.extended_attributes:
values.append("v8::DontEnum")
if "Unforgeable" in member.extended_attributes:
values.append("v8::DontDelete")
if not values:
values.append("v8::None")
return "static_cast<v8::PropertyAttribute>({})".format(" | ".join(values))
def _make_property_entry_on_which_object(member):
ON_INSTANCE = "V8DOMConfiguration::kOnInstance"
ON_PROTOTYPE = "V8DOMConfiguration::kOnPrototype"
ON_INTERFACE = "V8DOMConfiguration::kOnInterface"
if isinstance(member, web_idl.Constant):
return ON_INTERFACE
if hasattr(member, "is_static") and member.is_static:
return ON_INTERFACE
if "Global" in member.owner.extended_attributes:
return ON_INSTANCE
if "Unforgeable" in member.extended_attributes:
return ON_INSTANCE
return ON_PROTOTYPE
def _make_property_entry_check_receiver(member):
if ("LenientThis" in member.extended_attributes
or (isinstance(member, web_idl.Attribute)
and member.idl_type.unwrap().is_promise)
or (isinstance(member, web_idl.FunctionLike)
and member.return_type.unwrap().is_promise)):
return "V8DOMConfiguration::kDoNotCheckHolder"
else:
return "V8DOMConfiguration::kCheckHolder"
def _make_property_entry_has_side_effect(member):
if member.extended_attributes.value_of("Affects") == "Nothing":
return "V8DOMConfiguration::kHasNoSideEffect"
else:
return "V8DOMConfiguration::kHasSideEffect"
def _make_property_entry_world(world):
if world == CodeGenContext.MAIN_WORLD:
return "V8DOMConfiguration::kMainWorld"
if world == CodeGenContext.NON_MAIN_WORLDS:
return "V8DOMConfiguration::kNonMainWorlds"
if world == CodeGenContext.ALL_WORLDS:
return "V8DOMConfiguration::kAllWorlds"
assert False
def _make_property_entry_constant_type_and_value_format(member):
idl_type = member.idl_type.unwrap()
if (idl_type.keyword_typename == "long long"
or idl_type.keyword_typename == "unsigned long long"):
assert False, "64-bit constants are not yet supported."
if idl_type.keyword_typename == "unsigned long":
return ("V8DOMConfiguration::kConstantTypeUnsignedLong",
"static_cast<int>({value})")
if idl_type.is_integer:
return ("V8DOMConfiguration::kConstantTypeLong",
"static_cast<int>({value})")
if idl_type.is_floating_point_numeric:
return ("V8DOMConfiguration::kConstantTypeDouble",
"static_cast<double>({value})")
assert False, "Unsupported type: {}".format(idl_type.syntactic_form)
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}, "
"V8PrivateProperty::kNoCachedAccessor, "
"{v8_property_attribute}, "
"{on_which_object}, "
"{check_receiver}, "
"{has_side_effect}, "
"V8DOMConfiguration::kAlwaysCallGetter, "
"{world}"
"}},")
text = _format(
pattern,
property_name=entry.member.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.member),
on_which_object=_make_property_entry_on_which_object(entry.member),
check_receiver=_make_property_entry_check_receiver(entry.member),
has_side_effect=_make_property_entry_has_side_effect(entry.member),
world=_make_property_entry_world(entry.world))
entry_nodes.append(T(text))
return ListNode([
T("static constexpr V8DOMConfiguration::AccessorConfiguration " +
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.member.identifier,
constant_callback=entry.const_callback_name)
entry_nodes.append(T(text))
return ListNode([
T("static constexpr V8DOMConfiguration::ConstantCallbackConfiguration "
+ 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_type}, "
"{constant_value}"
"}},")
constant_type, constant_value_fmt = (
_make_property_entry_constant_type_and_value_format(entry.member))
constant_value = _format(
constant_value_fmt, value=entry.const_constant_name)
text = _format(
pattern,
property_name=entry.member.identifier,
constant_type=constant_type,
constant_value=constant_value)
entry_nodes.append(T(text))
return ListNode([
T("static constexpr V8DOMConfiguration::ConstantConfiguration " +
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}, "
"nullptr, "
"static_cast<v8::PropertyAttribute>(v8::DontEnum), "
"V8DOMConfiguration::kOnInstance, "
"V8DOMConfiguration::kDoNotCheckHolder, "
"V8DOMConfiguration::kHasNoSideEffect, "
"V8DOMConfiguration::kAlwaysCallGetter, "
"{world}"
"}}, ")
text = _format(
pattern,
property_name=entry.member.identifier,
exposed_construct_callback=entry.prop_callback_name,
world=_make_property_entry_world(entry.world))
entry_nodes.append(T(text))
return ListNode([
T("static constexpr V8DOMConfiguration::AttributeConfiguration " +
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
entry_nodes = []
for entry in operation_entries:
pattern = ("{{"
"\"{property_name}\", "
"{operation_callback}, "
"{function_length}, "
"{v8_property_attribute}, "
"{on_which_object}, "
"{check_receiver}, "
"V8DOMConfiguration::kDoNotCheckAccess, "
"{has_side_effect}, "
"{world}"
"}}, ")
text = _format(
pattern,
property_name=entry.member.identifier,
operation_callback=entry.op_callback_name,
function_length=entry.op_func_length,
v8_property_attribute=_make_property_entry_v8_property_attribute(
entry.member),
on_which_object=_make_property_entry_on_which_object(entry.member),
check_receiver=_make_property_entry_check_receiver(entry.member),
has_side_effect=_make_property_entry_has_side_effect(entry.member),
world=_make_property_entry_world(entry.world))
entry_nodes.append(T(text))
return ListNode([
T("static constexpr V8DOMConfiguration::MethodConfiguration " +
table_name + "[] = {"),
ListNode(entry_nodes),
T("};"),
])
class _PropEntryMember(object):
def __init__(self, is_context_dependent, exposure_conditional, world,
member):
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.member = member
class _PropEntryAttribute(_PropEntryMember):
def __init__(self, is_context_dependent, exposure_conditional, world,
attribute, attr_get_callback_name, attr_set_callback_name):
assert isinstance(attribute, web_idl.Attribute)
assert isinstance(attr_get_callback_name, str)
assert _is_none_or_str(attr_set_callback_name)
_PropEntryMember.__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(_PropEntryMember):
def __init__(self, is_context_dependent, exposure_conditional, world,
constant, const_callback_name, const_constant_name):
assert isinstance(constant, web_idl.Constant)
assert _is_none_or_str(const_callback_name)
assert isinstance(const_constant_name, str)
_PropEntryMember.__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(_PropEntryMember):
def __init__(self, is_context_dependent, exposure_conditional, world,
constructor_group, ctor_callback_name, ctor_func_length):
assert isinstance(constructor_group, web_idl.ConstructorGroup)
assert isinstance(ctor_callback_name, str)
assert isinstance(ctor_func_length, (int, long))
_PropEntryMember.__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(_PropEntryMember):
def __init__(self, is_context_dependent, exposure_conditional, world,
exposed_construct, prop_callback_name):
assert isinstance(prop_callback_name, str)
_PropEntryMember.__init__(self, is_context_dependent,
exposure_conditional, world,
exposed_construct)
self.prop_callback_name = prop_callback_name
class _PropEntryOperationGroup(_PropEntryMember):
def __init__(self, is_context_dependent, exposure_conditional, world,
operation_group, op_callback_name, op_func_length):
assert isinstance(operation_group, web_idl.OperationGroup)
assert isinstance(op_callback_name, str)
assert isinstance(op_func_length, (int, long))
_PropEntryMember.__init__(self, is_context_dependent,
exposure_conditional, world, operation_group)
self.op_callback_name = op_callback_name
self.op_func_length = op_func_length
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)
interface = cg_context.interface
global_names = interface.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)
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):
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)
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_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):
cgc = cg_context.make_copy(
exposed_construct=exposed_construct, for_world=world)
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_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)
op_callback_node = make_operation_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=operation_group,
op_callback_name=op_callback_name,
op_func_length=operation_group.min_num_of_required_arguments))
iterate(interface.attributes, process_attribute)
iterate(interface.constants, process_constant)
iterate(interface.constructor_groups, process_constructor_group)
iterate(interface.exposed_constructs, process_exposed_construct)
iterate(interface.operation_groups, process_operation_group)
return callback_def_nodes
def make_install_interface_template(cg_context, function_name, class_name,
trampoline_var_name, constructor_entries,
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 _is_none_or_str(install_unconditional_func_name)
assert _is_none_or_str(install_context_independent_func_name)
T = TextNode
arg_decls = [
"v8::Isolate* isolate",
"const DOMWrapperWorld& world",
"v8::Local<v8::FunctionTemplate> 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,
class_name=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_vars({
"isolate": "isolate",
"world": "world",
"interface_template": "interface_template",
})
bind_installer_local_vars(body, cg_context)
body.extend([
T("V8DOMConfiguration::InitializeDOMInterfaceTemplate("
"${isolate}, ${interface_template}, "
"${wrapper_type_info}->interface_name, ${parent_interface_template}, "
"kV8DefaultWrapperInternalFieldCount);"),
EmptyNode(),
])
for entry in constructor_entries:
set_callback = _format("${interface_template}->SetCallHandler({});",
entry.ctor_callback_name)
set_length = _format("${interface_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())
if ("Global" in cg_context.class_like.extended_attributes
or cg_context.class_like.identifier == "Location"):
if "Global" in cg_context.class_like.extended_attributes:
body.append(T("// [Global]"))
if cg_context.class_like.identifier == "Location":
body.append(T("// Location exotic object"))
body.extend([
T("${instance_template}->SetImmutableProto();"),
T("${prototype_template}->SetImmutableProto();"),
EmptyNode(),
])
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
def make_install_properties(cg_context, function_name, class_name,
trampoline_var_name, is_context_dependent,
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 _is_none_or_str(trampoline_var_name)
assert isinstance(is_context_dependent, bool)
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 not (attribute_entries or constant_entries or exposed_construct_entries
or operation_entries):
return None, None, None
if is_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::Function> interface_object",
]
else:
arg_decls = [
"v8::Isolate* isolate",
"const DOMWrapperWorld& world",
"v8::Local<v8::ObjectTemplate> instance_template",
"v8::Local<v8::ObjectTemplate> prototype_template",
"v8::Local<v8::FunctionTemplate> 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)
if is_context_dependent:
args = [
"context",
"world",
"instance_object",
"prototype_object",
"interface_object",
]
else:
args = [
"isolate",
"world",
"instance_template",
"prototype_template",
"interface_template",
]
text = _format(
"return {func}({args});",
func=trampoline_var_name,
args=", ".join(args))
trampoline_def.body.append(TextNode(text))
func_decl = CxxFuncDeclNode(
name=function_name,
arg_decls=arg_decls,
return_type=return_type,
static=True)
func_def = CxxFuncDefNode(
name=function_name,
class_name=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 is_context_dependent:
body.add_template_vars({
"v8_context": "context", # 'context' is reserved by Mako.
"world": "world",
"instance_object": "instance_object",
"prototype_object": "prototype_object",
"interface_object": "interface_object",
})
else:
body.add_template_vars({
"isolate": "isolate",
"world": "world",
"instance_template": "instance_template",
"prototype_template": "prototype_template",
"interface_template": "interface_template",
})
bind_installer_local_vars(body, cg_context)
def group_by_condition(entries):
unconditional_entries = []
conditional_to_entries = {}
for entry in entries:
assert entry.is_context_dependent == is_context_dependent
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.iteritems():
body.append(
CxxUnlikelyIfNode(
cond=conditional,
body=[
make_table_func(table_name, entries),
TextNode(installer_call_text),
]))
body.append(EmptyNode())
table_name = "kAttributeTable"
if is_context_dependent:
installer_call_text = (
"V8DOMConfiguration::InstallAccessors(${isolate}, ${world}, "
"${instance_object}, ${prototype_object}, ${interface_object}, "
"${signature}, kAttributeTable, base::size(kAttributeTable));")
else:
installer_call_text = (
"V8DOMConfiguration::InstallAccessors(${isolate}, ${world}, "
"${instance_template}, ${prototype_template}, "
"${interface_template}, ${signature}, "
"kAttributeTable, base::size(kAttributeTable));")
install_properties(table_name, attribute_entries,
_make_attribute_registration_table, installer_call_text)
table_name = "kConstantCallbackTable"
if is_context_dependent:
installer_call_text = (
"V8DOMConfiguration::InstallConstants(${isolate}, "
"${interface_object}, ${prototype_object}, "
"kConstantCallbackTable, base::size(kConstantCallbackTable));")
else:
installer_call_text = (
"V8DOMConfiguration::InstallConstants(${isolate}, "
"${interface_template}, ${prototype_template}, "
"kConstantCallbackTable, base::size(kConstantCallbackTable));")
constant_callback_entries = 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"
if is_context_dependent:
installer_call_text = (
"V8DOMConfiguration::InstallConstants(${isolate}, "
"${interface_object}, ${prototype_object}, "
"kConstantValueTable, base::size(kConstantValueTable));")
else:
installer_call_text = (
"V8DOMConfiguration::InstallConstants(${isolate}, "
"${interface_template}, ${prototype_template}, "
"kConstantValueTable, base::size(kConstantValueTable));")
constant_value_entries = 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"
if is_context_dependent:
installer_call_text = (
"V8DOMConfiguration::InstallAttributes(${isolate}, ${world}, "
"${instance_object}, ${prototype_object}, "
"kExposedConstructTable, base::size(kExposedConstructTable));")
else:
installer_call_text = (
"V8DOMConfiguration::InstallAttributes(${isolate}, ${world}, "
"${instance_template}, ${prototype_template}, "
"kExposedConstructTable, base::size(kExposedConstructTable));")
install_properties(table_name, exposed_construct_entries,
_make_exposed_construct_registration_table,
installer_call_text)
table_name = "kOperationTable"
if is_context_dependent:
installer_call_text = (
"V8DOMConfiguration::InstallMethods(${isolate}, ${world}, "
"${instance_object}, ${prototype_object}, ${interface_object}, "
"${signature}, kOperationTable, base::size(kOperationTable));")
else:
installer_call_text = (
"V8DOMConfiguration::InstallMethods(${isolate}, ${world}, "
"${instance_template}, ${prototype_template}, "
"${interface_template}, ${signature}, "
"kOperationTable, base::size(kOperationTable));")
install_properties(table_name, operation_entries,
_make_operation_registration_table, installer_call_text)
return func_decl, func_def, trampoline_def
def make_cross_component_init(cg_context, function_name, class_name):
"""
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)
T = TextNode
F = lambda *args, **kwargs: T(_format(*args, **kwargs))
trampoline_var_defs = ListNode([
T("// Cross-component 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),
])
func_decl = CxxFuncDeclNode(
name=function_name, arg_decls=[], return_type="void", static=True)
func_def = CxxFuncDefNode(
name=function_name,
class_name=class_name,
arg_decls=[],
return_type="void")
func_def.set_base_template_vars(cg_context.template_bindings())
body = func_def.body
body.extend([
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_defs
# ----------------------------------------------------------------------------
# Main functions
# ----------------------------------------------------------------------------
def generate_interface(interface):
path_manager = PathManager(interface)
api_component = path_manager.api_component
impl_component = path_manager.impl_component
is_cross_components = path_manager.is_cross_components
# Class names
api_class_name = v8_bridge_class_name(interface)
if is_cross_components:
impl_class_name = "{}::Impl".format(api_class_name)
else:
impl_class_name = api_class_name
cg_context = CodeGenContext(interface=interface, class_name=api_class_name)
# Filepaths
filename = "v8_example_interface"
api_header_path = path_manager.api_path(filename, ext="h")
api_source_path = path_manager.api_path(filename, ext="cc")
if is_cross_components:
impl_header_path = path_manager.impl_path(filename, ext="h")
impl_source_path = path_manager.impl_path(filename, 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(interface)),
],
final=True,
export=component_export(api_component))
api_class_def.set_base_template_vars(cg_context.template_bindings())
if is_cross_components:
impl_class_def = CxxClassDefNode(
impl_class_name,
final=True,
export=component_export(impl_component))
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
constant_defs = ListNode()
for constant in interface.constants:
cgc = cg_context.make_copy(constant=constant)
constant_defs.append(
make_constant_constant_def(cgc, constant_name(cgc)))
# 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
(cross_component_init_decl, cross_component_init_def,
trampoline_var_defs) = make_cross_component_init(
cg_context, "Init", class_name=impl_class_name)
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)
# 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,
trampoline_var_name=tp_install_unconditional_props,
is_context_dependent=False,
attribute_entries=filter(is_unconditional, attribute_entries),
constant_entries=filter(is_unconditional, constant_entries),
exposed_construct_entries=filter(is_unconditional,
exposed_construct_entries),
operation_entries=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,
trampoline_var_name=tp_install_context_independent_props,
is_context_dependent=False,
attribute_entries=filter(is_context_independent, attribute_entries),
constant_entries=filter(is_context_independent, constant_entries),
exposed_construct_entries=filter(is_context_independent,
exposed_construct_entries),
operation_entries=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,
trampoline_var_name=tp_install_context_dependent_props,
is_context_dependent=True,
attribute_entries=filter(is_context_dependent, attribute_entries),
constant_entries=filter(is_context_dependent, constant_entries),
exposed_construct_entries=filter(is_context_dependent,
exposed_construct_entries),
operation_entries=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,
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,
])
# 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([
component_export_header(api_component),
"third_party/blink/renderer/platform/bindings/v8_interface_bridge.h",
])
api_header_node.accumulator.add_class_decl(blink_class_name(interface))
if is_cross_components:
impl_header_node.accumulator.add_include_headers([
api_header_path,
component_export_header(impl_component),
])
impl_source_node.accumulator.add_include_headers([
"third_party/blink/renderer/bindings/core/v8/"
"native_value_traits_impl.h",
"third_party/blink/renderer/bindings/core/v8/v8_dom_configuration.h",
"third_party/blink/renderer/platform/bindings/exception_messages.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(interface))
# Assemble the parts.
api_header_blink_ns.body.append(api_class_def)
if is_cross_components:
impl_header_blink_ns.body.append(impl_class_def)
if is_cross_components:
api_class_def.public_section.append(installer_function_trampolines)
api_class_def.private_section.append(trampoline_var_defs)
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)
if constant_defs:
api_class_def.public_section.append(EmptyNode())
api_class_def.public_section.append(constant_defs)
impl_source_blink_ns.body.extend([
CxxNamespaceNode(name="", body=callback_defs),
EmptyNode(),
installer_function_defs,
])
# 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_interfaces(web_idl_database):
interface = web_idl_database.find("Navigator")
generate_interface(interface)