summaryrefslogtreecommitdiffstats
path: root/js/src/vm/make_opcode_doc.py
blob: 11ce81a3ed59f827bd0aad0446d86c6be61f63ec (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
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
#!/usr/bin/env python3 -B
# 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/.


""" Usage: python make_opcode_doc.py

    This script generates SpiderMonkey bytecode documentation
    from js/src/vm/Opcodes.h.

    Output is written to stdout and should be pasted into the following
    MDN page:
    https://developer.mozilla.org/en-US/docs/SpiderMonkey/Internals/Bytecode
"""

import os
import sys

# Allow this script to be run from anywhere.
this_dir = os.path.dirname(os.path.realpath(__file__))
sys.path.insert(0, this_dir)


from xml.sax.saxutils import escape

import jsopcode

try:
    import markdown
except ModuleNotFoundError as exc:
    if exc.name == "markdown":
        # Right, most people won't have python-markdown installed. Suggest the
        # most likely path to getting this running.
        print("Failed to import markdown: " + exc.msg, file=sys.stderr)
        if os.path.exists(os.path.join(this_dir, "venv")):
            print(
                "It looks like you previously created a virtualenv here. Try this:\n"
                "    . venv/bin/activate",
                file=sys.stderr,
            )
            sys.exit(1)
        print(
            "Try this:\n"
            "    pip3 install markdown\n"
            "Or, if you want to avoid installing things globally:\n"
            "    python3 -m venv venv && . venv/bin/activate && pip3 install markdown",
            file=sys.stderr,
        )
        sys.exit(1)
    raise exc
except ImportError as exc:
    # Oh no! Markdown failed to load. Check for a specific known issue.
    if exc.msg.startswith("bad magic number in 'opcode'") and os.path.isfile(
        os.path.join(this_dir, "opcode.pyc")
    ):
        print(
            "Failed to import markdown due to bug 1506380.\n"
            "This is dumb--it's an old Python cache file in your directory. Try this:\n"
            "    rm " + this_dir + "/opcode.pyc\n"
            "The file is obsolete since November 2018.",
            file=sys.stderr,
        )
        sys.exit(1)
    raise exc


SOURCE_BASE = "https://searchfox.org/mozilla-central/source"

FORMAT_TO_IGNORE = {
    "JOF_BYTE",
    "JOF_UINT8",
    "JOF_UINT16",
    "JOF_UINT24",
    "JOF_UINT32",
    "JOF_INT8",
    "JOF_INT32",
    "JOF_TABLESWITCH",
    "JOF_REGEXP",
    "JOF_DOUBLE",
    "JOF_LOOPHEAD",
    "JOF_BIGINT",
}


def format_format(format):
    format = [flag for flag in format if flag not in FORMAT_TO_IGNORE]
    if len(format) == 0:
        return ""
    return "<div>Format: {format}</div>\n".format(format=", ".join(format))


def maybe_escape(value, format_str, fallback=""):
    if value:
        return format_str.format(escape(value))
    return fallback


OPCODE_FORMAT = """\
<dt id="{id}">{names}</dt>
<dd>
{operands}{stack}{desc}
{format}</dd>
"""


def print_opcode(opcode):
    opcodes = [opcode] + opcode.group
    names = ", ".join(maybe_escape(code.op, "<code>{}</code>") for code in opcodes)
    operands = maybe_escape(opcode.operands, "<div>Operands: <code>({})</code></div>\n")
    stack_uses = maybe_escape(opcode.stack_uses, "<code>{}</code> ")
    stack_defs = maybe_escape(opcode.stack_defs, " <code>{}</code>")
    if stack_uses or stack_defs:
        stack = "<div>Stack: {}&rArr;{}</div>\n".format(stack_uses, stack_defs)
    else:
        stack = ""

    print(
        OPCODE_FORMAT.format(
            id=opcodes[0].op,
            names=names,
            operands=operands,
            stack=stack,
            desc=markdown.markdown(opcode.desc),
            format=format_format(opcode.format_),
        )
    )


id_cache = dict()
id_count = dict()


def make_element_id(category, type=""):
    key = "{}:{}".format(category, type)
    if key in id_cache:
        return id_cache[key]

    if type == "":
        id = category.replace(" ", "_")
    else:
        id = type.replace(" ", "_")

    if id in id_count:
        id_count[id] += 1
        id = "{}_{}".format(id, id_count[id])
    else:
        id_count[id] = 1

    id_cache[key] = id
    return id


def print_doc(index):
    print(
        """<div>{{{{SpiderMonkeySidebar("Internals")}}}}</div>

<h2 id="Bytecode_Listing">Bytecode Listing</h2>

<p>This document is automatically generated from
<a href="{source_base}/js/src/vm/Opcodes.h">Opcodes.h</a> by
<a href="{source_base}/js/src/vm/make_opcode_doc.py">make_opcode_doc.py</a>.</p>
""".format(
            source_base=SOURCE_BASE
        )
    )

    for (category_name, types) in index:
        print(
            '<h3 id="{id}">{name}</h3>'.format(
                name=category_name, id=make_element_id(category_name)
            )
        )
        for (type_name, opcodes) in types:
            if type_name:
                print(
                    '<h4 id="{id}">{name}</h4>'.format(
                        name=type_name, id=make_element_id(category_name, type_name)
                    )
                )
            print("<dl>")
            for opcode in opcodes:
                print_opcode(opcode)
            print("</dl>")


if __name__ == "__main__":
    if len(sys.argv) != 1:
        print("Usage: mach python make_opcode_doc.py", file=sys.stderr)
        sys.exit(1)
    js_src_vm_dir = os.path.dirname(os.path.realpath(__file__))
    root_dir = os.path.abspath(os.path.join(js_src_vm_dir, "..", "..", ".."))

    index, _ = jsopcode.get_opcodes(root_dir)
    print_doc(index)