blob: a15961a64744e111b580c8d1a0fa80558ee1ce4b [file] [log] [blame]
#!/usr/bin/env python
#
# 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
except ValueError:
from cpp_generator import CppGenerator
from cpp_generator_templates import CppGeneratorTemplates as CppTemplates
from generator import Generator, ucfirst
from models import ObjectType, ArrayType
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')
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/JSONValues.h")),
(["JavaScriptCore", "WebKit"], ("WTF", "wtf/NeverDestroyed.h")),
(["JavaScriptCore", "WebKit"], ("WTF", "wtf/text/CString.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_domain(domain, 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_domain(domain, '\n\n'.join(implementations))
def _generate_small_dispatcher_switch_implementation_for_domain(self, domain):
commands = self.commands_for_domain(domain)
cases = []
cases.append(' if (method == "%s")' % commands[0].command_name)
cases.append(' %s(requestId, WTFMove(parameters));' % commands[0].command_name)
for command in commands[1:]:
cases.append(' else if (method == "%s")' % command.command_name)
cases.append(' %s(requestId, WTFMove(parameters));' % command.command_name)
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(' { "%(commandName)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):
out_parameter_assignments = []
formal_parameters = []
for parameter in command.return_parameters:
param_args = {
'keyedSetMethod': CppGenerator.cpp_setter_method_for_type(parameter.type),
'parameterKey': parameter.parameter_name,
'parameterName': parameter.parameter_name,
'parameterType': CppGenerator.cpp_type_for_stack_in_parameter(parameter),
'helpersNamespace': self.helpers_namespace(),
}
formal_parameters.append('%s %s' % (CppGenerator.cpp_type_for_formal_async_parameter(parameter), parameter.parameter_name))
if parameter.is_optional:
if CppGenerator.should_use_wrapper_for_return_type(parameter.type):
out_parameter_assignments.append(' if (%(parameterName)s.hasValue())' % param_args)
out_parameter_assignments.append(' jsonMessage->%(keyedSetMethod)s("%(parameterKey)s"_s, *%(parameterName)s);' % param_args)
else:
out_parameter_assignments.append(' if (%(parameterName)s)' % param_args)
out_parameter_assignments.append(' jsonMessage->%(keyedSetMethod)s("%(parameterKey)s"_s, %(parameterName)s);' % param_args)
elif parameter.type.is_enum():
out_parameter_assignments.append(' jsonMessage->%(keyedSetMethod)s("%(parameterKey)s"_s, Inspector::Protocol::%(helpersNamespace)s::getEnumConstantValue(%(parameterName)s));' % param_args)
else:
out_parameter_assignments.append(' jsonMessage->%(keyedSetMethod)s("%(parameterKey)s"_s, %(parameterName)s);' % param_args)
async_args = {
'domainName': domain.domain_name,
'callbackName': ucfirst(command.command_name) + 'Callback',
'formalParameters': ", ".join(formal_parameters),
'outParameterAssignments': "\n".join(out_parameter_assignments)
}
return Template(CppTemplates.BackendDispatcherImplementationAsyncCommand).substitute(None, **async_args)
def _generate_dispatcher_implementation_for_command(self, command, domain):
in_parameter_declarations = []
out_parameter_declarations = []
out_parameter_assignments = []
alternate_dispatcher_method_parameters = ['requestId']
method_parameters = []
if not command.is_async:
method_parameters.append('error')
for parameter in command.call_parameters:
parameter_name = 'in_' + parameter.parameter_name
if parameter.is_optional:
parameter_name = 'opt_' + parameter_name
out_success_argument = 'nullptr'
if parameter.is_optional:
out_success_argument = '&%s_valueFound' % parameter_name
in_parameter_declarations.append(' bool %s_valueFound = false;' % parameter_name)
# Now add appropriate operators.
parameter_expression = parameter_name
if CppGenerator.should_use_references_for_type(parameter.type):
if parameter.is_optional:
parameter_expression = '%s.get()' % parameter_expression
else:
# This assumes that we have already proved the object is non-null.
# If a required property is missing, InspectorBackend::getObject will
# append a protocol error, and the method dispatcher will return without
# invoking the backend method (and dereferencing the object).
parameter_expression = '*%s' % parameter_expression
elif parameter.is_optional:
parameter_expression = '&%s' % parameter_expression
param_args = {
'parameterType': CppGenerator.cpp_type_for_stack_in_parameter(parameter),
'parameterKey': parameter.parameter_name,
'parameterName': parameter_name,
'parameterExpression': parameter_expression,
'keyedGetMethod': CppGenerator.cpp_getter_method_for_type(parameter.type),
'successOutParam': out_success_argument
}
in_parameter_declarations.append(' %(parameterType)s %(parameterName)s = m_backendDispatcher->%(keyedGetMethod)s(parameters.get(), "%(parameterKey)s"_s, %(successOutParam)s);' % param_args)
if parameter.is_optional:
optional_in_parameter_string = '%(parameterName)s_valueFound ? %(parameterExpression)s : nullptr' % param_args
alternate_dispatcher_method_parameters.append(optional_in_parameter_string)
method_parameters.append(optional_in_parameter_string)
else:
alternate_dispatcher_method_parameters.append(parameter_expression)
method_parameters.append(parameter_expression)
if command.is_async:
async_args = {
'domainName': domain.domain_name,
'callbackName': ucfirst(command.command_name) + 'Callback'
}
out_parameter_assignments.append(' callback->disable();')
out_parameter_assignments.append(' m_backendDispatcher->reportProtocolError(BackendDispatcher::ServerError, error);')
out_parameter_assignments.append(' return;')
method_parameters.append('callback.copyRef()')
else:
for parameter in command.return_parameters:
param_args = {
'parameterType': CppGenerator.cpp_type_for_stack_out_parameter(parameter),
'parameterKey': parameter.parameter_name,
'parameterName': parameter.parameter_name,
'keyedSetMethod': CppGenerator.cpp_setter_method_for_type(parameter.type),
'helpersNamespace': self.helpers_namespace(),
}
out_parameter_declarations.append(' %(parameterType)s out_%(parameterName)s;' % param_args)
if parameter.is_optional:
if CppGenerator.should_use_wrapper_for_return_type(parameter.type):
out_parameter_assignments.append(' if (out_%(parameterName)s.hasValue())' % param_args)
out_parameter_assignments.append(' result->%(keyedSetMethod)s("%(parameterKey)s"_s, *out_%(parameterName)s);' % param_args)
else:
out_parameter_assignments.append(' if (out_%(parameterName)s)' % param_args)
out_parameter_assignments.append(' result->%(keyedSetMethod)s("%(parameterKey)s"_s, out_%(parameterName)s);' % param_args)
elif parameter.type.is_enum():
out_parameter_assignments.append(' result->%(keyedSetMethod)s("%(parameterKey)s"_s, Inspector::Protocol::%(helpersNamespace)s::getEnumConstantValue(out_%(parameterName)s));' % param_args)
else:
out_parameter_assignments.append(' result->%(keyedSetMethod)s("%(parameterKey)s"_s, out_%(parameterName)s);' % param_args)
if CppGenerator.should_pass_by_copy_for_return_type(parameter.type) or parameter.is_optional and CppGenerator.should_use_wrapper_for_return_type(parameter.type):
method_parameters.append('out_' + parameter.parameter_name)
else:
method_parameters.append('&out_' + parameter.parameter_name)
command_args = {
'domainName': domain.domain_name,
'callbackName': '%sCallback' % ucfirst(command.command_name),
'commandName': command.command_name,
'inParameterDeclarations': '\n'.join(in_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 requestId, RefPtr<JSON::Object>&&)' % command_args)
else:
lines.append('void %(domainName)sBackendDispatcher::%(commandName)s(long requestId, RefPtr<JSON::Object>&& 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')
lines.append('')
if command.is_async:
lines.append(' Ref<%(domainName)sBackendDispatcherHandler::%(callbackName)s> callback = adoptRef(*new %(domainName)sBackendDispatcherHandler::%(callbackName)s(m_backendDispatcher.copyRef(), requestId));' % command_args)
else:
lines.append(' ErrorString error;')
lines.append(' Ref<JSON::Object> result = JSON::Object::create();')
if len(command.return_parameters) > 0:
lines.extend(out_parameter_declarations)
lines.append(' m_agent->%(commandName)s(%(invocationParameters)s);' % command_args)
lines.append('')
if not command.is_async:
if len(command.return_parameters) > 1:
lines.append(' if (!error.length()) {')
lines.extend(out_parameter_assignments)
lines.append(' }')
elif len(command.return_parameters) == 1:
lines.append(' if (!error.length())')
lines.extend(out_parameter_assignments)
lines.append('')
lines.append(' if (!error.length())')
lines.append(' m_backendDispatcher->sendResponse(requestId, WTFMove(result), false);')
lines.append(' else')
lines.append(' m_backendDispatcher->reportProtocolError(BackendDispatcher::ServerError, WTFMove(error));')
lines.append('}')
return "\n".join(lines)