| #!/usr/bin/env python |
| # |
| # Copyright (c) 2014-2018 Apple Inc. All rights reserved. |
| # Copyright (c) 2014 University of Washington. All rights reserved. |
| # |
| # Redistribution and use in source and binary forms, with or without |
| # modification, are permitted provided that the following conditions |
| # are met: |
| # 1. Redistributions of source code must retain the above copyright |
| # notice, this list of conditions and the following disclaimer. |
| # 2. Redistributions in binary form must reproduce the above copyright |
| # notice, this list of conditions and the following disclaimer in the |
| # documentation and/or other materials provided with the distribution. |
| # |
| # THIS SOFTWARE IS PROVIDED BY APPLE INC. AND ITS CONTRIBUTORS ``AS IS'' |
| # AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, |
| # THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR |
| # PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR ITS CONTRIBUTORS |
| # BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR |
| # CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF |
| # SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS |
| # INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN |
| # CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) |
| # ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF |
| # THE POSSIBILITY OF SUCH DAMAGE. |
| |
| import logging |
| import os.path |
| import re |
| |
| try: |
| from .generator import ucfirst, Generator |
| from .models import PrimitiveType, ObjectType, ArrayType, EnumType, AliasedType, Frameworks |
| except ValueError: |
| 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': 'JSON::Object', |
| 'array': 'JSON::Array', |
| 'any': 'JSON::Value' |
| } |
| |
| 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, AliasedType): |
| _type = _type.aliased_type # Fall through to enum or primitive. |
| |
| if isinstance(_type, ObjectType) and len(_type.members) == 0: |
| return 'JSON::Object' |
| if isinstance(_type, ArrayType): |
| if _type.raw_name() is None: # Otherwise, fall through and use typedef'd name. |
| return 'JSON::ArrayOf<%s>' % CppGenerator.cpp_protocol_type_for_type(_type.element_type) |
| if isinstance(_type, (ObjectType, 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 = 'JSON::Object' |
| if parameter.is_optional: |
| return 'const %s*' % cpp_name |
| else: |
| return 'const %s&' % cpp_name |
| if isinstance(_type, ArrayType): |
| cpp_name = 'JSON::Array' |
| 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*' % 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*' % 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<JSON::Object>' |
| elif _type.qualified_name() in ['any']: |
| return 'RefPtr<JSON::Value>' |
| elif is_optional: |
| return 'const %s*' % 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 "Optional<%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, (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 "Optional<%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 |
| if isinstance(_type, EnumType): |
| if _type.is_anonymous: |
| cpp_name = '%sBackendDispatcherHandler::%s' % (_type.type_domain().domain_name, ucfirst(parameter.parameter_name)) |
| else: |
| cpp_name = 'Inspector::Protocol::%s::%s' % (_type.type_domain().domain_name, _type.raw_name()) |
| |
| if parameter.is_optional: |
| return "Optional<%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<JSON::Object>" |
| if isinstance(_type, ArrayType): |
| return "RefPtr<JSON::Array>" |
| 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 "Optional<%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 "Optional<%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 "Optional<%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") |