blob: e4ff7705408128c0bfd4bc9009a3919f1ed4137a [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 .code_node import CodeNode
from .code_node import FunctionDefinitionNode
from .code_node import LiteralNode
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 import UnlikelyExitNode
from .codegen_accumulator import CodeGenAccumulator
from .codegen_context import CodeGenContext
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 enclose_with_namespace
from .codegen_utils import make_copyright_header
from .codegen_utils import make_header_include_directives
from .codegen_utils import write_code_node_to_file
from .mako_renderer import MakoRenderer
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))
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)
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("receiver", "v8::Local<v8::Object> ${receiver} = ${info}.This();"),
S("receiver_context", ("v8::Local<v8::Context> ${receiver_context} = "
"${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 = ("${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)))
code_node.register_code_symbols(local_vars)
code_node.add_template_vars(template_vars)
def bind_return_value(code_node, cg_context):
assert isinstance(code_node, SymbolScopeNode)
assert isinstance(cg_context, CodeGenContext)
def create_definition(symbol_node):
if cg_context.return_type.unwrap().is_void:
text = "${blink_api_call};"
elif cg_context.is_return_by_argument:
pattern = "{_1} ${return_value};\n${blink_api_call};"
_1 = blink_type_info(cg_context.return_type).value_t
text = _format(pattern, _1=_1)
else:
text = "const auto& ${return_value} = ${blink_api_call};"
node = SymbolDefinitionNode(symbol_node, [TextNode(text)])
if cg_context.may_throw_exception:
node.append(
UnlikelyExitNode(
cond=TextNode("${exception_state}.HadException()"),
body=SymbolScopeNode([TextNode("return;")])))
return node
code_node.register_code_symbol(
SymbolNode("return_value", definition_constructor=create_definition))
def bind_v8_set_return_value(code_node, cg_context):
assert isinstance(code_node, SymbolScopeNode)
assert isinstance(cg_context, CodeGenContext)
pattern = "{_1}({_2});"
_1 = "V8SetReturnValue"
_2 = ["${info}", "${return_value}"]
return_type = cg_context.return_type.unwrap(nullable=True, typedef=True)
if return_type.is_void:
# Render a SymbolNode |return_value| discarding the content text, and
# let a symbol definition be added.
pattern = "<% str(return_value) %>"
elif (cg_context.for_world == cg_context.MAIN_WORLD
and return_type.is_interface):
_1 = "V8SetReturnValueForMainWorld"
elif return_type.is_interface:
_2.append("${creation_context_object}")
text = _format(pattern, _1=_1, _2=", ".join(_2))
code_node.add_template_var("v8_set_return_value", TextNode(text))
_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_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]"),
UnlikelyExitNode(
cond=T("!${v8_class}::HasInstance(${receiver}, ${isolate})"),
body=SymbolScopeNode([T("return;")])),
])
if cg_context.return_type.unwrap().is_promise:
return SequenceNode([
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_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 = SymbolScopeNode([
T(use_counter),
T("V8SetReturnValueNull(${info});\n"
"return;"),
])
return SequenceNode([
T("// [CheckSecurity=ReturnValue]"),
UnlikelyExitNode(cond=cond, body=body),
])
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_overloaded_function_name(function_like):
if isinstance(function_like, web_idl.Constructor):
return name_style.func("constructor", "overload",
function_like.overload_index + 1)
else:
return name_style.func(function_like.identifier, "op", "overload",
function_like.overload_index + 1)
def _make_overload_dispatcher_per_arg_size(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.
"""
# 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 = _make_overloaded_function_name(func_like)
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 = SymbolScopeNode([
TextNode("if (" + conditional.to_text() + ") {"),
node,
TextNode("}"),
])
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_type or u.is_dictionary)
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):
cgc = CodeGenContext(
interface=idl_type.unwrap().type_definition_object)
dispatch_if(
_format("{}::HasInstance(${isolate}, {value})", cgc.v8_class))
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(items)
if arg_size > 0:
node = SymbolScopeNode([
T("if (${info}.Length() >= " + str(arg_size) + ") {"),
node,
T("break;") if can_fail else None,
T("}"),
])
did_use_break = did_use_break or can_fail
terms = map(
lambda item: expr_from_exposure(item.function_like.exposure),
items)
conditional = expr_or(terms)
if not conditional.is_always_true:
node = SymbolScopeNode([
T("if (" + conditional.to_text() + ") {"),
node,
T("}"),
])
branches.append(node)
if did_use_break:
branches = SymbolScopeNode([
T("do { // Dummy loop for use of 'break'"),
branches,
T("} while (false);"),
])
# Make the entire branches an indivisible chunk so that SymbolDefinitionNode
# will not be inserted in-between.
branches = LiteralNode(branches)
if not did_use_break and arg_size == 0 and conditional.is_always_true:
return branches
return SequenceNode([
branches,
T(""),
T("${exception_state}.ThrowTypeError"
"(\"Overload resolution failed.\");\n"
"return;"),
])
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 = 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 (!impl->""" + pred + """()) {
v8::Local<v8::Value> v8_value;
if (v8_private_cached_attribute.GetOrUndefined(${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(${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"
"(${receiver}, ${info}.GetReturnValue().Get());")
if "SaveSameObject" in cg_context.member_like.extended_attributes:
return TextNode("// [SaveSameObject]\n"
"v8_private_save_same_object.Set"
"(${receiver}, ${info}.GetReturnValue().Get());")
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
def make_attribute_get_callback_def(cg_context, function_name):
assert isinstance(cg_context, CodeGenContext)
assert isinstance(function_name, str)
T = TextNode
cg_context = cg_context.make_copy(attribute_get=True)
func_def = FunctionDefinitionNode(
name=T(function_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())
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),
make_return_value_cache_return_early(cg_context),
T(""),
make_check_security_of_return_value(cg_context),
T("${v8_set_return_value}"),
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)
return None
def make_operation_function_def(cg_context, function_name):
assert isinstance(cg_context, CodeGenContext)
assert isinstance(function_name, str)
T = TextNode
func_def = FunctionDefinitionNode(
name=T(function_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())
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}"),
])
return func_def
def make_overload_dispatcher_function_def(cg_context, function_name):
assert isinstance(cg_context, CodeGenContext)
assert isinstance(function_name, str)
T = TextNode
func_def = FunctionDefinitionNode(
name=T(function_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())
bind_callback_local_vars(body, cg_context)
body.append(make_overload_dispatcher(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.constructor_group or 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:
node.append(
make_operation_function_def(
cg_context.make_copy(operation=operation),
_make_overloaded_function_name(operation)))
node.append(
make_overload_dispatcher_function_def(cg_context, function_name))
return node
def bind_template_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("instance_template",
("v8::Local<v8::ObjectTemplate> ${instance_template} = "
"${interface_template}->InstanceTemplate();")),
S("prototype_template",
("v8::Local<v8::ObjectTemplate> ${prototype_template} = "
"${interface_template}->PrototypeTemplate();")),
S("signature",
("v8::Local<v8::Signature> ${signature} = "
"v8::Signature::New(${isolate}, ${interface_template});")),
S("wrapper_type_info",
("const WrapperTypeInfo* const ${wrapper_type_info} = "
"${v8_class}::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)))
code_node.register_code_symbols(local_vars)
def make_install_interface_template_def(cg_context):
assert isinstance(cg_context, CodeGenContext)
T = TextNode
func_def = FunctionDefinitionNode(
name=T("InstallInterfaceTemplate"),
arg_decls=[
T("v8::Isolate* isolate"),
T("const DOMWrapperWorld& world"),
T("v8::Local<v8::FunctionTemplate> interface_template"),
],
return_type=T("void"))
body = func_def.body
body.add_template_var("isolate", "isolate")
body.add_template_var("world", "world")
body.add_template_var("interface_template", "interface_template")
body.add_template_vars(cg_context.template_bindings())
binders = [
bind_template_installer_local_vars,
]
for bind in binders:
bind(body, cg_context)
body.extend([
T("V8DOMConfiguration::InitializeDOMInterfaceTemplate("
"${isolate}, ${interface_template}, "
"${wrapper_type_info}->interface_name, ${parent_interface_template}, "
"kV8DefaultWrapperInternalFieldCount);"),
])
if cg_context.class_like.constructor_groups:
body.extend([
T("${interface_template}->SetCallHandler(ConstructorCallback);"),
T("${interface_template}->SetLength("
"${class_like.constructor_groups[0]"
".min_num_of_required_arguments});"),
])
return func_def
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("TestNamespace")
cg_context = CodeGenContext(interface=interface)
root_node = SymbolScopeNode(separator_last="\n")
root_node.set_accumulator(CodeGenAccumulator())
root_node.set_renderer(MakoRenderer())
root_node.accumulator.add_include_headers(
collect_include_headers(interface))
code_node = SequenceNode()
for attribute in interface.attributes:
func_name = name_style.func(attribute.identifier,
"AttributeGetCallback")
code_node.append(
make_attribute_get_callback_def(
cg_context.make_copy(attribute=attribute), func_name))
func_name = name_style.func(attribute.identifier,
"AttributeSetCallback")
code_node.append(
make_attribute_set_callback_def(
cg_context.make_copy(attribute=attribute), func_name))
for constructor_group in interface.constructor_groups:
func_name = name_style.func("ConstructorCallback")
code_node.append(
make_operation_callback_def(
cg_context.make_copy(constructor_group=constructor_group),
func_name))
for operation_group in interface.operation_groups:
func_name = name_style.func(operation_group.identifier,
"OperationCallback")
code_node.append(
make_operation_callback_def(
cg_context.make_copy(operation_group=operation_group),
func_name))
code_node.append(make_install_interface_template_def(cg_context))
root_node.extend([
make_copyright_header(),
TextNode(""),
make_header_include_directives(root_node.accumulator),
TextNode(""),
enclose_with_namespace(code_node, name_style.namespace("blink")),
])
write_code_node_to_file(root_node, filepath)