Commit d69b47a6 authored by Yuki Shiino's avatar Yuki Shiino Committed by Commit Bot

bind-gen: Implement attribute callback (2 of N)

This patch implements attribute callback a little more, and also
does the following things.

- Sort helper functions in alphabetically
- s/LiteralNode/TextNode/
  TextNode works just fine for almost all cases, and faster than
  LiteralNode.

XxxxAttribute{Getter,Setter}Callback of the old bindings
generator is mostly covered in this patch.

Bug: 839389
Change-Id: I1aa75335766f069a130c9d96ee47a6bca0a1b783
Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/1906486
Commit-Queue: Yuki Shiino <yukishiino@chromium.org>
Reviewed-by: default avatarHitoshi Yoshida <peria@chromium.org>
Cr-Commit-Position: refs/heads/master@{#714178}
parent c2cad1f5
......@@ -5,11 +5,11 @@
import os.path
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 .code_node import CodeNode
from .code_node import FunctionDefinitionNode
from .code_node import LiteralNode
from .code_node import SymbolDefinitionNode
from .code_node import SymbolNode
from .code_node import SymbolScopeNode
......@@ -27,6 +27,82 @@ from .mako_renderer import MakoRenderer
_format = CodeNode.format_template
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:
assert False, "Variadic arguments are not yet supported"
else:
v8_value = "${{info}}[{}]".format(argument.index)
code_node.register_code_symbol(
make_v8_to_blink_value(name, v8_value, argument.idl_type))
def bind_blink_api_call(code_node, cg_context):
assert isinstance(code_node, SymbolScopeNode)
assert isinstance(cg_context, CodeGenContext)
property_implemented_as = (
cg_context.member_like.code_generator_info.property_implemented_as)
func_name = (property_implemented_as
or name_style.api_func(cg_context.member_like.identifier))
if cg_context.attribute_set:
func_name = name_style.api_func("set", func_name)
if cg_context.member_like.is_static:
receiver_implemented_as = (
cg_context.member_like.code_generator_info.receiver_implemented_as)
class_name = (receiver_implemented_as
or name_style.class_(cg_context.class_like.identifier))
func_name = "{}::{}".format(class_name, func_name)
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}")
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,
1):
name = name_style.arg_f("arg{}_{}", index, 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}")
text = _format("{_1}({_2})", _1=func_name, _2=", ".join(arguments))
code_node.add_template_var("blink_api_call", TextNode(text))
def bind_callback_local_vars(code_node, cg_context):
assert isinstance(code_node, SymbolScopeNode)
assert isinstance(cg_context, CodeGenContext)
......@@ -113,82 +189,6 @@ def bind_callback_local_vars(code_node, cg_context):
code_node.add_template_vars(template_vars)
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:
assert False, "Variadic arguments are not yet supported"
else:
v8_value = "${{info}}[{}]".format(argument.index)
code_node.register_code_symbol(
make_v8_to_blink_value(name, v8_value, argument.idl_type))
def bind_blink_api_call(code_node, cg_context):
assert isinstance(code_node, SymbolScopeNode)
assert isinstance(cg_context, CodeGenContext)
property_implemented_as = (
cg_context.member_like.code_generator_info.property_implemented_as)
func_name = (property_implemented_as
or name_style.api_func(cg_context.member_like.identifier))
if cg_context.attribute_set:
func_name = name_style.api_func("set", func_name)
if cg_context.member_like.is_static:
receiver_implemented_as = (
cg_context.member_like.code_generator_info.receiver_implemented_as)
class_name = (receiver_implemented_as
or name_style.class_(cg_context.class_like.identifier))
func_name = "{}::{}".format(class_name, func_name)
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}")
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,
1):
name = name_style.arg_f("arg{}_{}", index, 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}")
text = _format("{_1}({_2})", _1=func_name, _2=", ".join(arguments))
code_node.add_template_var("blink_api_call", TextNode(text))
def bind_return_value(code_node, cg_context):
assert isinstance(code_node, SymbolScopeNode)
assert isinstance(cg_context, CodeGenContext)
......@@ -236,34 +236,226 @@ def bind_v8_set_return_value(code_node, cg_context):
code_node.add_template_var("v8_set_return_value", TextNode(text))
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 SymbolScopeNode([
T("// [LenientThis]"),
UnlikelyExitNode(
cond=T("!${v8_class}::HasInstance(${receiver}, ${isolate})"),
body=SymbolScopeNode([T("return;")])),
])
if cg_context.return_type.unwrap().is_promise:
return SymbolScopeNode([
T("// Promise returning function: "
"Convert a TypeError to a reject promise."),
UnlikelyExitNode(
cond=T("!${v8_class}::HasInstance(${receiver}, ${isolate})"),
body=SymbolScopeNode([
T("${exception_state}.ThrowTypeError("
"\"Illegal invocation\");"),
T("return;")
])),
])
return None
def make_log_activity(cg_context):
assert isinstance(cg_context, CodeGenContext)
ext_attrs = cg_context.member_like.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 = ""
if cg_context.attribute_set:
_1 = "LogSetter"
_4 = ", ${info}[0]"
if 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_report_deprecate_as(cg_context):
assert isinstance(cg_context, CodeGenContext)
name = cg_context.member_like.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)
ext_attrs = cg_context.member_like.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.operation:
suffix = "_Method"
name = cg_context.member_like.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(cg_context.member_like.identifier),
suffix)
node = SymbolScopeNode()
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_runtime_call_timer_scope(cg_context):
assert isinstance(cg_context, CodeGenContext)
pattern = "RUNTIME_CALL_TIMER_SCOPE{_1}(${isolate}, {_2});"
_1 = "_DISABLED_BY_DEFAULT"
suffix = ""
if cg_context.attribute_get:
suffix = "_Getter"
elif cg_context.attribute_set:
suffix = "_Setter"
counter = cg_context.member_like.extended_attributes.value_of(
"RuntimeCallStatsCounter")
if counter:
_2 = "k{}{}".format(counter, suffix)
else:
_2 = "\"Blink_{}_{}{}\"".format(
blink_class_name(cg_context.class_like),
cg_context.member_like.identifier, suffix)
node = TextNode(_format(pattern, _1=_1, _2=_2))
node.accumulate(
CodeGenAccumulator.require_include_headers([
"third_party/blink/renderer/platform/bindings/runtime_call_stats.h",
]))
return node
_callback_common_binders = (
bind_blink_api_arguments,
bind_blink_api_call,
bind_callback_local_vars,
bind_return_value,
bind_v8_set_return_value,
)
def make_attribute_get_def(cg_context):
assert isinstance(cg_context, CodeGenContext)
L = LiteralNode
T = TextNode
cg_context = cg_context.make_copy(attribute_get=True)
func_name = name_style.func(cg_context.attribute.identifier,
"AttributeGetCallback")
func_def = FunctionDefinitionNode(
name=L("AttributeGetCallback"),
arg_decls=[L("const v8::FunctionCallbackInfo<v8::Value>& info")],
return_type=L("void"))
name=T(func_name),
arg_decls=[T("const v8::FunctionCallbackInfo<v8::Value>& info")],
return_type=T("void"))
body = func_def.body
body.add_template_var("info", "info")
body.add_template_vars(cg_context.template_bindings())
binders = [
bind_callback_local_vars,
bind_blink_api_arguments,
bind_blink_api_call,
bind_return_value,
bind_v8_set_return_value,
]
for bind in binders:
for bind in _callback_common_binders:
bind(body, cg_context)
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),
T(""),
make_check_receiver(cg_context),
T(""),
T("${v8_set_return_value}"),
])
......@@ -273,29 +465,31 @@ def make_attribute_get_def(cg_context):
def make_operation_def(cg_context):
assert isinstance(cg_context, CodeGenContext)
L = LiteralNode
T = TextNode
func_name = name_style.func(cg_context.operation.identifier,
"OperationCallback")
func_def = FunctionDefinitionNode(
name=L("OperationCallback"),
arg_decls=[L("const v8::FunctionCallbackInfo<v8::Value>& info")],
return_type=L("void"))
name=T(func_name),
arg_decls=[T("const v8::FunctionCallbackInfo<v8::Value>& info")],
return_type=T("void"))
body = func_def.body
body.add_template_var("info", "info")
body.add_template_vars(cg_context.template_bindings())
binders = [
bind_callback_local_vars,
bind_blink_api_arguments,
bind_blink_api_call,
bind_return_value,
bind_v8_set_return_value,
]
for bind in binders:
for bind in _callback_common_binders:
bind(body, cg_context)
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),
T(""),
make_check_receiver(cg_context),
T(""),
T("${v8_set_return_value}"),
])
......@@ -339,17 +533,16 @@ def bind_template_installer_local_vars(code_node, cg_context):
def make_install_interface_template_def(cg_context):
assert isinstance(cg_context, CodeGenContext)
L = LiteralNode
T = TextNode
func_def = FunctionDefinitionNode(
name=L("InstallInterfaceTemplate"),
name=T("InstallInterfaceTemplate"),
arg_decls=[
L("v8::Isolate* isolate"),
L("const DOMWrapperWorld& world"),
L("v8::Local<v8::FunctionTemplate> interface_template"),
T("v8::Isolate* isolate"),
T("const DOMWrapperWorld& world"),
T("v8::Local<v8::FunctionTemplate> interface_template"),
],
return_type=L("void"))
return_type=T("void"))
body = func_def.body
body.add_template_var("isolate", "isolate")
......@@ -385,7 +578,7 @@ def generate_interfaces(web_idl_database, output_dirs):
filename = "v8_example_interface.cc"
filepath = os.path.join(output_dirs['core'], filename)
interface = web_idl_database.find("Node")
interface = web_idl_database.find("CSS")
cg_context = CodeGenContext(interface=interface)
......@@ -413,9 +606,9 @@ def generate_interfaces(web_idl_database, output_dirs):
root_node.extend([
make_copyright_header(),
LiteralNode(""),
TextNode(""),
make_header_include_directives(root_node.accumulator),
LiteralNode(""),
TextNode(""),
enclose_with_namespace(code_node, name_style.namespace("blink")),
])
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment