blob: e09a559c38992503afa2cfea8fe66b04efae9799 [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 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 import WeakDependencyNode
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 CxxSwitchNode
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_and
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 component_export
from .codegen_utils import component_export_header
from .codegen_utils import enclose_with_header_guard
from .codegen_utils import make_copyright_header
from .codegen_utils import make_forward_declarations
from .codegen_utils import make_header_include_directives
from .codegen_utils import write_code_node_to_file
from .mako_renderer import MakoRenderer
from .package_initializer import package_initializer
from .path_manager import PathManager
from .task_queue import TaskQueue
def _is_none_or_str(arg):
return arg is None or isinstance(arg, str)
def backward_compatible_api_func(cg_context):
"""
Returns the Blink function name compatible with the old bindings generator.
"""
assert isinstance(cg_context, CodeGenContext)
name = (cg_context.member_like.code_generator_info.property_implemented_as
or cg_context.member_like.identifier
or cg_context.property_.identifier)
if cg_context.attribute_get:
# modules/webaudio/biquad_filter_node.idl has readonly attribute "Q"
# and somehow it's implemented as "q" in Blink.
if name == "Q":
name = "q"
if cg_context.attribute_set:
tokens = name_style.raw.tokenize(name)
if tokens[0] in ("IDL", "css", "xml"):
tokens[0] = tokens[0].upper()
else:
tokens[0] = tokens[0].capitalize()
tokens.insert(0, "set")
name = "".join(tokens)
if cg_context.indexed_property_getter and not name:
name = "AnonymousIndexedGetter"
if cg_context.indexed_property_setter and not name:
name = "AnonymousIndexedSetter"
if cg_context.named_property_getter and not name:
name = "AnonymousNamedGetter"
if cg_context.named_property_setter and not name:
name = "AnonymousNamedSetter"
if cg_context.named_property_deleter and not name:
name = "AnonymousNamedDeleter"
return name
def callback_function_name(cg_context,
overload_index=None,
for_cross_origin=False,
no_alloc_direct_call=False):
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:
if cg_context.is_named_constructor:
kind = "NamedConstructor"
else:
property_name = ""
kind = "Constructor"
elif cg_context.exposed_construct:
if cg_context.is_named_constructor:
kind = "NamedConstructorProperty"
elif cg_context.legacy_window_alias:
kind = "LegacyWindowAlias"
else:
kind = "ExposedConstruct"
elif cg_context.operation_group:
kind = "Operation"
elif cg_context.stringifier:
kind = "Operation"
if for_cross_origin:
suffix = "CrossOrigin"
elif overload_index is not None:
suffix = "Overload{}".format(overload_index + 1)
elif no_alloc_direct_call:
suffix = "NoAllocDirectCallback"
else:
suffix = "Callback"
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, suffix, world_suffix)
def constant_name(cg_context):
assert isinstance(cg_context, CodeGenContext)
assert cg_context.constant
property_name = cg_context.property_.identifier.lower()
return name_style.constant(property_name)
def custom_function_name(cg_context):
assert isinstance(cg_context, CodeGenContext)
if cg_context.named_property_getter:
return "NamedPropertyGetterCustom"
if cg_context.named_property_setter:
return "NamedPropertySetterCustom"
if cg_context.named_property_deleter:
return "NamedPropertyDeleterCustom"
if cg_context.attribute_get:
suffix = "AttributeGetterCustom"
elif cg_context.attribute_set:
suffix = "AttributeSetterCustom"
elif cg_context.operation_group:
suffix = "MethodCustom"
else:
assert False
return name_style.func(cg_context.property_.identifier, suffix)
# ----------------------------------------------------------------------------
# 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:
real_type = cg_context.attribute.idl_type.unwrap(typedef=True)
if real_type.is_enumeration:
pattern = """\
// https://heycam.github.io/webidl/#dfn-attribute-setter
// step 4.6.1. Let S be ? ToString(V).
const auto&& arg1_value_string =
NativeValueTraits<IDLStringV2>::NativeValue(
${isolate}, ${v8_property_value}, ${exception_state});
if (${exception_state}.HadException())
return;
// step 4.6.2. If S is not one of the enumeration's values, then return
// undefined.
const auto arg1_value_maybe_enum = {enum_type}::Create(arg1_value_string);
if (!arg1_value_maybe_enum) {{
bindings::ReportInvalidEnumSetToAttribute(
${isolate}, arg1_value_string, "{enum_type_name}", ${exception_state});
return; // Return undefined.
}}
const auto ${arg1_value} = arg1_value_maybe_enum.value();
"""
text = _format(pattern,
enum_type=blink_class_name(
real_type.type_definition_object),
enum_type_name=real_type.identifier)
code_node.register_code_symbol(SymbolNode("arg1_value", text))
return
name = "arg1_value"
v8_value = "${v8_property_value}"
code_node.register_code_symbol(
make_v8_to_blink_value(name, v8_value,
cg_context.attribute.idl_type))
return
for argument in cg_context.function_like.arguments:
name = name_style.arg_f("arg{}_{}", argument.index + 1,
argument.identifier)
if argument.is_variadic:
code_node.register_code_symbol(
make_v8_to_blink_value_variadic(name, "${info}",
argument.index,
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=argument,
cg_context=cg_context))
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("blink_property_name",
("const AtomicString& ${blink_property_name} = "
"ToCoreAtomicString(${v8_property_name}.As<v8::String>());")),
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_script_state", ("ScriptState* ${current_script_state} = "
"ScriptState::From(${current_context});")),
S("isolate", "v8::Isolate* ${isolate} = ${info}.GetIsolate();"),
S("non_undefined_argument_length",
("const int ${non_undefined_argument_length} = "
"bindings::NonUndefinedArgumentLength(${info});")),
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_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 = not (
(cg_context.member_like and cg_context.member_like.is_static)
or cg_context.constructor)
# 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)))
# execution_context
pattern = "ExecutionContext* ${execution_context} = {_1};"
_1 = ("${receiver_execution_context}"
if is_receiver_context else "${current_execution_context}")
local_vars.append(S("execution_context", _format(pattern, _1=_1)))
node = S("current_execution_context",
("ExecutionContext* ${current_execution_context} = "
"ExecutionContext::From(${current_context});"))
node.accumulate(
CodeGenAccumulator.require_include_headers([
"third_party/blink/renderer/core/execution_context/execution_context.h"
]))
local_vars.append(node)
node = S("receiver_execution_context",
("ExecutionContext* ${receiver_execution_context} = "
"ExecutionContext::From(${receiver_context});"))
node.accumulate(
CodeGenAccumulator.require_include_headers([
"third_party/blink/renderer/core/execution_context/execution_context.h"
]))
local_vars.append(node)
# execution_context_of_document_tree
pattern = "ExecutionContext* ${execution_context_of_document_tree} = {_1};"
if is_receiver_context:
_1 = "bindings::ExecutionContextFromV8Wrappable(${blink_receiver})"
else:
_1 = "${current_execution_context}"
text = _format(pattern, _1=_1)
local_vars.append(S("execution_context_of_document_tree", text))
# 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_group:
_1 = "ExceptionState::kConstructionContext"
elif cg_context.indexed_property_getter:
_1 = "ExceptionState::kIndexedGetterContext"
elif cg_context.indexed_property_setter:
_1 = "ExceptionState::kIndexedSetterContext"
elif cg_context.named_property_getter:
_1 = "ExceptionState::kNamedGetterContext"
elif cg_context.named_property_setter:
_1 = "ExceptionState::kNamedSetterContext"
elif cg_context.named_property_deleter:
_1 = "ExceptionState::kNamedDeletionContext"
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}"]
if cg_context.is_named_constructor:
_1.append("\"{}\"".format(cg_context.property_.identifier))
else:
_1.append("${class_like_name}")
if (cg_context.property_ and cg_context.property_.identifier
and not cg_context.constructor_group):
_1.append("${property_name}")
_2 = ""
if cg_context.is_return_type_promise_type:
_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}::ToWrappableUnsafe(${v8_receiver});")
else:
text = ("LocalDOMWindow* ${blink_receiver} = To<LocalDOMWindow>("
"${class_name}::ToWrappableUnsafe(${v8_receiver}));")
else:
pattern = ("{_1}* ${blink_receiver} = "
"${class_name}::ToWrappableUnsafe(${v8_receiver});")
_1 = blink_class_name(cg_context.class_like)
text = _format(pattern, _1=_1)
local_vars.append(S("blink_receiver", text))
# v8_property_value
if cg_context.v8_callback_type == CodeGenContext.V8_FUNCTION_CALLBACK:
# In case of V8_ACCESSOR_NAME_SETTER_CALLBACK, |v8_property_value| is
# defined as an argument. In case of V8_FUNCTION_CALLBACK (of IDL
# attribute set function), |info[0]| is the value to be set.
local_vars.append(
S("v8_property_value",
"v8::Local<v8::Value> ${v8_property_value} = ${info}[0];"))
# v8_receiver
if cg_context.v8_callback_type == CodeGenContext.V8_FUNCTION_CALLBACK:
# In case of v8::FunctionCallbackInfo, This() is the receiver object.
local_vars.append(
S("v8_receiver",
"v8::Local<v8::Object> ${v8_receiver} = ${info}.This();"))
else:
# In case of v8::PropertyCallbackInfo, Holder() is the object that has
# the property being processed.
local_vars.append(
S("v8_receiver",
"v8::Local<v8::Object> ${v8_receiver} = ${info}.Holder();"))
# throw_security_error
template_vars["throw_security_error"] = T(
"BindingSecurity::FailedAccessCheckFor("
"${info}.GetIsolate(), "
"${class_name}::GetWrapperTypeInfo(), "
"${info}.Holder());")
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()
branches.accumulate(
CodeGenAccumulator.require_include_headers(
["third_party/blink/renderer/core/keywords.h"]))
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,
overriding_args=None):
assert isinstance(code_node, SymbolScopeNode)
assert isinstance(cg_context, CodeGenContext)
assert num_of_args is None or isinstance(num_of_args, int)
assert (overriding_args is None
or (isinstance(overriding_args, (list, tuple))
and all(isinstance(arg, str) for arg in overriding_args)))
arguments = []
ext_attrs = cg_context.member_like.extended_attributes
values = ext_attrs.values_of("CallWith") + (
ext_attrs.values_of("GetterCallWith") if cg_context.attribute_get else
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 "Document" in values:
arguments.append(
"*bindings::ToDocumentFromExecutionContext(${execution_context})")
if "ThisValue" in values:
arguments.append("ScriptValue(${isolate}, ${v8_receiver})")
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 overriding_args is not None:
arguments.extend(overriding_args)
elif 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 = backward_compatible_api_func(cg_context)
if cg_context.constructor:
if cg_context.is_named_constructor:
func_name = "CreateForJSConstructor"
else:
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, overriding_args=None):
assert isinstance(code_node, SymbolScopeNode)
assert isinstance(cg_context, CodeGenContext)
assert (overriding_args is None
or (isinstance(overriding_args, (list, tuple))
and all(isinstance(arg, str) for arg in overriding_args)))
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)
if overriding_args is None:
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)))
else:
api_calls.append((None,
_make_blink_api_call(
code_node,
cg_context,
overriding_args=overriding_args)))
nodes = []
is_return_type_void = ((not cg_context.return_type
or cg_context.return_type.unwrap().is_void) and
not cg_context.does_override_idl_return_type)
if not (is_return_type_void
or cg_context.does_override_idl_return_type):
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 "ReflectOnly" in cg_context.member_like.extended_attributes:
# [ReflectOnly]
nodes.append(F("auto ${return_value} = {};", api_call))
else:
nodes.append(F("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(
"${non_undefined_argument_length} <= {}",
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_bindings_trace_event(cg_context):
assert isinstance(cg_context, CodeGenContext)
event_name = "{}.{}".format(cg_context.class_like.identifier,
cg_context.property_.identifier)
if cg_context.attribute_get:
event_name = "{}.{}".format(event_name, "get")
elif cg_context.attribute_set:
event_name = "{}.{}".format(event_name, "set")
elif cg_context.constructor_group and not cg_context.is_named_constructor:
event_name = "{}.{}".format(cg_context.class_like.identifier,
"constructor")
return TextNode("BLINK_BINDINGS_TRACE_EVENT(\"{}\");".format(event_name))
def make_check_argument_length(cg_context):
assert isinstance(cg_context, CodeGenContext)
T = TextNode
F = lambda *args, **kwargs: T(_format(*args, **kwargs))
if cg_context.v8_callback_type != CodeGenContext.V8_FUNCTION_CALLBACK:
return None
if cg_context.attribute_get:
num_of_required_args = 0
elif cg_context.attribute_set:
idl_type = cg_context.attribute.idl_type
if not (idl_type.does_include_nullable_or_dict
or idl_type.unwrap().is_any or "LegacyTreatNonObjectAsNull" in
idl_type.unwrap().extended_attributes
or "PutForwards" in cg_context.attribute.extended_attributes
or "Replaceable" in cg_context.attribute.extended_attributes):
# ES undefined in ${v8_property_value} will cause a TypeError
# anyway, so omit the check against the number of arguments.
return None
num_of_required_args = 1
elif cg_context.function_like:
num_of_required_args = (
cg_context.function_like.num_of_required_arguments)
elif isinstance(cg_context.property_, web_idl.OverloadGroup):
num_of_required_args = (
cg_context.property_.min_num_of_required_arguments)
else:
assert False
if num_of_required_args == 0:
return None
return CxxUnlikelyIfNode(
cond=_format("UNLIKELY(${info}.Length() < {})", num_of_required_args),
body=[
F(("${exception_state}.ThrowTypeError("
"ExceptionMessages::NotEnoughArguments"
"({}, ${info}.Length()));"), num_of_required_args),
T("return;"),
])
def make_check_constructor_call(cg_context):
assert isinstance(cg_context, CodeGenContext)
T = TextNode
node = SequenceNode([
CxxUnlikelyIfNode(
cond="!${info}.IsConstructCall()",
body=T("${exception_state}.ThrowTypeError("
"ExceptionMessages::ConstructorCalledAsFunction());\n"
"return;")),
])
if not cg_context.is_named_constructor:
node.append(
CxxLikelyIfNode(
cond=("ConstructorMode::Current(${isolate}) == "
"ConstructorMode::kWrapExistingObject"),
body=T("bindings::V8SetReturnValue(${info}, ${v8_receiver});\n"
"return;")))
node.accumulate(
CodeGenAccumulator.require_include_headers([
"third_party/blink/renderer/platform/bindings/v8_object_constructor.h"
]))
return node
def make_check_receiver(cg_context):
assert isinstance(cg_context, CodeGenContext)
T = TextNode
if cg_context.member_like.is_static:
return None
if (cg_context.attribute and
"LegacyLenientThis" in cg_context.attribute.extended_attributes):
return SequenceNode([
T("// [LegacyLenientThis]"),
CxxUnlikelyIfNode(
cond="!${class_name}::HasInstance(${isolate}, ${v8_receiver})",
body=T("return;")),
])
if cg_context.is_return_type_promise_type:
return SequenceNode([
T("// Promise returning function: "
"Convert a TypeError to a reject promise."),
CxxUnlikelyIfNode(
cond="!${class_name}::HasInstance(${isolate}, ${v8_receiver})",
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.property_.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("bindings::V8SetReturnValue(${info}, nullptr);\n"
"return;"),
]
node = SequenceNode([
T("// [CheckSecurity=ReturnValue]"),
CxxUnlikelyIfNode(cond=cond, body=body),
])
node.accumulate(
CodeGenAccumulator.require_include_headers([
"third_party/blink/renderer/bindings/core/v8/binding_security.h",
"third_party/blink/renderer/core/frame/web_feature.h",
"third_party/blink/renderer/platform/instrumentation/use_counter.h",
]))
return node
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 = ", ${v8_property_value}"
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",
"third_party/blink/renderer/platform/bindings/v8_per_context_data.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),
func_and_type[1].type_definition_object.identifier)
# 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))
# V8 specific optimization: BufferSource = ArrayBufferView or ArrayBuffer
is_typedef_name = lambda t, name: t.is_typedef and t.identifier == name
func_like = find(
lambda t, u: is_typedef_name(t.unwrap(typedef=False), "BufferSource"))
if func_like:
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()")
# V8 specific optimization: ArrayBufferView
func_like = find(lambda t, u: u.is_array_buffer_view)
if func_like:
dispatch_if("{value}->IsArrayBufferView()")
# 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, ...
typed_array_types = ("Int8Array", "Int16Array", "Int32Array", "Uint8Array",
"Uint16Array", "Uint32Array", "Uint8ClampedArray",
"Float32Array", "Float64Array")
for typed_array_type in typed_array_types:
func_like = find(lambda t, u: u.keyword_typename == typed_array_type)
if func_like:
dispatch_if(_format("{value}->Is{}()", typed_array_type))
# 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(
CxxUnlikelyIfNode(cond="${exception_state}.HadException()",
body=TextNode("return;")))
# 12.10. if Type(V) is Object and ...
func_like = find(lambda t, u: u.is_callback_interface or u.is_dictionary or
u.is_record or u.is_object)
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_enumeration),
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
F = lambda *args, **kwargs: T(_format(*args, **kwargs))
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)
# TODO(yukishiino): Runtime-enabled features should be taken into account
# when calculating the max argument size.
max_arg_size = max(map(args_size, items))
arg_count_def = F("const int arg_count = std::min(${info}.Length(), {});",
max_arg_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="arg_count == {}".format(arg_size),
body=[node, T("break;") if can_fail else None])
did_use_break = did_use_break or can_fail
conditional = expr_or(
list(
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)
branches = SequenceNode([
arg_count_def,
branches,
])
if not did_use_break and arg_size == 0 and conditional.is_always_true:
return branches
return SequenceNode([
branches,
EmptyNode(),
make_check_argument_length(cg_context),
T("${exception_state}.ThrowTypeError"
"(\"Overload resolution failed.\");\n"
"return;"),
])
def make_report_coop_access(cg_context):
assert isinstance(cg_context, CodeGenContext)
if cg_context.class_like.identifier != "Window":
return None
ext_attrs = cg_context.member_like.extended_attributes
if "CrossOrigin" not in ext_attrs:
return None
values = ext_attrs.values_of("CrossOrigin")
if (cg_context.attribute_get and not (not values or "Getter" in values)):
return None
elif (cg_context.attribute_set and not ("Setter" in values)):
return None
return TextNode("${blink_receiver}->ReportCoopAccess(${property_name});")
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("
"${current_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_measure_web_feature_constant(cg_context):
assert isinstance(cg_context, CodeGenContext)
target = cg_context.member_like or cg_context.property_
ext_attrs = target.extended_attributes
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 = ext_attrs.value_of("MeasureAs") or ext_attrs.value_of("Measure")
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.property_.identifier),
suffix)
return "WebFeature::{}".format(name)
def make_report_high_entropy(cg_context):
assert isinstance(cg_context, CodeGenContext)
target = cg_context.member_like or cg_context.property_
ext_attrs = target.extended_attributes
if cg_context.attribute_set or "HighEntropy" not in ext_attrs:
return None
assert "Measure" in ext_attrs or "MeasureAs" in ext_attrs, "{}: {}".format(
cg_context.idl_location_and_name,
"[HighEntropy] must be specified with either [Measure] or "
"[MeasureAs].")
if ext_attrs.value_of("HighEntropy") == "Direct":
text = _format(
"// [HighEntropy=Direct]\n"
"Dactyloscoper::RecordDirectSurface("
"${current_execution_context}, {measure_constant}, "
"${return_value});",
measure_constant=_make_measure_web_feature_constant(cg_context))
else:
text = _format(
"// [HighEntropy]\n"
"Dactyloscoper::Record("
"${current_execution_context}, {measure_constant});",
measure_constant=_make_measure_web_feature_constant(cg_context))
node = TextNode(text)
node.accumulate(
CodeGenAccumulator.require_include_headers(
["third_party/blink/renderer/core/frame/dactyloscoper.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):
return None
text = _format(
"// [Measure], [MeasureAs]\n"
"UseCounter::Count(${current_execution_context}, {measure_constant});",
measure_constant=_make_measure_web_feature_constant(cg_context))
node = TextNode(text)
node.accumulate(
CodeGenAccumulator.require_include_headers([
"third_party/blink/renderer/core/frame/web_feature.h",
"third_party/blink/renderer/platform/instrumentation/use_counter.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)) {
return;
}
if (!v8_value->IsUndefined()) {
bindings::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)) {
return;
}
if (!v8_value->IsUndefined()) {
bindings::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, overriding_name=None):
assert isinstance(cg_context, CodeGenContext)
assert _is_none_or_str(overriding_name)
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 and
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), overriding_name
or 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
or cg_context.indexed_property_setter
or cg_context.named_property_setter
or cg_context.named_property_deleter)
if "CEReactions" not in cg_context.member_like.extended_attributes:
return None
nodes = [
TextNode("// [CEReactions]"),
TextNode("CEReactionsScope ce_reactions_scope;"),
]
nodes[-1].accumulate(
CodeGenAccumulator.require_include_headers([
"third_party/blink/renderer/core/html/custom/ce_reactions_scope.h"
]))
# CEReactions scope is not tolerant of V8 exception, so it's necessary to
# invoke custom element reactions before throwing an exception. Thus, put
# an ExceptionState before CEReactions scope.
nodes.insert(0, WeakDependencyNode(dep_syms=["exception_state"]))
return SequenceNode(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(${isolate}, "
"\"${attribute.extended_attributes.value_of(\"PutForwards\")}\""
"), ${v8_property_value}).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}), "
"${v8_property_value}).To(&did_create)) {\n"
" return;\n"
"}"),
])
def make_v8_set_return_value(cg_context):
assert isinstance(cg_context, CodeGenContext)
T = TextNode
F = lambda *args, **kwargs: T(_format(*args, **kwargs))
if cg_context.does_override_idl_return_type:
return T("bindings::V8SetReturnValue(${info}, ${return_value});")
if not cg_context.return_type 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() %>")
operation = cg_context.operation
if operation and (operation.is_setter or operation.is_deleter):
# Blink implementation returns in a type different from the IDL type.
# Namely, IndexedPropertySetterResult, NamedPropertySetterResult, and
# NamedPropertyDeleterResult are returned ignoring the operation's
# return type.
return T("bindings::V8SetReturnValue(${info}, ${return_value});")
return_type = cg_context.return_type
if return_type.is_typedef:
if return_type.identifier in ("EventHandler",
"OnBeforeUnloadEventHandler",
"OnErrorEventHandler"):
return T("bindings::V8SetReturnValue(${info}, ${return_value}, "
"${isolate}, ${blink_receiver});")
# [CheckSecurity=ReturnValue]
#
# The returned object must be wrapped in its own realm instead of the
# receiver object's relevant realm or the current realm.
#
# [CheckSecurity=ReturnValue] is used only for 'contentDocument' attribute
# and 'getSVGDocument' operation of HTML{IFrame,Frame,Object,Embed}Element
# interfaces, and Window.frameElement attribute, so far.
#
# All the interfaces above except for Window support 'contentWindow'
# attribute and that's the global object of the creation context of the
# returned V8 wrapper. Window.frameElement is implemented with [Custom]
# for now and there is no need to support it.
#
# Note that the global object has its own context and there is no need to
# pass the creation context to ToV8.
if (cg_context.member_like.extended_attributes.value_of("CheckSecurity") ==
"ReturnValue"):
return T("""\
// [CheckSecurity=ReturnValue]
bindings::V8SetReturnValue(
${info},
ToV8(${return_value},
ToV8(${blink_receiver}->contentWindow(),
v8::Local<v8::Object>(),
${isolate}).As<v8::Object>(),
${isolate}));\
""")
return_type = return_type.unwrap(typedef=True)
return_type_body = return_type.unwrap()
PRIMITIVE_TYPE_TO_CXX_TYPE = {
"boolean": "bool",
"byte": "int8_t",
"octet": "uint8_t",
"short": "int16_t",
"unsigned short": "uint16_t",
"long": "int32_t",
"unsigned long": "uint32_t",
"long long": "int64_t",
"unsigned long long": "uint64_t",
"float": "float",
"unrestricted float": "float",
"double": "double",
"unrestricted double": "double",
}
cxx_type = PRIMITIVE_TYPE_TO_CXX_TYPE.get(
return_type_body.keyword_typename)
if cxx_type:
return F(
"bindings::V8SetReturnValue(${info}, ${return_value}, "
"bindings::V8ReturnValue::PrimitiveType<{cxx_type}>());",
cxx_type=cxx_type)
# TODO(yukishiino): Remove |return_type_body.is_enumeration| below once
# the migration from String to V8Enum type is done.
if return_type_body.is_string or return_type_body.is_enumeration:
args = ["${info}", "${return_value}", "${isolate}"]
if return_type.is_nullable:
args.append("bindings::V8ReturnValue::kNullable")
else:
args.append("bindings::V8ReturnValue::kNonNullable")
return T("bindings::V8SetReturnValue({});".format(", ".join(args)))
if return_type_body.is_interface:
args = ["${info}", "${return_value}"]
if cg_context.for_world == cg_context.MAIN_WORLD:
args.append("bindings::V8ReturnValue::kMainWorld")
elif cg_context.constructor or cg_context.member_like.is_static:
args.append("${creation_context}")
else:
args.append("${blink_receiver}")
return T("bindings::V8SetReturnValue({});".format(", ".join(args)))
if return_type.is_frozen_array:
return T(
"bindings::V8SetReturnValue("
"${info}, "
"ToV8(${return_value}, ${creation_context_object}, ${isolate}), "
"bindings::V8ReturnValue::kFrozen);")
if return_type.is_promise:
return T("bindings::V8SetReturnValue"
"(${info}, ${return_value}.V8Value());")
return T("bindings::V8SetReturnValue(${info}, "
"ToV8(${return_value}, ${creation_context_object}, ${isolate}));")
def _make_empty_callback_def(cg_context, function_name):
assert isinstance(cg_context, CodeGenContext)
assert isinstance(function_name, str)
if cg_context.v8_callback_type == CodeGenContext.V8_FUNCTION_CALLBACK:
arg_decls = ["const v8::FunctionCallbackInfo<v8::Value>& info"]
arg_names = ["info"]
elif (cg_context.v8_callback_type == CodeGenContext.
V8_ACCESSOR_NAME_GETTER_CALLBACK):
arg_decls = [
"v8::Local<v8::Name> v8_property_name",
"const v8::PropertyCallbackInfo<v8::Value>& info",
]
arg_names = ["v8_property_name", "info"]
elif (cg_context.v8_callback_type == CodeGenContext.
V8_ACCESSOR_NAME_SETTER_CALLBACK):
arg_decls = [
"v8::Local<v8::Name> v8_property_name",
"v8::Local<v8::Value> v8_property_value",
"const v8::PropertyCallbackInfo<void>& info",
]
arg_names = ["v8_property_name", "v8_property_value", "info"]
elif (cg_context.v8_callback_type == CodeGenContext.
V8_GENERIC_NAMED_PROPERTY_SETTER_CALLBACK):
arg_decls = [
"v8::Local<v8::Name> v8_property_name",
"v8::Local<v8::Value> v8_property_value",
"const v8::PropertyCallbackInfo<v8::Value>& info",
]
arg_names = ["v8_property_name", "v8_property_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
for arg_name in arg_names:
body.add_template_var(arg_name, arg_name)
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_check_receiver(cg_context),
EmptyNode(),
make_runtime_call_timer_scope(cg_context),
make_bindings_trace_event(cg_context),
make_report_coop_access(cg_context),
make_report_deprecate_as(cg_context),
make_report_measure_as(cg_context),
make_log_activity(cg_context),
EmptyNode(),
])
if "Getter" in cg_context.property_.extended_attributes.values_of(
"Custom"):
text = _format("${class_name}::{}(${info});",
custom_function_name(cg_context))
body.append(TextNode(text))
return func_def
body.extend([
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_report_high_entropy(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 ("LegacyLenientSetter", "PutForwards",
"Replaceable")):
return None
func_def = _make_empty_callback_def(cg_context, function_name)
body = func_def.body
if "LegacyLenientSetter" in ext_attrs:
body.append(TextNode("// [LegacyLenientSetter]"))
return func_def
body.extend([
make_check_receiver(cg_context),
EmptyNode(),
make_runtime_call_timer_scope(cg_context),
make_bindings_trace_event(cg_context),
make_report_deprecate_as(cg_context),
make_report_measure_as(cg_context),
make_log_activity(cg_context),
EmptyNode(),
])
if "Setter" in cg_context.property_.extended_attributes.values_of(
"Custom"):
text = _format("${class_name}::{}(${v8_property_value}, ${info});",
custom_function_name(cg_context))
body.append(TextNode(text))
return func_def
# Binary size reduction hack
# 1. Drop the check of argument length although this is a violation of
# Web IDL.
# 2. Leverage the nature of [LegacyTreatNonObjectAsNull] (ES to IDL
# conversion never fails).
if (cg_context.attribute.idl_type.is_typedef
and (cg_context.attribute.idl_type.identifier in (
"EventHandler", "OnBeforeUnloadEventHandler",
"OnErrorEventHandler"))):
body.extend([
TextNode("""\
EventListener* event_handler = JSEventHandler::CreateOrNull(
${v8_property_value},
JSEventHandler::HandlerType::k${attribute.idl_type.identifier});\
"""),
])
code_generator_info = cg_context.attribute.code_generator_info
func_name = name_style.api_func("set", cg_context.attribute.identifier)
if code_generator_info.defined_in_partial:
class_name = (code_generator_info.receiver_implemented_as
or name_style.class_(
cg_context.attribute.owner_mixin.identifier))
text = _format(
"{class_name}::{func_name}"
"(*${blink_receiver}, event_handler);",
class_name=class_name,
func_name=func_name)
else:
text = _format("${blink_receiver}->{func_name}(event_handler);",
func_name=func_name)
body.append(TextNode(text))
return func_def
# Binary size reduction hack
# When the following conditions are met, the implementation is shared.
# 1. The attribute is annotated with [CEReactions, Reflect] and not
# annotated with other extended attributes having side effect.
# 2. The interface is implementing Element.
def optimize_element_cereactions_reflect():
has_cereactions = False
has_reflect = False
for key in ext_attrs.keys():
if key == "CEReactions":
has_cereactions = True
elif key == "Reflect":
has_reflect = True
elif key in ("Affects", "DeprecateAs", "Exposed", "LogActivity",
"LogAllWorlds", "Measure", "MeasureAs",
"ReflectEmpty", "ReflectInvalid", "ReflectMissing",
"ReflectOnly", "RuntimeCallStatsCounter",
"RuntimeEnabled", "SecureContext", "URL",
"Unscopable"):
pass
else:
return None
if not (has_cereactions and has_reflect):
return None
if not cg_context.interface.does_implement("Element"):
return None
content_attribute = _make_reflect_content_attribute_key(
body, cg_context)
idl_type = cg_context.attribute.idl_type.unwrap(typedef=True)
if idl_type.is_boolean:
func_name = "PerformAttributeSetCEReactionsReflectTypeBoolean"
elif idl_type.type_name == "String":
func_name = "PerformAttributeSetCEReactionsReflectTypeString"
elif idl_type.type_name == "StringTreatNullAs":
func_name = ("PerformAttributeSetCEReactionsReflect"
"TypeStringLegacyNullToEmptyString")
elif idl_type.type_name == "StringOrNull":
func_name = "PerformAttributeSetCEReactionsReflectTypeStringOrNull"
else:
return None
text = _format(
"bindings::{func_name}"
"(${info}, {content_attribute}, "
"${class_like_name}, ${property_name});",
func_name=func_name,
content_attribute=content_attribute)
return TextNode(text)
node = optimize_element_cereactions_reflect()
if node:
body.append(node)
return func_def
body.extend([
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)
body = func_def.body
v8_set_return_value = _format(
"bindings::V8SetReturnValue(${info}, ${class_name}::Constant::{});",
constant_name(cg_context))
body.extend([
make_runtime_call_timer_scope(cg_context),
make_bindings_trace_event(cg_context),
logging_nodes,
EmptyNode(),
TextNode(v8_set_return_value),
make_report_high_entropy(cg_context),
])
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_operation_entry(cg_context))
body.append(EmptyNode())
body.append(make_cooperative_scheduling_safepoint(cg_context))
body.append(EmptyNode())
if cg_context.constructor_group:
body.append(make_constructor_entry(cg_context))
body.append(EmptyNode())
body.append(make_overload_dispatcher(cg_context))
return func_def
def make_constructor_entry(cg_context):
assert isinstance(cg_context, CodeGenContext)
return SequenceNode([
make_runtime_call_timer_scope(cg_context),
make_bindings_trace_event(cg_context),
EmptyNode(),
make_check_constructor_call(cg_context),
])
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
if len(cg_context.constructor_group) == 1:
body.append(make_constructor_entry(cg_context))
body.append(EmptyNode())
body.extend([
make_report_deprecate_as(cg_context),
make_report_measure_as(cg_context),
make_log_activity(cg_context),
EmptyNode(),
make_check_argument_length(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))
body.accumulate(
CodeGenAccumulator.require_include_headers([
"third_party/blink/renderer/bindings/core/v8/v8_html_constructor.h"
]))
else:
body.append(
T("v8::Local<v8::Object> v8_wrapper = "
"${return_value}->AssociateWithWrapper(${isolate}, "
"${class_name}::GetWrapperTypeInfo(), ${v8_receiver});"))
body.append(T("bindings::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)
body = func_def.body
if (cg_context.exposed_construct.is_interface
or cg_context.exposed_construct.is_callback_interface):
tag = "bindings::V8ReturnValue::kInterfaceObject"
elif cg_context.exposed_construct.is_namespace:
tag = "bindings::V8ReturnValue::kNamespaceObject"
else:
assert False
v8_set_return_value = _format(
"bindings::V8SetReturnValue"
"(${info}, {bridge}::GetWrapperTypeInfo(), {tag});",
bridge=v8_bridge_class_name(cg_context.exposed_construct),
tag=tag)
body.extend([
make_runtime_call_timer_scope(cg_context),
make_bindings_trace_event(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_named_constructor_property_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_bindings_trace_event(cg_context),
make_report_deprecate_as(cg_context),
make_report_measure_as(cg_context),
make_log_activity(cg_context),
EmptyNode(),
])
constructor_group = cg_context.exposed_construct
assert isinstance(constructor_group, web_idl.ConstructorGroup)
assert isinstance(constructor_group.owner, web_idl.Interface)
named_ctor_v8_bridge = v8_bridge_class_name(constructor_group.owner)
cgc = CodeGenContext(
interface=constructor_group.owner,
constructor_group=constructor_group,
is_named_constructor=True,
class_name=named_ctor_v8_bridge)
named_ctor_name = callback_function_name(cgc)
named_ctor_def = make_constructor_callback_def(cgc, named_ctor_name)
return_value_cache_return_early = """\
static const V8PrivateProperty::SymbolKey kPrivatePropertyNamedConstructor;
auto&& v8_private_named_constructor =
V8PrivateProperty::GetSymbol(${isolate}, kPrivatePropertyNamedConstructor);
v8::Local<v8::Value> v8_named_constructor;
if (!v8_private_named_constructor.GetOrUndefined(${v8_receiver})
.ToLocal(&v8_named_constructor)) {
return;
}
if (!v8_named_constructor->IsUndefined()) {
bindings::V8SetReturnValue(${info}, v8_named_constructor);
return;
}
"""
pattern = """\
v8::Local<v8::Value> v8_value;
if (!bindings::CreateNamedConstructorFunction(
${script_state},
{callback},
"{func_name}",
{func_length},
{v8_bridge}::GetWrapperTypeInfo())
.ToLocal(&v8_value)) {
return;
}
bindings::V8SetReturnValue(${info}, v8_value);
"""
create_named_constructor_function = _format(
pattern,
callback=named_ctor_name,
func_name=constructor_group.identifier,
func_length=constructor_group.min_num_of_required_arguments,
v8_bridge=named_ctor_v8_bridge)
return_value_cache_update_value = """\
v8_private_named_constructor.Set(${v8_receiver}, v8_value);
"""
body.extend([
TextNode(return_value_cache_return_early),
TextNode(create_named_constructor_function),
TextNode(return_value_cache_update_value),
])
return SequenceNode([named_ctor_def, EmptyNode(), func_def])
def make_no_alloc_direct_call_callback_def(cg_context, function_name):
assert isinstance(cg_context, CodeGenContext)
assert isinstance(function_name, str)
assert cg_context.operation_group and len(cg_context.operation_group) == 1
func_like = cg_context.operation_group[0]
return_type = ("void" if func_like.return_type.is_void else
blink_type_info(func_like.return_type).value_t)
arg_type_and_names = [(blink_type_info(arg.idl_type).value_t,
name_style.arg_f("arg{}_{}", index + 1,
arg.identifier))
for index, arg in enumerate(func_like.arguments)]
arg_decls = ["v8::ApiObject arg0_receiver"] + [
"{} {}".format(arg_type, arg_name)
for arg_type, arg_name in arg_type_and_names
]
arg_decls.append("v8::FastApiCallbackOptions& arg_callback_options")
func_def = CxxFuncDefNode(name=function_name,
arg_decls=arg_decls,
return_type=return_type)
body = func_def.body
pattern = """\
ThreadState::NoAllocationScope no_alloc_scope(ThreadState::Current());
v8::Object* v8_receiver = reinterpret_cast<v8::Object*>(&arg0_receiver);
v8::Isolate* isolate = v8_receiver->GetIsolate();
v8::Isolate::DisallowJavascriptExecutionScope no_js_exec_scope(
isolate,
v8::Isolate::DisallowJavascriptExecutionScope::CRASH_ON_FAILURE);
{blink_class}* blink_receiver =
ToScriptWrappable(v8_receiver)->ToImpl<{blink_class}>();
return blink_receiver->{member_func}({blink_arguments}, arg_callback_options);\
"""
blink_class = blink_class_name(cg_context.interface)
member_func = backward_compatible_api_func(cg_context)
blink_arguments = ", ".join(
[arg_name for arg_type, arg_name in arg_type_and_names])
body.append(
TextNode(
_format(pattern,
blink_class=blink_class,
member_func=member_func,
blink_arguments=blink_arguments)))
return func_def
def make_operation_entry(cg_context):
assert isinstance(cg_context, CodeGenContext)
return SequenceNode([
make_runtime_call_timer_scope(cg_context),
make_bindings_trace_event(cg_context),
])
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
if not cg_context.operation_group or len(cg_context.operation_group) == 1:
body.append(make_operation_entry(cg_context))
body.append(EmptyNode())
body.extend([
make_check_receiver(cg_context),
EmptyNode(),
make_report_coop_access(cg_context),
make_report_deprecate_as(cg_context),
make_report_measure_as(cg_context),
make_log_activity(cg_context),
EmptyNode(),
])
if "Custom" in cg_context.property_.extended_attributes:
text = _format("${class_name}::{}(${info});",
custom_function_name(cg_context))
body.append(TextNode(text))
return func_def
body.extend([
make_check_argument_length(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),
make_report_high_entropy(cg_context),
])
return func_def
def make_operation_callback_def(cg_context,
function_name,
no_alloc_direct_callback_name=None):
assert isinstance(cg_context, CodeGenContext)
assert isinstance(function_name, str)
operation_group = cg_context.operation_group
assert (not ("Custom" in operation_group.extended_attributes)
or len(operation_group) == 1)
assert (not ("NoAllocDirectCall" in operation_group.extended_attributes)
or len(operation_group) == 1)
if "NoAllocDirectCall" in operation_group.extended_attributes:
return ListNode([
make_operation_function_def(
cg_context.make_copy(operation=operation_group[0]),
function_name),
EmptyNode(),
make_no_alloc_direct_call_callback_def(
cg_context.make_copy(operation=operation_group[0]),
no_alloc_direct_callback_name),
])
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
def make_stringifier_callback_def(cg_context, function_name):
assert isinstance(cg_context, CodeGenContext)
assert isinstance(function_name, str)
if cg_context.stringifier.attribute:
return make_attribute_get_callback_def(
cg_context.make_copy(
attribute=cg_context.stringifier.attribute,
attribute_get=True), function_name)
elif cg_context.stringifier.operation:
return make_operation_function_def(
cg_context.make_copy(operation=cg_context.stringifier.operation),
function_name)
assert False
# ----------------------------------------------------------------------------
# Callback functions of indexed and named interceptors
# ----------------------------------------------------------------------------
def _make_interceptor_callback(cg_context, function_name, arg_decls, arg_names,
class_name, runtime_call_timer_name):
assert isinstance(cg_context, CodeGenContext)
assert isinstance(function_name, str)
assert isinstance(arg_decls, (list, tuple))
assert all(isinstance(arg_decl, str) for arg_decl in arg_decls)
assert isinstance(arg_names, (list, tuple))
assert all(isinstance(arg_name, str) for arg_name in arg_names)
assert _is_none_or_str(class_name)
assert isinstance(runtime_call_timer_name, str)
func_decl = CxxFuncDeclNode(
name=function_name,
arg_decls=arg_decls,
return_type="void",
static=True)
func_def = _make_interceptor_callback_def(cg_context, function_name,
arg_decls, arg_names, class_name,
runtime_call_timer_name)
return func_decl, func_def
def _make_interceptor_callback_def(cg_context, function_name, arg_decls,
arg_names, class_name,
runtime_call_timer_name):
assert isinstance(cg_context, CodeGenContext)
assert isinstance(function_name, str)
assert isinstance(arg_decls, (list, tuple))
assert all(isinstance(arg_decl, str) for arg_decl in arg_decls)
assert isinstance(arg_names, (list, tuple))
assert all(isinstance(arg_name, str) for arg_name in arg_names)
assert _is_none_or_str(class_name)
assert isinstance(runtime_call_timer_name, str)
func_def = CxxFuncDefNode(
name=function_name,
arg_decls=arg_decls,
return_type="void",
class_name=class_name)
func_def.set_base_template_vars(cg_context.template_bindings())
body = func_def.body
for arg_name in arg_names:
body.add_template_var(arg_name, arg_name)
bind_callback_local_vars(body, cg_context)
body.extend([
make_runtime_call_timer_scope(cg_context, runtime_call_timer_name),
EmptyNode(),
])
return func_def
def make_indexed_property_getter_callback(cg_context, function_name):
assert isinstance(cg_context, CodeGenContext)
assert isinstance(function_name, str)
arg_decls = [
"uint32_t index",
"const v8::PropertyCallbackInfo<v8::Value>& info",
]
arg_names = ["index", "info"]
func_decl, func_def = _make_interceptor_callback(
cg_context, function_name, arg_decls, arg_names, cg_context.class_name,
"IndexedPropertyGetter")
body = func_def.body
if not cg_context.interface.indexed_and_named_properties.indexed_getter:
body.append(
TextNode("""\
v8::Local<v8::String> property_name =
V8AtomicString(${isolate}, AtomicString::Number(${index}));
${class_name}::NamedPropertyGetterCallback(property_name, ${info});
"""))
return func_decl, func_def
bind_return_value(body, cg_context, overriding_args=["${index}"])
body.extend([
TextNode("""\
// LegacyPlatformObjectGetOwnProperty
// https://heycam.github.io/webidl/#LegacyPlatformObjectGetOwnProperty
// step 1.2. If index is a supported property index, then:
// step 3. Return OrdinaryGetOwnProperty(O, P).
if (${index} >= ${blink_receiver}->length())
return; // Do not intercept. Fallback to OrdinaryGetOwnProperty.
"""),
make_v8_set_return_value(cg_context),
])
return func_decl, func_def
def make_indexed_property_setter_callback(cg_context, function_name):
assert isinstance(cg_context, CodeGenContext)
assert isinstance(function_name, str)
arg_decls = [
"uint32_t index",
"v8::Local<v8::Value> v8_property_value",
"const v8::PropertyCallbackInfo<v8::Value>& info",
]
arg_names = ["index", "v8_property_value", "info"]
func_decl, func_def = _make_interceptor_callback(
cg_context, function_name, arg_decls, arg_names, cg_context.class_name,
"IndexedPropertySetter")
body = func_def.body
if not cg_context.interface.indexed_and_named_properties.indexed_getter:
body.append(
TextNode("""\
v8::Local<v8::String> property_name =
V8AtomicString(${isolate}, AtomicString::Number(${index}));
${class_name}::NamedPropertySetterCallback(
property_name, ${v8_property_value}, ${info});
"""))
return func_decl, func_def
if not cg_context.indexed_property_setter:
body.append(
TextNode("""\
// 3.9.2. [[Set]]
// https://heycam.github.io/webidl/#legacy-platform-object-set
// OrdinarySetWithOwnDescriptor will end up calling DefineOwnProperty,
// which will fail when the receiver object is this legacy platform
// object.
bindings::V8SetReturnValue(${info}, nullptr);
if (${info}.ShouldThrowOnError()) {
ExceptionState exception_state(${info}.GetIsolate(),
ExceptionState::kIndexedSetterContext,
"${interface.identifier}");
exception_state.ThrowTypeError(
"Indexed property setter is not supported.");
}
"""))
return func_decl, func_def
bind_return_value(
body,
cg_context,
overriding_args=["${index}", "${blink_property_value}"])
body.register_code_symbol(
make_v8_to_blink_value(
"blink_property_value",
"${v8_property_value}",
cg_context.indexed_property_setter.arguments[1].idl_type,
argument=cg_context.indexed_property_setter.arguments[1]))
body.extend([
TextNode("""\
// 3.9.2. [[Set]]
// https://heycam.github.io/webidl/#legacy-platform-object-set
// step 1. If O and Receiver are the same object, then:\
"""),
CxxLikelyIfNode(cond="${info}.Holder() == ${info}.This()",
body=[
TextNode("""\
// step 1.1.1. Invoke the indexed property setter with P and V.\
"""),
make_steps_of_ce_reactions(cg_context),
EmptyNode(),
make_v8_set_return_value(cg_context),
TextNode("""\
bindings::V8SetReturnValue(${info}, nullptr);
return;"""),
]),
EmptyNode(),
TextNode("""\
// Do not intercept. Fallback to OrdinarySetWithOwnDescriptor.
"""),
])
return func_decl, func_def
def make_indexed_property_deleter_callback(cg_context, function_name):
assert isinstance(cg_context, CodeGenContext)
assert isinstance(function_name, str)
arg_decls = [
"uint32_t index",
"const v8::PropertyCallbackInfo<v8::Boolean>& info",
]
arg_names = ["index", "info"]
func_decl, func_def = _make_interceptor_callback(
cg_context, function_name, arg_decls, arg_names, cg_context.class_name,
"IndexedPropertyDeleter")
body = func_def.body
if not cg_context.interface.indexed_and_named_properties.indexed_getter:
body.append(
TextNode("""\
v8::Local<v8::String> property_name =
V8AtomicString(${isolate}, AtomicString::Number(${index}));
${class_name}::NamedPropertyDeleterCallback(property_name, ${info});
"""))
return func_decl, func_def
body.append(
TextNode("""\
// 3.9.4. [[Delete]]
// https://heycam.github.io/webidl/#legacy-platform-object-delete
// step 1.2. If index is not a supported property index, then return true.
// step 1.3. Return false.
const bool is_supported = ${index} < ${blink_receiver}->length();
bindings::V8SetReturnValue(${info}, !is_supported);
if (is_supported and ${info}.ShouldThrowOnError()) {
ExceptionState exception_state(${info}.GetIsolate(),
ExceptionState::kIndexedDeletionContext,
"${interface.identifier}");
exception_state.ThrowTypeError("Index property deleter is not supported.");
}
"""))
return func_decl, func_def
def make_indexed_property_definer_callback(cg_context, function_name):
assert isinstance(cg_context, CodeGenContext)
assert isinstance(function_name, str)
arg_decls = [
"uint32_t index",
"const v8::PropertyDescriptor& v8_property_desc",
"const v8::PropertyCallbackInfo<v8::Value>& info",
]
arg_names = ["index", "v8_property_desc", "info"]
func_decl, func_def = _make_interceptor_callback(
cg_context, function_name, arg_decls, arg_names, cg_context.class_name,
"IndexedPropertyDefiner")
body = func_def.body
if not cg_context.interface.indexed_and_named_properties.indexed_getter:
body.append(
TextNode("""\
v8::Local<v8::String> property_name =
V8AtomicString(${isolate}, AtomicString::Number(${index}));
${class_name}::NamedPropertyDefinerCallback(
property_name, ${v8_property_desc}, ${info});
"""))
return func_decl, func_def
body.append(
TextNode("""\
// 3.9.3. [[DefineOwnProperty]]
// https://heycam.github.io/webidl/#legacy-platform-object-defineownproperty
// step 1.1. If the result of calling IsDataDescriptor(Desc) is false, then
// return false.
if (v8_property_desc.has_get() || v8_property_desc.has_set()) {
bindings::V8SetReturnValue(${info}, nullptr);
if (${info}.ShouldThrowOnError()) {
ExceptionState exception_state(${info}.GetIsolate(),
ExceptionState::kIndexedSetterContext,
"${interface.identifier}");
exception_state.ThrowTypeError("Accessor properties are not allowed.");
}
return;
}
"""))
if not cg_context.interface.indexed_and_named_properties.indexed_setter:
body.append(
TextNode("""\
// step 1.2. If O does not implement an interface with an indexed property
// setter, then return false.
bindings::V8SetReturnValue(${info}, nullptr);
if (${info}.ShouldThrowOnError()) {
ExceptionState exception_state(${info}.GetIsolate(),
ExceptionState::kIndexedSetterContext,
"${interface.identifier}");
exception_state.ThrowTypeError("Index property setter is not supported.");
}
"""))
else:
body.append(
TextNode("""\
// step 1.3. Invoke the indexed property setter with P and Desc.[[Value]].
${class_name}::IndexedPropertySetterCallback(
${index}, ${v8_property_desc}.value(), ${info});
"""))
return func_decl, func_def
def make_indexed_property_descriptor_callback(cg_context, function_name):
assert isinstance(cg_context, CodeGenContext)
assert isinstance(function_name, str)
arg_decls = [
"uint32_t index",
"const v8::PropertyCallbackInfo<v8::Value>& info",
]
arg_names = ["index", "info"]
func_decl, func_def = _make_interceptor_callback(
cg_context, function_name, arg_decls, arg_names, cg_context.class_name,
"IndexedPropertyDescriptor")
body = func_def.body
if not cg_context.interface.indexed_and_named_properties.indexed_getter:
body.append(
TextNode("""\
v8::Local<v8::String> property_name =
V8AtomicString(${isolate}, AtomicString::Number(${index}));
${class_name}::NamedPropertyDescriptorCallback(property_name, ${info});
"""))
return func_decl, func_def
pattern = """\
// LegacyPlatformObjectGetOwnProperty
// https://heycam.github.io/webidl/#LegacyPlatformObjectGetOwnProperty
// step 1.2.3. If operation was defined without an identifier, then set
// value to the result of performing the steps listed in the interface
// description to determine the value of an indexed property with index
// as the index.
// step 1.2.4. Otherwise, operation was defined with an identifier. Set
// value to the result of performing the steps listed in the description
// of operation with index as the only argument value.
${class_name}::IndexedPropertyGetterCallback(${index}, ${info});
v8::Local<v8::Value> v8_value = ${info}.GetReturnValue().Get();
// step 1.2. If index is a supported property index, then:
// step 3. Return OrdinaryGetOwnProperty(O, P).
if (v8_value->IsUndefined())
return; // Do not intercept. Fallback to OrdinaryGetOwnProperty.
// step 1.2.6. Set desc.[[Value]] to the result of converting value to an
// ECMAScript value.
// step 1.2.7. If O implements an interface with an indexed property setter,
// then set desc.[[Writable]] to true, otherwise set it to false.
// step 1.2.8. Set desc.[[Enumerable]] and desc.[[Configurable]] to true.
v8::PropertyDescriptor desc(v8_value, /*writable=*/{cxx_writable});
desc.set_enumerable(true);
desc.set_configurable(true);
bindings::V8SetReturnValue(${info}, desc);"""
writable = bool(
cg_context.interface.indexed_and_named_properties.indexed_setter)
cxx_writable = "true" if writable else "false"
body.append(TextNode(_format(pattern, cxx_writable=cxx_writable)))
return func_decl, func_def
def make_indexed_property_enumerator_callback(cg_context, function_name):
assert isinstance(cg_context, CodeGenContext)
assert isinstance(function_name, str)
if not cg_context.interface.indexed_and_named_properties.indexed_getter:
return None, None
arg_decls = ["const v8::PropertyCallbackInfo<v8::Array>& info"]
arg_names = ["info"]
func_decl, func_def = _make_interceptor_callback(
cg_context, function_name, arg_decls, arg_names, cg_context.class_name,
"IndexedPropertyEnumerator")
body = func_def.body
body.append(
TextNode("""\
// 3.9.6. [[OwnPropertyKeys]]
// https://heycam.github.io/webidl/#legacy-platform-object-ownpropertykeys
// step 2. If O supports indexed properties, then for each index of O's
// supported property indices, in ascending numerical order, append
// ! ToString(index) to keys.
uint32_t length = ${blink_receiver}->length();
v8::Local<v8::Array> array =
bindings::EnumerateIndexedProperties(${isolate}, length);
bindings::V8SetReturnValue(${info}, array);
"""))
body.accumulate(
CodeGenAccumulator.require_include_headers([
"third_party/blink/renderer/bindings/core/v8/generated_code_helper.h"
]))
return func_decl, func_def
def make_named_property_getter_callback(cg_context, function_name):
assert isinstance(cg_context, CodeGenContext)
assert isinstance(function_name, str)
arg_decls = [
"v8::Local<v8::Name> v8_property_name",
"const v8::PropertyCallbackInfo<v8::Value>& info",
]
arg_names = ["v8_property_name", "info"]
func_decl, func_def = _make_interceptor_callback(
cg_context, function_name, arg_decls, arg_names, cg_context.class_name,
"NamedPropertyGetter")
body = func_def.body
bind_return_value(
body, cg_context, overriding_args=["${blink_property_name}"])
if "Custom" in cg_context.named_property_getter.extended_attributes:
text = _format("${class_name}::{}(${blink_property_name}, ${info});",
custom_function_name(cg_context))
body.append(TextNode(text))
return func_decl, func_def
# The named property getter's implementation of Blink is not designed to
# represent the property existence, and we have to determine the property
# existence by heuristics.
type = cg_context.return_type.unwrap()
if type.is_any or type.is_object:
not_found_expr = "${return_value}.IsEmpty()"
elif type.is_string:
not_found_expr = "${return_value}.IsNull()"
elif type.is_interface:
not_found_expr = "!${return_value}"
elif type.is_union:
not_found_expr = "${return_value}.IsNull()"
else:
assert False
body.extend([
TextNode("""\
// LegacyPlatformObjectGetOwnProperty
// https://heycam.github.io/webidl/#LegacyPlatformObjectGetOwnProperty
// step 2.1. If the result of running the named property visibility
// algorithm with property name P and object O is true, then:\
"""),
CxxUnlikelyIfNode(
cond=not_found_expr,
body=[
TextNode("// step 3. Return OrdinaryGetOwnProperty(O, P)."),
TextNode("return; // Do not intercept."),
]),
TextNode("""\
// step 2.1.3. If operation was defined without an identifier, then set
// value to the result of performing the steps listed in the interface
// description to determine the value of a named property with P as the
// name.
// step 2.1.4. Otherwise, operation was defined with an identifier. Set
// value to the result of performing the steps listed in the description
// of operation with P as the only argument value.\
"""),
make_v8_set_return_value(cg_context),
])
return func_decl, func_def
def make_named_property_setter_callback(cg_context, function_name):
assert isinstance(cg_context, CodeGenContext)
assert isinstance(function_name, str)
arg_decls = [
"v8::Local<v8::Name> v8_property_name",
"v8::Local<v8::Value> v8_property_value",
"const v8::PropertyCallbackInfo<v8::Value>& info",
]
arg_names = ["v8_property_name", "v8_property_value", "info"]
func_decl, func_def = _make_interceptor_callback(
cg_context, function_name, arg_decls, arg_names, cg_context.class_name,
"NamedPropertySetter")
body = func_def.body
if not cg_context.named_property_setter:
body.append(
TextNode("""\
// 3.9.2. [[Set]]
// https://heycam.github.io/webidl/#legacy-platform-object-set
// step 3. Perform ? OrdinarySetWithOwnDescriptor(O, P, V, Receiver, ownDesc).\
"""))
if ("LegacyOverrideBuiltIns" in
cg_context.interface.extended_attributes):
body.append(
TextNode("""\
// [LegacyOverrideBuiltIns]
if (${info}.Holder()->GetRealNamedPropertyAttributesInPrototypeChain(
${current_context}, ${v8_property_name}).IsJust()) {
return; // Fallback to the existing property.
}
"""))
body.append(
TextNode("""\
${class_name}::NamedPropertyGetterCallback(${v8_property_name}, ${info});
const bool is_creating = ${info}.GetReturnValue().Get()->IsUndefined();
if (!is_creating) {
bindings::V8SetReturnValue(${info}, nullptr);
if (${info}.ShouldThrowOnError()) {
ExceptionState exception_state(${info}.GetIsolate(),
ExceptionState::kNamedSetterContext,
"${interface.identifier}");
exception_state.ThrowTypeError(
"Named property setter is not supported.");
}
return;
}
// Do not intercept. Fallback and let it define a new own property.
"""))
return func_decl, func_def
bind_return_value(
body,
cg_context,
overriding_args=["${blink_property_name}", "${blink_property_value}"])
body.register_code_symbol(
make_v8_to_blink_value(
"blink_property_value",
"${v8_property_value}",
cg_context.named_property_setter.arguments[1].idl_type,
argument=cg_context.named_property_setter.arguments[1]))
if "Custom" in cg_context.named_property_setter.extended_attributes:
text = _format(
"${class_name}::{}"
"(${blink_property_name}, ${v8_property_value}, ${info});",
custom_function_name(cg_context))
body.append(TextNode(text))
return func_decl, func_def
body.extend([
TextNode("""\
// 3.9.2. [[Set]]
// https://heycam.github.io/webidl/#legacy-platform-object-set
// step 1. If O and Receiver are the same object, then:\
"""),
CxxLikelyIfNode(cond="${info}.Holder() == ${info}.This()",
body=[
TextNode("""\
// step 1.2.1. Invoke the named property setter with P and V.\
"""),
make_steps_of_ce_reactions(cg_context),
EmptyNode(),
make_v8_set_return_value(cg_context),
TextNode("""\
% if interface.identifier == "CSSStyleDeclaration":
// CSSStyleDeclaration is abusing named properties.
// Do not intercept if the property is not found.
% else:
bindings::V8SetReturnValue(${info}, nullptr);
% endif
return;"""),
]),
EmptyNode(),
TextNode("""\
// Do not intercept. Fallback to OrdinarySetWithOwnDescriptor.
"""),
])
return func_decl, func_def
def make_named_property_deleter_callback(cg_context, function_name):
assert isinstance(cg_context, CodeGenContext)
assert isinstance(function_name, str)
arg_decls = [
"v8::Local<v8::Name> v8_property_name",
"const v8::PropertyCallbackInfo<v8::Boolean>& info",
]
arg_names = ["v8_property_name", "info"]
func_decl, func_def = _make_interceptor_callback(
cg_context, function_name, arg_decls, arg_names, cg_context.class_name,
"NamedPropertyDeleter")
body = func_def.body
props = cg_context.interface.indexed_and_named_properties
if (not cg_context.named_property_deleter
and "NotEnumerable" in props.named_getter.extended_attributes):
body.append(
TextNode("""\
// 3.9.4. [[Delete]]
// https://heycam.github.io/webidl/#legacy-platform-object-delete
// step 2. If O supports named properties, O does not implement an interface
// with the [Global] extended attribute and the result of calling the
// named property visibility algorithm with property name P and object O
// is true, then:
//
// There is no easy way to determine whether the named property is visible
// or not. Just do not intercept and fallback to the default behavior.
"""))
return func_decl, func_def
if not cg_context.named_property_deleter:
body.append(
TextNode("""\
// 3.9.4. [[Delete]]
// https://heycam.github.io/webidl/#legacy-platform-object-delete
// step 2. If O supports named properties, O does not implement an interface
// with the [Global] extended attribute and the result of calling the
// named property visibility algorithm with property name P and object O
// is true, then:
// step 2.1. If O does not implement an interface with a named property
// deleter, then return false.
ExceptionState exception_state(${info}.GetIsolate(),
ExceptionState::kNamedDeletionContext,
"${interface.identifier}");
bool does_exist = ${blink_receiver}->NamedPropertyQuery(
${blink_property_name}, exception_state);
if (exception_state.HadException())
return;
if (does_exist) {
bindings::V8SetReturnValue(${info}, false);
if (${info}.ShouldThrowOnError()) {
exception_state.ThrowTypeError("Named property deleter is not supported.");
}
return;
}
// Do not intercept.
"""))
return func_decl, func_def
bind_return_value(
body, cg_context, overriding_args=["${blink_property_name}"])
if "Custom" in cg_context.named_property_deleter.extended_attributes:
text = _format("${class_name}::{}(${blink_property_name}, ${info});",
custom_function_name(cg_context))
body.append(TextNode(text))
return func_decl, func_def
body.extend([
TextNode("""\
// 3.9.4. [[Delete]]
// https://heycam.github.io/webidl/#legacy-platform-object-delete\
"""),
make_steps_of_ce_reactions(cg_context),
EmptyNode(),
make_v8_set_return_value(cg_context),
TextNode("""\
if (${return_value} == NamedPropertyDeleterResult::kDidNotDelete) {
if (${info}.ShouldThrowOnError()) {
ExceptionState exception_state(${info}.GetIsolate(),
ExceptionState::kNamedDeletionContext,
"${interface.identifier}");
exception_state.ThrowTypeError("Failed to delete a property.");
}
return;
}"""),
])
return func_decl, func_def
def make_named_property_definer_callback(cg_context, function_name):
assert isinstance(cg_context, CodeGenContext)
assert isinstance(function_name, str)
arg_decls = [
"v8::Local<v8::Name> v8_property_name",
"const v8::PropertyDescriptor& v8_property_desc",
"const v8::PropertyCallbackInfo<v8::Value>& info",
]
arg_names = ["v8_property_name", "v8_property_desc", "info"]
func_decl, func_def = _make_interceptor_callback(
cg_context, function_name, arg_decls, arg_names, cg_context.class_name,
"NamedPropertyDefiner")
body = func_def.body
if cg_context.interface.identifier == "CSSStyleDeclaration":
body.append(
TextNode("""\
// CSSStyleDeclaration is abusing named properties.
// Do not intercept. Fallback to OrdinaryDefineOwnProperty.
"""))
elif cg_context.interface.identifier in ("HTMLEmbedElement",
"HTMLObjectElement"):
body.append(
TextNode("""\
// HTMLEmbedElement and HTMLObjectElement are abusing named properties.
// Do not intercept. Fallback to OrdinaryDefineOwnProperty.
"""))
elif not cg_context.interface.indexed_and_named_properties.named_setter:
body.append(
TextNode("""\
// 3.9.3. [[DefineOwnProperty]]
// https://heycam.github.io/webidl/#legacy-platform-object-defineownproperty
// step 2.1. Let creating be true if P is not a supported property name, and
// false otherwise.
// step 2.2.1. If creating is false and O does not implement an interface
// with a named property setter, then return false.
${class_name}::NamedPropertyGetterCallback(${v8_property_name}, ${info});
const bool is_creating = ${info}.GetReturnValue().Get()->IsUndefined();
if (!is_creating) {
bindings::V8SetReturnValue(${info}, nullptr);
if (${info}.ShouldThrowOnError()) {
ExceptionState exception_state(${info}.GetIsolate(),
ExceptionState::kNamedSetterContext,
"${interface.identifier}");
exception_state.ThrowTypeError("Named property setter is not supported.");
}
return;
}
// Do not intercept. Fallback to OrdinaryDefineOwnProperty.
"""))
else:
body.append(
TextNode("""\
// 3.9.3. [[DefineOwnProperty]]
// https://heycam.github.io/webidl/#legacy-platform-object-defineownproperty
// step 2.2.2. If O implements an interface with a named property setter,
// then:
// step 2.2.2.1. If the result of calling IsDataDescriptor(Desc) is false,
// then return false.
if (v8_property_desc.has_get() || v8_property_desc.has_set()) {
bindings::V8SetReturnValue(${info}, nullptr);
if (${info}.ShouldThrowOnError()) {
ExceptionState exception_state(${info}.GetIsolate(),
ExceptionState::kNamedSetterContext,
"${interface.identifier}");
exception_state.ThrowTypeError("Accessor properties are not allowed.");
}
return;
}
// step 2.2.2.2. Invoke the named property setter with P and Desc.[[Value]].
${class_name}::NamedPropertySetterCallback(
${v8_property_name}, ${v8_property_desc}.value(), ${info});
bindings::V8SetReturnValue(${info}, nullptr);
"""))
return func_decl, func_def
def make_named_property_descriptor_callback(cg_context, function_name):
assert isinstance(cg_context, CodeGenContext)
assert isinstance(function_name, str)
arg_decls = [
"v8::Local<v8::Name> v8_property_name",
"const v8::PropertyCallbackInfo<v8::Value>& info",
]
arg_names = ["v8_property_name", "info"]
func_decl, func_def = _make_interceptor_callback(
cg_context, function_name, arg_decls, arg_names, cg_context.class_name,
"NamedPropertyDescriptor")
body = func_def.body
body.append(
TextNode("""\
// LegacyPlatformObjectGetOwnProperty
// https://heycam.github.io/webidl/#LegacyPlatformObjectGetOwnProperty\
"""))
if ("LegacyOverrideBuiltIns" not in
cg_context.interface.extended_attributes):
body.append(
TextNode("""\
// step 2.1. If the result of running the named property visibility algorithm
// with property name P and object O is true, then:
if (${v8_receiver}->GetRealNamedPropertyAttributesInPrototypeChain(
${current_context}, ${v8_property_name}).IsJust()) {
return; // Do not intercept. Fallback to OrdinaryGetOwnProperty.
}
"""))
pattern = """\
// step 2.1.3. If operation was defined without an identifier, then set
// value to the result of performing the steps listed in the interface
// description to determine the value of a named property with P as the
// name.
// step 2.1.4. Otherwise, operation was defined with an identifier. Set
// value to the result of performing the steps listed in the description
// of operation with P as the only argument value.
${class_name}::NamedPropertyGetterCallback(${v8_property_name}, ${info});
v8::Local<v8::Value> v8_value = ${info}.GetReturnValue().Get();
// step 2.1. If the result of running the named property visibility
// algorithm with property name P and object O is true, then:
// step 3. Return OrdinaryGetOwnProperty(O, P).
if (v8_value->IsUndefined())
return; // Do not intercept. Fallback to OrdinaryGetOwnProperty.
// step 2.1.6. Set desc.[[Value]] to the result of converting value to an
// ECMAScript value.
// step 2.1.7. If O implements an interface with a named property setter,
// then set desc.[[Writable]] to true, otherwise set it to false.
// step 2.1.8. If O implements an interface with the
// [LegacyUnenumerableNamedProperties] extended attribute, then set
// desc.[[Enumerable]] to false, otherwise set it to true.
// step 2.1.9. Set desc.[[Configurable]] to true.
v8::PropertyDescriptor desc(v8_value, /*writable=*/{cxx_writable});
desc.set_enumerable({cxx_enumerable});
desc.set_configurable(true);
bindings::V8SetReturnValue(${info}, desc);
"""
props = cg_context.interface.indexed_and_named_properties
writable = bool(props.named_setter)
cxx_writable = "true" if writable else "false"
enumerable = props.is_named_property_enumerable
cxx_enumerable = "true" if enumerable else "false"
body.append(
TextNode(
_format(
pattern,
cxx_writable=cxx_writable,
cxx_enumerable=cxx_enumerable)))
return func_decl, func_def
def make_named_property_query_callback(cg_context, function_name):
assert isinstance(cg_context, CodeGenContext)
assert isinstance(function_name, str)
props = cg_context.interface.indexed_and_named_properties
if "NotEnumerable" in props.named_getter.extended_attributes:
return None, None
arg_decls = [
"v8::Local<v8::Name> v8_property_name",
"const v8::PropertyCallbackInfo<v8::Integer>& info",
]
arg_names = ["v8_property_name", "info"]
func_decl, func_def = _make_interceptor_callback(
cg_context, function_name, arg_decls, arg_names, cg_context.class_name,
"NamedPropertyQuery")
body = func_def.body
flags = []
if not props.named_setter:
flags.append("v8::ReadOnly")
if not props.is_named_property_enumerable:
flags.append("v8::DontEnum")
if not flags:
flags.append("v8::None")
if len(flags) == 1:
property_attribute = flags[0]
else:
property_attribute = " | ".join(flags)
body.extend([
TextNode("""\
ExceptionState exception_state(${isolate},
ExceptionState::kNamedGetterContext,
"${interface.identifier}");
bool does_exist = ${blink_receiver}->NamedPropertyQuery(
${blink_property_name}, exception_state);
if (!does_exist)
return; // Do not intercept.
"""),
TextNode(
_format(
"bindings::V8SetReturnValue"
"(${info}, uint32_t({property_attribute}));",
property_attribute=