DXR is a code search and navigation tool aimed at making sense of large projects. It supports full-text and regex searches as well as structural queries.

Mercurial (b6d82b1a6b02)

VCS Links

Line Code
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200
# This Source Code Form is subject to the terms of the Mozilla Public
# License, v. 2.0. If a copy of the MPL was not distributed with this file,
# You can obtain one at http://mozilla.org/MPL/2.0/.

import string
import sys
import textwrap
import yaml

###############################################################################
# Language-agnostic functionality                                             #
###############################################################################

template_header = "/* This file was autogenerated by " \
  "toolkit/crashreporter/generate_crash_reporter_sources.py. DO NOT EDIT */\n\n"


def validate_annotations(annotations):
    """ Ensure that the annotations have all the required fields """

    for (name, data) in sorted(annotations.items()):
        if "description" not in data:
            print("Annotation " + name + " does not have a description\n")
            sys.exit(1)
        if "type" not in data:
            print("Annotation " + name + " does not have a type\n")
            sys.exit(1)
        else:
            annotation_type = data.get("type")
            valid_types = ["boolean", "integer", "string"]
            if not any(annotation_type == t for t in valid_types):
                print("Annotation " + name + " has an unknown type: " + annotation_type + "\n")
                sys.exit(1)


def read_annotations(annotations_filename):
    """Read the annotations from a YAML file.
    If an error is encountered quit the program."""

    try:
        with open(annotations_filename, "r") as annotations_file:
            annotations = yaml.safe_load(annotations_file)
    except (IOError, ValueError) as e:
        print("Error parsing " + annotations_filename + ":\n" + str(e) + "\n")
        sys.exit(1)

    validate_annotations(annotations)

    return annotations


def read_template(template_filename):
    """Read the contents of the template.
    If an error is encountered quit the program."""

    try:
        with open(template_filename, "r") as template_file:
            template = template_file.read()
    except IOError as ex:
        print("Error when reading " + template_filename + ":\n" + str(ex) + "\n")
        sys.exit(1)

    return template


def extract_crash_ping_whitelist(annotations):
    """Extract an array holding the names of the annotations whitelisted for
    inclusion in the crash ping."""

    return [name
            for (name, data)
            in sorted(annotations.items())
            if data.get("ping", False)]


def extract_content_process_blacklist(annotations):
    """Extract an array holding the names of the annotations blacklisted when
    read from a content process."""

    return [name
            for (name, data)
            in sorted(annotations.items())
            if not data.get("content", True)]

###############################################################################
# C++ code generation                                                         #
###############################################################################


def generate_strings(annotations):
    """Generate strings corresponding to every annotation."""

    names = ["  \"" + data.get("altname", name) + "\""
             for (name, data)
             in sorted(annotations.items())]

    return ",\n".join(names)


def generate_enum(annotations):
    """Generate the C++ typed enum holding all the annotations and return it
    as a string."""

    enum = ""

    for i, (name, _) in enumerate(sorted(annotations.items())):
        enum += "  " + name + " = " + str(i) + ",\n"

    enum += "  Count = " + str(len(annotations))

    return enum


def generate_array_initializer(contents):
    """Generates the initializer for a C++ array of annotations."""

    initializer = ["  Annotation::" + name for name in contents]

    return ",\n".join(initializer)


def generate_header(template, annotations):
    """Generate a header by filling the template with the the list of
    annotations and return it as a string."""

    whitelist = extract_crash_ping_whitelist(annotations)
    blacklist = extract_content_process_blacklist(annotations)

    return template_header + string.Template(template).substitute({
               "enum": generate_enum(annotations),
               "strings": generate_strings(annotations),
               "whitelist": generate_array_initializer(whitelist),
               "blacklist": generate_array_initializer(blacklist),
           })


def emit_header(output, template_filename, annotations_filename):
    """Generate the C++ header from the template and write it out."""

    annotations = read_annotations(annotations_filename)
    template = read_template(template_filename)
    generated_header = generate_header(template, annotations)

    try:
        output.write(generated_header)
    except IOError as ex:
        print("Error while writing out the generated file:\n" + str(ex) + "\n")
        sys.exit(1)

###############################################################################
# Java code generation                                                        #
###############################################################################


def generate_java_array_initializer(contents):
    """Generates the initializer for an array of strings.
    Effectively turns `["a", "b"]` into '   \"a\",\n   \"b\"\n'."""

    initializer = ""

    for name in contents:
        initializer += "        \"" + name + "\",\n"

    return initializer.strip(",\n")


def generate_class(template, annotations):
    """Fill the class template from the list of annotations."""

    whitelist = extract_crash_ping_whitelist(annotations)

    return template_header + string.Template(template).substitute({
               "whitelist": generate_java_array_initializer(whitelist),
           })


def emit_class(output, annotations_filename):
    """Generate the CrashReporterConstants.java file."""

    template = textwrap.dedent("""\
    package org.mozilla.gecko;

    /**
     * Constants used by the crash reporter. These are generated so that they
     * are kept in sync with the other C++ and JS users.
     */
    public class CrashReporterConstants {
        public static final String[] ANNOTATION_WHITELIST = {
    ${whitelist}
        };
    }""")

    annotations = read_annotations(annotations_filename)
    generated_class = generate_class(template, annotations)

    try:
        output.write(generated_class)
    except IOError as ex:
        print("Error while writing out the generated file:\n" + str(ex) + "\n")
        sys.exit(1)