diff options
Diffstat (limited to 'Source/JavaScriptCore/inspector/scripts/codegen')
26 files changed, 5569 insertions, 0 deletions
diff --git a/Source/JavaScriptCore/inspector/scripts/codegen/__init__.py b/Source/JavaScriptCore/inspector/scripts/codegen/__init__.py new file mode 100644 index 000000000..37dbe9436 --- /dev/null +++ b/Source/JavaScriptCore/inspector/scripts/codegen/__init__.py @@ -0,0 +1,25 @@ +# Required for Python to search this directory for module files + +from models import * +from generator import * +from cpp_generator import * +from objc_generator import * + +from generate_cpp_alternate_backend_dispatcher_header import * +from generate_cpp_backend_dispatcher_header import * +from generate_cpp_backend_dispatcher_implementation import * +from generate_cpp_frontend_dispatcher_header import * +from generate_cpp_frontend_dispatcher_implementation import * +from generate_cpp_protocol_types_header import * +from generate_cpp_protocol_types_implementation import * +from generate_js_backend_commands import * +from generate_objc_backend_dispatcher_header import * +from generate_objc_backend_dispatcher_implementation import * +from generate_objc_configuration_header import * +from generate_objc_configuration_implementation import * +from generate_objc_frontend_dispatcher_implementation import * +from generate_objc_header import * +from generate_objc_internal_header import * +from generate_objc_protocol_types_implementation import * +from generate_objc_protocol_type_conversions_header import * +from generate_objc_protocol_type_conversions_implementation import * diff --git a/Source/JavaScriptCore/inspector/scripts/codegen/cpp_generator.py b/Source/JavaScriptCore/inspector/scripts/codegen/cpp_generator.py new file mode 100644 index 000000000..c459fcac3 --- /dev/null +++ b/Source/JavaScriptCore/inspector/scripts/codegen/cpp_generator.py @@ -0,0 +1,319 @@ +#!/usr/bin/env python +# +# Copyright (c) 2014, 2016 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 os.path +import re + +from generator import ucfirst, Generator +from models import PrimitiveType, ObjectType, ArrayType, EnumType, AliasedType, Frameworks + +log = logging.getLogger('global') + +_PRIMITIVE_TO_CPP_NAME_MAP = { + 'boolean': 'bool', + 'integer': 'int', + 'number': 'double', + 'string': 'String', + 'object': 'Inspector::InspectorObject', + 'array': 'Inspector::InspectorArray', + 'any': 'Inspector::InspectorValue' +} + +class CppGenerator(Generator): + def __init__(self, *args, **kwargs): + Generator.__init__(self, *args, **kwargs) + + def protocol_name(self): + return self.model().framework.setting('cpp_protocol_group', '') + + def helpers_namespace(self): + return '%sHelpers' % self.protocol_name() + + # Miscellaneous text manipulation routines. + @staticmethod + def cpp_getter_method_for_type(_type): + if isinstance(_type, ObjectType): + return 'getObject' + if isinstance(_type, ArrayType): + return 'getArray' + if isinstance(_type, PrimitiveType): + if _type.raw_name() is 'integer': + return 'getInteger' + elif _type.raw_name() is 'number': + return 'getDouble' + elif _type.raw_name() is 'any': + return 'getValue' + else: + return 'get' + ucfirst(_type.raw_name()) + if isinstance(_type, AliasedType): + return CppGenerator.cpp_getter_method_for_type(_type.aliased_type) + if isinstance(_type, EnumType): + return CppGenerator.cpp_getter_method_for_type(_type.primitive_type) + + @staticmethod + def cpp_setter_method_for_type(_type): + if isinstance(_type, ObjectType): + return 'setObject' + if isinstance(_type, ArrayType): + return 'setArray' + if isinstance(_type, PrimitiveType): + if _type.raw_name() is 'integer': + return 'setInteger' + elif _type.raw_name() is 'number': + return 'setDouble' + elif _type.raw_name() is 'any': + return 'setValue' + else: + return 'set' + ucfirst(_type.raw_name()) + if isinstance(_type, AliasedType): + return CppGenerator.cpp_setter_method_for_type(_type.aliased_type) + if isinstance(_type, EnumType): + return CppGenerator.cpp_setter_method_for_type(_type.primitive_type) + + # Generate type representations for various situations. + @staticmethod + def cpp_protocol_type_for_type(_type): + if isinstance(_type, ObjectType) and len(_type.members) == 0: + return 'Inspector::InspectorObject' + if isinstance(_type, ArrayType): + if _type.raw_name() is None: # Otherwise, fall through and use typedef'd name. + return 'Inspector::Protocol::Array<%s>' % CppGenerator.cpp_protocol_type_for_type(_type.element_type) + if isinstance(_type, (ObjectType, AliasedType, EnumType, ArrayType)): + return 'Inspector::Protocol::%s::%s' % (_type.type_domain().domain_name, _type.raw_name()) + if isinstance(_type, PrimitiveType): + return CppGenerator.cpp_name_for_primitive_type(_type) + + @staticmethod + def cpp_protocol_type_for_type_member(type_member, object_declaration): + if isinstance(type_member.type, EnumType) and type_member.type.is_anonymous: + return '::'.join([CppGenerator.cpp_protocol_type_for_type(object_declaration.type), ucfirst(type_member.member_name)]) + else: + return CppGenerator.cpp_protocol_type_for_type(type_member.type) + + @staticmethod + def cpp_type_for_unchecked_formal_in_parameter(parameter): + _type = parameter.type + if isinstance(_type, AliasedType): + _type = _type.aliased_type # Fall through to enum or primitive. + + if isinstance(_type, EnumType): + _type = _type.primitive_type # Fall through to primitive. + + # This handles the 'any' type and objects with defined properties. + if isinstance(_type, ObjectType) or _type.qualified_name() is 'object': + cpp_name = 'Inspector::InspectorObject' + if parameter.is_optional: + return 'const %s*' % cpp_name + else: + return 'const %s&' % cpp_name + if isinstance(_type, ArrayType): + cpp_name = 'Inspector::InspectorArray' + if parameter.is_optional: + return 'const %s*' % cpp_name + else: + return 'const %s&' % cpp_name + if isinstance(_type, PrimitiveType): + cpp_name = CppGenerator.cpp_name_for_primitive_type(_type) + if parameter.is_optional: + return 'const %s* const' % cpp_name + elif _type.raw_name() in ['string']: + return 'const %s&' % cpp_name + else: + return cpp_name + + return "unknown_unchecked_formal_in_parameter_type" + + @staticmethod + def cpp_type_for_checked_formal_event_parameter(parameter): + return CppGenerator.cpp_type_for_type_with_name(parameter.type, parameter.parameter_name, parameter.is_optional) + + @staticmethod + def cpp_type_for_type_member(member): + return CppGenerator.cpp_type_for_type_with_name(member.type, member.member_name, False) + + @staticmethod + def cpp_type_for_type_with_name(_type, type_name, is_optional): + if isinstance(_type, (ArrayType, ObjectType)): + return 'RefPtr<%s>' % CppGenerator.cpp_protocol_type_for_type(_type) + if isinstance(_type, AliasedType): + builder_type = CppGenerator.cpp_protocol_type_for_type(_type) + if is_optional: + return 'const %s* const' % builder_type + elif _type.aliased_type.qualified_name() in ['integer', 'number']: + return CppGenerator.cpp_name_for_primitive_type(_type.aliased_type) + elif _type.aliased_type.qualified_name() in ['string']: + return 'const %s&' % builder_type + else: + return builder_type + if isinstance(_type, PrimitiveType): + cpp_name = CppGenerator.cpp_name_for_primitive_type(_type) + if _type.qualified_name() in ['object']: + return 'RefPtr<Inspector::InspectorObject>' + elif _type.qualified_name() in ['any']: + return 'RefPtr<Inspector::InspectorValue>' + elif is_optional: + return 'const %s* const' % cpp_name + elif _type.qualified_name() in ['string']: + return 'const %s&' % cpp_name + else: + return cpp_name + if isinstance(_type, EnumType): + if _type.is_anonymous: + enum_type_name = ucfirst(type_name) + else: + enum_type_name = 'Inspector::Protocol::%s::%s' % (_type.type_domain().domain_name, _type.raw_name()) + + if is_optional: + return '%s*' % enum_type_name + else: + return '%s' % enum_type_name + + @staticmethod + def cpp_type_for_formal_out_parameter(parameter): + _type = parameter.type + + if isinstance(_type, AliasedType): + _type = _type.aliased_type # Fall through. + + if isinstance(_type, (ObjectType, ArrayType)): + return 'RefPtr<%s>&' % CppGenerator.cpp_protocol_type_for_type(_type) + if isinstance(_type, PrimitiveType): + cpp_name = CppGenerator.cpp_name_for_primitive_type(_type) + if parameter.is_optional: + return "Inspector::Protocol::OptOutput<%s>*" % cpp_name + else: + return '%s*' % cpp_name + if isinstance(_type, EnumType): + if _type.is_anonymous: + return '%sBackendDispatcherHandler::%s*' % (_type.type_domain().domain_name, ucfirst(parameter.parameter_name)) + else: + return 'Inspector::Protocol::%s::%s*' % (_type.type_domain().domain_name, _type.raw_name()) + + raise ValueError("unknown formal out parameter type.") + + # FIXME: this is only slightly different from out parameters; they could be unified. + @staticmethod + def cpp_type_for_formal_async_parameter(parameter): + _type = parameter.type + if isinstance(_type, AliasedType): + _type = _type.aliased_type # Fall through. + + if isinstance(_type, EnumType): + _type = _type.primitive_type # Fall through. + + if isinstance(_type, (ObjectType, ArrayType)): + return 'RefPtr<%s>&&' % CppGenerator.cpp_protocol_type_for_type(_type) + if isinstance(_type, PrimitiveType): + cpp_name = CppGenerator.cpp_name_for_primitive_type(_type) + if parameter.is_optional: + return "Inspector::Protocol::OptOutput<%s>*" % cpp_name + elif _type.qualified_name() in ['integer', 'number']: + return CppGenerator.cpp_name_for_primitive_type(_type) + elif _type.qualified_name() in ['string']: + return 'const %s&' % cpp_name + else: + return cpp_name + + raise ValueError("Unknown formal async parameter type.") + + # In-parameters don't use builder types, because they could be passed + # "open types" that are manually constructed out of InspectorObjects. + + # FIXME: Only parameters that are actually open types should need non-builder parameter types. + @staticmethod + def cpp_type_for_stack_in_parameter(parameter): + _type = parameter.type + if isinstance(_type, AliasedType): + _type = _type.aliased_type # Fall through. + + if isinstance(_type, EnumType): + _type = _type.primitive_type # Fall through. + + if isinstance(_type, ObjectType): + return "RefPtr<Inspector::InspectorObject>" + if isinstance(_type, ArrayType): + return "RefPtr<Inspector::InspectorArray>" + if isinstance(_type, PrimitiveType): + cpp_name = CppGenerator.cpp_name_for_primitive_type(_type) + if _type.qualified_name() in ['any', 'object']: + return "RefPtr<%s>" % CppGenerator.cpp_name_for_primitive_type(_type) + elif parameter.is_optional and _type.qualified_name() not in ['boolean', 'string', 'integer']: + return "Inspector::Protocol::OptOutput<%s>" % cpp_name + else: + return cpp_name + + @staticmethod + def cpp_type_for_stack_out_parameter(parameter): + _type = parameter.type + if isinstance(_type, (ArrayType, ObjectType)): + return 'RefPtr<%s>' % CppGenerator.cpp_protocol_type_for_type(_type) + if isinstance(_type, AliasedType): + builder_type = CppGenerator.cpp_protocol_type_for_type(_type) + if parameter.is_optional: + return "Inspector::Protocol::OptOutput<%s>" % builder_type + return '%s' % builder_type + if isinstance(_type, PrimitiveType): + cpp_name = CppGenerator.cpp_name_for_primitive_type(_type) + if parameter.is_optional: + return "Inspector::Protocol::OptOutput<%s>" % cpp_name + else: + return cpp_name + if isinstance(_type, EnumType): + if _type.is_anonymous: + return '%sBackendDispatcherHandler::%s' % (_type.type_domain().domain_name, ucfirst(parameter.parameter_name)) + else: + return 'Inspector::Protocol::%s::%s' % (_type.type_domain().domain_name, _type.raw_name()) + + @staticmethod + def cpp_assertion_method_for_type_member(type_member, object_declaration): + + def assertion_method_for_type(_type): + return 'BindingTraits<%s>::assertValueHasExpectedType' % CppGenerator.cpp_protocol_type_for_type(_type) + + if isinstance(type_member.type, AliasedType): + return assertion_method_for_type(type_member.type.aliased_type) + if isinstance(type_member.type, EnumType) and type_member.type.is_anonymous: + return 'BindingTraits<%s>::assertValueHasExpectedType' % CppGenerator.cpp_protocol_type_for_type_member(type_member, object_declaration) + + return assertion_method_for_type(type_member.type) + + @staticmethod + def cpp_name_for_primitive_type(_type): + return _PRIMITIVE_TO_CPP_NAME_MAP.get(_type.raw_name()) + + # Decide whether certain helpers are necessary in a situation. + @staticmethod + def should_use_wrapper_for_return_type(_type): + return not isinstance(_type, (ArrayType, ObjectType)) + + @staticmethod + def should_use_references_for_type(_type): + return isinstance(_type, (ArrayType, ObjectType)) or (isinstance(_type, (PrimitiveType)) and _type.qualified_name() in ["any", "object"]) + + @staticmethod + def should_pass_by_copy_for_return_type(_type): + return isinstance(_type, (ArrayType, ObjectType)) or (isinstance(_type, (PrimitiveType)) and _type.qualified_name() == "object") diff --git a/Source/JavaScriptCore/inspector/scripts/codegen/cpp_generator_templates.py b/Source/JavaScriptCore/inspector/scripts/codegen/cpp_generator_templates.py new file mode 100755 index 000000000..b2523ffe2 --- /dev/null +++ b/Source/JavaScriptCore/inspector/scripts/codegen/cpp_generator_templates.py @@ -0,0 +1,258 @@ +#!/usr/bin/env python +# +# Copyright (c) 2014, 2015 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. + +# Generator templates, which can be filled with string.Template. +# Following are classes that fill the templates from the typechecked model. + +class CppGeneratorTemplates: + + HeaderPrelude = ( + """#pragma once + +${includes} + +namespace Inspector { + +${typedefs}""") + + HeaderPostlude = ( + """} // namespace Inspector""") + + ImplementationPrelude = ( + """#include "config.h" +#include ${primaryInclude} + +${secondaryIncludes} + +namespace Inspector {""") + + ImplementationPostlude = ( + """} // namespace Inspector +""") + + AlternateDispatchersHeaderPrelude = ( + """#pragma once + +#if ENABLE(INSPECTOR_ALTERNATE_DISPATCHERS) + +${includes} + +namespace Inspector { + +class AlternateBackendDispatcher { +public: + void setBackendDispatcher(RefPtr<BackendDispatcher>&& dispatcher) { m_backendDispatcher = WTFMove(dispatcher); } + BackendDispatcher* backendDispatcher() const { return m_backendDispatcher.get(); } +private: + RefPtr<BackendDispatcher> m_backendDispatcher; +}; +""") + + AlternateDispatchersHeaderPostlude = ( + """} // namespace Inspector + +#endif // ENABLE(INSPECTOR_ALTERNATE_DISPATCHERS)""") + + AlternateBackendDispatcherHeaderDomainHandlerInterfaceDeclaration = ( + """class Alternate${domainName}BackendDispatcher : public AlternateBackendDispatcher { +public: + virtual ~Alternate${domainName}BackendDispatcher() { } +${commandDeclarations} +};""") + + BackendDispatcherHeaderDomainHandlerDeclaration = ( + """${classAndExportMacro} ${domainName}BackendDispatcherHandler { +public: +${commandDeclarations} +protected: + virtual ~${domainName}BackendDispatcherHandler(); +};""") + + BackendDispatcherHeaderDomainDispatcherDeclaration = ( + """${classAndExportMacro} ${domainName}BackendDispatcher final : public SupplementalBackendDispatcher { +public: + static Ref<${domainName}BackendDispatcher> create(BackendDispatcher&, ${domainName}BackendDispatcherHandler*); + void dispatch(long requestId, const String& method, Ref<InspectorObject>&& message) override; +${commandDeclarations} +private: + ${domainName}BackendDispatcher(BackendDispatcher&, ${domainName}BackendDispatcherHandler*); + ${domainName}BackendDispatcherHandler* m_agent { nullptr }; +};""") + + BackendDispatcherHeaderDomainDispatcherAlternatesDeclaration = ( + """#if ENABLE(INSPECTOR_ALTERNATE_DISPATCHERS) +public: + void setAlternateDispatcher(Alternate${domainName}BackendDispatcher* alternateDispatcher) { m_alternateDispatcher = alternateDispatcher; } +private: + Alternate${domainName}BackendDispatcher* m_alternateDispatcher { nullptr }; +#endif""") + + BackendDispatcherHeaderAsyncCommandDeclaration = ( + """ ${classAndExportMacro} ${callbackName} : public BackendDispatcher::CallbackBase { + public: + ${callbackName}(Ref<BackendDispatcher>&&, int id); + void sendSuccess(${outParameters}); + }; + virtual void ${commandName}(${inParameters}) = 0;""") + + BackendDispatcherImplementationSmallSwitch = ( + """void ${domainName}BackendDispatcher::dispatch(long requestId, const String& method, Ref<InspectorObject>&& message) +{ + Ref<${domainName}BackendDispatcher> protect(*this); + + RefPtr<InspectorObject> parameters; + message->getObject(ASCIILiteral("params"), parameters); + +${dispatchCases} + else + m_backendDispatcher->reportProtocolError(BackendDispatcher::MethodNotFound, makeString('\\'', "${domainName}", '.', method, "' was not found")); +}""") + + BackendDispatcherImplementationLargeSwitch = ( +"""void ${domainName}BackendDispatcher::dispatch(long requestId, const String& method, Ref<InspectorObject>&& message) +{ + Ref<${domainName}BackendDispatcher> protect(*this); + + RefPtr<InspectorObject> parameters; + message->getObject(ASCIILiteral("params"), parameters); + + typedef void (${domainName}BackendDispatcher::*CallHandler)(long requestId, RefPtr<InspectorObject>&& message); + typedef HashMap<String, CallHandler> DispatchMap; + static NeverDestroyed<DispatchMap> dispatchMap; + if (dispatchMap.get().isEmpty()) { + static const struct MethodTable { + const char* name; + CallHandler handler; + } commands[] = { +${dispatchCases} + }; + size_t length = WTF_ARRAY_LENGTH(commands); + for (size_t i = 0; i < length; ++i) + dispatchMap.get().add(commands[i].name, commands[i].handler); + } + + auto findResult = dispatchMap.get().find(method); + if (findResult == dispatchMap.get().end()) { + m_backendDispatcher->reportProtocolError(BackendDispatcher::MethodNotFound, makeString('\\'', "${domainName}", '.', method, "' was not found")); + return; + } + + ((*this).*findResult->value)(requestId, WTFMove(parameters)); +}""") + + BackendDispatcherImplementationDomainConstructor = ( + """Ref<${domainName}BackendDispatcher> ${domainName}BackendDispatcher::create(BackendDispatcher& backendDispatcher, ${domainName}BackendDispatcherHandler* agent) +{ + return adoptRef(*new ${domainName}BackendDispatcher(backendDispatcher, agent)); +} + +${domainName}BackendDispatcher::${domainName}BackendDispatcher(BackendDispatcher& backendDispatcher, ${domainName}BackendDispatcherHandler* agent) + : SupplementalBackendDispatcher(backendDispatcher) + , m_agent(agent) +{ + m_backendDispatcher->registerDispatcherForDomain(ASCIILiteral("${domainName}"), this); +}""") + + BackendDispatcherImplementationPrepareCommandArguments = ( +"""${inParameterDeclarations} + if (m_backendDispatcher->hasProtocolErrors()) { + m_backendDispatcher->reportProtocolError(BackendDispatcher::InvalidParams, String::format("Some arguments of method \'%s\' can't be processed", "${domainName}.${commandName}")); + return; + } +""") + + BackendDispatcherImplementationAsyncCommand = ( +"""${domainName}BackendDispatcherHandler::${callbackName}::${callbackName}(Ref<BackendDispatcher>&& backendDispatcher, int id) : BackendDispatcher::CallbackBase(WTFMove(backendDispatcher), id) { } + +void ${domainName}BackendDispatcherHandler::${callbackName}::sendSuccess(${formalParameters}) +{ + Ref<InspectorObject> jsonMessage = InspectorObject::create(); +${outParameterAssignments} + CallbackBase::sendSuccess(WTFMove(jsonMessage)); +}""") + + FrontendDispatcherDomainDispatcherDeclaration = ( +"""${classAndExportMacro} ${domainName}FrontendDispatcher { +public: + ${domainName}FrontendDispatcher(FrontendRouter& frontendRouter) : m_frontendRouter(frontendRouter) { } +${eventDeclarations} +private: + FrontendRouter& m_frontendRouter; +};""") + + ProtocolObjectBuilderDeclarationPrelude = ( +""" template<int STATE> + class Builder { + private: + RefPtr<InspectorObject> m_result; + + template<int STEP> Builder<STATE | STEP>& castState() + { + return *reinterpret_cast<Builder<STATE | STEP>*>(this); + } + + Builder(Ref</*${objectType}*/InspectorObject>&& object) + : m_result(WTFMove(object)) + { + COMPILE_ASSERT(STATE == NoFieldsSet, builder_created_in_non_init_state); + } + friend class ${objectType}; + public:""") + + ProtocolObjectBuilderDeclarationPostlude = ( +""" + Ref<${objectType}> release() + { + COMPILE_ASSERT(STATE == AllFieldsSet, result_is_not_ready); + COMPILE_ASSERT(sizeof(${objectType}) == sizeof(InspectorObject), cannot_cast); + + Ref<InspectorObject> result = m_result.releaseNonNull(); + return WTFMove(*reinterpret_cast<Ref<${objectType}>*>(&result)); + } + }; + + /* + * Synthetic constructor: +${constructorExample} + */ + static Builder<NoFieldsSet> create() + { + return Builder<NoFieldsSet>(InspectorObject::create()); + }""") + + ProtocolObjectRuntimeCast = ( +"""RefPtr<${objectType}> BindingTraits<${objectType}>::runtimeCast(RefPtr<InspectorValue>&& value) +{ + RefPtr<InspectorObject> result; + bool castSucceeded = value->asObject(result); + ASSERT_UNUSED(castSucceeded, castSucceeded); +#if !ASSERT_DISABLED + BindingTraits<${objectType}>::assertValueHasExpectedType(result.get()); +#endif // !ASSERT_DISABLED + COMPILE_ASSERT(sizeof(${objectType}) == sizeof(InspectorObjectBase), type_cast_problem); + return static_cast<${objectType}*>(static_cast<InspectorObjectBase*>(result.get())); +} +""") diff --git a/Source/JavaScriptCore/inspector/scripts/codegen/generate_cpp_alternate_backend_dispatcher_header.py b/Source/JavaScriptCore/inspector/scripts/codegen/generate_cpp_alternate_backend_dispatcher_header.py new file mode 100755 index 000000000..86e864974 --- /dev/null +++ b/Source/JavaScriptCore/inspector/scripts/codegen/generate_cpp_alternate_backend_dispatcher_header.py @@ -0,0 +1,93 @@ +#!/usr/bin/env python +# +# Copyright (c) 2014, 2016 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 +import re +from string import Template + +from cpp_generator import CppGenerator +from cpp_generator_templates import CppGeneratorTemplates as CppTemplates + +log = logging.getLogger('global') + + +class CppAlternateBackendDispatcherHeaderGenerator(CppGenerator): + def __init__(self, *args, **kwargs): + CppGenerator.__init__(self, *args, **kwargs) + + def output_filename(self): + return '%sAlternateBackendDispatchers.h' % self.protocol_name() + + def generate_output(self): + headers = [ + '"%sProtocolTypes.h"' % self.protocol_name(), + '<inspector/InspectorFrontendRouter.h>', + '<JavaScriptCore/InspectorBackendDispatcher.h>', + ] + + header_args = { + 'includes': '\n'.join(['#include ' + header for header in headers]), + } + + domains = self.domains_to_generate() + sections = [] + sections.append(self.generate_license()) + sections.append(Template(CppTemplates.AlternateDispatchersHeaderPrelude).substitute(None, **header_args)) + sections.append('\n'.join(filter(None, map(self._generate_handler_declarations_for_domain, domains)))) + sections.append(Template(CppTemplates.AlternateDispatchersHeaderPostlude).substitute(None, **header_args)) + return '\n\n'.join(sections) + + def _generate_handler_declarations_for_domain(self, domain): + commands = self.commands_for_domain(domain) + + if not len(commands): + return '' + + command_declarations = [] + for command in commands: + command_declarations.append(self._generate_handler_declaration_for_command(command)) + + handler_args = { + 'domainName': domain.domain_name, + 'commandDeclarations': '\n'.join(command_declarations), + } + + return self.wrap_with_guard_for_domain(domain, Template(CppTemplates.AlternateBackendDispatcherHeaderDomainHandlerInterfaceDeclaration).substitute(None, **handler_args)) + + def _generate_handler_declaration_for_command(self, command): + lines = [] + parameters = ['long callId'] + for _parameter in command.call_parameters: + parameters.append('%s in_%s' % (CppGenerator.cpp_type_for_unchecked_formal_in_parameter(_parameter), _parameter.parameter_name)) + + command_args = { + 'commandName': command.command_name, + 'parameters': ', '.join(parameters), + } + lines.append(' virtual void %(commandName)s(%(parameters)s) = 0;' % command_args) + return '\n'.join(lines) diff --git a/Source/JavaScriptCore/inspector/scripts/codegen/generate_cpp_backend_dispatcher_header.py b/Source/JavaScriptCore/inspector/scripts/codegen/generate_cpp_backend_dispatcher_header.py new file mode 100755 index 000000000..992622bdd --- /dev/null +++ b/Source/JavaScriptCore/inspector/scripts/codegen/generate_cpp_backend_dispatcher_header.py @@ -0,0 +1,217 @@ +#!/usr/bin/env python +# +# Copyright (c) 2014-2016 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 re +import string +from string import Template + +from cpp_generator import CppGenerator +from cpp_generator_templates import CppGeneratorTemplates as CppTemplates +from generator import Generator, ucfirst +from models import EnumType + +log = logging.getLogger('global') + + +class CppBackendDispatcherHeaderGenerator(CppGenerator): + def __init__(self, *args, **kwargs): + CppGenerator.__init__(self, *args, **kwargs) + + def output_filename(self): + return "%sBackendDispatchers.h" % self.protocol_name() + + def domains_to_generate(self): + return filter(lambda domain: len(self.commands_for_domain(domain)) > 0, Generator.domains_to_generate(self)) + + def generate_output(self): + headers = [ + '"%sProtocolObjects.h"' % self.protocol_name(), + '<inspector/InspectorBackendDispatcher.h>', + '<wtf/text/WTFString.h>'] + + typedefs = [('String', 'ErrorString')] + + header_args = { + 'includes': '\n'.join(['#include ' + header for header in headers]), + 'typedefs': '\n'.join(['typedef %s %s;' % typedef for typedef in typedefs]), + } + + domains = self.domains_to_generate() + + sections = [] + sections.append(self.generate_license()) + sections.append(Template(CppTemplates.HeaderPrelude).substitute(None, **header_args)) + if self.model().framework.setting('alternate_dispatchers', False): + sections.append(self._generate_alternate_handler_forward_declarations_for_domains(domains)) + sections.extend(map(self._generate_handler_declarations_for_domain, domains)) + sections.extend(map(self._generate_dispatcher_declarations_for_domain, domains)) + sections.append(Template(CppTemplates.HeaderPostlude).substitute(None, **header_args)) + return "\n\n".join(sections) + + # Private methods. + + def _generate_alternate_handler_forward_declarations_for_domains(self, domains): + if not domains: + return '' + + lines = [] + lines.append('#if ENABLE(INSPECTOR_ALTERNATE_DISPATCHERS)') + for domain in domains: + lines.append(self.wrap_with_guard_for_domain(domain, 'class Alternate%sBackendDispatcher;' % domain.domain_name)) + lines.append('#endif // ENABLE(INSPECTOR_ALTERNATE_DISPATCHERS)') + return '\n'.join(lines) + + def _generate_handler_declarations_for_domain(self, domain): + classComponents = ['class'] + exportMacro = self.model().framework.setting('export_macro', None) + if exportMacro is not None: + classComponents.append(exportMacro) + + used_enum_names = set() + + command_declarations = [] + for command in self.commands_for_domain(domain): + command_declarations.append(self._generate_handler_declaration_for_command(command, used_enum_names)) + + handler_args = { + 'classAndExportMacro': " ".join(classComponents), + 'domainName': domain.domain_name, + 'commandDeclarations': "\n".join(command_declarations) + } + + return self.wrap_with_guard_for_domain(domain, Template(CppTemplates.BackendDispatcherHeaderDomainHandlerDeclaration).substitute(None, **handler_args)) + + def _generate_anonymous_enum_for_parameter(self, parameter, command): + enum_args = { + 'parameterName': parameter.parameter_name, + 'commandName': command.command_name + } + + lines = [] + lines.append(' // Named after parameter \'%(parameterName)s\' while generating command/event %(commandName)s.' % enum_args) + lines.append(' enum class %s {' % ucfirst(parameter.parameter_name)) + for enum_value in parameter.type.enum_values(): + lines.append(' %s = %d,' % (Generator.stylized_name_for_enum_value(enum_value), self.encoding_for_enum_value(enum_value))) + lines.append(' }; // enum class %s' % ucfirst(parameter.parameter_name)) + return '\n'.join(lines) + + def _generate_handler_declaration_for_command(self, command, used_enum_names): + if command.is_async: + return self._generate_async_handler_declaration_for_command(command) + + lines = [] + parameters = ['ErrorString&'] + for _parameter in command.call_parameters: + parameter_name = 'in_' + _parameter.parameter_name + if _parameter.is_optional: + parameter_name = 'opt_' + parameter_name + + parameters.append("%s %s" % (CppGenerator.cpp_type_for_unchecked_formal_in_parameter(_parameter), parameter_name)) + + if isinstance(_parameter.type, EnumType) and _parameter.type.is_anonymous and _parameter.parameter_name not in used_enum_names: + lines.append(self._generate_anonymous_enum_for_parameter(_parameter, command)) + used_enum_names.add(_parameter.parameter_name) + + for _parameter in command.return_parameters: + parameter_name = 'out_' + _parameter.parameter_name + if _parameter.is_optional: + parameter_name = 'opt_' + parameter_name + parameters.append("%s %s" % (CppGenerator.cpp_type_for_formal_out_parameter(_parameter), parameter_name)) + + if isinstance(_parameter.type, EnumType) and _parameter.type.is_anonymous and _parameter.parameter_name not in used_enum_names: + lines.append(self._generate_anonymous_enum_for_parameter(_parameter, command)) + used_enum_names.add(_parameter.parameter_name) + + command_args = { + 'commandName': command.command_name, + 'parameters': ", ".join(parameters) + } + lines.append(' virtual void %(commandName)s(%(parameters)s) = 0;' % command_args) + return '\n'.join(lines) + + def _generate_async_handler_declaration_for_command(self, command): + callbackName = "%sCallback" % ucfirst(command.command_name) + + in_parameters = ['ErrorString&'] + for _parameter in command.call_parameters: + parameter_name = 'in_' + _parameter.parameter_name + if _parameter.is_optional: + parameter_name = 'opt_' + parameter_name + + in_parameters.append("%s %s" % (CppGenerator.cpp_type_for_unchecked_formal_in_parameter(_parameter), parameter_name)) + in_parameters.append("Ref<%s>&& callback" % callbackName) + + out_parameters = [] + for _parameter in command.return_parameters: + out_parameters.append("%s %s" % (CppGenerator.cpp_type_for_formal_async_parameter(_parameter), _parameter.parameter_name)) + + class_components = ['class'] + export_macro = self.model().framework.setting('export_macro', None) + if export_macro: + class_components.append(export_macro) + + command_args = { + 'classAndExportMacro': ' '.join(class_components), + 'callbackName': callbackName, + 'commandName': command.command_name, + 'inParameters': ", ".join(in_parameters), + 'outParameters': ", ".join(out_parameters), + } + + return Template(CppTemplates.BackendDispatcherHeaderAsyncCommandDeclaration).substitute(None, **command_args) + + def _generate_dispatcher_declarations_for_domain(self, domain): + classComponents = ['class'] + exportMacro = self.model().framework.setting('export_macro', None) + if exportMacro is not None: + classComponents.append(exportMacro) + + declarations = [] + commands = self.commands_for_domain(domain) + if len(commands) > 0: + declarations.append('private:') + declarations.extend(map(self._generate_dispatcher_declaration_for_command, commands)) + + declaration_args = { + 'domainName': domain.domain_name, + } + + # Add in a few more declarations at the end if needed. + if self.model().framework.setting('alternate_dispatchers', False): + declarations.append(Template(CppTemplates.BackendDispatcherHeaderDomainDispatcherAlternatesDeclaration).substitute(None, **declaration_args)) + + handler_args = { + 'classAndExportMacro': " ".join(classComponents), + 'domainName': domain.domain_name, + 'commandDeclarations': "\n".join(declarations) + } + + return self.wrap_with_guard_for_domain(domain, Template(CppTemplates.BackendDispatcherHeaderDomainDispatcherDeclaration).substitute(None, **handler_args)) + + def _generate_dispatcher_declaration_for_command(self, command): + return " void %s(long requestId, RefPtr<InspectorObject>&& parameters);" % command.command_name diff --git a/Source/JavaScriptCore/inspector/scripts/codegen/generate_cpp_backend_dispatcher_implementation.py b/Source/JavaScriptCore/inspector/scripts/codegen/generate_cpp_backend_dispatcher_implementation.py new file mode 100755 index 000000000..fccedcba7 --- /dev/null +++ b/Source/JavaScriptCore/inspector/scripts/codegen/generate_cpp_backend_dispatcher_implementation.py @@ -0,0 +1,324 @@ +#!/usr/bin/env python +# +# Copyright (c) 2014-2016 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 + +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 filter(lambda domain: len(self.commands_for_domain(domain)) > 0, Generator.domains_to_generate(self)) + + def generate_output(self): + secondary_headers = [ + '<inspector/InspectorFrontendRouter.h>', + '<inspector/InspectorValues.h>', + '<wtf/NeverDestroyed.h>', + '<wtf/text/CString.h>'] + + secondary_includes = ['#include %s' % header for header in secondary_headers] + + 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(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_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.isAssigned())' % param_args) + out_parameter_assignments.append(' jsonMessage->%(keyedSetMethod)s(ASCIILiteral("%(parameterKey)s"), %(parameterName)s.getValue());' % param_args) + else: + out_parameter_assignments.append(' if (%(parameterName)s)' % param_args) + out_parameter_assignments.append(' jsonMessage->%(keyedSetMethod)s(ASCIILiteral("%(parameterKey)s"), %(parameterName)s);' % param_args) + elif parameter.type.is_enum(): + out_parameter_assignments.append(' jsonMessage->%(keyedSetMethod)s(ASCIILiteral("%(parameterKey)s"), Inspector::Protocol::%(helpersNamespace)s::getEnumConstantValue(%(parameterName)s));' % param_args) + else: + out_parameter_assignments.append(' jsonMessage->%(keyedSetMethod)s(ASCIILiteral("%(parameterKey)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 = ['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(), ASCIILiteral("%(parameterKey)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.isAssigned())' % param_args) + out_parameter_assignments.append(' result->%(keyedSetMethod)s(ASCIILiteral("%(parameterKey)s"), out_%(parameterName)s.getValue());' % param_args) + else: + out_parameter_assignments.append(' if (out_%(parameterName)s)' % param_args) + out_parameter_assignments.append(' result->%(keyedSetMethod)s(ASCIILiteral("%(parameterKey)s"), out_%(parameterName)s);' % param_args) + elif parameter.type.is_enum(): + out_parameter_assignments.append(' result->%(keyedSetMethod)s(ASCIILiteral("%(parameterKey)s"), Inspector::Protocol::%(helpersNamespace)s::getEnumConstantValue(out_%(parameterName)s));' % param_args) + else: + out_parameter_assignments.append(' result->%(keyedSetMethod)s(ASCIILiteral("%(parameterKey)s"), out_%(parameterName)s);' % param_args) + + if CppGenerator.should_pass_by_copy_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<InspectorObject>&&)' % command_args) + else: + lines.append('void %(domainName)sBackendDispatcher::%(commandName)s(long requestId, RefPtr<InspectorObject>&& 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('') + + lines.append(' ErrorString error;') + lines.append(' Ref<InspectorObject> result = InspectorObject::create();') + if command.is_async: + lines.append(' Ref<%(domainName)sBackendDispatcherHandler::%(callbackName)s> callback = adoptRef(*new %(domainName)sBackendDispatcherHandler::%(callbackName)s(m_backendDispatcher.copyRef(), requestId));' % command_args) + if len(command.return_parameters) > 0: + lines.extend(out_parameter_declarations) + lines.append(' m_agent->%(commandName)s(%(invocationParameters)s);' % command_args) + lines.append('') + if command.is_async: + 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(' }') + elif len(command.return_parameters) == 1: + lines.append(' if (!error.length())') + lines.extend(out_parameter_assignments) + lines.append('') + + if not command.is_async: + lines.append(' if (!error.length())') + lines.append(' m_backendDispatcher->sendResponse(requestId, WTFMove(result));') + lines.append(' else') + lines.append(' m_backendDispatcher->reportProtocolError(BackendDispatcher::ServerError, WTFMove(error));') + lines.append('}') + return "\n".join(lines) diff --git a/Source/JavaScriptCore/inspector/scripts/codegen/generate_cpp_frontend_dispatcher_header.py b/Source/JavaScriptCore/inspector/scripts/codegen/generate_cpp_frontend_dispatcher_header.py new file mode 100755 index 000000000..ed58b1e2f --- /dev/null +++ b/Source/JavaScriptCore/inspector/scripts/codegen/generate_cpp_frontend_dispatcher_header.py @@ -0,0 +1,116 @@ +#!/usr/bin/env python +# +# Copyright (c) 2014-2016 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 re +import string +from string import Template + +from cpp_generator import CppGenerator +from cpp_generator_templates import CppGeneratorTemplates as CppTemplates +from generator import Generator, ucfirst +from models import EnumType + +log = logging.getLogger('global') + + +class CppFrontendDispatcherHeaderGenerator(CppGenerator): + def __init__(self, *args, **kwargs): + CppGenerator.__init__(self, *args, **kwargs) + + def output_filename(self): + return "%sFrontendDispatchers.h" % self.protocol_name() + + def domains_to_generate(self): + return filter(lambda domain: len(self.events_for_domain(domain)) > 0, Generator.domains_to_generate(self)) + + def generate_output(self): + headers = [ + '"%sProtocolObjects.h"' % self.protocol_name(), + '<inspector/InspectorValues.h>', + '<wtf/text/WTFString.h>'] + + header_args = { + 'includes': '\n'.join(['#include ' + header for header in headers]), + 'typedefs': 'class FrontendRouter;', + } + + sections = [] + sections.append(self.generate_license()) + sections.append(Template(CppTemplates.HeaderPrelude).substitute(None, **header_args)) + sections.extend(map(self._generate_dispatcher_declarations_for_domain, self.domains_to_generate())) + sections.append(Template(CppTemplates.HeaderPostlude).substitute(None, **header_args)) + return "\n\n".join(sections) + + # Private methods. + + def _generate_anonymous_enum_for_parameter(self, parameter, event): + enum_args = { + 'parameterName': parameter.parameter_name, + 'eventName': event.event_name + } + + lines = [] + lines.append(' // Named after parameter \'%(parameterName)s\' while generating command/event %(eventName)s.' % enum_args) + lines.append(' enum class %s {' % ucfirst(parameter.parameter_name)) + for enum_value in parameter.type.enum_values(): + lines.append(' %s = %d,' % (Generator.stylized_name_for_enum_value(enum_value), self.encoding_for_enum_value(enum_value))) + lines.append(' }; // enum class %s' % ucfirst(parameter.parameter_name)) + return "\n".join(lines) + + def _generate_dispatcher_declarations_for_domain(self, domain): + classComponents = ['class'] + exportMacro = self.model().framework.setting('export_macro', None) + if exportMacro is not None: + classComponents.append(exportMacro) + + used_enum_names = set([]) + + events = self.events_for_domain(domain) + event_declarations = [] + for event in events: + event_declarations.append(self._generate_dispatcher_declaration_for_event(event, domain, used_enum_names)) + + handler_args = { + 'classAndExportMacro': " ".join(classComponents), + 'domainName': domain.domain_name, + 'eventDeclarations': "\n".join(event_declarations) + } + + return self.wrap_with_guard_for_domain(domain, Template(CppTemplates.FrontendDispatcherDomainDispatcherDeclaration).substitute(None, **handler_args)) + + def _generate_dispatcher_declaration_for_event(self, event, domain, used_enum_names): + formal_parameters = [] + lines = [] + for parameter in event.event_parameters: + formal_parameters.append('%s %s' % (CppGenerator.cpp_type_for_checked_formal_event_parameter(parameter), parameter.parameter_name)) + if isinstance(parameter.type, EnumType) and parameter.parameter_name not in used_enum_names: + lines.append(self._generate_anonymous_enum_for_parameter(parameter, event)) + used_enum_names.add(parameter.parameter_name) + + lines.append(" void %s(%s);" % (event.event_name, ", ".join(formal_parameters))) + return "\n".join(lines) diff --git a/Source/JavaScriptCore/inspector/scripts/codegen/generate_cpp_frontend_dispatcher_implementation.py b/Source/JavaScriptCore/inspector/scripts/codegen/generate_cpp_frontend_dispatcher_implementation.py new file mode 100755 index 000000000..0d0806903 --- /dev/null +++ b/Source/JavaScriptCore/inspector/scripts/codegen/generate_cpp_frontend_dispatcher_implementation.py @@ -0,0 +1,125 @@ +#!/usr/bin/env python +# +# Copyright (c) 2014-2016 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 + +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 CppFrontendDispatcherImplementationGenerator(CppGenerator): + def __init__(self, *args, **kwargs): + CppGenerator.__init__(self, *args, **kwargs) + + def output_filename(self): + return "%sFrontendDispatchers.cpp" % self.protocol_name() + + def domains_to_generate(self): + return filter(lambda domain: len(self.events_for_domain(domain)) > 0, Generator.domains_to_generate(self)) + + def generate_output(self): + secondary_headers = [ + '"InspectorFrontendRouter.h"', + '<wtf/text/CString.h>', + ] + + header_args = { + 'primaryInclude': '"%sFrontendDispatchers.h"' % self.protocol_name(), + 'secondaryIncludes': "\n".join(['#include %s' % header for header in secondary_headers]), + } + + sections = [] + sections.append(self.generate_license()) + sections.append(Template(CppTemplates.ImplementationPrelude).substitute(None, **header_args)) + sections.extend(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_dispatcher_implementations_for_domain(self, domain): + implementations = [] + events = self.events_for_domain(domain) + for event in events: + implementations.append(self._generate_dispatcher_implementation_for_event(event, domain)) + + return self.wrap_with_guard_for_domain(domain, '\n\n'.join(implementations)) + + def _generate_dispatcher_implementation_for_event(self, event, domain): + lines = [] + parameter_assignments = [] + formal_parameters = [] + + for parameter in event.event_parameters: + + parameter_value = parameter.parameter_name + if parameter.is_optional and not CppGenerator.should_pass_by_copy_for_return_type(parameter.type): + parameter_value = '*' + parameter_value + if parameter.type.is_enum(): + parameter_value = 'Inspector::Protocol::%s::getEnumConstantValue(%s)' % (self.helpers_namespace(), parameter_value) + + parameter_args = { + 'parameterType': CppGenerator.cpp_type_for_stack_out_parameter(parameter), + 'parameterName': parameter.parameter_name, + 'parameterValue': parameter_value, + 'keyedSetMethod': CppGenerator.cpp_setter_method_for_type(parameter.type), + } + + if parameter.is_optional: + parameter_assignments.append(' if (%(parameterName)s)' % parameter_args) + parameter_assignments.append(' paramsObject->%(keyedSetMethod)s(ASCIILiteral("%(parameterName)s"), %(parameterValue)s);' % parameter_args) + else: + parameter_assignments.append(' paramsObject->%(keyedSetMethod)s(ASCIILiteral("%(parameterName)s"), %(parameterValue)s);' % parameter_args) + + formal_parameters.append('%s %s' % (CppGenerator.cpp_type_for_checked_formal_event_parameter(parameter), parameter.parameter_name)) + + event_args = { + 'domainName': domain.domain_name, + 'eventName': event.event_name, + 'formalParameters': ", ".join(formal_parameters) + } + + lines.append('void %(domainName)sFrontendDispatcher::%(eventName)s(%(formalParameters)s)' % event_args) + lines.append('{') + lines.append(' Ref<InspectorObject> jsonMessage = InspectorObject::create();') + lines.append(' jsonMessage->setString(ASCIILiteral("method"), ASCIILiteral("%(domainName)s.%(eventName)s"));' % event_args) + + if len(parameter_assignments) > 0: + lines.append(' Ref<InspectorObject> paramsObject = InspectorObject::create();') + lines.extend(parameter_assignments) + lines.append(' jsonMessage->setObject(ASCIILiteral("params"), WTFMove(paramsObject));') + + lines.append('') + lines.append(' m_frontendRouter.sendEvent(jsonMessage->toJSONString());') + lines.append('}') + return "\n".join(lines) diff --git a/Source/JavaScriptCore/inspector/scripts/codegen/generate_cpp_protocol_types_header.py b/Source/JavaScriptCore/inspector/scripts/codegen/generate_cpp_protocol_types_header.py new file mode 100755 index 000000000..1f91cdd0f --- /dev/null +++ b/Source/JavaScriptCore/inspector/scripts/codegen/generate_cpp_protocol_types_header.py @@ -0,0 +1,424 @@ +#!/usr/bin/env python +# +# Copyright (c) 2014, 2016 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 re +import string +from operator import methodcaller +from string import Template + +from cpp_generator import CppGenerator +from cpp_generator_templates import CppGeneratorTemplates as CppTemplates +from generator import Generator, ucfirst +from models import EnumType, ObjectType, PrimitiveType, AliasedType, ArrayType, Frameworks + +log = logging.getLogger('global') + + +class CppProtocolTypesHeaderGenerator(CppGenerator): + def __init__(self, *args, **kwargs): + CppGenerator.__init__(self, *args, **kwargs) + + def output_filename(self): + return "%sProtocolObjects.h" % self.protocol_name() + + def generate_output(self): + domains = self.domains_to_generate() + self.calculate_types_requiring_shape_assertions(domains) + + headers = set([ + '<inspector/InspectorProtocolTypes.h>', + '<wtf/Assertions.h>', + ]) + + header_args = { + 'includes': '\n'.join(['#include ' + header for header in sorted(headers)]), + 'typedefs': '', + } + + sections = [] + sections.append(self.generate_license()) + sections.append(Template(CppTemplates.HeaderPrelude).substitute(None, **header_args)) + sections.append('namespace Protocol {') + sections.append(self._generate_forward_declarations(domains)) + sections.append(self._generate_typedefs(domains)) + sections.extend(self._generate_enum_constant_value_conversion_methods()) + builder_sections = map(self._generate_builders_for_domain, domains) + sections.extend(filter(lambda section: len(section) > 0, builder_sections)) + sections.append(self._generate_forward_declarations_for_binding_traits()) + sections.extend(self._generate_declarations_for_enum_conversion_methods()) + sections.append('} // namespace Protocol') + sections.append(Template(CppTemplates.HeaderPostlude).substitute(None, **header_args)) + return "\n\n".join(sections) + + # Private methods. + + # FIXME: move builders out of classes, uncomment forward declaration + + def _generate_forward_declarations(self, domains): + sections = [] + + for domain in domains: + declaration_types = [decl.type for decl in self.type_declarations_for_domain(domain)] + object_types = filter(lambda _type: isinstance(_type, ObjectType), declaration_types) + enum_types = filter(lambda _type: isinstance(_type, EnumType), declaration_types) + sorted(object_types, key=methodcaller('raw_name')) + sorted(enum_types, key=methodcaller('raw_name')) + + if len(object_types) + len(enum_types) == 0: + continue + + domain_lines = [] + domain_lines.append('namespace %s {' % domain.domain_name) + + # Forward-declare all classes so the type builders won't break if rearranged. + domain_lines.extend('class %s;' % object_type.raw_name() for object_type in object_types) + domain_lines.extend('enum class %s;' % enum_type.raw_name() for enum_type in enum_types) + domain_lines.append('} // %s' % domain.domain_name) + sections.append(self.wrap_with_guard_for_domain(domain, '\n'.join(domain_lines))) + + if len(sections) == 0: + return '' + else: + return """// Forward declarations. +%s +// End of forward declarations. +""" % '\n\n'.join(sections) + + def _generate_typedefs(self, domains): + sections = map(self._generate_typedefs_for_domain, domains) + sections = filter(lambda text: len(text) > 0, sections) + + if len(sections) == 0: + return '' + else: + return """// Typedefs. +%s +// End of typedefs.""" % '\n\n'.join(sections) + + def _generate_typedefs_for_domain(self, domain): + type_declarations = self.type_declarations_for_domain(domain) + primitive_declarations = filter(lambda decl: isinstance(decl.type, AliasedType), type_declarations) + array_declarations = filter(lambda decl: isinstance(decl.type, ArrayType), type_declarations) + if len(primitive_declarations) == 0 and len(array_declarations) == 0: + return '' + + sections = [] + for declaration in primitive_declarations: + primitive_name = CppGenerator.cpp_name_for_primitive_type(declaration.type.aliased_type) + typedef_lines = [] + if len(declaration.description) > 0: + typedef_lines.append('/* %s */' % declaration.description) + typedef_lines.append('typedef %s %s;' % (primitive_name, declaration.type_name)) + sections.append('\n'.join(typedef_lines)) + + for declaration in array_declarations: + element_type = CppGenerator.cpp_protocol_type_for_type(declaration.type.element_type) + typedef_lines = [] + if len(declaration.description) > 0: + typedef_lines.append('/* %s */' % declaration.description) + typedef_lines.append('typedef Inspector::Protocol::Array<%s> %s;' % (element_type, declaration.type_name)) + sections.append('\n'.join(typedef_lines)) + + lines = [] + lines.append('namespace %s {' % domain.domain_name) + lines.append('\n'.join(sections)) + lines.append('} // %s' % domain.domain_name) + return self.wrap_with_guard_for_domain(domain, '\n'.join(lines)) + + def _generate_enum_constant_value_conversion_methods(self): + if not self.assigned_enum_values(): + return [] + + return_type = 'String' + return_type_with_export_macro = [return_type] + export_macro = self.model().framework.setting('export_macro', None) + if export_macro is not None: + return_type_with_export_macro[:0] = [export_macro] + + lines = [] + lines.append('namespace %s {' % self.helpers_namespace()) + lines.append('\n'.join([ + '%s getEnumConstantValue(int code);' % ' '.join(return_type_with_export_macro), + '', + 'template<typename T> %s getEnumConstantValue(T enumValue)' % return_type, + '{', + ' return getEnumConstantValue(static_cast<int>(enumValue));', + '}', + ])) + lines.append('} // namespace %s' % self.helpers_namespace()) + return lines + + def _generate_builders_for_domain(self, domain): + sections = [] + + type_declarations = self.type_declarations_for_domain(domain) + for type_declaration in type_declarations: + if isinstance(type_declaration.type, EnumType): + sections.append(self._generate_struct_for_enum_declaration(type_declaration)) + elif isinstance(type_declaration.type, ObjectType): + sections.append(self._generate_class_for_object_declaration(type_declaration, domain)) + + sections = filter(lambda section: len(section) > 0, sections) + if len(sections) == 0: + return '' + + lines = [] + lines.append('namespace %s {' % domain.domain_name) + lines.append('\n'.join(sections)) + lines.append('} // %s' % domain.domain_name) + return self.wrap_with_guard_for_domain(domain, '\n'.join(lines)) + + def _generate_class_for_object_declaration(self, type_declaration, domain): + if len(type_declaration.type_members) == 0: + return '' + + enum_members = filter(lambda member: isinstance(member.type, EnumType) and member.type.is_anonymous, type_declaration.type_members) + required_members = filter(lambda member: not member.is_optional, type_declaration.type_members) + optional_members = filter(lambda member: member.is_optional, type_declaration.type_members) + object_name = type_declaration.type_name + + lines = [] + if len(type_declaration.description) > 0: + lines.append('/* %s */' % type_declaration.description) + base_class = 'Inspector::InspectorObject' + if not Generator.type_has_open_fields(type_declaration.type): + base_class = base_class + 'Base' + lines.append('class %s : public %s {' % (object_name, base_class)) + lines.append('public:') + for enum_member in enum_members: + lines.append(' // Named after property name \'%s\' while generating %s.' % (enum_member.member_name, object_name)) + lines.append(self._generate_struct_for_anonymous_enum_member(enum_member)) + lines.append(self._generate_builder_state_enum(type_declaration)) + + constructor_example = [] + constructor_example.append(' * Ref<%s> result = %s::create()' % (object_name, object_name)) + for member in required_members: + constructor_example.append(' * .set%s(...)' % ucfirst(member.member_name)) + constructor_example.append(' * .release()') + + builder_args = { + 'objectType': object_name, + 'constructorExample': '\n'.join(constructor_example) + ';', + } + + lines.append(Template(CppTemplates.ProtocolObjectBuilderDeclarationPrelude).substitute(None, **builder_args)) + for type_member in required_members: + lines.append(self._generate_builder_setter_for_member(type_member, domain)) + lines.append(Template(CppTemplates.ProtocolObjectBuilderDeclarationPostlude).substitute(None, **builder_args)) + for member in optional_members: + lines.append(self._generate_unchecked_setter_for_member(member, domain)) + + if Generator.type_has_open_fields(type_declaration.type): + lines.append('') + lines.append(' // Property names for type generated as open.') + for type_member in type_declaration.type_members: + export_macro = self.model().framework.setting('export_macro', None) + lines.append(' %s static const char* %s;' % (export_macro, ucfirst(type_member.member_name))) + + lines.append('};') + lines.append('') + return '\n'.join(lines) + + def _generate_struct_for_enum_declaration(self, enum_declaration): + lines = [] + lines.append('/* %s */' % enum_declaration.description) + lines.extend(self._generate_struct_for_enum_type(enum_declaration.type_name, enum_declaration.type)) + return '\n'.join(lines) + + def _generate_struct_for_anonymous_enum_member(self, enum_member): + def apply_indentation(line): + if line.startswith(('#', '/*', '*/', '//')) or len(line) is 0: + return line + else: + return ' ' + line + + indented_lines = map(apply_indentation, self._generate_struct_for_enum_type(enum_member.member_name, enum_member.type)) + return '\n'.join(indented_lines) + + def _generate_struct_for_enum_type(self, enum_name, enum_type): + lines = [] + enum_name = ucfirst(enum_name) + lines.append('enum class %s {' % enum_name) + for enum_value in enum_type.enum_values(): + lines.append(' %s = %s,' % (Generator.stylized_name_for_enum_value(enum_value), self.encoding_for_enum_value(enum_value))) + lines.append('}; // enum class %s' % enum_name) + return lines # The caller may want to adjust indentation, so don't join these lines. + + def _generate_builder_state_enum(self, type_declaration): + lines = [] + required_members = filter(lambda member: not member.is_optional, type_declaration.type_members) + enum_values = [] + + lines.append(' enum {') + lines.append(' NoFieldsSet = 0,') + for i in range(len(required_members)): + enum_value = "%sSet" % ucfirst(required_members[i].member_name) + enum_values.append(enum_value) + lines.append(' %s = 1 << %d,' % (enum_value, i)) + if len(enum_values) > 0: + lines.append(' AllFieldsSet = (%s)' % ' | '.join(enum_values)) + else: + lines.append(' AllFieldsSet = 0') + lines.append(' };') + lines.append('') + return '\n'.join(lines) + + def _generate_builder_setter_for_member(self, type_member, domain): + setter_args = { + 'camelName': ucfirst(type_member.member_name), + 'keyedSet': CppGenerator.cpp_setter_method_for_type(type_member.type), + 'name': type_member.member_name, + 'parameterType': CppGenerator.cpp_type_for_type_member(type_member), + 'helpersNamespace': self.helpers_namespace(), + } + + lines = [] + lines.append('') + lines.append(' Builder<STATE | %(camelName)sSet>& set%(camelName)s(%(parameterType)s value)' % setter_args) + lines.append(' {') + lines.append(' COMPILE_ASSERT(!(STATE & %(camelName)sSet), property_%(name)s_already_set);' % setter_args) + + if isinstance(type_member.type, EnumType): + lines.append(' m_result->%(keyedSet)s(ASCIILiteral("%(name)s"), Inspector::Protocol::%(helpersNamespace)s::getEnumConstantValue(value));' % setter_args) + else: + lines.append(' m_result->%(keyedSet)s(ASCIILiteral("%(name)s"), value);' % setter_args) + lines.append(' return castState<%(camelName)sSet>();' % setter_args) + lines.append(' }') + return '\n'.join(lines) + + def _generate_unchecked_setter_for_member(self, type_member, domain): + setter_args = { + 'camelName': ucfirst(type_member.member_name), + 'keyedSet': CppGenerator.cpp_setter_method_for_type(type_member.type), + 'name': type_member.member_name, + 'parameterType': CppGenerator.cpp_type_for_type_member(type_member), + 'helpersNamespace': self.helpers_namespace(), + } + + lines = [] + lines.append('') + lines.append(' void set%(camelName)s(%(parameterType)s value)' % setter_args) + lines.append(' {') + if isinstance(type_member.type, EnumType): + lines.append(' InspectorObjectBase::%(keyedSet)s(ASCIILiteral("%(name)s"), Inspector::Protocol::%(helpersNamespace)s::getEnumConstantValue(value));' % setter_args) + elif CppGenerator.should_use_references_for_type(type_member.type): + lines.append(' InspectorObjectBase::%(keyedSet)s(ASCIILiteral("%(name)s"), WTFMove(value));' % setter_args) + else: + lines.append(' InspectorObjectBase::%(keyedSet)s(ASCIILiteral("%(name)s"), value);' % setter_args) + lines.append(' }') + return '\n'.join(lines) + + def _generate_forward_declarations_for_binding_traits(self): + # A list of (builder_type, needs_runtime_cast) + type_arguments = [] + + for domain in self.domains_to_generate(): + type_declarations = self.type_declarations_for_domain(domain) + declarations_to_generate = filter(lambda decl: self.type_needs_shape_assertions(decl.type), type_declarations) + + for type_declaration in declarations_to_generate: + for type_member in type_declaration.type_members: + if isinstance(type_member.type, EnumType): + type_arguments.append((CppGenerator.cpp_protocol_type_for_type_member(type_member, type_declaration), False)) + + if isinstance(type_declaration.type, ObjectType): + type_arguments.append((CppGenerator.cpp_protocol_type_for_type(type_declaration.type), Generator.type_needs_runtime_casts(type_declaration.type))) + + struct_keywords = ['struct'] + function_keywords = ['static void'] + export_macro = self.model().framework.setting('export_macro', None) + if export_macro is not None: + struct_keywords.append(export_macro) + #function_keywords[1:1] = [export_macro] + + lines = [] + for argument in type_arguments: + lines.append('template<> %s BindingTraits<%s> {' % (' '.join(struct_keywords), argument[0])) + if argument[1]: + lines.append('static RefPtr<%s> runtimeCast(RefPtr<Inspector::InspectorValue>&& value);' % argument[0]) + lines.append('#if !ASSERT_DISABLED') + lines.append('%s assertValueHasExpectedType(Inspector::InspectorValue*);' % ' '.join(function_keywords)) + lines.append('#endif // !ASSERT_DISABLED') + lines.append('};') + return '\n'.join(lines) + + def _generate_declarations_for_enum_conversion_methods(self): + sections = [] + sections.append('\n'.join([ + 'namespace %s {' % self.helpers_namespace(), + '', + 'template<typename ProtocolEnumType>', + 'std::optional<ProtocolEnumType> parseEnumValueFromString(const String&);', + ])) + + def return_type_with_export_macro(cpp_protocol_type): + enum_return_type = 'std::optional<%s>' % cpp_protocol_type + result_terms = [enum_return_type] + export_macro = self.model().framework.setting('export_macro', None) + if export_macro is not None: + result_terms[:0] = [export_macro] + return ' '.join(result_terms) + + def type_member_is_anonymous_enum_type(type_member): + return isinstance(type_member.type, EnumType) and type_member.type.is_anonymous + + for domain in self.domains_to_generate(): + type_declarations = self.type_declarations_for_domain(domain) + declaration_types = [decl.type for decl in type_declarations] + object_types = filter(lambda _type: isinstance(_type, ObjectType), declaration_types) + enum_types = filter(lambda _type: isinstance(_type, EnumType), declaration_types) + if len(object_types) + len(enum_types) == 0: + continue + + sorted(object_types, key=methodcaller('raw_name')) + sorted(enum_types, key=methodcaller('raw_name')) + + domain_lines = [] + domain_lines.append("// Enums in the '%s' Domain" % domain.domain_name) + for enum_type in enum_types: + cpp_protocol_type = CppGenerator.cpp_protocol_type_for_type(enum_type) + domain_lines.append('template<>') + domain_lines.append('%s parseEnumValueFromString<%s>(const String&);' % (return_type_with_export_macro(cpp_protocol_type), cpp_protocol_type)) + + for object_type in object_types: + for enum_member in filter(type_member_is_anonymous_enum_type, object_type.members): + cpp_protocol_type = CppGenerator.cpp_protocol_type_for_type_member(enum_member, object_type.declaration()) + domain_lines.append('template<>') + domain_lines.append('%s parseEnumValueFromString<%s>(const String&);' % (return_type_with_export_macro(cpp_protocol_type), cpp_protocol_type)) + + if len(domain_lines) == 1: + continue # No real declarations to emit, just the domain comment. Skip. + + sections.append(self.wrap_with_guard_for_domain(domain, '\n'.join(domain_lines))) + + if len(sections) == 1: + return [] # No real sections to emit, just the namespace and template declaration. Skip. + + sections.append('} // namespace %s' % self.helpers_namespace()) + + return ['\n\n'.join(sections)] diff --git a/Source/JavaScriptCore/inspector/scripts/codegen/generate_cpp_protocol_types_implementation.py b/Source/JavaScriptCore/inspector/scripts/codegen/generate_cpp_protocol_types_implementation.py new file mode 100755 index 000000000..f302a7342 --- /dev/null +++ b/Source/JavaScriptCore/inspector/scripts/codegen/generate_cpp_protocol_types_implementation.py @@ -0,0 +1,260 @@ +#!/usr/bin/env python +# +# Copyright (c) 2014, 2016 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 +from operator import methodcaller + +from cpp_generator import CppGenerator +from cpp_generator_templates import CppGeneratorTemplates as CppTemplates +from generator import Generator, ucfirst +from models import AliasedType, ArrayType, EnumType, ObjectType + +log = logging.getLogger('global') + + +class CppProtocolTypesImplementationGenerator(CppGenerator): + def __init__(self, *args, **kwargs): + CppGenerator.__init__(self, *args, **kwargs) + + def output_filename(self): + return "%sProtocolObjects.cpp" % self.protocol_name() + + def generate_output(self): + domains = self.domains_to_generate() + self.calculate_types_requiring_shape_assertions(domains) + + secondary_headers = [ + '<wtf/Optional.h>', + '<wtf/text/CString.h>', + ] + + header_args = { + 'primaryInclude': '"%sProtocolObjects.h"' % self.protocol_name(), + 'secondaryIncludes': "\n".join(['#include %s' % header for header in secondary_headers]), + } + + sections = [] + sections.append(self.generate_license()) + sections.append(Template(CppTemplates.ImplementationPrelude).substitute(None, **header_args)) + sections.append('namespace Protocol {') + sections.extend(self._generate_enum_mapping_and_conversion_methods(domains)) + sections.append(self._generate_open_field_names()) + builder_sections = map(self._generate_builders_for_domain, domains) + sections.extend(filter(lambda section: len(section) > 0, builder_sections)) + sections.append('} // namespace Protocol') + sections.append(Template(CppTemplates.ImplementationPostlude).substitute(None, **header_args)) + + return "\n\n".join(sections) + + # Private methods. + + def _generate_enum_mapping(self): + if not self.assigned_enum_values(): + return [] + + lines = [] + lines.append('static const char* const enum_constant_values[] = {') + lines.extend([' "%s",' % enum_value for enum_value in self.assigned_enum_values()]) + lines.append('};') + lines.append('') + lines.append('String getEnumConstantValue(int code) {') + lines.append(' return enum_constant_values[code];') + lines.append('}') + return ['\n'.join(lines)] + + def _generate_enum_conversion_methods_for_domain(self, domain): + + def type_member_is_anonymous_enum_type(type_member): + return isinstance(type_member.type, EnumType) and type_member.type.is_anonymous + + def generate_conversion_method_body(enum_type, cpp_protocol_type): + body_lines = [] + body_lines.extend([ + 'template<>', + 'std::optional<%s> parseEnumValueFromString<%s>(const String& protocolString)' % (cpp_protocol_type, cpp_protocol_type), + '{', + ' static const size_t constantValues[] = {', + ]) + + enum_values = enum_type.enum_values() + for enum_value in enum_values: + body_lines.append(' (size_t)%s::%s,' % (cpp_protocol_type, Generator.stylized_name_for_enum_value(enum_value))) + + body_lines.extend([ + ' };', + ' for (size_t i = 0; i < %d; ++i)' % len(enum_values), + ' if (protocolString == enum_constant_values[constantValues[i]])', + ' return (%s)constantValues[i];' % cpp_protocol_type, + '', + ' return std::nullopt;', + '}', + '', + ]) + return body_lines + + type_declarations = self.type_declarations_for_domain(domain) + declaration_types = [decl.type for decl in type_declarations] + object_types = filter(lambda _type: isinstance(_type, ObjectType), declaration_types) + enum_types = filter(lambda _type: isinstance(_type, EnumType), declaration_types) + if len(object_types) + len(enum_types) == 0: + return '' + + sorted(object_types, key=methodcaller('raw_name')) + sorted(enum_types, key=methodcaller('raw_name')) + + lines = [] + lines.append("// Enums in the '%s' Domain" % domain.domain_name) + for enum_type in enum_types: + cpp_protocol_type = CppGenerator.cpp_protocol_type_for_type(enum_type) + lines.extend(generate_conversion_method_body(enum_type, cpp_protocol_type)) + + for object_type in object_types: + for enum_member in filter(type_member_is_anonymous_enum_type, object_type.members): + cpp_protocol_type = CppGenerator.cpp_protocol_type_for_type_member(enum_member, object_type.declaration()) + lines.extend(generate_conversion_method_body(enum_member.type, cpp_protocol_type)) + + if len(lines) == 1: + return '' # No real declarations to emit, just the domain comment. + + return self.wrap_with_guard_for_domain(domain, '\n'.join(lines)) + + def _generate_enum_mapping_and_conversion_methods(self, domains): + sections = [] + sections.append('namespace %s {' % self.helpers_namespace()) + sections.extend(self._generate_enum_mapping()) + enum_parser_sections = map(self._generate_enum_conversion_methods_for_domain, domains) + sections.extend(filter(lambda section: len(section) > 0, enum_parser_sections)) + if len(sections) == 1: + return [] # No declarations to emit, just the namespace. + + sections.append('} // namespace %s' % self.helpers_namespace()) + return sections + + def _generate_open_field_names(self): + lines = [] + for domain in self.domains_to_generate(): + type_declarations = self.type_declarations_for_domain(domain) + for type_declaration in filter(lambda decl: Generator.type_has_open_fields(decl.type), type_declarations): + for type_member in sorted(type_declaration.type_members, key=lambda member: member.member_name): + field_name = '::'.join(['Inspector', 'Protocol', domain.domain_name, ucfirst(type_declaration.type_name), ucfirst(type_member.member_name)]) + lines.append('const char* %s = "%s";' % (field_name, type_member.member_name)) + + return '\n'.join(lines) + + def _generate_builders_for_domain(self, domain): + sections = [] + type_declarations = self.type_declarations_for_domain(domain) + declarations_to_generate = filter(lambda decl: self.type_needs_shape_assertions(decl.type), type_declarations) + + for type_declaration in declarations_to_generate: + for type_member in type_declaration.type_members: + if isinstance(type_member.type, EnumType): + sections.append(self._generate_assertion_for_enum(type_member, type_declaration)) + + if isinstance(type_declaration.type, ObjectType): + sections.append(self._generate_assertion_for_object_declaration(type_declaration)) + if Generator.type_needs_runtime_casts(type_declaration.type): + sections.append(self._generate_runtime_cast_for_object_declaration(type_declaration)) + + return '\n\n'.join(sections) + + def _generate_runtime_cast_for_object_declaration(self, object_declaration): + args = { + 'objectType': CppGenerator.cpp_protocol_type_for_type(object_declaration.type) + } + return Template(CppTemplates.ProtocolObjectRuntimeCast).substitute(None, **args) + + def _generate_assertion_for_object_declaration(self, object_declaration): + required_members = filter(lambda member: not member.is_optional, object_declaration.type_members) + optional_members = filter(lambda member: member.is_optional, object_declaration.type_members) + should_count_properties = not Generator.type_has_open_fields(object_declaration.type) + lines = [] + + lines.append('#if !ASSERT_DISABLED') + lines.append('void BindingTraits<%s>::assertValueHasExpectedType(Inspector::InspectorValue* value)' % (CppGenerator.cpp_protocol_type_for_type(object_declaration.type))) + lines.append("""{ + ASSERT_ARG(value, value); + RefPtr<InspectorObject> object; + bool castSucceeded = value->asObject(object); + ASSERT_UNUSED(castSucceeded, castSucceeded);""") + for type_member in required_members: + args = { + 'memberName': type_member.member_name, + 'assertMethod': CppGenerator.cpp_assertion_method_for_type_member(type_member, object_declaration) + } + + lines.append(""" { + InspectorObject::iterator %(memberName)sPos = object->find(ASCIILiteral("%(memberName)s")); + ASSERT(%(memberName)sPos != object->end()); + %(assertMethod)s(%(memberName)sPos->value.get()); + }""" % args) + + if should_count_properties: + lines.append('') + lines.append(' int foundPropertiesCount = %s;' % len(required_members)) + + for type_member in optional_members: + args = { + 'memberName': type_member.member_name, + 'assertMethod': CppGenerator.cpp_assertion_method_for_type_member(type_member, object_declaration) + } + + lines.append(""" { + InspectorObject::iterator %(memberName)sPos = object->find(ASCIILiteral("%(memberName)s")); + if (%(memberName)sPos != object->end()) { + %(assertMethod)s(%(memberName)sPos->value.get());""" % args) + + if should_count_properties: + lines.append(' ++foundPropertiesCount;') + lines.append(' }') + lines.append(' }') + + if should_count_properties: + lines.append(' if (foundPropertiesCount != object->size())') + lines.append(' FATAL("Unexpected properties in object: %s\\n", object->toJSONString().ascii().data());') + lines.append('}') + lines.append('#endif // !ASSERT_DISABLED') + return '\n'.join(lines) + + def _generate_assertion_for_enum(self, enum_member, object_declaration): + lines = [] + lines.append('#if !ASSERT_DISABLED') + lines.append('void %s(Inspector::InspectorValue* value)' % CppGenerator.cpp_assertion_method_for_type_member(enum_member, object_declaration)) + lines.append('{') + lines.append(' ASSERT_ARG(value, value);') + lines.append(' String result;') + lines.append(' bool castSucceeded = value->asString(result);') + lines.append(' ASSERT(castSucceeded);') + + assert_condition = ' || '.join(['result == "%s"' % enum_value for enum_value in enum_member.type.enum_values()]) + lines.append(' ASSERT(%s);' % assert_condition) + lines.append('}') + lines.append('#endif // !ASSERT_DISABLED') + + return '\n'.join(lines) diff --git a/Source/JavaScriptCore/inspector/scripts/codegen/generate_js_backend_commands.py b/Source/JavaScriptCore/inspector/scripts/codegen/generate_js_backend_commands.py new file mode 100755 index 000000000..555c12f89 --- /dev/null +++ b/Source/JavaScriptCore/inspector/scripts/codegen/generate_js_backend_commands.py @@ -0,0 +1,145 @@ +#!/usr/bin/env python +# +# Copyright (c) 2014, 2016 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 + +from generator import Generator, ucfirst +from generator_templates import GeneratorTemplates as Templates +from models import EnumType + +log = logging.getLogger('global') + + +class JSBackendCommandsGenerator(Generator): + def __init__(self, *args, **kwargs): + Generator.__init__(self, *args, **kwargs) + + def output_filename(self): + return "InspectorBackendCommands.js" + + def should_generate_domain(self, domain): + type_declarations = self.type_declarations_for_domain(domain) + domain_enum_types = filter(lambda declaration: isinstance(declaration.type, EnumType), type_declarations) + return len(self.commands_for_domain(domain)) > 0 or len(self.events_for_domain(domain)) > 0 or len(domain_enum_types) > 0 + + def domains_to_generate(self): + return filter(self.should_generate_domain, Generator.domains_to_generate(self)) + + def generate_output(self): + sections = [] + sections.append(self.generate_license()) + sections.extend(map(self.generate_domain, self.domains_to_generate())) + return "\n\n".join(sections) + + def generate_domain(self, domain): + lines = [] + args = { + 'domain': domain.domain_name + } + + lines.append('// %(domain)s.' % args) + + type_declarations = self.type_declarations_for_domain(domain) + commands = self.commands_for_domain(domain) + events = self.events_for_domain(domain) + + has_async_commands = any(map(lambda command: command.is_async, commands)) + if len(events) > 0 or has_async_commands: + lines.append('InspectorBackend.register%(domain)sDispatcher = InspectorBackend.registerDomainDispatcher.bind(InspectorBackend, "%(domain)s");' % args) + + for declaration in type_declarations: + if declaration.type.is_enum(): + enum_args = { + 'domain': domain.domain_name, + 'enumName': declaration.type_name, + 'enumMap': ", ".join(['%s: "%s"' % (Generator.stylized_name_for_enum_value(enum_value), enum_value) for enum_value in declaration.type.enum_values()]) + } + lines.append('InspectorBackend.registerEnum("%(domain)s.%(enumName)s", {%(enumMap)s});' % enum_args) + + def is_anonymous_enum_member(type_member): + return isinstance(type_member.type, EnumType) and type_member.type.is_anonymous + + for _member in filter(is_anonymous_enum_member, declaration.type_members): + enum_args = { + 'domain': domain.domain_name, + 'enumName': '%s%s' % (declaration.type_name, ucfirst(_member.member_name)), + 'enumMap': ", ".join(['%s: "%s"' % (Generator.stylized_name_for_enum_value(enum_value), enum_value) for enum_value in _member.type.enum_values()]) + } + lines.append('InspectorBackend.registerEnum("%(domain)s.%(enumName)s", {%(enumMap)s});' % enum_args) + + def is_anonymous_enum_param(param): + return isinstance(param.type, EnumType) and param.type.is_anonymous + + for event in events: + for param in filter(is_anonymous_enum_param, event.event_parameters): + enum_args = { + 'domain': domain.domain_name, + 'enumName': '%s%s' % (ucfirst(event.event_name), ucfirst(param.parameter_name)), + 'enumMap': ", ".join(['%s: "%s"' % (Generator.stylized_name_for_enum_value(enum_value), enum_value) for enum_value in param.type.enum_values()]) + } + lines.append('InspectorBackend.registerEnum("%(domain)s.%(enumName)s", {%(enumMap)s});' % enum_args) + + event_args = { + 'domain': domain.domain_name, + 'eventName': event.event_name, + 'params': ", ".join(['"%s"' % parameter.parameter_name for parameter in event.event_parameters]) + } + lines.append('InspectorBackend.registerEvent("%(domain)s.%(eventName)s", [%(params)s]);' % event_args) + + for command in commands: + def generate_parameter_object(parameter): + optional_string = "true" if parameter.is_optional else "false" + pairs = [] + pairs.append('"name": "%s"' % parameter.parameter_name) + pairs.append('"type": "%s"' % Generator.js_name_for_parameter_type(parameter.type)) + pairs.append('"optional": %s' % optional_string) + return "{%s}" % ", ".join(pairs) + + command_args = { + 'domain': domain.domain_name, + 'commandName': command.command_name, + 'callParams': ", ".join([generate_parameter_object(parameter) for parameter in command.call_parameters]), + 'returnParams': ", ".join(['"%s"' % parameter.parameter_name for parameter in command.return_parameters]), + } + lines.append('InspectorBackend.registerCommand("%(domain)s.%(commandName)s", [%(callParams)s], [%(returnParams)s]);' % command_args) + + if commands or events: + activate_args = { + 'domain': domain.domain_name, + 'availability': domain.availability, + } + if domain.availability: + lines.append('InspectorBackend.activateDomain("%(domain)s", "%(availability)s");' % activate_args) + else: + lines.append('InspectorBackend.activateDomain("%(domain)s");' % activate_args) + + if domain.workerSupported: + lines.append('InspectorBackend.workerSupportedDomain("%s");' % domain.domain_name) + + return "\n".join(lines) diff --git a/Source/JavaScriptCore/inspector/scripts/codegen/generate_objc_backend_dispatcher_header.py b/Source/JavaScriptCore/inspector/scripts/codegen/generate_objc_backend_dispatcher_header.py new file mode 100755 index 000000000..0531ed53b --- /dev/null +++ b/Source/JavaScriptCore/inspector/scripts/codegen/generate_objc_backend_dispatcher_header.py @@ -0,0 +1,106 @@ +#!/usr/bin/env python +# +# Copyright (c) 2014, 2016 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 +import re +from string import Template + +from cpp_generator import CppGenerator +from generator import Generator +from models import Frameworks +from objc_generator import ObjCGenerator +from objc_generator_templates import ObjCGeneratorTemplates as ObjCTemplates + +log = logging.getLogger('global') + + +class ObjCBackendDispatcherHeaderGenerator(ObjCGenerator): + def __init__(self, *args, **kwargs): + ObjCGenerator.__init__(self, *args, **kwargs) + + def output_filename(self): + return '%sBackendDispatchers.h' % self.protocol_name() + + def domains_to_generate(self): + return filter(self.should_generate_commands_for_domain, Generator.domains_to_generate(self)) + + def generate_output(self): + headers = [ + '<JavaScriptCore/InspectorAlternateBackendDispatchers.h>', + '<wtf/RetainPtr.h>', + ] + + header_args = { + 'includes': '\n'.join(['#include ' + header for header in headers]), + 'forwardDeclarations': self._generate_objc_forward_declarations(), + } + + domains = self.domains_to_generate() + sections = [] + sections.append(self.generate_license()) + sections.append(Template(ObjCTemplates.BackendDispatcherHeaderPrelude).substitute(None, **header_args)) + sections.extend(map(self._generate_objc_handler_declarations_for_domain, domains)) + sections.append(Template(ObjCTemplates.BackendDispatcherHeaderPostlude).substitute(None, **header_args)) + return '\n\n'.join(sections) + + def _generate_objc_forward_declarations(self): + lines = [] + for domain in self.domains_to_generate(): + if self.commands_for_domain(domain): + lines.append('@protocol %s%sDomainHandler;' % (self.objc_prefix(), domain.domain_name)) + return '\n'.join(lines) + + def _generate_objc_handler_declarations_for_domain(self, domain): + commands = self.commands_for_domain(domain) + if not commands: + return '' + + command_declarations = [] + for command in commands: + command_declarations.append(self._generate_objc_handler_declaration_for_command(command)) + + handler_args = { + 'domainName': domain.domain_name, + 'commandDeclarations': '\n'.join(command_declarations), + 'objcPrefix': self.objc_prefix(), + } + + return self.wrap_with_guard_for_domain(domain, Template(ObjCTemplates.BackendDispatcherHeaderDomainHandlerObjCDeclaration).substitute(None, **handler_args)) + + def _generate_objc_handler_declaration_for_command(self, command): + lines = [] + parameters = ['long requestId'] + for _parameter in command.call_parameters: + parameters.append('%s in_%s' % (CppGenerator.cpp_type_for_unchecked_formal_in_parameter(_parameter), _parameter.parameter_name)) + + command_args = { + 'commandName': command.command_name, + 'parameters': ', '.join(parameters), + } + lines.append(' virtual void %(commandName)s(%(parameters)s) override;' % command_args) + return '\n'.join(lines) diff --git a/Source/JavaScriptCore/inspector/scripts/codegen/generate_objc_backend_dispatcher_implementation.py b/Source/JavaScriptCore/inspector/scripts/codegen/generate_objc_backend_dispatcher_implementation.py new file mode 100755 index 000000000..0b1055d24 --- /dev/null +++ b/Source/JavaScriptCore/inspector/scripts/codegen/generate_objc_backend_dispatcher_implementation.py @@ -0,0 +1,210 @@ +#!/usr/bin/env python +# +# Copyright (c) 2014-2016 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 +import re +from string import Template + +from cpp_generator import CppGenerator +from generator import Generator +from models import PrimitiveType, EnumType, AliasedType, Frameworks +from objc_generator import ObjCTypeCategory, ObjCGenerator, join_type_and_name +from objc_generator_templates import ObjCGeneratorTemplates as ObjCTemplates + +log = logging.getLogger('global') + + +class ObjCBackendDispatcherImplementationGenerator(ObjCGenerator): + def __init__(self, *args, **kwargs): + ObjCGenerator.__init__(self, *args, **kwargs) + + def output_filename(self): + return '%sBackendDispatchers.mm' % self.protocol_name() + + def domains_to_generate(self): + return filter(self.should_generate_commands_for_domain, Generator.domains_to_generate(self)) + + def generate_output(self): + secondary_headers = [ + '"%sInternal.h"' % self.protocol_name(), + '"%sTypeConversions.h"' % self.protocol_name(), + '<JavaScriptCore/InspectorValues.h>', + ] + + header_args = { + 'primaryInclude': '"%sBackendDispatchers.h"' % self.protocol_name(), + 'secondaryIncludes': '\n'.join(['#include %s' % header for header in secondary_headers]), + } + + domains = self.domains_to_generate() + sections = [] + sections.append(self.generate_license()) + sections.append(Template(ObjCTemplates.BackendDispatcherImplementationPrelude).substitute(None, **header_args)) + sections.extend(map(self._generate_handler_implementation_for_domain, domains)) + sections.append(Template(ObjCTemplates.BackendDispatcherImplementationPostlude).substitute(None, **header_args)) + return '\n\n'.join(sections) + + def _generate_handler_implementation_for_domain(self, domain): + commands = self.commands_for_domain(domain) + + if not commands: + return '' + + command_declarations = [] + for command in commands: + command_declarations.append(self._generate_handler_implementation_for_command(domain, command)) + + return '\n'.join(command_declarations) + + def _generate_handler_implementation_for_command(self, domain, command): + lines = [] + parameters = ['long requestId'] + for parameter in command.call_parameters: + parameters.append('%s in_%s' % (CppGenerator.cpp_type_for_unchecked_formal_in_parameter(parameter), parameter.parameter_name)) + + command_args = { + 'domainName': domain.domain_name, + 'commandName': command.command_name, + 'parameters': ', '.join(parameters), + 'successCallback': self._generate_success_block_for_command(domain, command), + 'conversions': self._generate_conversions_for_command(domain, command), + 'invocation': self._generate_invocation_for_command(domain, command), + } + + return self.wrap_with_guard_for_domain(domain, Template(ObjCTemplates.BackendDispatcherHeaderDomainHandlerImplementation).substitute(None, **command_args)) + + def _generate_success_block_for_command(self, domain, command): + lines = [] + + if command.return_parameters: + success_block_parameters = [] + for parameter in command.return_parameters: + objc_type = self.objc_type_for_param(domain, command.command_name, parameter) + var_name = ObjCGenerator.identifier_to_objc_identifier(parameter.parameter_name) + success_block_parameters.append(join_type_and_name(objc_type, var_name)) + lines.append(' id successCallback = ^(%s) {' % ', '.join(success_block_parameters)) + else: + lines.append(' id successCallback = ^{') + + if command.return_parameters: + lines.append(' Ref<InspectorObject> resultObject = InspectorObject::create();') + + required_pointer_parameters = filter(lambda parameter: not parameter.is_optional and ObjCGenerator.is_type_objc_pointer_type(parameter.type), command.return_parameters) + for parameter in required_pointer_parameters: + var_name = ObjCGenerator.identifier_to_objc_identifier(parameter.parameter_name) + lines.append(' THROW_EXCEPTION_FOR_REQUIRED_PARAMETER(%s, @"%s");' % (var_name, var_name)) + objc_array_class = self.objc_class_for_array_type(parameter.type) + if objc_array_class and objc_array_class.startswith(self.objc_prefix()): + lines.append(' THROW_EXCEPTION_FOR_BAD_TYPE_IN_ARRAY(%s, [%s class]);' % (var_name, objc_array_class)) + + optional_pointer_parameters = filter(lambda parameter: parameter.is_optional and ObjCGenerator.is_type_objc_pointer_type(parameter.type), command.return_parameters) + for parameter in optional_pointer_parameters: + var_name = ObjCGenerator.identifier_to_objc_identifier(parameter.parameter_name) + lines.append(' THROW_EXCEPTION_FOR_BAD_OPTIONAL_PARAMETER(%s, @"%s");' % (var_name, var_name)) + objc_array_class = self.objc_class_for_array_type(parameter.type) + if objc_array_class and objc_array_class.startswith(self.objc_prefix()): + lines.append(' THROW_EXCEPTION_FOR_BAD_TYPE_IN_OPTIONAL_ARRAY(%s, [%s class]);' % (var_name, objc_array_class)) + + for parameter in command.return_parameters: + keyed_set_method = CppGenerator.cpp_setter_method_for_type(parameter.type) + var_name = ObjCGenerator.identifier_to_objc_identifier(parameter.parameter_name) + var_expression = '*%s' % var_name if parameter.is_optional else var_name + export_expression = self.objc_protocol_export_expression_for_variable(parameter.type, var_expression) + if not parameter.is_optional: + lines.append(' resultObject->%s(ASCIILiteral("%s"), %s);' % (keyed_set_method, parameter.parameter_name, export_expression)) + else: + lines.append(' if (%s)' % var_name) + lines.append(' resultObject->%s(ASCIILiteral("%s"), %s);' % (keyed_set_method, parameter.parameter_name, export_expression)) + lines.append(' backendDispatcher()->sendResponse(requestId, WTFMove(resultObject));') + else: + lines.append(' backendDispatcher()->sendResponse(requestId, InspectorObject::create());') + + lines.append(' };') + return '\n'.join(lines) + + def _generate_conversions_for_command(self, domain, command): + lines = [] + if command.call_parameters: + lines.append('') + + def in_param_expression(param_name, parameter): + _type = parameter.type + if isinstance(_type, AliasedType): + _type = _type.aliased_type # Fall through to enum or primitive. + if isinstance(_type, EnumType): + _type = _type.primitive_type # Fall through to primitive. + if isinstance(_type, PrimitiveType): + if _type.raw_name() in ['array', 'any', 'object']: + return '&%s' % param_name if not parameter.is_optional else param_name + return '*%s' % param_name if parameter.is_optional else param_name + return '&%s' % param_name if not parameter.is_optional else param_name + + for parameter in command.call_parameters: + in_param_name = 'in_%s' % parameter.parameter_name + objc_in_param_name = 'o_%s' % in_param_name + objc_type = self.objc_type_for_param(domain, command.command_name, parameter, False) + if isinstance(parameter.type, EnumType): + objc_type = 'std::optional<%s>' % objc_type + param_expression = in_param_expression(in_param_name, parameter) + import_expression = self.objc_protocol_import_expression_for_parameter(param_expression, domain, command.command_name, parameter) + if not parameter.is_optional: + lines.append(' %s = %s;' % (join_type_and_name(objc_type, objc_in_param_name), import_expression)) + + if isinstance(parameter.type, EnumType): + lines.append(' if (!%s) {' % objc_in_param_name) + lines.append(' backendDispatcher()->reportProtocolError(BackendDispatcher::InvalidParams, String::format("Parameter \'%%s\' of method \'%%s\' cannot be processed", "%s", "%s.%s"));' % (parameter.parameter_name, domain.domain_name, command.command_name)) + lines.append(' return;') + lines.append(' }') + + else: + lines.append(' %s;' % join_type_and_name(objc_type, objc_in_param_name)) + lines.append(' if (%s)' % in_param_name) + lines.append(' %s = %s;' % (objc_in_param_name, import_expression)) + + if lines: + lines.append('') + return '\n'.join(lines) + + def _generate_invocation_for_command(self, domain, command): + pairs = [] + pairs.append('WithErrorCallback:errorCallback') + pairs.append('successCallback:successCallback') + for parameter in command.call_parameters: + in_param_name = 'in_%s' % parameter.parameter_name + objc_in_param_expression = 'o_%s' % in_param_name + if not parameter.is_optional: + # FIXME: we don't handle optional enum values in commands here because it isn't used anywhere yet. + # We'd need to change the delegate's signature to take std::optional for optional enum values. + if isinstance(parameter.type, EnumType): + objc_in_param_expression = '%s.value()' % objc_in_param_expression + + pairs.append('%s:%s' % (parameter.parameter_name, objc_in_param_expression)) + else: + optional_expression = '(%s ? &%s : nil)' % (in_param_name, objc_in_param_expression) + pairs.append('%s:%s' % (parameter.parameter_name, optional_expression)) + return ' [m_delegate %s%s];' % (command.command_name, ' '.join(pairs)) diff --git a/Source/JavaScriptCore/inspector/scripts/codegen/generate_objc_configuration_header.py b/Source/JavaScriptCore/inspector/scripts/codegen/generate_objc_configuration_header.py new file mode 100755 index 000000000..a232e2ce4 --- /dev/null +++ b/Source/JavaScriptCore/inspector/scripts/codegen/generate_objc_configuration_header.py @@ -0,0 +1,85 @@ +#!/usr/bin/env python +# +# Copyright (c) 2014, 2016 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 + +from generator import Generator +from objc_generator import ObjCGenerator +from objc_generator_templates import ObjCGeneratorTemplates as ObjCTemplates + +log = logging.getLogger('global') + + +class ObjCConfigurationHeaderGenerator(ObjCGenerator): + def __init__(self, *args, **kwargs): + ObjCGenerator.__init__(self, *args, **kwargs) + + def output_filename(self): + return '%sConfiguration.h' % self.protocol_name() + + def generate_output(self): + headers = [ + '<WebInspector/%s.h>' % self.protocol_name(), + ] + + header_args = { + 'includes': '\n'.join(['#import ' + header for header in headers]), + } + + domains = self.domains_to_generate() + sections = [] + sections.append(self.generate_license()) + sections.append(Template(ObjCTemplates.GenericHeaderPrelude).substitute(None, **header_args)) + sections.append(self._generate_configuration_interface_for_domains(domains)) + sections.append(Template(ObjCTemplates.GenericHeaderPostlude).substitute(None, **header_args)) + return '\n\n'.join(sections) + + def _generate_configuration_interface_for_domains(self, domains): + lines = [] + lines.append('__attribute__((visibility ("default")))') + lines.append('@interface %sConfiguration : NSObject' % self.protocol_name()) + for domain in domains: + lines.extend(self._generate_properties_for_domain(domain)) + lines.append('@end') + return '\n'.join(lines) + + def _generate_properties_for_domain(self, domain): + property_args = { + 'objcPrefix': self.objc_prefix(), + 'domainName': domain.domain_name, + 'variableNamePrefix': ObjCGenerator.variable_name_prefix_for_domain(domain), + } + + lines = [] + + if self.should_generate_commands_for_domain(domain): + lines.append(Template(ObjCTemplates.ConfigurationCommandProperty).substitute(None, **property_args)) + if self.should_generate_events_for_domain(domain): + lines.append(Template(ObjCTemplates.ConfigurationEventProperty).substitute(None, **property_args)) + return lines diff --git a/Source/JavaScriptCore/inspector/scripts/codegen/generate_objc_configuration_implementation.py b/Source/JavaScriptCore/inspector/scripts/codegen/generate_objc_configuration_implementation.py new file mode 100755 index 000000000..910bcbab1 --- /dev/null +++ b/Source/JavaScriptCore/inspector/scripts/codegen/generate_objc_configuration_implementation.py @@ -0,0 +1,147 @@ +#!/usr/bin/env python +# +# Copyright (c) 2014, 2016 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 + +from generator import Generator +from objc_generator import ObjCGenerator +from objc_generator_templates import ObjCGeneratorTemplates as ObjCTemplates + +log = logging.getLogger('global') + + +class ObjCConfigurationImplementationGenerator(ObjCGenerator): + def __init__(self, *args, **kwargs): + ObjCGenerator.__init__(self, *args, **kwargs) + + def output_filename(self): + return '%sConfiguration.mm' % self.protocol_name() + + def generate_output(self): + secondary_headers = [ + '"%sInternal.h"' % self.protocol_name(), + '"%sBackendDispatchers.h"' % self.protocol_name(), + '<JavaScriptCore/AlternateDispatchableAgent.h>', + '<JavaScriptCore/AugmentableInspectorController.h>', + '<JavaScriptCore/InspectorAlternateBackendDispatchers.h>', + '<JavaScriptCore/InspectorBackendDispatchers.h>', + ] + + header_args = { + 'primaryInclude': '"%sConfiguration.h"' % self.protocol_name(), + 'secondaryIncludes': '\n'.join(['#import %s' % header for header in secondary_headers]), + } + + domains = self.domains_to_generate() + sections = [] + sections.append(self.generate_license()) + sections.append(Template(ObjCTemplates.ImplementationPrelude).substitute(None, **header_args)) + sections.append(self._generate_configuration_implementation_for_domains(domains)) + sections.append(Template(ObjCTemplates.ImplementationPostlude).substitute(None, **header_args)) + return '\n\n'.join(sections) + + def _generate_configuration_implementation_for_domains(self, domains): + lines = [] + lines.append('@implementation %sConfiguration' % self.protocol_name()) + lines.append('{') + lines.append(' AugmentableInspectorController* _controller;') + lines.extend(self._generate_ivars(domains)) + lines.append('}') + lines.append('') + lines.append('- (instancetype)initWithController:(AugmentableInspectorController*)controller') + lines.append('{') + lines.append(' self = [super init];') + lines.append(' if (!self)') + lines.append(' return nil;') + lines.append(' ASSERT(controller);') + lines.append(' _controller = controller;') + lines.append(' return self;') + lines.append('}') + lines.append('') + lines.extend(self._generate_dealloc(domains)) + lines.append('') + for domain in domains: + if self.should_generate_commands_for_domain(domain): + lines.append(self._generate_handler_setter_for_domain(domain)) + lines.append('') + if self.should_generate_events_for_domain(domain): + lines.append(self._generate_event_dispatcher_getter_for_domain(domain)) + lines.append('') + lines.append('@end') + return '\n'.join(lines) + + def _generate_ivars(self, domains): + lines = [] + for domain in domains: + if self.should_generate_commands_for_domain(domain): + objc_class_name = '%s%sDomainHandler' % (self.objc_prefix(), domain.domain_name) + ivar_name = '_%sHandler' % ObjCGenerator.variable_name_prefix_for_domain(domain) + lines.append(' id<%s> %s;' % (objc_class_name, ivar_name)) + if self.should_generate_events_for_domain(domain): + objc_class_name = '%s%sDomainEventDispatcher' % (self.objc_prefix(), domain.domain_name) + ivar_name = '_%sEventDispatcher' % ObjCGenerator.variable_name_prefix_for_domain(domain) + lines.append(' %s *%s;' % (objc_class_name, ivar_name)) + return lines + + def _generate_dealloc(self, domains): + lines = [] + lines.append('- (void)dealloc') + lines.append('{') + for domain in domains: + if self.should_generate_commands_for_domain(domain): + lines.append(' [_%sHandler release];' % ObjCGenerator.variable_name_prefix_for_domain(domain)) + if self.should_generate_events_for_domain(domain): + lines.append(' [_%sEventDispatcher release];' % ObjCGenerator.variable_name_prefix_for_domain(domain)) + lines.append(' [super dealloc];') + lines.append('}') + return lines + + def _generate_handler_setter_for_domain(self, domain): + setter_args = { + 'objcPrefix': self.objc_prefix(), + 'domainName': domain.domain_name, + 'variableNamePrefix': ObjCGenerator.variable_name_prefix_for_domain(domain), + } + return Template(ObjCTemplates.ConfigurationCommandPropertyImplementation).substitute(None, **setter_args) + + def _generate_event_dispatcher_getter_for_domain(self, domain): + getter_args = { + 'objcPrefix': self.objc_prefix(), + 'domainName': domain.domain_name, + 'variableNamePrefix': ObjCGenerator.variable_name_prefix_for_domain(domain), + } + return Template(ObjCTemplates.ConfigurationGetterImplementation).substitute(None, **getter_args) + + def _variable_name_prefix_for_domain(self, domain): + domain_name = domain.domain_name + if domain_name.startswith('DOM'): + return 'dom' + domain_name[3:] + if domain_name.startswith('CSS'): + return 'css' + domain_name[3:] + return domain_name[:1].lower() + domain_name[1:] diff --git a/Source/JavaScriptCore/inspector/scripts/codegen/generate_objc_frontend_dispatcher_implementation.py b/Source/JavaScriptCore/inspector/scripts/codegen/generate_objc_frontend_dispatcher_implementation.py new file mode 100755 index 000000000..722d5d392 --- /dev/null +++ b/Source/JavaScriptCore/inspector/scripts/codegen/generate_objc_frontend_dispatcher_implementation.py @@ -0,0 +1,154 @@ +#!/usr/bin/env python +# +# Copyright (c) 2014-2016 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 + +from cpp_generator import CppGenerator +from generator import Generator, ucfirst +from objc_generator import ObjCGenerator +from objc_generator_templates import ObjCGeneratorTemplates as ObjCTemplates + +log = logging.getLogger('global') + + +class ObjCFrontendDispatcherImplementationGenerator(ObjCGenerator): + def __init__(self, *args, **kwargs): + ObjCGenerator.__init__(self, *args, **kwargs) + + def output_filename(self): + return '%sEventDispatchers.mm' % self.protocol_name() + + def domains_to_generate(self): + return filter(self.should_generate_events_for_domain, Generator.domains_to_generate(self)) + + def generate_output(self): + secondary_headers = [ + '"%sTypeConversions.h"' % self.protocol_name(), + '<JavaScriptCore/InspectorValues.h>', + ] + + header_args = { + 'primaryInclude': '"%sInternal.h"' % self.protocol_name(), + 'secondaryIncludes': '\n'.join(['#import %s' % header for header in secondary_headers]), + } + + domains = self.domains_to_generate() + sections = [] + sections.append(self.generate_license()) + sections.append(Template(ObjCTemplates.ImplementationPrelude).substitute(None, **header_args)) + sections.extend(map(self._generate_event_dispatcher_implementations, domains)) + sections.append(Template(ObjCTemplates.ImplementationPostlude).substitute(None, **header_args)) + return '\n\n'.join(sections) + + def _generate_event_dispatcher_implementations(self, domain): + if not self.should_generate_events_for_domain(domain): + return '' + + lines = [] + objc_name = '%s%sDomainEventDispatcher' % (self.objc_prefix(), domain.domain_name) + lines.append('@implementation %s' % objc_name) + lines.append('{') + lines.append(' AugmentableInspectorController* _controller;') + lines.append('}') + lines.append('') + lines.append('- (instancetype)initWithController:(AugmentableInspectorController*)controller;') + lines.append('{') + lines.append(' self = [super init];') + lines.append(' if (!self)') + lines.append(' return nil;') + lines.append(' ASSERT(controller);') + lines.append(' _controller = controller;') + lines.append(' return self;') + lines.append('}') + lines.append('') + for event in self.events_for_domain(domain): + lines.append(self._generate_event(domain, event)) + lines.append('') + lines.append('@end') + return '\n'.join(lines) + + def _generate_event(self, domain, event): + lines = [] + lines.append(self._generate_event_signature(domain, event)) + lines.append('{') + lines.append(' const FrontendRouter& router = _controller->frontendRouter();') + lines.append('') + + required_pointer_parameters = filter(lambda parameter: not parameter.is_optional and ObjCGenerator.is_type_objc_pointer_type(parameter.type), event.event_parameters) + for parameter in required_pointer_parameters: + var_name = ObjCGenerator.identifier_to_objc_identifier(parameter.parameter_name) + lines.append(' THROW_EXCEPTION_FOR_REQUIRED_PARAMETER(%s, @"%s");' % (var_name, var_name)) + objc_array_class = self.objc_class_for_array_type(parameter.type) + if objc_array_class and objc_array_class.startswith(self.objc_prefix()): + lines.append(' THROW_EXCEPTION_FOR_BAD_TYPE_IN_ARRAY(%s, [%s class]);' % (var_name, objc_array_class)) + + optional_pointer_parameters = filter(lambda parameter: parameter.is_optional and ObjCGenerator.is_type_objc_pointer_type(parameter.type), event.event_parameters) + for parameter in optional_pointer_parameters: + var_name = ObjCGenerator.identifier_to_objc_identifier(parameter.parameter_name) + lines.append(' THROW_EXCEPTION_FOR_BAD_OPTIONAL_PARAMETER(%s, @"%s");' % (var_name, var_name)) + objc_array_class = self.objc_class_for_array_type(parameter.type) + if objc_array_class and objc_array_class.startswith(self.objc_prefix()): + lines.append(' THROW_EXCEPTION_FOR_BAD_TYPE_IN_OPTIONAL_ARRAY(%s, [%s class]);' % (var_name, objc_array_class)) + + if required_pointer_parameters or optional_pointer_parameters: + lines.append('') + + lines.append(' Ref<InspectorObject> jsonMessage = InspectorObject::create();') + lines.append(' jsonMessage->setString(ASCIILiteral("method"), ASCIILiteral("%s.%s"));' % (domain.domain_name, event.event_name)) + if event.event_parameters: + lines.extend(self._generate_event_out_parameters(domain, event)) + lines.append(' router.sendEvent(jsonMessage->toJSONString());') + lines.append('}') + return '\n'.join(lines) + + def _generate_event_signature(self, domain, event): + if not event.event_parameters: + return '- (void)%s' % event.event_name + pairs = [] + for parameter in event.event_parameters: + param_name = parameter.parameter_name + pairs.append('%s:(%s)%s' % (param_name, self.objc_type_for_param(domain, event.event_name, parameter), param_name)) + pairs[0] = ucfirst(pairs[0]) + return '- (void)%sWith%s' % (event.event_name, ' '.join(pairs)) + + def _generate_event_out_parameters(self, domain, event): + lines = [] + lines.append(' Ref<InspectorObject> paramsObject = InspectorObject::create();') + for parameter in event.event_parameters: + keyed_set_method = CppGenerator.cpp_setter_method_for_type(parameter.type) + var_name = parameter.parameter_name + safe_var_name = '(*%s)' % var_name if parameter.is_optional else var_name + export_expression = self.objc_protocol_export_expression_for_variable(parameter.type, safe_var_name) + if not parameter.is_optional: + lines.append(' paramsObject->%s(ASCIILiteral("%s"), %s);' % (keyed_set_method, parameter.parameter_name, export_expression)) + else: + lines.append(' if (%s)' % (parameter.parameter_name)) + lines.append(' paramsObject->%s(ASCIILiteral("%s"), %s);' % (keyed_set_method, parameter.parameter_name, export_expression)) + lines.append(' jsonMessage->setObject(ASCIILiteral("params"), WTFMove(paramsObject));') + return lines diff --git a/Source/JavaScriptCore/inspector/scripts/codegen/generate_objc_header.py b/Source/JavaScriptCore/inspector/scripts/codegen/generate_objc_header.py new file mode 100755 index 000000000..801f40a86 --- /dev/null +++ b/Source/JavaScriptCore/inspector/scripts/codegen/generate_objc_header.py @@ -0,0 +1,245 @@ +#!/usr/bin/env python +# +# Copyright (c) 2014, 2016 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 + +from generator import Generator, ucfirst +from models import ObjectType, EnumType, Platforms +from objc_generator import ObjCGenerator, join_type_and_name +from objc_generator_templates import ObjCGeneratorTemplates as ObjCTemplates + +log = logging.getLogger('global') + + +def add_newline(lines): + if lines and lines[-1] == '': + return + lines.append('') + + +class ObjCHeaderGenerator(ObjCGenerator): + def __init__(self, *args, **kwargs): + ObjCGenerator.__init__(self, *args, **kwargs) + + def output_filename(self): + return '%s.h' % self.protocol_name() + + def generate_output(self): + headers = set([ + '<WebInspector/%sJSONObject.h>' % ObjCGenerator.OBJC_STATIC_PREFIX, + ]) + + header_args = { + 'includes': '\n'.join(['#import ' + header for header in sorted(headers)]), + } + + domains = self.domains_to_generate() + type_domains = filter(self.should_generate_types_for_domain, domains) + command_domains = filter(self.should_generate_commands_for_domain, domains) + event_domains = filter(self.should_generate_events_for_domain, domains) + + # FIXME: <https://webkit.org/b/138222> Web Inspector: Reduce unnecessary enums/types generated in ObjC Protocol Interfaces + # Currently we generate enums/types for all types in the type_domains. For the built-in + # JSC domains (Debugger, Runtime) this produces extra unused types. We only need to + # generate these types if they are referenced by the command_domains or event_domains. + + sections = [] + sections.append(self.generate_license()) + sections.append(Template(ObjCTemplates.HeaderPrelude).substitute(None, **header_args)) + sections.append('\n'.join(filter(None, map(self._generate_forward_declarations, type_domains)))) + sections.append(self._generate_enum_for_platforms()) + sections.append('\n'.join(filter(None, map(self._generate_enums, type_domains)))) + sections.append('\n'.join(filter(None, map(self._generate_types, type_domains)))) + + if self.get_generator_setting('generate_backend', False): + sections.append('\n\n'.join(filter(None, map(self._generate_command_protocols, command_domains)))) + sections.append('\n\n'.join(filter(None, map(self._generate_event_interfaces, event_domains)))) + + sections.append(Template(ObjCTemplates.HeaderPostlude).substitute(None)) + return '\n\n'.join(sections) + + def _generate_forward_declarations(self, domain): + lines = [] + for declaration in self.type_declarations_for_domain(domain): + if (isinstance(declaration.type, ObjectType)): + objc_name = self.objc_name_for_type(declaration.type) + lines.append('@class %s;' % objc_name) + return '\n'.join(lines) + + def _generate_enums(self, domain): + lines = [] + + # Type enums and member enums. + for declaration in self.type_declarations_for_domain(domain): + if isinstance(declaration.type, EnumType): + add_newline(lines) + lines.append(self._generate_anonymous_enum_for_declaration(domain, declaration)) + else: + for member in declaration.type_members: + if isinstance(member.type, EnumType) and member.type.is_anonymous: + add_newline(lines) + lines.append(self._generate_anonymous_enum_for_member(domain, declaration, member)) + + # Anonymous command enums. + for command in self.commands_for_domain(domain): + for parameter in command.call_parameters: + if isinstance(parameter.type, EnumType) and parameter.type.is_anonymous: + add_newline(lines) + lines.append(self._generate_anonymous_enum_for_parameter(domain, command.command_name, parameter)) + for parameter in command.return_parameters: + if isinstance(parameter.type, EnumType) and parameter.type.is_anonymous: + add_newline(lines) + lines.append(self._generate_anonymous_enum_for_parameter(domain, command.command_name, parameter)) + + # Anonymous event enums. + for event in self.events_for_domain(domain): + for parameter in event.event_parameters: + if isinstance(parameter.type, EnumType) and parameter.type.is_anonymous: + add_newline(lines) + lines.append(self._generate_anonymous_enum_for_parameter(domain, event.event_name, parameter)) + + return '\n'.join(lines) + + def _generate_types(self, domain): + lines = [] + # Type interfaces. + for declaration in self.type_declarations_for_domain(domain): + if isinstance(declaration.type, ObjectType): + add_newline(lines) + lines.append(self._generate_type_interface(domain, declaration)) + return '\n'.join(lines) + + def _generate_enum_for_platforms(self): + objc_enum_name = '%sPlatform' % self.objc_prefix() + enum_values = [platform.name for platform in Platforms] + return self._generate_enum(objc_enum_name, enum_values) + + def _generate_anonymous_enum_for_declaration(self, domain, declaration): + objc_enum_name = self.objc_enum_name_for_anonymous_enum_declaration(declaration) + return self._generate_enum(objc_enum_name, declaration.type.enum_values()) + + def _generate_anonymous_enum_for_member(self, domain, declaration, member): + objc_enum_name = self.objc_enum_name_for_anonymous_enum_member(declaration, member) + return self._generate_enum(objc_enum_name, member.type.enum_values()) + + def _generate_anonymous_enum_for_parameter(self, domain, event_or_command_name, parameter): + objc_enum_name = self.objc_enum_name_for_anonymous_enum_parameter(domain, event_or_command_name, parameter) + return self._generate_enum(objc_enum_name, parameter.type.enum_values()) + + def _generate_enum(self, enum_name, enum_values): + lines = [] + lines.append('typedef NS_ENUM(NSInteger, %s) {' % enum_name) + for enum_value in enum_values: + lines.append(' %s%s,' % (enum_name, Generator.stylized_name_for_enum_value(enum_value))) + lines.append('};') + return '\n'.join(lines) + + def _generate_type_interface(self, domain, declaration): + lines = [] + objc_name = self.objc_name_for_type(declaration.type) + lines.append('__attribute__((visibility ("default")))') + lines.append('@interface %s : %sJSONObject' % (objc_name, ObjCGenerator.OBJC_STATIC_PREFIX)) + + # The initializers that take a payload or inspector object are only needed by the frontend. + if self.get_generator_setting('generate_frontend', False): + lines.append('- (instancetype)initWithPayload:(NSDictionary<NSString *, id> *)payload;') + lines.append('- (instancetype)initWithJSONObject:(RWIProtocolJSONObject *)jsonObject;') + + required_members = filter(lambda member: not member.is_optional, declaration.type_members) + optional_members = filter(lambda member: member.is_optional, declaration.type_members) + if required_members: + lines.append(self._generate_init_method_for_required_members(domain, declaration, required_members)) + for member in required_members: + lines.append('/* required */ ' + self._generate_member_property(declaration, member)) + for member in optional_members: + lines.append('/* optional */ ' + self._generate_member_property(declaration, member)) + lines.append('@end') + return '\n'.join(lines) + + def _generate_init_method_for_required_members(self, domain, declaration, required_members): + pairs = [] + for member in required_members: + objc_type = self.objc_type_for_member(declaration, member) + var_name = ObjCGenerator.identifier_to_objc_identifier(member.member_name) + pairs.append('%s:(%s)%s' % (var_name, objc_type, var_name)) + pairs[0] = ucfirst(pairs[0]) + return '- (instancetype)initWith%s;' % ' '.join(pairs) + + def _generate_member_property(self, declaration, member): + accessor_type = self.objc_accessor_type_for_member(member) + objc_type = self.objc_type_for_member(declaration, member) + return '@property (nonatomic, %s) %s;' % (accessor_type, join_type_and_name(objc_type, ObjCGenerator.identifier_to_objc_identifier(member.member_name))) + + def _generate_command_protocols(self, domain): + lines = [] + if self.commands_for_domain(domain): + objc_name = '%s%sDomainHandler' % (self.objc_prefix(), domain.domain_name) + lines.append('@protocol %s <NSObject>' % objc_name) + lines.append('@required') + for command in self.commands_for_domain(domain): + lines.append(self._generate_single_command_protocol(domain, command)) + lines.append('@end') + return '\n'.join(lines) + + def _generate_single_command_protocol(self, domain, command): + pairs = [] + pairs.append('ErrorCallback:(void(^)(NSString *error))errorCallback') + pairs.append('successCallback:(%s)successCallback' % self._callback_block_for_command(domain, command)) + for parameter in command.call_parameters: + param_name = parameter.parameter_name + pairs.append('%s:(%s)%s' % (param_name, self.objc_type_for_param(domain, command.command_name, parameter), param_name)) + return '- (void)%sWith%s;' % (command.command_name, ' '.join(pairs)) + + def _callback_block_for_command(self, domain, command): + pairs = [] + for parameter in command.return_parameters: + pairs.append(join_type_and_name(self.objc_type_for_param(domain, command.command_name, parameter), parameter.parameter_name)) + return 'void(^)(%s)' % ', '.join(pairs) + + def _generate_event_interfaces(self, domain): + lines = [] + events = self.events_for_domain(domain) + if len(events): + objc_name = '%s%sDomainEventDispatcher' % (self.objc_prefix(), domain.domain_name) + lines.append('__attribute__((visibility ("default")))') + lines.append('@interface %s : NSObject' % objc_name) + for event in events: + lines.append(self._generate_single_event_interface(domain, event)) + lines.append('@end') + return '\n'.join(lines) + + def _generate_single_event_interface(self, domain, event): + if not event.event_parameters: + return '- (void)%s;' % event.event_name + pairs = [] + for parameter in event.event_parameters: + param_name = parameter.parameter_name + pairs.append('%s:(%s)%s' % (param_name, self.objc_type_for_param(domain, event.event_name, parameter), param_name)) + pairs[0] = ucfirst(pairs[0]) + return '- (void)%sWith%s;' % (event.event_name, ' '.join(pairs)) diff --git a/Source/JavaScriptCore/inspector/scripts/codegen/generate_objc_internal_header.py b/Source/JavaScriptCore/inspector/scripts/codegen/generate_objc_internal_header.py new file mode 100755 index 000000000..9f06ced40 --- /dev/null +++ b/Source/JavaScriptCore/inspector/scripts/codegen/generate_objc_internal_header.py @@ -0,0 +1,74 @@ +#!/usr/bin/env python +# +# Copyright (c) 2014, 2016 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 + +from generator import Generator, ucfirst +from objc_generator import ObjCGenerator +from objc_generator_templates import ObjCGeneratorTemplates as ObjCTemplates + +log = logging.getLogger('global') + + +class ObjCInternalHeaderGenerator(ObjCGenerator): + def __init__(self, *args, **kwargs): + ObjCGenerator.__init__(self, *args, **kwargs) + + def output_filename(self): + return '%sInternal.h' % self.protocol_name() + + def generate_output(self): + headers = set([ + '"%s.h"' % self.protocol_name(), + '"%sJSONObjectPrivate.h"' % self.protocol_name(), + '<JavaScriptCore/InspectorValues.h>', + '<JavaScriptCore/AugmentableInspectorController.h>', + ]) + + header_args = { + 'includes': '\n'.join(['#import ' + header for header in sorted(headers)]), + } + + event_domains = filter(self.should_generate_events_for_domain, self.domains_to_generate()) + + sections = [] + sections.append(self.generate_license()) + sections.append(Template(ObjCTemplates.GenericHeaderPrelude).substitute(None, **header_args)) + sections.append('\n\n'.join(filter(None, map(self._generate_event_dispatcher_private_interfaces, event_domains)))) + sections.append(Template(ObjCTemplates.GenericHeaderPostlude).substitute(None, **header_args)) + return '\n\n'.join(sections) + + def _generate_event_dispatcher_private_interfaces(self, domain): + lines = [] + if len(self.events_for_domain(domain)): + objc_name = '%s%sDomainEventDispatcher' % (self.objc_prefix(), domain.domain_name) + lines.append('@interface %s (Private)' % objc_name) + lines.append('- (instancetype)initWithController:(Inspector::AugmentableInspectorController*)controller;') + lines.append('@end') + return '\n'.join(lines) diff --git a/Source/JavaScriptCore/inspector/scripts/codegen/generate_objc_protocol_type_conversions_header.py b/Source/JavaScriptCore/inspector/scripts/codegen/generate_objc_protocol_type_conversions_header.py new file mode 100755 index 000000000..ea9e6e2d7 --- /dev/null +++ b/Source/JavaScriptCore/inspector/scripts/codegen/generate_objc_protocol_type_conversions_header.py @@ -0,0 +1,165 @@ +#!/usr/bin/env python +# +# Copyright (c) 2014, 2016 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 + +from generator import Generator +from models import EnumType, Frameworks, Platforms +from objc_generator import ObjCGenerator +from objc_generator_templates import ObjCGeneratorTemplates as ObjCTemplates + +log = logging.getLogger('global') + + +def add_newline(lines): + if lines and lines[-1] == '': + return + lines.append('') + + +class ObjCProtocolTypeConversionsHeaderGenerator(ObjCGenerator): + def __init__(self, *args, **kwargs): + ObjCGenerator.__init__(self, *args, **kwargs) + + def output_filename(self): + return '%sTypeConversions.h' % self.protocol_name() + + def domains_to_generate(self): + return filter(self.should_generate_types_for_domain, Generator.domains_to_generate(self)) + + def generate_output(self): + headers = [ + '"%s.h"' % self.protocol_name(), + Generator.string_for_file_include('%sArrayConversions.h' % ObjCGenerator.OBJC_STATIC_PREFIX, Frameworks.WebInspector, self.model().framework), + ] + headers.sort() + + header_args = { + 'includes': '\n'.join(['#import ' + header for header in headers]), + } + + domains = self.domains_to_generate() + sections = [] + sections.append(self.generate_license()) + sections.append(Template(ObjCTemplates.TypeConversionsHeaderPrelude).substitute(None, **header_args)) + sections.append(Template(ObjCTemplates.TypeConversionsHeaderStandard).substitute(None)) + sections.append(self._generate_enum_conversion_for_platforms()) + sections.extend(map(self._generate_enum_conversion_functions, domains)) + sections.append(Template(ObjCTemplates.TypeConversionsHeaderPostlude).substitute(None, **header_args)) + return '\n\n'.join(sections) + + def _generate_enum_conversion_functions(self, domain): + lines = [] + + # Type enums and member enums. + for declaration in self.type_declarations_for_domain(domain): + if isinstance(declaration.type, EnumType): + add_newline(lines) + lines.append(self._generate_anonymous_enum_conversion_for_declaration(domain, declaration)) + else: + for member in declaration.type_members: + if (isinstance(member.type, EnumType) and member.type.is_anonymous): + add_newline(lines) + lines.append(self._generate_anonymous_enum_conversion_for_member(domain, declaration, member)) + + # Anonymous command enums. + for command in self.commands_for_domain(domain): + for parameter in command.call_parameters: + if (isinstance(parameter.type, EnumType) and parameter.type.is_anonymous): + add_newline(lines) + lines.append(self._generate_anonymous_enum_conversion_for_parameter(domain, command.command_name, parameter)) + for parameter in command.return_parameters: + if (isinstance(parameter.type, EnumType) and parameter.type.is_anonymous): + add_newline(lines) + lines.append(self._generate_anonymous_enum_conversion_for_parameter(domain, command.command_name, parameter)) + + # Anonymous event enums. + for event in self.events_for_domain(domain): + for parameter in event.event_parameters: + if (isinstance(parameter.type, EnumType) and parameter.type.is_anonymous): + add_newline(lines) + lines.append(self._generate_anonymous_enum_conversion_for_parameter(domain, event.event_name, parameter)) + + return '\n'.join(lines) + + def _generate_enum_conversion_for_platforms(self): + objc_enum_name = '%sPlatform' % self.objc_prefix() + enum_values = [platform.name for platform in Platforms] + lines = [] + lines.append(self._generate_enum_objc_to_protocol_string(objc_enum_name, enum_values)) + lines.append(self._generate_enum_from_protocol_string(objc_enum_name, enum_values)) + return '\n\n'.join(lines) + + def _generate_anonymous_enum_conversion_for_declaration(self, domain, declaration): + objc_enum_name = self.objc_enum_name_for_anonymous_enum_declaration(declaration) + enum_values = declaration.type.enum_values() + lines = [] + lines.append(self._generate_enum_objc_to_protocol_string(objc_enum_name, enum_values)) + lines.append(self._generate_enum_from_protocol_string(objc_enum_name, enum_values)) + return '\n\n'.join(lines) + + def _generate_anonymous_enum_conversion_for_member(self, domain, declaration, member): + objc_enum_name = self.objc_enum_name_for_anonymous_enum_member(declaration, member) + enum_values = member.type.enum_values() + lines = [] + lines.append(self._generate_enum_objc_to_protocol_string(objc_enum_name, enum_values)) + lines.append(self._generate_enum_from_protocol_string(objc_enum_name, enum_values)) + return '\n\n'.join(lines) + + def _generate_anonymous_enum_conversion_for_parameter(self, domain, event_or_command_name, parameter): + objc_enum_name = self.objc_enum_name_for_anonymous_enum_parameter(domain, event_or_command_name, parameter) + enum_values = parameter.type.enum_values() + lines = [] + lines.append(self._generate_enum_objc_to_protocol_string(objc_enum_name, enum_values)) + lines.append(self._generate_enum_from_protocol_string(objc_enum_name, enum_values)) + return '\n\n'.join(lines) + + def _generate_enum_objc_to_protocol_string(self, objc_enum_name, enum_values): + lines = [] + lines.append('inline String toProtocolString(%s value)' % objc_enum_name) + lines.append('{') + lines.append(' switch(value) {') + for enum_value in enum_values: + lines.append(' case %s%s:' % (objc_enum_name, Generator.stylized_name_for_enum_value(enum_value))) + lines.append(' return ASCIILiteral("%s");' % enum_value) + lines.append(' }') + lines.append('}') + return '\n'.join(lines) + + def _generate_enum_from_protocol_string(self, objc_enum_name, enum_values): + lines = [] + lines.append('template<>') + lines.append('inline std::optional<%s> fromProtocolString(const String& value)' % objc_enum_name) + lines.append('{') + for enum_value in enum_values: + lines.append(' if (value == "%s")' % enum_value) + lines.append(' return %s%s;' % (objc_enum_name, Generator.stylized_name_for_enum_value(enum_value))) + lines.append(' return std::nullopt;') + lines.append('}') + return '\n'.join(lines) diff --git a/Source/JavaScriptCore/inspector/scripts/codegen/generate_objc_protocol_type_conversions_implementation.py b/Source/JavaScriptCore/inspector/scripts/codegen/generate_objc_protocol_type_conversions_implementation.py new file mode 100644 index 000000000..4293e3bd8 --- /dev/null +++ b/Source/JavaScriptCore/inspector/scripts/codegen/generate_objc_protocol_type_conversions_implementation.py @@ -0,0 +1,138 @@ +#!/usr/bin/env python +# +# Copyright (c) 2016 Apple Inc. 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 + +from generator import Generator +from models import EnumType, ObjectType, ArrayType, AliasedType, PrimitiveType, Frameworks +from objc_generator import ObjCGenerator +from objc_generator_templates import ObjCGeneratorTemplates as ObjCTemplates + +log = logging.getLogger('global') + + +def add_newline(lines): + if lines and lines[-1] == '': + return + lines.append('') + + +class ObjCProtocolTypeConversionsImplementationGenerator(ObjCGenerator): + def __init__(self, *args, **kwargs): + ObjCGenerator.__init__(self, *args, **kwargs) + + def output_filename(self): + return '%sTypeConversions.mm' % self.protocol_name() + + def domains_to_generate(self): + return filter(self.should_generate_types_for_domain, Generator.domains_to_generate(self)) + + def generate_output(self): + secondary_headers = [ + '"%s.h"' % self.protocol_name(), + '"%sTypeParser.h"' % self.protocol_name(), + Generator.string_for_file_include('%sJSONObjectPrivate.h' % ObjCGenerator.OBJC_STATIC_PREFIX, Frameworks.WebInspector, self.model().framework), + ] + secondary_headers.sort() + + header_args = { + 'primaryInclude': '"%sTypeConversions.h"' % self.protocol_name(), + 'secondaryIncludes': '\n'.join(['#import ' + header for header in secondary_headers]), + } + + domains = self.domains_to_generate() + sections = [] + sections.append(self.generate_license()) + sections.append(Template(ObjCTemplates.ImplementationPrelude).substitute(None, **header_args)) + sections.append(self._generate_type_factory_category_interface(domains)) + sections.append(self._generate_type_factory_category_implementation(domains)) + sections.append(Template(ObjCTemplates.ImplementationPostlude).substitute(None, **header_args)) + return '\n\n'.join(sections) + + def _generate_type_factory_category_interface(self, domains): + lines = [] + for domain in domains: + lines.append('@interface %sTypeConversions (%sDomain)' % (self.protocol_name(), domain.domain_name)) + lines.append('') + + for declaration in self.type_declarations_for_domain(domain): + lines.append(self._generate_type_factory_method_declaration(domain, declaration)) + + add_newline(lines) + lines.append('@end') + + return '\n'.join(lines) + + def _generate_type_factory_method_declaration(self, domain, declaration): + resolved_type = declaration.type + if isinstance(resolved_type, AliasedType): + resolved_type = resolved_type.aliased_type + if isinstance(resolved_type, (ObjectType, ArrayType, PrimitiveType)): + objc_type = self.objc_class_for_type(resolved_type) + return '+ (void)_parse%s:(%s **)outValue fromPayload:(id)payload;' % (declaration.type.raw_name(), objc_type) + if isinstance(resolved_type, EnumType): + return '+ (void)_parse%s:(NSNumber **)outValue fromPayload:(id)payload;' % declaration.type.raw_name() + + def _generate_type_factory_category_implementation(self, domains): + lines = [] + for domain in domains: + lines.append('@implementation %sTypeConversions (%sDomain)' % (self.protocol_name(), domain.domain_name)) + lines.append('') + + for declaration in self.type_declarations_for_domain(domain): + lines.append(self._generate_type_factory_method_implementation(domain, declaration)) + add_newline(lines) + lines.append('@end') + return '\n'.join(lines) + + def _generate_type_factory_method_implementation(self, domain, declaration): + lines = [] + resolved_type = declaration.type + if isinstance(resolved_type, AliasedType): + resolved_type = resolved_type.aliased_type + + objc_class = self.objc_class_for_type(resolved_type) + if isinstance(resolved_type, (ObjectType, ArrayType, PrimitiveType)): + lines.append('+ (void)_parse%s:(%s **)outValue fromPayload:(id)payload' % (declaration.type.raw_name(), objc_class)) + if isinstance(resolved_type, EnumType): + lines.append('+ (void)_parse%s:(NSNumber **)outValue fromPayload:(id)payload' % declaration.type.raw_name()) + + lines.append('{') + if isinstance(resolved_type, EnumType): + lines.append(' THROW_EXCEPTION_FOR_BAD_TYPE(payload, [NSString class]);') + lines.append(' std::optional<%(type)s> result = Inspector::fromProtocolString<%(type)s>(payload);' % {'type': self.objc_name_for_type(resolved_type)}) + lines.append(' THROW_EXCEPTION_FOR_BAD_ENUM_VALUE(result, @"%s");' % declaration.type.raw_name()) + lines.append(' *outValue = @(result.value());') + elif isinstance(resolved_type, (ArrayType, PrimitiveType)): + lines.append(' THROW_EXCEPTION_FOR_BAD_TYPE(payload, [%s class]);' % objc_class) + lines.append(' *outValue = (%s *)payload;' % objc_class) + elif isinstance(resolved_type, ObjectType): + lines.append(' THROW_EXCEPTION_FOR_BAD_TYPE(payload, [NSDictionary class]);') + lines.append(' *outValue = [[%s alloc] initWithPayload:payload];' % (objc_class)) + lines.append('}') + return '\n'.join(lines) diff --git a/Source/JavaScriptCore/inspector/scripts/codegen/generate_objc_protocol_types_implementation.py b/Source/JavaScriptCore/inspector/scripts/codegen/generate_objc_protocol_types_implementation.py new file mode 100755 index 000000000..a319c94d7 --- /dev/null +++ b/Source/JavaScriptCore/inspector/scripts/codegen/generate_objc_protocol_types_implementation.py @@ -0,0 +1,215 @@ +#!/usr/bin/env python +# +# Copyright (c) 2014, 2016 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 + +from generator import Generator, ucfirst +from models import ObjectType, EnumType, Frameworks +from objc_generator import ObjCGenerator +from objc_generator_templates import ObjCGeneratorTemplates as ObjCTemplates + +log = logging.getLogger('global') + + +def add_newline(lines): + if lines and lines[-1] == '': + return + lines.append('') + + +class ObjCProtocolTypesImplementationGenerator(ObjCGenerator): + def __init__(self, *args, **kwargs): + ObjCGenerator.__init__(self, *args, **kwargs) + + def output_filename(self): + return '%sTypes.mm' % self.protocol_name() + + def domains_to_generate(self): + return filter(self.should_generate_types_for_domain, Generator.domains_to_generate(self)) + + def generate_output(self): + secondary_headers = [ + '"%sTypeConversions.h"' % self.protocol_name(), + Generator.string_for_file_include('%sJSONObjectPrivate.h' % ObjCGenerator.OBJC_STATIC_PREFIX, Frameworks.WebInspector, self.model().framework), + '<JavaScriptCore/InspectorValues.h>', + '<wtf/Assertions.h>', + ] + + # The FooProtocolInternal.h header is only needed to declare the backend-side event dispatcher bindings. + primaryIncludeName = self.protocol_name() + if self.get_generator_setting('generate_backend', False): + primaryIncludeName += 'Internal' + + header_args = { + 'primaryInclude': '"%s.h"' % primaryIncludeName, + 'secondaryIncludes': '\n'.join(['#import %s' % header for header in secondary_headers]), + } + + domains = self.domains_to_generate() + sections = [] + sections.append(self.generate_license()) + sections.append(Template(ObjCTemplates.ImplementationPrelude).substitute(None, **header_args)) + sections.extend(map(self.generate_type_implementations, domains)) + sections.append(Template(ObjCTemplates.ImplementationPostlude).substitute(None, **header_args)) + return '\n\n'.join(sections) + + def generate_type_implementations(self, domain): + lines = [] + for declaration in self.type_declarations_for_domain(domain): + if (isinstance(declaration.type, ObjectType)): + add_newline(lines) + lines.append(self.generate_type_implementation(domain, declaration)) + return '\n'.join(lines) + + def generate_type_implementation(self, domain, declaration): + lines = [] + lines.append('@implementation %s' % self.objc_name_for_type(declaration.type)) + # The initializer that takes a payload is only needed by the frontend. + if self.get_generator_setting('generate_frontend', False): + lines.append('') + lines.append(self._generate_init_method_for_payload(domain, declaration)) + lines.append(self._generate_init_method_for_json_object(domain, declaration)) + required_members = filter(lambda member: not member.is_optional, declaration.type_members) + if required_members: + lines.append('') + lines.append(self._generate_init_method_for_required_members(domain, declaration, required_members)) + for member in declaration.type_members: + lines.append('') + lines.append(self._generate_setter_for_member(domain, declaration, member)) + lines.append('') + lines.append(self._generate_getter_for_member(domain, declaration, member)) + lines.append('') + lines.append('@end') + return '\n'.join(lines) + + def _generate_init_method_for_json_object(self, domain, declaration): + lines = [] + lines.append('- (instancetype)initWithJSONObject:(RWIProtocolJSONObject *)jsonObject') + lines.append('{') + lines.append(' if (!(self = [super initWithInspectorObject:[jsonObject toInspectorObject].get()]))') + lines.append(' return nil;') + lines.append('') + lines.append(' return self;') + lines.append('}') + return '\n'.join(lines) + + def _generate_init_method_for_payload(self, domain, declaration): + lines = [] + lines.append('- (instancetype)initWithPayload:(nonnull NSDictionary<NSString *, id> *)payload') + lines.append('{') + lines.append(' if (!(self = [super init]))') + lines.append(' return nil;') + lines.append('') + + for member in declaration.type_members: + member_name = member.member_name + + if not member.is_optional: + lines.append(' THROW_EXCEPTION_FOR_REQUIRED_PROPERTY(payload[@"%s"], @"%s");' % (member_name, member_name)) + + objc_type = self.objc_type_for_member(declaration, member) + var_name = ObjCGenerator.identifier_to_objc_identifier(member_name) + conversion_expression = self.payload_to_objc_expression_for_member(declaration, member) + if isinstance(member.type, EnumType): + lines.append(' std::optional<%s> %s = %s;' % (objc_type, var_name, conversion_expression)) + if not member.is_optional: + lines.append(' THROW_EXCEPTION_FOR_BAD_ENUM_VALUE(%s, @"%s");' % (var_name, member_name)) + lines.append(' self.%s = %s.value();' % (var_name, var_name)) + else: + lines.append(' if (%s)' % var_name) + lines.append(' self.%s = %s.value();' % (var_name, var_name)) + else: + lines.append(' self.%s = %s;' % (var_name, conversion_expression)) + + lines.append('') + + lines.append(' return self;') + lines.append('}') + return '\n'.join(lines) + + def _generate_init_method_for_required_members(self, domain, declaration, required_members): + pairs = [] + for member in required_members: + objc_type = self.objc_type_for_member(declaration, member) + var_name = ObjCGenerator.identifier_to_objc_identifier(member.member_name) + pairs.append('%s:(%s)%s' % (var_name, objc_type, var_name)) + pairs[0] = ucfirst(pairs[0]) + lines = [] + lines.append('- (instancetype)initWith%s' % ' '.join(pairs)) + lines.append('{') + lines.append(' if (!(self = [super init]))') + lines.append(' return nil;') + lines.append('') + + required_pointer_members = filter(lambda member: ObjCGenerator.is_type_objc_pointer_type(member.type), required_members) + if required_pointer_members: + for member in required_pointer_members: + var_name = ObjCGenerator.identifier_to_objc_identifier(member.member_name) + lines.append(' THROW_EXCEPTION_FOR_REQUIRED_PROPERTY(%s, @"%s");' % (var_name, var_name)) + objc_array_class = self.objc_class_for_array_type(member.type) + if objc_array_class and objc_array_class.startswith(self.objc_prefix()): + lines.append(' THROW_EXCEPTION_FOR_BAD_TYPE_IN_ARRAY(%s, [%s class]);' % (var_name, objc_array_class)) + lines.append('') + + for member in required_members: + var_name = ObjCGenerator.identifier_to_objc_identifier(member.member_name) + lines.append(' self.%s = %s;' % (var_name, var_name)) + + lines.append('') + lines.append(' return self;') + lines.append('}') + return '\n'.join(lines) + + def _generate_setter_for_member(self, domain, declaration, member): + objc_type = self.objc_type_for_member(declaration, member) + var_name = ObjCGenerator.identifier_to_objc_identifier(member.member_name) + setter_method = ObjCGenerator.objc_setter_method_for_member(declaration, member) + conversion_expression = self.objc_to_protocol_expression_for_member(declaration, member, var_name) + lines = [] + lines.append('- (void)set%s:(%s)%s' % (ucfirst(var_name), objc_type, var_name)) + lines.append('{') + objc_array_class = self.objc_class_for_array_type(member.type) + if objc_array_class and objc_array_class.startswith(self.objc_prefix()): + lines.append(' THROW_EXCEPTION_FOR_BAD_TYPE_IN_ARRAY(%s, [%s class]);' % (var_name, objc_array_class)) + lines.append(' [super %s:%s forKey:@"%s"];' % (setter_method, conversion_expression, member.member_name)) + lines.append('}') + return '\n'.join(lines) + + def _generate_getter_for_member(self, domain, declaration, member): + objc_type = self.objc_type_for_member(declaration, member) + var_name = ObjCGenerator.identifier_to_objc_identifier(member.member_name) + getter_method = ObjCGenerator.objc_getter_method_for_member(declaration, member) + basic_expression = '[super %s:@"%s"]' % (getter_method, member.member_name) + conversion_expression = self.protocol_to_objc_expression_for_member(declaration, member, basic_expression) + lines = [] + lines.append('- (%s)%s' % (objc_type, var_name)) + lines.append('{') + lines.append(' return %s;' % conversion_expression) + lines.append('}') + return '\n'.join(lines) diff --git a/Source/JavaScriptCore/inspector/scripts/codegen/generator.py b/Source/JavaScriptCore/inspector/scripts/codegen/generator.py new file mode 100755 index 000000000..2d33b94ea --- /dev/null +++ b/Source/JavaScriptCore/inspector/scripts/codegen/generator.py @@ -0,0 +1,274 @@ +#!/usr/bin/env python +# +# Copyright (c) 2014 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 os.path +import re +from string import Template + +from generator_templates import GeneratorTemplates as Templates +from models import PrimitiveType, ObjectType, ArrayType, EnumType, AliasedType, Frameworks, Platforms + +log = logging.getLogger('global') + + +def ucfirst(str): + return str[:1].upper() + str[1:] + +_ALWAYS_SPECIALCASED_ENUM_VALUE_SUBSTRINGS = set(['API', 'CSS', 'DOM', 'HTML', 'JIT', 'XHR', 'XML', 'IOS', 'MacOS']) +_ALWAYS_SPECIALCASED_ENUM_VALUE_LOOKUP_TABLE = dict([(s.upper(), s) for s in _ALWAYS_SPECIALCASED_ENUM_VALUE_SUBSTRINGS]) + +# These objects are built manually by creating and setting InspectorValues. +# Before sending these over the protocol, their shapes are checked against the specification. +# So, any types referenced by these types require debug-only assertions that check values. +# Calculating necessary assertions is annoying, and adds a lot of complexity to the generator. + +# FIXME: This should be converted into a property in JSON. +_TYPES_NEEDING_RUNTIME_CASTS = set([ + "Runtime.ObjectPreview", + "Runtime.RemoteObject", + "Runtime.PropertyDescriptor", + "Runtime.InternalPropertyDescriptor", + "Runtime.CollectionEntry", + "Debugger.FunctionDetails", + "Debugger.CallFrame", + "Canvas.TraceLog", + "Canvas.ResourceInfo", + "Canvas.ResourceState", + # This should be a temporary hack. TimelineEvent should be created via generated C++ API. + "Timeline.TimelineEvent", + # For testing purposes only. + "Test.TypeNeedingCast" +]) + +# FIXME: This should be converted into a property in JSON. +_TYPES_WITH_OPEN_FIELDS = set([ + "Timeline.TimelineEvent", + # InspectorStyleSheet not only creates this property but wants to read it and modify it. + "CSS.CSSProperty", + # InspectorNetworkAgent needs to update mime-type. + "Network.Response", + # For testing purposes only. + "Test.OpenParameterBundle" +]) + + +class Generator: + def __init__(self, model, platform, input_filepath): + self._model = model + self._platform = platform + self._input_filepath = input_filepath + self._settings = {} + + def model(self): + return self._model + + def platform(self): + return self._platform + + def set_generator_setting(self, key, value): + self._settings[key] = value + + def can_generate_platform(self, model_platform): + return model_platform is Platforms.Generic or self._platform is Platforms.All or model_platform is self._platform + + def type_declarations_for_domain(self, domain): + return [type_declaration for type_declaration in domain.all_type_declarations() if self.can_generate_platform(type_declaration.platform)] + + def commands_for_domain(self, domain): + return [command for command in domain.all_commands() if self.can_generate_platform(command.platform)] + + def events_for_domain(self, domain): + return [event for event in domain.all_events() if self.can_generate_platform(event.platform)] + + # The goofy name is to disambiguate generator settings from framework settings. + def get_generator_setting(self, key, default=None): + return self._settings.get(key, default) + + def generate_license(self): + return Template(Templates.CopyrightBlock).substitute(None, inputFilename=os.path.basename(self._input_filepath)) + + # These methods are overridden by subclasses. + def non_supplemental_domains(self): + return filter(lambda domain: not domain.is_supplemental, self.model().domains) + + def domains_to_generate(self): + return self.non_supplemental_domains() + + def generate_output(self): + pass + + def output_filename(self): + pass + + def encoding_for_enum_value(self, enum_value): + if not hasattr(self, "_assigned_enum_values"): + self._traverse_and_assign_enum_values() + + return self._enum_value_encodings[enum_value] + + def assigned_enum_values(self): + if not hasattr(self, "_assigned_enum_values"): + self._traverse_and_assign_enum_values() + + return self._assigned_enum_values[:] # Slice. + + @staticmethod + def type_needs_runtime_casts(_type): + return _type.qualified_name() in _TYPES_NEEDING_RUNTIME_CASTS + + @staticmethod + def type_has_open_fields(_type): + return _type.qualified_name() in _TYPES_WITH_OPEN_FIELDS + + def type_needs_shape_assertions(self, _type): + if not hasattr(self, "_types_needing_shape_assertions"): + self.calculate_types_requiring_shape_assertions(self.model().domains) + + return _type in self._types_needing_shape_assertions + + # To restrict the domains over which we compute types needing assertions, call this method + # before generating any output with the desired domains parameter. The computed + # set of types will not be automatically regenerated on subsequent calls to + # Generator.types_needing_shape_assertions(). + def calculate_types_requiring_shape_assertions(self, domains): + domain_names = map(lambda domain: domain.domain_name, domains) + log.debug("> Calculating types that need shape assertions (eligible domains: %s)" % ", ".join(domain_names)) + + # Mutates the passed-in set; this simplifies checks to prevent infinite recursion. + def gather_transitively_referenced_types(_type, gathered_types): + if _type in gathered_types: + return + + if isinstance(_type, ObjectType): + log.debug("> Adding type %s to list of types needing shape assertions." % _type.qualified_name()) + gathered_types.add(_type) + for type_member in _type.members: + gather_transitively_referenced_types(type_member.type, gathered_types) + elif isinstance(_type, EnumType): + log.debug("> Adding type %s to list of types needing shape assertions." % _type.qualified_name()) + gathered_types.add(_type) + elif isinstance(_type, AliasedType): + gather_transitively_referenced_types(_type.aliased_type, gathered_types) + elif isinstance(_type, ArrayType): + gather_transitively_referenced_types(_type.element_type, gathered_types) + + found_types = set() + for domain in domains: + for declaration in self.type_declarations_for_domain(domain): + if declaration.type.qualified_name() in _TYPES_NEEDING_RUNTIME_CASTS: + log.debug("> Gathering types referenced by %s to generate shape assertions." % declaration.type.qualified_name()) + gather_transitively_referenced_types(declaration.type, found_types) + + self._types_needing_shape_assertions = found_types + + # Private helper instance methods. + def _traverse_and_assign_enum_values(self): + self._enum_value_encodings = {} + self._assigned_enum_values = [] + all_types = [] + + domains = self.non_supplemental_domains() + + for domain in domains: + for type_declaration in self.type_declarations_for_domain(domain): + all_types.append(type_declaration.type) + for type_member in type_declaration.type_members: + all_types.append(type_member.type) + + for domain in domains: + for event in self.events_for_domain(domain): + all_types.extend([parameter.type for parameter in event.event_parameters]) + + for domain in domains: + for command in self.commands_for_domain(domain): + all_types.extend([parameter.type for parameter in command.call_parameters]) + all_types.extend([parameter.type for parameter in command.return_parameters]) + + for _type in all_types: + if not isinstance(_type, EnumType): + continue + map(self._assign_encoding_for_enum_value, _type.enum_values()) + + def _assign_encoding_for_enum_value(self, enum_value): + if enum_value in self._enum_value_encodings: + return + + self._enum_value_encodings[enum_value] = len(self._assigned_enum_values) + self._assigned_enum_values.append(enum_value) + + # Miscellaneous text manipulation routines. + def wrap_with_guard_for_domain(self, domain, text): + if self.model().framework is Frameworks.WebInspector: + return text + guard = domain.feature_guard + if guard: + return Generator.wrap_with_guard(guard, text) + return text + + @staticmethod + def wrap_with_guard(guard, text): + return '\n'.join([ + '#if %s' % guard, + text, + '#endif // %s' % guard, + ]) + + @staticmethod + def stylized_name_for_enum_value(enum_value): + regex = '(%s)' % "|".join(_ALWAYS_SPECIALCASED_ENUM_VALUE_SUBSTRINGS) + + def replaceCallback(match): + return _ALWAYS_SPECIALCASED_ENUM_VALUE_LOOKUP_TABLE[match.group(1).upper()] + + # Split on hyphen, introduce camelcase, and force uppercasing of acronyms. + subwords = map(ucfirst, enum_value.split('-')) + return re.sub(re.compile(regex, re.IGNORECASE), replaceCallback, "".join(subwords)) + + @staticmethod + def js_name_for_parameter_type(_type): + _type = _type + if isinstance(_type, AliasedType): + _type = _type.aliased_type # Fall through. + if isinstance(_type, EnumType): + _type = _type.primitive_type # Fall through. + + if isinstance(_type, (ArrayType, ObjectType)): + return 'object' + if isinstance(_type, PrimitiveType): + if _type.qualified_name() in ['object', 'any']: + return 'object' + elif _type.qualified_name() in ['integer', 'number']: + return 'number' + else: + return _type.qualified_name() + + @staticmethod + def string_for_file_include(filename, file_framework, target_framework): + if file_framework is target_framework: + return '"%s"' % filename + else: + return '<%s/%s>' % (file_framework.name, filename) diff --git a/Source/JavaScriptCore/inspector/scripts/codegen/generator_templates.py b/Source/JavaScriptCore/inspector/scripts/codegen/generator_templates.py new file mode 100644 index 000000000..891681ffd --- /dev/null +++ b/Source/JavaScriptCore/inspector/scripts/codegen/generator_templates.py @@ -0,0 +1,61 @@ +#!/usr/bin/env python +# +# Copyright (c) 2014, 2016 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. + +# Generator templates, which can be filled with string.Template. +# Following are classes that fill the templates from the typechecked model. + + +class GeneratorTemplates: + CopyrightBlock = ( + """/* + * Copyright (C) 2013 Google Inc. All rights reserved. + * Copyright (C) 2013-2016 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. + */ + +// DO NOT EDIT THIS FILE. It is automatically generated from ${inputFilename} +// by the script: Source/JavaScriptCore/inspector/scripts/generate-inspector-protocol-bindings.py""") diff --git a/Source/JavaScriptCore/inspector/scripts/codegen/models.py b/Source/JavaScriptCore/inspector/scripts/codegen/models.py new file mode 100755 index 000000000..b286a5c40 --- /dev/null +++ b/Source/JavaScriptCore/inspector/scripts/codegen/models.py @@ -0,0 +1,680 @@ +#!/usr/bin/env python +# +# Copyright (c) 2014 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 collections + +log = logging.getLogger('global') + + +def ucfirst(str): + return str[:1].upper() + str[1:] + + +def find_duplicates(l): + return [key for key, count in collections.Counter(l).items() if count > 1] + + +_FRAMEWORK_CONFIG_MAP = { + "Global": { + }, + "JavaScriptCore": { + "cpp_protocol_group": "Inspector", + "export_macro": "JS_EXPORT_PRIVATE", + "alternate_dispatchers": True, + }, + "WebKit": { + "cpp_protocol_group": "Automation", + "objc_protocol_group": "WD", + "objc_prefix": "WD", + }, + "WebInspector": { + "objc_protocol_group": "RWI", + "objc_prefix": "RWI", + }, + # Used for code generator tests. + "Test": { + "alternate_dispatchers": True, + "cpp_protocol_group": "Test", + "objc_protocol_group": "Test", + "objc_prefix": "Test", + } +} + + +class ParseException(Exception): + pass + + +class TypecheckException(Exception): + pass + + +class Framework: + def __init__(self, name): + self._settings = _FRAMEWORK_CONFIG_MAP[name] + self.name = name + + def setting(self, key, default=''): + return self._settings.get(key, default) + + @staticmethod + def fromString(frameworkString): + if frameworkString == "Global": + return Frameworks.Global + + if frameworkString == "JavaScriptCore": + return Frameworks.JavaScriptCore + + if frameworkString == "WebKit": + return Frameworks.WebKit + + if frameworkString == "WebInspector": + return Frameworks.WebInspector + + if frameworkString == "Test": + return Frameworks.Test + + raise ParseException("Unknown framework: %s" % frameworkString) + + +class Frameworks: + Global = Framework("Global") + JavaScriptCore = Framework("JavaScriptCore") + WebKit = Framework("WebKit") + WebInspector = Framework("WebInspector") + Test = Framework("Test") + + +class Platform: + def __init__(self, name): + self.name = name + + @staticmethod + def fromString(platformString): + platformString = platformString.lower() + if platformString == "ios": + return Platforms.iOS + + if platformString == "macos": + return Platforms.macOS + + if platformString == "all": + return Platforms.All + + if platformString == "generic" or not platformString: + return Platforms.Generic + + raise ParseException("Unknown platform: %s" % platformString) + + +class Platforms: + All = Platform("all") + Generic = Platform("generic") + iOS = Platform("ios") + macOS = Platform("macos") + + # Allow iteration over all platforms. See <http://stackoverflow.com/questions/5434400/>. + class __metaclass__(type): + def __iter__(self): + for attr in dir(Platforms): + if not attr.startswith("__"): + yield getattr(Platforms, attr) + +class TypeReference: + def __init__(self, type_kind, referenced_type_name, enum_values, array_items): + self.type_kind = type_kind + self.referenced_type_name = referenced_type_name + self.enum_values = enum_values + if array_items is None: + self.array_type_ref = None + else: + self.array_type_ref = TypeReference(array_items.get('type'), array_items.get('$ref'), array_items.get('enum'), array_items.get('items')) + + if type_kind is not None and referenced_type_name is not None: + raise ParseException("Type reference cannot have both 'type' and '$ref' keys.") + + all_primitive_types = ["integer", "number", "string", "boolean", "enum", "object", "array", "any"] + if type_kind is not None and type_kind not in all_primitive_types: + raise ParseException("Type reference '%s' is not a primitive type. Allowed values: %s" % (type_kind, ', '.join(all_primitive_types))) + + if type_kind == "array" and array_items is None: + raise ParseException("Type reference with type 'array' must have key 'items' to define array element type.") + + if enum_values is not None and len(enum_values) == 0: + raise ParseException("Type reference with enum values must have at least one enum value.") + + def referenced_name(self): + if self.referenced_type_name is not None: + return self.referenced_type_name + else: + return self.type_kind # one of all_primitive_types + + +class Type: + def __init__(self): + pass + + def __eq__(self, other): + return self.qualified_name() == other.qualified_name() + + def __hash__(self): + return self.qualified_name().__hash__() + + def raw_name(self): + return self._name + + # These methods should be overridden by subclasses. + def is_enum(self): + return False + + def type_domain(self): + pass + + def qualified_name(self): + pass + + # This is used to resolve nested types after instances are created. + def resolve_type_references(self, protocol): + pass + + +class PrimitiveType(Type): + def __init__(self, name): + self._name = name + + def __repr__(self): + return 'PrimitiveType[%s]' % self.qualified_name() + + def type_domain(self): + return None + + def qualified_name(self): + return self.raw_name() + + +class AliasedType(Type): + def __init__(self, declaration, domain, aliased_type_ref): + self._name = declaration.type_name + self._declaration = declaration + self._domain = domain + self._aliased_type_ref = aliased_type_ref + self.aliased_type = None + + def __repr__(self): + if self.aliased_type is not None: + return 'AliasedType[%s -> %r]' % (self.qualified_name(), self.aliased_type) + else: + return 'AliasedType[%s -> (unresolved)]' % self.qualified_name() + + def is_enum(self): + return self.aliased_type.is_enum() + + def type_domain(self): + return self._domain + + def qualified_name(self): + return ".".join([self.type_domain().domain_name, self.raw_name()]) + + def resolve_type_references(self, protocol): + if self.aliased_type is not None: + return + + self.aliased_type = protocol.lookup_type_reference(self._aliased_type_ref, self.type_domain()) + log.debug("< Resolved type reference for aliased type in %s: %s" % (self.qualified_name(), self.aliased_type.qualified_name())) + + +class EnumType(Type): + def __init__(self, declaration, domain, values, primitive_type_ref, is_anonymous=False): + self._name = "(anonymous)" if declaration is None else declaration.type_name + self._declaration = declaration + self._domain = domain + self._values = values + self._primitive_type_ref = primitive_type_ref + self.primitive_type = None + self.is_anonymous = is_anonymous + + def __repr__(self): + return 'EnumType[value_type=%s; values=%s]' % (self.qualified_name(), ', '.join(map(str, self.enum_values()))) + + def is_enum(self): + return True + + def enum_values(self): + return self._values + + def type_domain(self): + return self._domain + + def declaration(self): + return self._declaration + + def qualified_name(self): + return ".".join([self.type_domain().domain_name, self.raw_name()]) + + def resolve_type_references(self, protocol): + if self.primitive_type is not None: + return + + self.primitive_type = protocol.lookup_type_reference(self._primitive_type_ref, Domains.GLOBAL) + log.debug("< Resolved type reference for enum type in %s: %s" % (self.qualified_name(), self.primitive_type.qualified_name())) + log.debug("<< enum values: %s" % self.enum_values()) + + +class ArrayType(Type): + def __init__(self, declaration, element_type_ref, domain): + self._name = None if declaration is None else declaration.type_name + self._declaration = declaration + self._domain = domain + self._element_type_ref = element_type_ref + self.element_type = None + + def __repr__(self): + if self.element_type is not None: + return 'ArrayType[element_type=%r]' % self.element_type + else: + return 'ArrayType[element_type=(unresolved)]' + + def declaration(self): + return self._declaration + + def type_domain(self): + return self._domain + + def qualified_name(self): + return ".".join(["array", self.element_type.qualified_name()]) + + def resolve_type_references(self, protocol): + if self.element_type is not None: + return + + self.element_type = protocol.lookup_type_reference(self._element_type_ref, self.type_domain()) + log.debug("< Resolved type reference for element type in %s: %s" % (self.qualified_name(), self.element_type.qualified_name())) + + +class ObjectType(Type): + def __init__(self, declaration, domain): + self._name = declaration.type_name + self._declaration = declaration + self._domain = domain + self.members = declaration.type_members + + def __repr__(self): + return 'ObjectType[%s]' % self.qualified_name() + + def declaration(self): + return self._declaration + + def type_domain(self): + return self._domain + + def qualified_name(self): + return ".".join([self.type_domain().domain_name, self.raw_name()]) + + +def check_for_required_properties(props, obj, what): + for prop in props: + if prop not in obj: + raise ParseException("When parsing %s, required property missing: %s" % (what, prop)) + + +class Protocol: + def __init__(self, framework_name): + self.domains = [] + self.types_by_name = {} + self.framework = Framework.fromString(framework_name) + + def parse_specification(self, json, isSupplemental): + log.debug("parse toplevel") + + if isinstance(json, dict) and 'domains' in json: + json = json['domains'] + if not isinstance(json, list): + json = [json] + + for domain in json: + self.parse_domain(domain, isSupplemental) + + def parse_domain(self, json, isSupplemental): + check_for_required_properties(['domain'], json, "domain") + log.debug("parse domain " + json['domain']) + + types = [] + commands = [] + events = [] + + if 'types' in json: + if not isinstance(json['types'], list): + raise ParseException("Malformed domain specification: types is not an array") + types.extend([self.parse_type_declaration(declaration) for declaration in json['types']]) + + if 'commands' in json: + if not isinstance(json['commands'], list): + raise ParseException("Malformed domain specification: commands is not an array") + commands.extend([self.parse_command(command) for command in json['commands']]) + + if 'events' in json: + if not isinstance(json['events'], list): + raise ParseException("Malformed domain specification: events is not an array") + events.extend([self.parse_event(event) for event in json['events']]) + + if 'availability' in json: + if not commands and not events: + raise ParseException("Malformed domain specification: availability should only be included if there are commands or events.") + allowed_activation_strings = set(['web']) + if json['availability'] not in allowed_activation_strings: + raise ParseException('Malformed domain specification: availability is an unsupported string. Was: "%s", Allowed values: %s' % (json['availability'], ', '.join(allowed_activation_strings))) + + if 'workerSupported' in json: + if not isinstance(json['workerSupported'], bool): + raise ParseException('Malformed domain specification: workerSupported is not a boolean. Was: "%s"' % json['availability']) + + self.domains.append(Domain(json['domain'], json.get('description', ''), json.get('featureGuard'), json.get('availability'), json.get('workerSupported', False), isSupplemental, types, commands, events)) + + def parse_type_declaration(self, json): + check_for_required_properties(['id', 'type'], json, "type") + log.debug("parse type %s" % json['id']) + + type_members = [] + + if 'properties' in json: + if not isinstance(json['properties'], list): + raise ParseException("Malformed type specification: properties is not an array") + type_members.extend([self.parse_type_member(member) for member in json['properties']]) + + duplicate_names = find_duplicates([member.member_name for member in type_members]) + if len(duplicate_names) > 0: + raise ParseException("Malformed domain specification: type declaration for %s has duplicate member names" % json['id']) + + type_ref = TypeReference(json['type'], json.get('$ref'), json.get('enum'), json.get('items')) + platform = Platform.fromString(json.get('platform', 'generic')) + return TypeDeclaration(json['id'], type_ref, json.get("description", ""), platform, type_members) + + def parse_type_member(self, json): + check_for_required_properties(['name'], json, "type member") + log.debug("parse type member %s" % json['name']) + + type_ref = TypeReference(json.get('type'), json.get('$ref'), json.get('enum'), json.get('items')) + return TypeMember(json['name'], type_ref, json.get('optional', False), json.get('description', "")) + + def parse_command(self, json): + check_for_required_properties(['name'], json, "command") + log.debug("parse command %s" % json['name']) + + call_parameters = [] + return_parameters = [] + + if 'parameters' in json: + if not isinstance(json['parameters'], list): + raise ParseException("Malformed command specification: parameters is not an array") + call_parameters.extend([self.parse_call_or_return_parameter(parameter) for parameter in json['parameters']]) + + duplicate_names = find_duplicates([param.parameter_name for param in call_parameters]) + if len(duplicate_names) > 0: + raise ParseException("Malformed domain specification: call parameter list for command %s has duplicate parameter names" % json['name']) + + if 'returns' in json: + if not isinstance(json['returns'], list): + raise ParseException("Malformed command specification: returns is not an array") + return_parameters.extend([self.parse_call_or_return_parameter(parameter) for parameter in json['returns']]) + + duplicate_names = find_duplicates([param.parameter_name for param in return_parameters]) + if len(duplicate_names) > 0: + raise ParseException("Malformed domain specification: return parameter list for command %s has duplicate parameter names" % json['name']) + + platform = Platform.fromString(json.get('platform', 'generic')) + return Command(json['name'], call_parameters, return_parameters, json.get('description', ""), platform, json.get('async', False)) + + def parse_event(self, json): + check_for_required_properties(['name'], json, "event") + log.debug("parse event %s" % json['name']) + + event_parameters = [] + + if 'parameters' in json: + if not isinstance(json['parameters'], list): + raise ParseException("Malformed event specification: parameters is not an array") + event_parameters.extend([self.parse_call_or_return_parameter(parameter) for parameter in json['parameters']]) + + duplicate_names = find_duplicates([param.parameter_name for param in event_parameters]) + if len(duplicate_names) > 0: + raise ParseException("Malformed domain specification: parameter list for event %s has duplicate parameter names" % json['name']) + + platform = Platform.fromString(json.get('platform', 'generic')) + return Event(json['name'], event_parameters, json.get('description', ""), platform) + + def parse_call_or_return_parameter(self, json): + check_for_required_properties(['name'], json, "parameter") + log.debug("parse parameter %s" % json['name']) + + type_ref = TypeReference(json.get('type'), json.get('$ref'), json.get('enum'), json.get('items')) + return Parameter(json['name'], type_ref, json.get('optional', False), json.get('description', "")) + + def resolve_types(self): + qualified_declared_type_names = set(['boolean', 'string', 'integer', 'number', 'enum', 'array', 'object', 'any']) + + self.types_by_name['string'] = PrimitiveType('string') + for _primitive_type in ['boolean', 'integer', 'number']: + self.types_by_name[_primitive_type] = PrimitiveType(_primitive_type) + for _object_type in ['any', 'object']: + self.types_by_name[_object_type] = PrimitiveType(_object_type) + + # Gather qualified type names from type declarations in each domain. + for domain in self.domains: + for declaration in domain.all_type_declarations(): + # Basic sanity checking. + if declaration.type_ref.referenced_type_name is not None: + raise TypecheckException("Type declarations must name a base type, not a type reference.") + + # Find duplicate qualified type names. + qualified_type_name = ".".join([domain.domain_name, declaration.type_name]) + if qualified_type_name in qualified_declared_type_names: + raise TypecheckException("Duplicate type declaration: %s" % qualified_type_name) + + qualified_declared_type_names.add(qualified_type_name) + + type_instance = None + + kind = declaration.type_ref.type_kind + if declaration.type_ref.enum_values is not None: + primitive_type_ref = TypeReference(declaration.type_ref.type_kind, None, None, None) + type_instance = EnumType(declaration, domain, declaration.type_ref.enum_values, primitive_type_ref) + elif kind == "array": + type_instance = ArrayType(declaration, declaration.type_ref.array_type_ref, domain) + elif kind == "object": + type_instance = ObjectType(declaration, domain) + else: + type_instance = AliasedType(declaration, domain, declaration.type_ref) + + log.debug("< Created fresh type %r for declaration %s" % (type_instance, qualified_type_name)) + self.types_by_name[qualified_type_name] = type_instance + + # Resolve all type references recursively. + for domain in self.domains: + domain.resolve_type_references(self) + + def lookup_type_for_declaration(self, declaration, domain): + # This will only match a type defined in the same domain, where prefixes aren't required. + qualified_name = ".".join([domain.domain_name, declaration.type_name]) + if qualified_name in self.types_by_name: + found_type = self.types_by_name[qualified_name] + found_type.resolve_type_references(self) + return found_type + + raise TypecheckException("Lookup failed for type declaration: %s (referenced from domain: %s)" % (declaration.type_name, domain.domain_name)) + + def lookup_type_reference(self, type_ref, domain): + # If reference is to an anonymous array type, create a fresh instance. + if type_ref.type_kind == "array": + type_instance = ArrayType(None, type_ref.array_type_ref, domain) + type_instance.resolve_type_references(self) + log.debug("< Created fresh type instance for anonymous array type: %s" % type_instance.qualified_name()) + return type_instance + + # If reference is to an anonymous enum type, create a fresh instance. + if type_ref.enum_values is not None: + # We need to create a type reference without enum values as the enum's nested type. + primitive_type_ref = TypeReference(type_ref.type_kind, None, None, None) + type_instance = EnumType(None, domain, type_ref.enum_values, primitive_type_ref, True) + type_instance.resolve_type_references(self) + log.debug("< Created fresh type instance for anonymous enum type: %s" % type_instance.qualified_name()) + return type_instance + + # This will match when referencing a type defined in the same domain, where prefixes aren't required. + qualified_name = ".".join([domain.domain_name, type_ref.referenced_name()]) + if qualified_name in self.types_by_name: + found_type = self.types_by_name[qualified_name] + found_type.resolve_type_references(self) + log.debug("< Lookup succeeded for unqualified type: %s" % found_type.qualified_name()) + return found_type + + # This will match primitive types and fully-qualified types from a different domain. + if type_ref.referenced_name() in self.types_by_name: + found_type = self.types_by_name[type_ref.referenced_name()] + found_type.resolve_type_references(self) + log.debug("< Lookup succeeded for primitive or qualified type: %s" % found_type.qualified_name()) + return found_type + + raise TypecheckException("Lookup failed for type reference: %s (referenced from domain: %s)" % (type_ref.referenced_name(), domain.domain_name)) + + +class Domain: + def __init__(self, domain_name, description, feature_guard, availability, workerSupported, isSupplemental, type_declarations, commands, events): + self.domain_name = domain_name + self.description = description + self.feature_guard = feature_guard + self.availability = availability + self.workerSupported = workerSupported + self.is_supplemental = isSupplemental + self._type_declarations = type_declarations + self._commands = commands + self._events = events + + def all_type_declarations(self): + return self._type_declarations + + def all_commands(self): + return self._commands + + def all_events(self): + return self._events + + def resolve_type_references(self, protocol): + log.debug("> Resolving type declarations for domain: %s" % self.domain_name) + for declaration in self._type_declarations: + declaration.resolve_type_references(protocol, self) + + log.debug("> Resolving types in commands for domain: %s" % self.domain_name) + for command in self._commands: + command.resolve_type_references(protocol, self) + + log.debug("> Resolving types in events for domain: %s" % self.domain_name) + for event in self._events: + event.resolve_type_references(protocol, self) + + +class Domains: + GLOBAL = Domain("", "The global domain, in which primitive types are implicitly declared.", None, None, True, False, [], [], []) + + +class TypeDeclaration: + def __init__(self, type_name, type_ref, description, platform, type_members): + self.type_name = type_name + self.type_ref = type_ref + self.description = description + self.platform = platform + self.type_members = type_members + + if self.type_name != ucfirst(self.type_name): + raise ParseException("Types must begin with an uppercase character.") + + def resolve_type_references(self, protocol, domain): + log.debug(">> Resolving type references for type declaration: %s" % self.type_name) + self.type = protocol.lookup_type_for_declaration(self, domain) + for member in self.type_members: + member.resolve_type_references(protocol, domain) + + +class TypeMember: + def __init__(self, member_name, type_ref, is_optional, description): + self.member_name = member_name + self.type_ref = type_ref + self.is_optional = is_optional + self.description = description + + if not isinstance(self.is_optional, bool): + raise ParseException("The 'optional' flag for a type member must be a boolean literal.") + + def resolve_type_references(self, protocol, domain): + log.debug(">>> Resolving type references for type member: %s" % self.member_name) + self.type = protocol.lookup_type_reference(self.type_ref, domain) + + +class Parameter: + def __init__(self, parameter_name, type_ref, is_optional, description): + self.parameter_name = parameter_name + self.type_ref = type_ref + self.is_optional = is_optional + self.description = description + + if not isinstance(self.is_optional, bool): + raise ParseException("The 'optional' flag for a parameter must be a boolean literal.") + + def resolve_type_references(self, protocol, domain): + log.debug(">>> Resolving type references for parameter: %s" % self.parameter_name) + self.type = protocol.lookup_type_reference(self.type_ref, domain) + + +class Command: + def __init__(self, command_name, call_parameters, return_parameters, description, platform, is_async): + self.command_name = command_name + self.call_parameters = call_parameters + self.return_parameters = return_parameters + self.description = description + self.platform = platform + self.is_async = is_async + + def resolve_type_references(self, protocol, domain): + log.debug(">> Resolving type references for call parameters in command: %s" % self.command_name) + for parameter in self.call_parameters: + parameter.resolve_type_references(protocol, domain) + + log.debug(">> Resolving type references for return parameters in command: %s" % self.command_name) + for parameter in self.return_parameters: + parameter.resolve_type_references(protocol, domain) + + +class Event: + def __init__(self, event_name, event_parameters, description, platform): + self.event_name = event_name + self.event_parameters = event_parameters + self.description = description + self.platform = platform + + def resolve_type_references(self, protocol, domain): + log.debug(">> Resolving type references for parameters in event: %s" % self.event_name) + for parameter in self.event_parameters: + parameter.resolve_type_references(protocol, domain) diff --git a/Source/JavaScriptCore/inspector/scripts/codegen/objc_generator.py b/Source/JavaScriptCore/inspector/scripts/codegen/objc_generator.py new file mode 100755 index 000000000..21e179148 --- /dev/null +++ b/Source/JavaScriptCore/inspector/scripts/codegen/objc_generator.py @@ -0,0 +1,554 @@ +#!/usr/bin/env python +# +# Copyright (c) 2014, 2016 Apple Inc. 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 +from generator import Generator, ucfirst +from models import PrimitiveType, ObjectType, ArrayType, EnumType, AliasedType, Frameworks + +log = logging.getLogger('global') + + +def join_type_and_name(type_str, name_str): + if type_str.endswith('*'): + return type_str + name_str + return type_str + ' ' + name_str + + +def strip_block_comment_markers(str): + return str.replace('/*', '').replace('*/', '') + + +def remove_duplicate_from_str(str, possible_duplicate): + return str.replace(possible_duplicate + possible_duplicate, possible_duplicate) + + +_OBJC_IDENTIFIER_RENAME_MAP = { + 'this': 'thisObject', # Debugger.CallFrame.this + 'description': 'stringRepresentation', # Runtime.RemoteObject.description + 'id': 'identifier', # Page.Frame.id, Runtime.ExecutionContextDescription.id, Debugger.BreakpointAction.id +} + +_OBJC_IDENTIFIER_REVERSE_RENAME_MAP = dict((v, k) for k, v in _OBJC_IDENTIFIER_RENAME_MAP.iteritems()) + + +class ObjCTypeCategory: + Simple = 0 + String = 1 + Object = 2 + Array = 3 + + @staticmethod + def category_for_type(_type): + if (isinstance(_type, PrimitiveType)): + if _type.raw_name() is 'string': + return ObjCTypeCategory.String + if _type.raw_name() in ['object', 'any']: + return ObjCTypeCategory.Object + if _type.raw_name() is 'array': + return ObjCTypeCategory.Array + return ObjCTypeCategory.Simple + if (isinstance(_type, ObjectType)): + return ObjCTypeCategory.Object + if (isinstance(_type, ArrayType)): + return ObjCTypeCategory.Array + if (isinstance(_type, AliasedType)): + return ObjCTypeCategory.category_for_type(_type.aliased_type) + if (isinstance(_type, EnumType)): + return ObjCTypeCategory.category_for_type(_type.primitive_type) + return None + +# Almost all Objective-C class names require the use of a prefix that depends on the +# target framework's 'objc_prefix' setting. So, most helpers are instance methods. + +class ObjCGenerator(Generator): + # Do not use a dynamic prefix for RWIProtocolJSONObject since it's used as a common + # base class and needs a consistent Objective-C prefix to be in a reusable framework. + OBJC_HELPER_PREFIX = 'RWI' + OBJC_SHARED_PREFIX = 'Protocol' + OBJC_STATIC_PREFIX = '%s%s' % (OBJC_HELPER_PREFIX, OBJC_SHARED_PREFIX) + + def __init__(self, *args, **kwargs): + Generator.__init__(self, *args, **kwargs) + + # The 'protocol name' is used to prefix filenames for a protocol group (a set of domains generated together). + def protocol_name(self): + protocol_group = self.model().framework.setting('objc_protocol_group', '') + return '%s%s' % (protocol_group, ObjCGenerator.OBJC_SHARED_PREFIX) + + # The 'ObjC prefix' is used to prefix Objective-C class names and enums with a + # framework-specific prefix. It is separate from filename prefixes. + def objc_prefix(self): + framework_prefix = self.model().framework.setting('objc_prefix', None) + if not framework_prefix: + return '' + else: + return '%s%s' % (framework_prefix, ObjCGenerator.OBJC_SHARED_PREFIX) + + # Adjust identifier names that collide with ObjC keywords. + + @staticmethod + def identifier_to_objc_identifier(name): + return _OBJC_IDENTIFIER_RENAME_MAP.get(name, name) + + @staticmethod + def objc_identifier_to_identifier(name): + return _OBJC_IDENTIFIER_REVERSE_RENAME_MAP.get(name, name) + + # Generate ObjC types, command handlers, and event dispatchers for a subset of domains. + + DOMAINS_TO_GENERATE = ['CSS', 'DOM', 'DOMStorage', 'Network', 'Page', 'Automation', 'GenericTypes'] + + def should_generate_types_for_domain(self, domain): + if not len(self.type_declarations_for_domain(domain)): + return False + + if self.model().framework is Frameworks.Test: + return True + + whitelist = set(ObjCGenerator.DOMAINS_TO_GENERATE) + whitelist.update(set(['Console', 'Debugger', 'Runtime'])) + return domain.domain_name in whitelist + + def should_generate_commands_for_domain(self, domain): + if not len(self.commands_for_domain(domain)): + return False + + if self.model().framework is Frameworks.Test: + return True + + whitelist = set(ObjCGenerator.DOMAINS_TO_GENERATE) + return domain.domain_name in whitelist + + def should_generate_events_for_domain(self, domain): + if not len(self.events_for_domain(domain)): + return False + + if self.model().framework is Frameworks.Test: + return True + + whitelist = set(ObjCGenerator.DOMAINS_TO_GENERATE) + whitelist.add('Console') + return domain.domain_name in whitelist + + # ObjC enum and type names. + + def objc_name_for_type(self, type): + name = type.qualified_name().replace('.', '') + name = remove_duplicate_from_str(name, type.type_domain().domain_name) + return '%s%s' % (self.objc_prefix(), name) + + def objc_enum_name_for_anonymous_enum_declaration(self, declaration): + domain_name = declaration.type.type_domain().domain_name + name = '%s%s' % (domain_name, declaration.type.raw_name()) + name = remove_duplicate_from_str(name, domain_name) + return '%s%s' % (self.objc_prefix(), name) + + def objc_enum_name_for_anonymous_enum_member(self, declaration, member): + domain_name = member.type.type_domain().domain_name + name = '%s%s%s' % (domain_name, declaration.type.raw_name(), ucfirst(member.member_name)) + name = remove_duplicate_from_str(name, domain_name) + return '%s%s' % (self.objc_prefix(), name) + + def objc_enum_name_for_anonymous_enum_parameter(self, domain, event_or_command_name, parameter): + domain_name = domain.domain_name + name = '%s%s%s' % (domain_name, ucfirst(event_or_command_name), ucfirst(parameter.parameter_name)) + name = remove_duplicate_from_str(name, domain_name) + return '%s%s' % (self.objc_prefix(), name) + + def objc_enum_name_for_non_anonymous_enum(self, _type): + domain_name = _type.type_domain().domain_name + name = _type.qualified_name().replace('.', '') + name = remove_duplicate_from_str(name, domain_name) + return '%s%s' % (self.objc_prefix(), name) + + # Miscellaneous name handling. + + @staticmethod + def variable_name_prefix_for_domain(domain): + domain_name = domain.domain_name + if domain_name.startswith('DOM'): + return 'dom' + domain_name[3:] + if domain_name.startswith('CSS'): + return 'css' + domain_name[3:] + return domain_name[:1].lower() + domain_name[1:] + + # Type basics. + + @staticmethod + def objc_accessor_type_for_raw_name(raw_name): + if raw_name in ['string', 'array']: + return 'copy' + if raw_name in ['integer', 'number', 'boolean']: + return 'assign' + if raw_name in ['any', 'object']: + return 'retain' + return None + + @staticmethod + def objc_type_for_raw_name(raw_name): + if raw_name is 'string': + return 'NSString *' + if raw_name is 'array': + return 'NSArray *' + if raw_name is 'integer': + return 'int' + if raw_name is 'number': + return 'double' + if raw_name is 'boolean': + return 'BOOL' + if raw_name in ['any', 'object']: + return '%sJSONObject *' % ObjCGenerator.OBJC_STATIC_PREFIX + return None + + @staticmethod + def objc_class_for_raw_name(raw_name): + if raw_name is 'string': + return 'NSString' + if raw_name is 'array': + return 'NSArray' + if raw_name in ['integer', 'number', 'boolean']: + return 'NSNumber' + if raw_name in ['any', 'object']: + return '%sJSONObject' % ObjCGenerator.OBJC_STATIC_PREFIX + return None + + # FIXME: Can these protocol_type functions be removed in favor of C++ generators functions? + + @staticmethod + def protocol_type_for_raw_name(raw_name): + if raw_name is 'string': + return 'String' + if raw_name is 'integer': + return 'int' + if raw_name is 'number': + return 'double' + if raw_name is 'boolean': + return 'bool' + if raw_name in ['any', 'object']: + return 'InspectorObject' + return None + + @staticmethod + def protocol_type_for_type(_type): + if (isinstance(_type, AliasedType)): + _type = _type.aliased_type + if (isinstance(_type, PrimitiveType)): + return ObjCGenerator.protocol_type_for_raw_name(_type.raw_name()) + if (isinstance(_type, EnumType)): + return ObjCGenerator.protocol_type_for_type(_type.primitive_type) + if (isinstance(_type, ObjectType)): + return 'Inspector::Protocol::%s::%s' % (_type.type_domain().domain_name, _type.raw_name()) + if (isinstance(_type, ArrayType)): + sub_type = ObjCGenerator.protocol_type_for_type(_type.element_type) + return 'Inspector::Protocol::Array<%s>' % sub_type + return None + + @staticmethod + def is_type_objc_pointer_type(_type): + if (isinstance(_type, AliasedType)): + _type = _type.aliased_type + if (isinstance(_type, PrimitiveType)): + return _type.raw_name() in ['string', 'array', 'any', 'object'] + if (isinstance(_type, EnumType)): + return False + if (isinstance(_type, ObjectType)): + return True + if (isinstance(_type, ArrayType)): + return True + return None + + def objc_class_for_type(self, _type): + if (isinstance(_type, AliasedType)): + _type = _type.aliased_type + if (isinstance(_type, PrimitiveType)): + return ObjCGenerator.objc_class_for_raw_name(_type.raw_name()) + if (isinstance(_type, EnumType)): + return ObjCGenerator.objc_class_for_raw_name(_type.primitive_type.raw_name()) + if (isinstance(_type, ObjectType)): + return self.objc_name_for_type(_type) + if (isinstance(_type, ArrayType)): + sub_type = strip_block_comment_markers(self.objc_class_for_type(_type.element_type)) + return 'NSArray/*<%s>*/' % sub_type + return None + + def objc_class_for_array_type(self, _type): + if isinstance(_type, AliasedType): + _type = _type.aliased_type + if isinstance(_type, ArrayType): + return self.objc_class_for_type(_type.element_type) + return None + + def objc_accessor_type_for_member(self, member): + return self.objc_accessor_type_for_member_internal(member.type) + + def objc_accessor_type_for_member_internal(self, _type): + if (isinstance(_type, AliasedType)): + _type = _type.aliased_type + if (isinstance(_type, PrimitiveType)): + return self.objc_accessor_type_for_raw_name(_type.raw_name()) + if (isinstance(_type, EnumType)): + return 'assign' + if (isinstance(_type, ObjectType)): + return 'retain' + if (isinstance(_type, ArrayType)): + return 'copy' + return None + + def objc_type_for_member(self, declaration, member): + return self.objc_type_for_member_internal(member.type, declaration, member) + + def objc_type_for_member_internal(self, _type, declaration, member): + if (isinstance(_type, AliasedType)): + _type = _type.aliased_type + if (isinstance(_type, PrimitiveType)): + return self.objc_type_for_raw_name(_type.raw_name()) + if (isinstance(_type, EnumType)): + if (_type.is_anonymous): + return self.objc_enum_name_for_anonymous_enum_member(declaration, member) + return self.objc_enum_name_for_non_anonymous_enum(_type) + if (isinstance(_type, ObjectType)): + return self.objc_name_for_type(_type) + ' *' + if (isinstance(_type, ArrayType)): + sub_type = strip_block_comment_markers(self.objc_class_for_type(_type.element_type)) + return 'NSArray/*<%s>*/ *' % sub_type + return None + + def objc_type_for_param(self, domain, event_or_command_name, parameter, respect_optional=True): + objc_type = self.objc_type_for_param_internal(parameter.type, domain, event_or_command_name, parameter) + if respect_optional and parameter.is_optional: + if objc_type.endswith('*'): + return objc_type + '*' + return objc_type + ' *' + return objc_type + + def objc_type_for_param_internal(self, _type, domain, event_or_command_name, parameter): + if (isinstance(_type, AliasedType)): + _type = _type.aliased_type + if (isinstance(_type, PrimitiveType)): + return self.objc_type_for_raw_name(_type.raw_name()) + if (isinstance(_type, EnumType)): + if _type.is_anonymous: + return self.objc_enum_name_for_anonymous_enum_parameter(domain, event_or_command_name, parameter) + return self.objc_enum_name_for_non_anonymous_enum(_type) + if (isinstance(_type, ObjectType)): + return self.objc_name_for_type(_type) + ' *' + if (isinstance(_type, ArrayType)): + sub_type = strip_block_comment_markers(self.objc_class_for_type(_type.element_type)) + return 'NSArray/*<%s>*/ *' % sub_type + return None + + # ObjC <-> Protocol conversion for commands and events. + # - convert a command call parameter received from Protocol to ObjC for handler + # - convert a command return parameter in callback block from ObjC to Protocol to send + # - convert an event parameter from ObjC API to Protocol to send + + def objc_protocol_export_expression_for_variable(self, var_type, var_name): + category = ObjCTypeCategory.category_for_type(var_type) + if category in [ObjCTypeCategory.Simple, ObjCTypeCategory.String]: + if isinstance(var_type, EnumType): + return 'toProtocolString(%s)' % var_name + return var_name + if category is ObjCTypeCategory.Object: + return '[%s toInspectorObject]' % var_name + if category is ObjCTypeCategory.Array: + protocol_type = ObjCGenerator.protocol_type_for_type(var_type.element_type) + objc_class = self.objc_class_for_type(var_type.element_type) + if protocol_type == 'Inspector::Protocol::Array<String>': + return 'inspectorStringArrayArray(%s)' % var_name + if protocol_type is 'String' and objc_class is 'NSString': + return 'inspectorStringArray(%s)' % var_name + if protocol_type is 'int' and objc_class is 'NSNumber': + return 'inspectorIntegerArray(%s)' % var_name + if protocol_type is 'double' and objc_class is 'NSNumber': + return 'inspectorDoubleArray(%s)' % var_name + return 'inspectorObjectArray(%s)' % var_name + + def objc_protocol_import_expression_for_member(self, name, declaration, member): + if isinstance(member.type, EnumType): + if member.type.is_anonymous: + return 'fromProtocolString<%s>(%s)' % (self.objc_enum_name_for_anonymous_enum_member(declaration, member), name) + return 'fromProtocolString<%s>(%s)' % (self.objc_enum_name_for_non_anonymous_enum(member.type), name) + return self.objc_protocol_import_expression_for_variable(member.type, name) + + def objc_protocol_import_expression_for_parameter(self, name, domain, event_or_command_name, parameter): + if isinstance(parameter.type, EnumType): + if parameter.type.is_anonymous: + return 'fromProtocolString<%s>(%s)' % (self.objc_enum_name_for_anonymous_enum_parameter(domain, event_or_command_name, parameter), name) + return 'fromProtocolString<%s>(%s)' % (self.objc_enum_name_for_non_anonymous_enum(parameter.type), name) + return self.objc_protocol_import_expression_for_variable(parameter.type, name) + + def objc_protocol_import_expression_for_variable(self, var_type, var_name): + category = ObjCTypeCategory.category_for_type(var_type) + if category in [ObjCTypeCategory.Simple, ObjCTypeCategory.String]: + return var_name + if category is ObjCTypeCategory.Object: + objc_class = self.objc_class_for_type(var_type) + return '[[[%s alloc] initWithInspectorObject:%s] autorelease]' % (objc_class, var_name) + if category is ObjCTypeCategory.Array: + objc_class = self.objc_class_for_type(var_type.element_type) + if objc_class is 'NSString': + return 'objcStringArray(%s)' % var_name + if objc_class is 'NSNumber': # FIXME: Integer or Double? + return 'objcIntegerArray(%s)' % var_name + return 'objcArray<%s>(%s)' % (objc_class, var_name) + + # ObjC <-> JSON object conversion for types getters/setters. + # - convert a member setter from ObjC API to JSON object setter + # - convert a member getter from JSON object to ObjC API + + def objc_to_protocol_expression_for_member(self, declaration, member, sub_expression): + category = ObjCTypeCategory.category_for_type(member.type) + if category in [ObjCTypeCategory.Simple, ObjCTypeCategory.String]: + if isinstance(member.type, EnumType): + return 'toProtocolString(%s)' % sub_expression + return sub_expression + if category is ObjCTypeCategory.Object: + return sub_expression + if category is ObjCTypeCategory.Array: + objc_class = self.objc_class_for_type(member.type.element_type) + if objc_class is 'NSString': + return 'inspectorStringArray(%s)' % sub_expression + if objc_class is 'NSNumber': + protocol_type = ObjCGenerator.protocol_type_for_type(member.type.element_type) + if protocol_type is 'double': + return 'inspectorDoubleArray(%s)' % sub_expression + return 'inspectorIntegerArray(%s)' % sub_expression + return 'inspectorObjectArray(%s)' % sub_expression + + def protocol_to_objc_expression_for_member(self, declaration, member, sub_expression): + category = ObjCTypeCategory.category_for_type(member.type) + if category in [ObjCTypeCategory.Simple, ObjCTypeCategory.String]: + if isinstance(member.type, EnumType): + if member.type.is_anonymous: + return 'fromProtocolString<%s>(%s).value()' % (self.objc_enum_name_for_anonymous_enum_member(declaration, member), sub_expression) + return 'fromProtocolString<%s>(%s).value()' % (self.objc_enum_name_for_non_anonymous_enum(member.type), sub_expression) + return sub_expression + if category is ObjCTypeCategory.Object: + objc_class = self.objc_class_for_type(member.type) + return '[[%s alloc] initWithInspectorObject:[%s toInspectorObject].get()]' % (objc_class, sub_expression) + if category is ObjCTypeCategory.Array: + protocol_type = ObjCGenerator.protocol_type_for_type(member.type.element_type) + objc_class = self.objc_class_for_type(member.type.element_type) + if objc_class is 'NSString': + return 'objcStringArray(%s)' % sub_expression + if objc_class is 'NSNumber': + protocol_type = ObjCGenerator.protocol_type_for_type(member.type.element_type) + if protocol_type is 'double': + return 'objcDoubleArray(%s)' % sub_expression + return 'objcIntegerArray(%s)' % sub_expression + return 'objcArray<%s>(%s)' % (objc_class, sub_expression) + + def payload_to_objc_expression_for_member(self, declaration, member): + _type = member.type + if isinstance(_type, AliasedType): + _type = _type.aliased_type + if isinstance(_type, PrimitiveType): + sub_expression = 'payload[@"%s"]' % member.member_name + raw_name = _type.raw_name() + if raw_name is 'boolean': + return '[%s boolValue]' % sub_expression + if raw_name is 'integer': + return '[%s integerValue]' % sub_expression + if raw_name is 'number': + return '[%s doubleValue]' % sub_expression + if raw_name in ['any', 'object', 'array', 'string']: + return sub_expression # The setter will check the incoming value. + return None + if isinstance(member.type, EnumType): + sub_expression = 'payload[@"%s"]' % member.member_name + if member.type.is_anonymous: + return 'fromProtocolString<%s>(%s)' % (self.objc_enum_name_for_anonymous_enum_member(declaration, member), sub_expression) + else: + return 'fromProtocolString<%s>(%s)' % (self.objc_enum_name_for_non_anonymous_enum(member.type), sub_expression) + if isinstance(_type, ObjectType): + objc_class = self.objc_class_for_type(member.type) + return '[[%s alloc] initWithPayload:payload[@"%s"]]' % (objc_class, member.member_name) + if isinstance(_type, ArrayType): + objc_class = self.objc_class_for_type(member.type.element_type) + return 'objcArrayFromPayload<%s>(payload[@"%s"])' % (objc_class, member.member_name) + + # JSON object setter/getter selectors for types. + + @staticmethod + def objc_setter_method_for_member(declaration, member): + return ObjCGenerator.objc_setter_method_for_member_internal(member.type, declaration, member) + + @staticmethod + def objc_setter_method_for_member_internal(_type, declaration, member): + if (isinstance(_type, AliasedType)): + _type = _type.aliased_type + if (isinstance(_type, PrimitiveType)): + raw_name = _type.raw_name() + if raw_name is 'boolean': + return 'setBool' + if raw_name is 'integer': + return 'setInteger' + if raw_name is 'number': + return 'setDouble' + if raw_name is 'string': + return 'setString' + if raw_name in ['any', 'object']: + return 'setObject' + if raw_name is 'array': + return 'setInspectorArray' + return None + if (isinstance(_type, EnumType)): + return 'setString' + if (isinstance(_type, ObjectType)): + return 'setObject' + if (isinstance(_type, ArrayType)): + return 'setInspectorArray' + return None + + @staticmethod + def objc_getter_method_for_member(declaration, member): + return ObjCGenerator.objc_getter_method_for_member_internal(member.type, declaration, member) + + @staticmethod + def objc_getter_method_for_member_internal(_type, declaration, member): + if (isinstance(_type, AliasedType)): + _type = _type.aliased_type + if (isinstance(_type, PrimitiveType)): + raw_name = _type.raw_name() + if raw_name is 'boolean': + return 'boolForKey' + if raw_name is 'integer': + return 'integerForKey' + if raw_name is 'number': + return 'doubleForKey' + if raw_name is 'string': + return 'stringForKey' + if raw_name in ['any', 'object']: + return 'objectForKey' + if raw_name is 'array': + return 'inspectorArrayForKey' + return None + if (isinstance(_type, EnumType)): + return 'stringForKey' + if (isinstance(_type, ObjectType)): + return 'objectForKey' + if (isinstance(_type, ArrayType)): + return 'inspectorArrayForKey' + return None diff --git a/Source/JavaScriptCore/inspector/scripts/codegen/objc_generator_templates.py b/Source/JavaScriptCore/inspector/scripts/codegen/objc_generator_templates.py new file mode 100755 index 000000000..1c30c40d4 --- /dev/null +++ b/Source/JavaScriptCore/inspector/scripts/codegen/objc_generator_templates.py @@ -0,0 +1,155 @@ +#!/usr/bin/env python +# +# Copyright (c) 2014 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. + +# Generator templates, which can be filled with string.Template. +# Following are classes that fill the templates from the typechecked model. + +class ObjCGeneratorTemplates: + + HeaderPrelude = ( + """#import <Foundation/Foundation.h> + +${includes} +""") + + HeaderPostlude = ( + """""") + + TypeConversionsHeaderPrelude = ( + """${includes} + +namespace Inspector {""") + + TypeConversionsHeaderPostlude = ( + """} // namespace Inspector +""") + + GenericHeaderPrelude = ( + """${includes}""") + + GenericHeaderPostlude = ( + """""") + + TypeConversionsHeaderStandard = ( + """template<typename ObjCEnumType> +std::optional<ObjCEnumType> fromProtocolString(const String& value);""") + + BackendDispatcherHeaderPrelude = ( + """${includes} + +${forwardDeclarations} + +namespace Inspector { +""") + + BackendDispatcherHeaderPostlude = ( + """} // namespace Inspector +""") + + BackendDispatcherImplementationPrelude = ( + """#import "config.h" +#import ${primaryInclude} + +${secondaryIncludes} + +namespace Inspector {""") + + BackendDispatcherImplementationPostlude = ( + """} // namespace Inspector +""") + + ImplementationPrelude = ( + """#import ${primaryInclude} + +${secondaryIncludes} + +using namespace Inspector;""") + + ImplementationPostlude = ( + """""") + + BackendDispatcherHeaderDomainHandlerInterfaceDeclaration = ( + """class Alternate${domainName}BackendDispatcher : public AlternateBackendDispatcher { +public: + virtual ~Alternate${domainName}BackendDispatcher() { } +${commandDeclarations} +};""") + + BackendDispatcherHeaderDomainHandlerObjCDeclaration = ( + """class ObjCInspector${domainName}BackendDispatcher final : public Alternate${domainName}BackendDispatcher { +public: + ObjCInspector${domainName}BackendDispatcher(id<${objcPrefix}${domainName}DomainHandler> handler) { m_delegate = handler; } +${commandDeclarations} +private: + RetainPtr<id<${objcPrefix}${domainName}DomainHandler>> m_delegate; +};""") + + BackendDispatcherHeaderDomainHandlerImplementation = ( + """void ObjCInspector${domainName}BackendDispatcher::${commandName}(${parameters}) +{ + id errorCallback = ^(NSString *error) { + backendDispatcher()->reportProtocolError(requestId, BackendDispatcher::ServerError, error); + backendDispatcher()->sendPendingErrors(); + }; + +${successCallback} +${conversions} +${invocation} +} +""") + + ConfigurationCommandProperty = ( + """@property (nonatomic, retain, setter=set${domainName}Handler:) id<${objcPrefix}${domainName}DomainHandler> ${variableNamePrefix}Handler;""") + + ConfigurationEventProperty = ( + """@property (nonatomic, readonly) ${objcPrefix}${domainName}DomainEventDispatcher *${variableNamePrefix}EventDispatcher;""") + + ConfigurationCommandPropertyImplementation = ( + """- (void)set${domainName}Handler:(id<${objcPrefix}${domainName}DomainHandler>)handler +{ + if (handler == _${variableNamePrefix}Handler) + return; + + [_${variableNamePrefix}Handler release]; + _${variableNamePrefix}Handler = [handler retain]; + + auto alternateDispatcher = std::make_unique<ObjCInspector${domainName}BackendDispatcher>(handler); + auto alternateAgent = std::make_unique<AlternateDispatchableAgent<${domainName}BackendDispatcher, Alternate${domainName}BackendDispatcher>>(ASCIILiteral("${domainName}"), *_controller, WTFMove(alternateDispatcher)); + _controller->appendExtraAgent(WTFMove(alternateAgent)); +} + +- (id<${objcPrefix}${domainName}DomainHandler>)${variableNamePrefix}Handler +{ + return _${variableNamePrefix}Handler; +}""") + + ConfigurationGetterImplementation = ( + """- (${objcPrefix}${domainName}DomainEventDispatcher *)${variableNamePrefix}EventDispatcher +{ + if (!_${variableNamePrefix}EventDispatcher) + _${variableNamePrefix}EventDispatcher = [[${objcPrefix}${domainName}DomainEventDispatcher alloc] initWithController:_controller]; + return _${variableNamePrefix}EventDispatcher; +}""") |