blob: 6645784e0e9ea8eb0bb3d29a3513e72dd8221c28 [file] [log] [blame]
#!/usr/bin/env python3
#
# Copyright (c) 2014-2018 Apple Inc. All rights reserved.
# Copyright (c) 2014 University of Washington. All rights reserved.
#
# Redistribution and use in source and binary forms, with or without
# modification, are permitted provided that the following conditions
# are met:
# 1. Redistributions of source code must retain the above copyright
# notice, this list of conditions and the following disclaimer.
# 2. Redistributions in binary form must reproduce the above copyright
# notice, this list of conditions and the following disclaimer in the
# documentation and/or other materials provided with the distribution.
#
# THIS SOFTWARE IS PROVIDED BY APPLE INC. AND ITS CONTRIBUTORS ``AS IS''
# AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
# THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
# PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR ITS CONTRIBUTORS
# BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
# CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
# SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
# INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
# CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
# ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF
# THE POSSIBILITY OF SUCH DAMAGE.
import logging
import string
from string import Template
try:
from .cpp_generator import CppGenerator
from .cpp_generator_templates import CppGeneratorTemplates as CppTemplates
from .generator import Generator, ucfirst
from .models import ObjectType, ArrayType, AliasedType, EnumType
except ImportError:
from cpp_generator import CppGenerator
from cpp_generator_templates import CppGeneratorTemplates as CppTemplates
from generator import Generator, ucfirst
from models import ObjectType, ArrayType, AliasedType, EnumType
log = logging.getLogger('global')
class CppBackendDispatcherImplementationGenerator(CppGenerator):
def __init__(self, *args, **kwargs):
CppGenerator.__init__(self, *args, **kwargs)
def output_filename(self):
return "%sBackendDispatchers.cpp" % self.protocol_name()
def domains_to_generate(self):
return [domain for domain in Generator.domains_to_generate(self) if len(self.commands_for_domain(domain)) > 0]
def generate_output(self):
secondary_includes = self._generate_secondary_header_includes()
if self.model().framework.setting('alternate_dispatchers', False):
secondary_includes.append('')
secondary_includes.append('#if ENABLE(INSPECTOR_ALTERNATE_DISPATCHERS)')
secondary_includes.append('#include "%sAlternateBackendDispatchers.h"' % self.protocol_name())
secondary_includes.append('#endif // ENABLE(INSPECTOR_ALTERNATE_DISPATCHERS)')
header_args = {
'primaryInclude': '"%sBackendDispatchers.h"' % self.protocol_name(),
'secondaryIncludes': '\n'.join(secondary_includes),
}
sections = []
sections.append(self.generate_license())
sections.append(Template(CppTemplates.ImplementationPrelude).substitute(None, **header_args))
sections.append("\n".join(map(self._generate_handler_class_destructor_for_domain, self.domains_to_generate())))
sections.extend(list(map(self._generate_dispatcher_implementations_for_domain, self.domains_to_generate())))
sections.append(Template(CppTemplates.ImplementationPostlude).substitute(None, **header_args))
return "\n\n".join(sections)
# Private methods.
def _generate_secondary_header_includes(self):
header_includes = [
(["JavaScriptCore", "WebKit"], ("JavaScriptCore", "inspector/InspectorFrontendRouter.h")),
(["JavaScriptCore", "WebKit"], ("WTF", "wtf/NeverDestroyed.h")),
]
return self.generate_includes_from_entries(header_includes)
def _generate_handler_class_destructor_for_domain(self, domain):
destructor_args = {
'domainName': domain.domain_name
}
destructor = '%(domainName)sBackendDispatcherHandler::~%(domainName)sBackendDispatcherHandler() { }' % destructor_args
return self.wrap_with_guard_for_condition(domain.condition, destructor)
def _generate_dispatcher_implementations_for_domain(self, domain):
implementations = []
constructor_args = {
'domainName': domain.domain_name,
}
implementations.append(Template(CppTemplates.BackendDispatcherImplementationDomainConstructor).substitute(None, **constructor_args))
commands = self.commands_for_domain(domain)
if len(commands) <= 5:
implementations.append(self._generate_small_dispatcher_switch_implementation_for_domain(domain))
else:
implementations.append(self._generate_large_dispatcher_switch_implementation_for_domain(domain))
for command in commands:
if command.is_async:
implementations.append(self._generate_async_dispatcher_class_for_domain(command, domain))
implementations.append(self._generate_dispatcher_implementation_for_command(command, domain))
return self.wrap_with_guard_for_condition(domain.condition, '\n\n'.join(implementations))
def _generate_small_dispatcher_switch_implementation_for_domain(self, domain):
commands = self.commands_for_domain(domain)
cases = []
first_command_string = "\n".join([
' if (protocol_method == "%s"_s) {' % commands[0].command_name,
' %s(protocol_requestId, WTFMove(protocol_parameters));' % commands[0].command_name,
' return;',
' }',
])
cases.append(self.wrap_with_guard_for_condition(commands[0].condition, first_command_string))
for command in commands[1:]:
additional_command_string = "\n".join([
' if (protocol_method == "%s"_s) {' % command.command_name,
' %s(protocol_requestId, WTFMove(protocol_parameters));' % command.command_name,
' return;',
' }',
])
cases.append(self.wrap_with_guard_for_condition(command.condition, additional_command_string))
switch_args = {
'domainName': domain.domain_name,
'dispatchCases': "\n".join(cases)
}
return Template(CppTemplates.BackendDispatcherImplementationSmallSwitch).substitute(None, **switch_args)
def _generate_large_dispatcher_switch_implementation_for_domain(self, domain):
commands = self.commands_for_domain(domain)
cases = []
for command in commands:
args = {
'domainName': domain.domain_name,
'commandName': command.command_name
}
cases.append(self.wrap_with_guard_for_condition(command.condition, ' { "%(commandName)s"_s, &%(domainName)sBackendDispatcher::%(commandName)s },' % args))
switch_args = {
'domainName': domain.domain_name,
'dispatchCases': "\n".join(cases)
}
return Template(CppTemplates.BackendDispatcherImplementationLargeSwitch).substitute(None, **switch_args)
def _generate_async_dispatcher_class_for_domain(self, command, domain):
return_assignments = []
callback_parameters = []
for parameter in command.return_parameters:
parameter_name = parameter.parameter_name
if parameter.is_optional:
parameter_name = 'opt_' + parameter_name
parameter_value = parameter_name
_type = parameter.type
if isinstance(_type, AliasedType):
_type = _type.aliased_type
if isinstance(_type, EnumType) and _type.is_anonymous:
_type = _type.primitive_type
if _type.is_enum():
if parameter.is_optional:
parameter_value = '*' + parameter_value
parameter_value = 'Protocol::%s::getEnumConstantValue(%s)' % (self.helpers_namespace(), parameter_value)
elif CppGenerator.should_release_argument(_type, parameter.is_optional):
parameter_value = parameter_value + '.releaseNonNull()'
elif CppGenerator.should_dereference_argument(_type, parameter.is_optional):
parameter_value = '*' + parameter_value
elif CppGenerator.should_move_argument(_type, parameter.is_optional):
parameter_value = 'WTFMove(%s)' % parameter_value
param_args = {
'keyedSetMethod': CppGenerator.cpp_setter_method_for_type(_type),
'parameterKey': parameter.parameter_name,
'parameterName': parameter_name,
'parameterValue': parameter_value,
}
callback_parameters.append('%s %s' % (CppGenerator.cpp_type_for_command_return_argument(_type, parameter.is_optional), parameter_name))
if parameter.is_optional:
return_assignments.append(' if (!!%(parameterName)s)' % param_args)
return_assignments.append(' protocol_jsonMessage->%(keyedSetMethod)s("%(parameterKey)s"_s, %(parameterValue)s);' % param_args)
else:
return_assignments.append(' protocol_jsonMessage->%(keyedSetMethod)s("%(parameterKey)s"_s, %(parameterValue)s);' % param_args)
async_args = {
'domainName': domain.domain_name,
'callbackName': ucfirst(command.command_name) + 'Callback',
'callbackParameters': ", ".join(callback_parameters),
'returnAssignments': "\n".join(return_assignments)
}
return self.wrap_with_guard_for_condition(command.condition, Template(CppTemplates.BackendDispatcherImplementationAsyncCommand).substitute(None, **async_args))
def _generate_dispatcher_implementation_for_command(self, command, domain):
parameter_declarations = []
parameter_enum_resolutions = []
alternate_dispatcher_method_parameters = ['protocol_requestId']
method_parameters = []
for parameter in command.call_parameters:
parameter_name = parameter.parameter_name
if parameter.is_optional:
parameter_name = 'opt_' + parameter_name
parameter_name = 'in_' + parameter_name
variable_name = parameter_name
_type = parameter.type
if isinstance(_type, AliasedType):
_type = _type.aliased_type
if isinstance(_type, EnumType) and _type.is_anonymous:
_type = _type.primitive_type
if _type.is_enum():
parameter_name = parameter_name + '_json'
alternate_dispatcher_method_parameters.append(parameter_name)
enum_args = {
'helpersNamespace': self.helpers_namespace(),
'parameterKey': parameter.parameter_name,
'enumType': CppGenerator.cpp_protocol_type_for_type(_type),
'enumVariableName': variable_name,
'stringVariableName': parameter_name,
}
if len(parameter_enum_resolutions):
parameter_enum_resolutions.append('')
parameter_enum_resolutions.append(' auto %(enumVariableName)s = Protocol::%(helpersNamespace)s::parseEnumValueFromString<%(enumType)s>(%(stringVariableName)s);' % enum_args)
if parameter.is_optional:
parameter_expression = 'WTFMove(%s)' % variable_name
else:
parameter_enum_resolutions.append(' if (!%(enumVariableName)s) {' % enum_args)
parameter_enum_resolutions.append(' m_backendDispatcher->reportProtocolError(BackendDispatcher::ServerError, makeString("Unknown %(parameterKey)s: "_s, %(stringVariableName)s));' % enum_args)
parameter_enum_resolutions.append(' return;')
parameter_enum_resolutions.append(' }')
parameter_expression = '*' + variable_name
else:
if _type.raw_name() == 'string':
parameter_expression = variable_name
elif parameter.is_optional:
parameter_expression = 'WTFMove(%s)' % variable_name
elif _type.raw_name() in ['boolean', 'integer', 'number']:
parameter_expression = '*' + variable_name
else:
parameter_expression = variable_name + '.releaseNonNull()'
alternate_dispatcher_method_parameters.append(parameter_expression)
method_parameters.append(parameter_expression)
param_args = {
'parameterKey': parameter.parameter_name,
'parameterName': parameter_name,
'keyedGetMethod': CppGenerator.cpp_getter_method_for_type(_type),
'required': 'false' if parameter.is_optional else 'true',
}
parameter_declarations.append(' auto %(parameterName)s = m_backendDispatcher->%(keyedGetMethod)s(protocol_parameters.get(), "%(parameterKey)s"_s, %(required)s);' % param_args)
if command.is_async:
method_parameters.append('adoptRef(*new %sBackendDispatcherHandler::%s(m_backendDispatcher.copyRef(), protocol_requestId))' % (domain.domain_name, '%sCallback' % ucfirst(command.command_name)))
command_args = {
'domainName': domain.domain_name,
'commandName': command.command_name,
'parameterDeclarations': '\n'.join(parameter_declarations),
'invocationParameters': ', '.join(method_parameters),
'alternateInvocationParameters': ', '.join(alternate_dispatcher_method_parameters),
}
lines = []
if len(command.call_parameters) == 0:
lines.append('void %(domainName)sBackendDispatcher::%(commandName)s(long protocol_requestId, RefPtr<JSON::Object>&&)' % command_args)
else:
lines.append('void %(domainName)sBackendDispatcher::%(commandName)s(long protocol_requestId, RefPtr<JSON::Object>&& protocol_parameters)' % command_args)
lines.append('{')
if len(command.call_parameters) > 0:
lines.append(Template(CppTemplates.BackendDispatcherImplementationPrepareCommandArguments).substitute(None, **command_args))
if self.model().framework.setting('alternate_dispatchers', False):
lines.append('#if ENABLE(INSPECTOR_ALTERNATE_DISPATCHERS)')
lines.append(' if (m_alternateDispatcher) {')
lines.append(' m_alternateDispatcher->%(commandName)s(%(alternateInvocationParameters)s);' % command_args)
lines.append(' return;')
lines.append(' }')
lines.append('#endif // ENABLE(INSPECTOR_ALTERNATE_DISPATCHERS)')
lines.append('')
if len(parameter_enum_resolutions):
lines.extend(parameter_enum_resolutions)
lines.append('')
if command.is_async:
lines.append(' m_agent->%(commandName)s(%(invocationParameters)s);' % command_args)
else:
result_destructured_names = []
result_conversion_lines = []
for parameter in command.return_parameters:
parameter_name = parameter.parameter_name
if parameter.is_optional:
parameter_name = 'opt_' + parameter_name
parameter_name = 'out_' + parameter_name
result_destructured_names.append(parameter_name)
parameter_value = parameter_name
_type = parameter.type
if isinstance(_type, AliasedType):
_type = _type.aliased_type
if isinstance(_type, EnumType) and _type.is_anonymous:
_type = _type.primitive_type
if _type.is_enum():
if parameter.is_optional:
parameter_value = '*' + parameter_value
parameter_value = 'Protocol::%s::getEnumConstantValue(%s)' % (self.helpers_namespace(), parameter_value)
elif CppGenerator.should_release_argument(_type, parameter.is_optional):
parameter_value = parameter_value + '.releaseNonNull()'
elif CppGenerator.should_dereference_argument(_type, parameter.is_optional):
parameter_value = '*' + parameter_value
elif CppGenerator.should_move_argument(_type, parameter.is_optional):
parameter_value = 'WTFMove(%s)' % parameter_value
param_args = {
'keyedSetMethod': CppGenerator.cpp_setter_method_for_type(_type),
'parameterKey': parameter.parameter_name,
'parameterName': parameter_name,
'parameterValue': parameter_value,
}
if parameter.is_optional:
result_conversion_lines.append(' if (!!%(parameterName)s)' % param_args)
result_conversion_lines.append(' protocol_jsonMessage->%(keyedSetMethod)s("%(parameterKey)s"_s, %(parameterValue)s);' % param_args)
else:
result_conversion_lines.append(' protocol_jsonMessage->%(keyedSetMethod)s("%(parameterKey)s"_s, %(parameterValue)s);' % param_args)
lines.append(' auto result = m_agent->%(commandName)s(%(invocationParameters)s);' % command_args)
lines.append(' if (!result) {')
lines.append(' ASSERT(!result.error().isEmpty());')
lines.append(' m_backendDispatcher->reportProtocolError(BackendDispatcher::ServerError, result.error());')
lines.append(' return;')
lines.append(' }')
lines.append('')
if len(result_destructured_names) == 1:
lines.append(' auto %s = WTFMove(result.value());' % result_destructured_names[0])
lines.append('')
elif len(result_destructured_names) > 1:
lines.append(' auto [%s] = WTFMove(result.value());' % ", ".join(result_destructured_names))
lines.append('')
lines.append(' auto protocol_jsonMessage = JSON::Object::create();')
lines.extend(result_conversion_lines)
lines.append(' m_backendDispatcher->sendResponse(protocol_requestId, WTFMove(protocol_jsonMessage), false);')
lines.append('}')
return self.wrap_with_guard_for_condition(command.condition, "\n".join(lines))