| #!/usr/bin/env python |
| # |
| # Copyright (c) 2014-2018 Apple Inc. All rights reserved. |
| # Copyright (c) 2014 University of Washington. All rights reserved. |
| # |
| # Redistribution and use in source and binary forms, with or without |
| # modification, are permitted provided that the following conditions |
| # are met: |
| # 1. Redistributions of source code must retain the above copyright |
| # notice, this list of conditions and the following disclaimer. |
| # 2. Redistributions in binary form must reproduce the above copyright |
| # notice, this list of conditions and the following disclaimer in the |
| # documentation and/or other materials provided with the distribution. |
| # |
| # THIS SOFTWARE IS PROVIDED BY APPLE INC. AND ITS CONTRIBUTORS ``AS IS'' |
| # AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, |
| # THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR |
| # PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR ITS CONTRIBUTORS |
| # BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR |
| # CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF |
| # SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS |
| # INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN |
| # CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) |
| # ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF |
| # THE POSSIBILITY OF SUCH DAMAGE. |
| |
| |
| import logging |
| import string |
| from string import Template |
| from operator import methodcaller |
| |
| try: |
| 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 |
| except ValueError: |
| 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': self._generate_secondary_header_includes(), |
| } |
| |
| 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 = list(map(self._generate_builders_for_domain, domains)) |
| sections.extend([section for section in builder_sections if len(section) > 0]) |
| sections.append('} // namespace Protocol') |
| sections.append(Template(CppTemplates.ImplementationPostlude).substitute(None, **header_args)) |
| |
| return "\n\n".join(sections) |
| |
| # Private methods. |
| |
| def _generate_secondary_header_includes(self): |
| header_includes = [ |
| (["JavaScriptCore", "WebKit"], ("WTF", "wtf/Optional.h")), |
| (["JavaScriptCore", "WebKit"], ("WTF", "wtf/text/CString.h")), |
| ] |
| |
| return '\n'.join(self.generate_includes_from_entries(header_includes)) |
| |
| 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<>', |
| '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 WTF::nullopt;', |
| '}', |
| '', |
| ]) |
| return body_lines |
| |
| type_declarations = self.type_declarations_for_domain(domain) |
| declaration_types = [decl.type for decl in type_declarations] |
| object_types = [_type for _type in declaration_types if isinstance(_type, ObjectType)] |
| enum_types = [_type for _type in declaration_types if isinstance(_type, EnumType)] |
| 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 = list(map(self._generate_enum_conversion_methods_for_domain, domains)) |
| sections.extend([section for section in enum_parser_sections if len(section) > 0]) |
| 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 [decl for decl in type_declarations if Generator.type_has_open_fields(decl.type)]: |
| open_members = Generator.open_fields(type_declaration) |
| for type_member in sorted(open_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 = [decl for decl in type_declarations if self.type_needs_shape_assertions(decl.type)] |
| |
| 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 = [member for member in object_declaration.type_members if not member.is_optional] |
| optional_members = [member for member in object_declaration.type_members if member.is_optional] |
| should_count_properties = not Generator.type_has_open_fields(object_declaration.type) |
| lines = [] |
| |
| lines.append('void BindingTraits<%s>::assertValueHasExpectedType(JSON::Value* value)' % (CppGenerator.cpp_protocol_type_for_type(object_declaration.type))) |
| lines.append("""{ |
| #if ASSERT_DISABLED |
| UNUSED_PARAM(value); |
| #else |
| ASSERT_ARG(value, value); |
| RefPtr<JSON::Object> 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(""" { |
| auto %(memberName)sPos = object->find("%(memberName)s"_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(""" { |
| auto %(memberName)sPos = object->find("%(memberName)s"_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('#endif // !ASSERT_DISABLED') |
| lines.append('}') |
| return '\n'.join(lines) |
| |
| def _generate_assertion_for_enum(self, enum_member, object_declaration): |
| lines = [] |
| lines.append('void %s(JSON::Value* value)' % CppGenerator.cpp_assertion_method_for_type_member(enum_member, object_declaration)) |
| lines.append('{') |
| lines.append('#if ASSERT_DISABLED') |
| lines.append(' UNUSED_PARAM(value);') |
| lines.append('#else') |
| 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('#endif // !ASSERT_DISABLED') |
| lines.append('}') |
| |
| return '\n'.join(lines) |