summaryrefslogtreecommitdiffstats
path: root/js/src/GeneratePrefs.py
blob: b28114c25994c06f248a7ec85b40ade0ceaa104a (plain)
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
# 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/.

# This script generates js/public/PrefsGenerated.h from StaticPrefList.yaml

import buildconfig
import six
import yaml
from mozbuild.preprocessor import Preprocessor

HEADER_TEMPLATE = """\
/* 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/. */

#ifndef js_PrefsGenerated_h
#define js_PrefsGenerated_h

/* This file is generated by js/src/GeneratePrefs.py. Do not edit! */

#include "mozilla/Atomics.h"

#include <stdint.h>

%(contents)s

#endif // js_PrefsGenerated_h
"""


def load_yaml(yaml_path):
    # First invoke the preprocessor to handle #ifdefs in the YAML file.
    pp = Preprocessor()
    pp.context.update(buildconfig.defines["ALLDEFINES"])

    # To make #ifdef DEBUG work, use a similar hack as in emit_code in
    # generate_static_pref_list.py.
    if buildconfig.substs.get("MOZ_DEBUG"):
        pp.context["DEBUG"] = "1"

    pp.out = six.StringIO()
    pp.do_filter("substitution")
    pp.do_include(yaml_path)
    contents = pp.out.getvalue()
    return yaml.safe_load(contents)


# Returns the C++ type to use for the pref type from the YAML file. Always use
# the non-atomic type for return values and arguments. The field type is
# determined elsewhere.
def get_cpp_type(type):
    if type in ("bool", "RelaxedAtomicBool"):
        return "bool"
    if type in ("uint32_t", "RelaxedAtomicUint32"):
        return "uint32_t"
    if type in ("int32_t", "RelaxedAtomicInt32"):
        return "int32_t"
    raise Exception("Unexpected type: {}".format(type))


# Returns a C++ expression for the default pref value. Booleans in the YAML file
# are converted to Pythonic True or False, so those need special handling.
def get_cpp_init_value(val):
    if val is True:
        return "true"
    if val is False:
        return "false"
    return str(val)


def generate_prefs_header(c_out, yaml_path):
    prefs = load_yaml(yaml_path)

    js_options_prefix = "javascript.options."

    def is_js_pref(pref):
        set_spidermonkey_pref = pref.get("set_spidermonkey_pref", False)
        if set_spidermonkey_pref not in (False, "startup", "always"):
            raise Exception("Invalid value for set_spidermonkey_pref")

        # Ignore prefs that don't have the |set_spidermonkey_pref| attribute.
        if set_spidermonkey_pref is False:
            return False

        # Only support prefs with javascript.options prefix.
        if not pref["name"].startswith(js_options_prefix):
            raise Exception("set_spidermonkey_pref only works for JS prefs")

        return True

    # Remove all non-JS prefs and sort prefs by name.
    prefs = list(filter(is_js_pref, prefs))
    prefs.sort(key=lambda pref: pref["name"])

    class_fields = []
    class_fields_inits = []

    macro_entries = []
    browser_set_statements = []
    browser_set_non_startup_statements = []

    for pref in prefs:
        name = pref["name"]
        name = name[len(js_options_prefix) :]

        is_startup_pref = pref["set_spidermonkey_pref"] == "startup"

        cpp_name = name.replace(".", "_").replace("-", "_")
        type = get_cpp_type(pref["type"])
        init_value = get_cpp_init_value(pref["value"])

        setter_name = ("setAtStartup_" if is_startup_pref else "set_") + cpp_name

        # Use a relaxed atomic for non-startup prefs because those might be changed
        # after startup.
        field_type = type
        if not is_startup_pref:
            field_type = "mozilla::Atomic<{}, mozilla::Relaxed>".format(field_type)
        class_fields.append("static {} {}_;".format(field_type, cpp_name))
        class_fields_inits.append(
            "{} JS::Prefs::{}_{{{}}};".format(field_type, cpp_name, init_value)
        )

        is_startup_pref_bool = "true" if is_startup_pref else "false"

        # Generate a MACRO invocation like this:
        #   MACRO("arraybuffer_transfer", arraybuffer_transfer, bool, setAtStartup_arraybuffer_transfer, true)
        macro_entries.append(
            'MACRO("{}", {}, {}, {}, {})'.format(
                name, cpp_name, type, setter_name, is_startup_pref_bool
            )
        )

        # Generate a C++ statement to set the JS pref based on Gecko's StaticPrefs:
        #   JS::Prefs::setAtStartup_foo(StaticPrefs::javascript_options_foo());
        browser_pref_cpp_name = pref["name"].replace(".", "_").replace("-", "_")
        if pref.get("do_not_use_directly", False):
            browser_pref_cpp_name += "_DoNotUseDirectly"

        statement = "JS::Prefs::{}(mozilla::StaticPrefs::{}());".format(
            setter_name, browser_pref_cpp_name
        )
        browser_set_statements.append(statement)

        # For non-startup prefs, also generate code to update the pref after startup.
        if not is_startup_pref:
            browser_set_non_startup_statements.append(statement)

    contents = ""

    contents += "#define JS_PREF_CLASS_FIELDS \\\n"
    contents += "".join(map(lambda s: "  {}\\\n".format(s), class_fields))
    contents += "\n\n"

    contents += "#define JS_PREF_CLASS_FIELDS_INIT \\\n"
    contents += "".join(map(lambda s: "  {}\\\n".format(s), class_fields_inits))
    contents += "\n\n"

    contents += "#define FOR_EACH_JS_PREF(MACRO) \\\n"
    contents += "".join(map(lambda s: "  {}\\\n".format(s), macro_entries))
    contents += "\n\n"

    contents += "#define SET_JS_PREFS_FROM_BROWSER_PREFS \\\n"
    contents += "".join(map(lambda s: "  {}\\\n".format(s), browser_set_statements))
    contents += "\n\n"

    contents += "#define SET_NON_STARTUP_JS_PREFS_FROM_BROWSER_PREFS \\\n"
    contents += "".join(
        map(lambda s: "  {}\\\n".format(s), browser_set_non_startup_statements)
    )
    contents += "\n\n"

    c_out.write(
        HEADER_TEMPLATE
        % {
            "contents": contents,
        }
    )