make_opcode_doc.py (5288B)
1 #!/usr/bin/env python3 -B 2 # This Source Code Form is subject to the terms of the Mozilla Public 3 # License, v. 2.0. If a copy of the MPL was not distributed with this file, 4 # You can obtain one at http://mozilla.org/MPL/2.0/. 5 6 7 """Usage: python make_opcode_doc.py 8 9 This script generates SpiderMonkey bytecode documentation 10 from js/src/vm/Opcodes.h. 11 12 Output is written to stdout and should be pasted into the following 13 MDN page: 14 https://developer.mozilla.org/en-US/docs/SpiderMonkey/Internals/Bytecode 15 """ 16 17 import os 18 import sys 19 20 # Allow this script to be run from anywhere. 21 this_dir = os.path.dirname(os.path.realpath(__file__)) 22 sys.path.insert(0, this_dir) 23 24 25 from xml.sax.saxutils import escape 26 27 import jsopcode 28 29 try: 30 import markdown 31 except ModuleNotFoundError as exc: 32 if exc.name == "markdown": 33 # Right, most people won't have python-markdown installed. Suggest the 34 # most likely path to getting this running. 35 print("Failed to import markdown: " + exc.msg, file=sys.stderr) 36 if os.path.exists(os.path.join(this_dir, "venv")): 37 print( 38 "It looks like you previously created a virtualenv here. Try this:\n" 39 " . venv/bin/activate", 40 file=sys.stderr, 41 ) 42 sys.exit(1) 43 print( 44 "Try this:\n" 45 " pip3 install markdown\n" 46 "Or, if you want to avoid installing things globally:\n" 47 " python3 -m venv venv && . venv/bin/activate && pip3 install markdown", 48 file=sys.stderr, 49 ) 50 sys.exit(1) 51 raise exc 52 except ImportError as exc: 53 # Oh no! Markdown failed to load. Check for a specific known issue. 54 if exc.msg.startswith("bad magic number in 'opcode'") and os.path.isfile( 55 os.path.join(this_dir, "opcode.pyc") 56 ): 57 print( 58 "Failed to import markdown due to bug 1506380.\n" 59 "This is dumb--it's an old Python cache file in your directory. Try this:\n" 60 " rm " + this_dir + "/opcode.pyc\n" 61 "The file is obsolete since November 2018.", 62 file=sys.stderr, 63 ) 64 sys.exit(1) 65 raise exc 66 67 68 SOURCE_BASE = "https://searchfox.org/mozilla-central/source" 69 70 FORMAT_TO_IGNORE = { 71 "JOF_BYTE", 72 "JOF_UINT8", 73 "JOF_UINT16", 74 "JOF_UINT24", 75 "JOF_UINT32", 76 "JOF_INT8", 77 "JOF_INT32", 78 "JOF_TABLESWITCH", 79 "JOF_REGEXP", 80 "JOF_DOUBLE", 81 "JOF_LOOPHEAD", 82 "JOF_BIGINT", 83 } 84 85 86 def format_format(format): 87 format = [flag for flag in format if flag not in FORMAT_TO_IGNORE] 88 if len(format) == 0: 89 return "" 90 return "<div>Format: {format}</div>\n".format(format=", ".join(format)) 91 92 93 def maybe_escape(value, format_str, fallback=""): 94 if value: 95 return format_str.format(escape(value)) 96 return fallback 97 98 99 OPCODE_FORMAT = """\ 100 <dt id="{id}">{names}</dt> 101 <dd> 102 {operands}{stack}{desc} 103 {format}</dd> 104 """ 105 106 107 def print_opcode(opcode): 108 opcodes = [opcode] + opcode.group 109 names = ", ".join(maybe_escape(code.op, "<code>{}</code>") for code in opcodes) 110 operands = maybe_escape(opcode.operands, "<div>Operands: <code>({})</code></div>\n") 111 stack_uses = maybe_escape(opcode.stack_uses, "<code>{}</code> ") 112 stack_defs = maybe_escape(opcode.stack_defs, " <code>{}</code>") 113 if stack_uses or stack_defs: 114 stack = f"<div>Stack: {stack_uses}⇒{stack_defs}</div>\n" 115 else: 116 stack = "" 117 118 print( 119 OPCODE_FORMAT.format( 120 id=opcodes[0].op, 121 names=names, 122 operands=operands, 123 stack=stack, 124 desc=markdown.markdown(opcode.desc), 125 format=format_format(opcode.format_), 126 ) 127 ) 128 129 130 id_cache = dict() 131 id_count = dict() 132 133 134 def make_element_id(category, type=""): 135 key = f"{category}:{type}" 136 if key in id_cache: 137 return id_cache[key] 138 139 if type == "": 140 id = category.replace(" ", "_") 141 else: 142 id = type.replace(" ", "_") 143 144 if id in id_count: 145 id_count[id] += 1 146 id = f"{id}_{id_count[id]}" 147 else: 148 id_count[id] = 1 149 150 id_cache[key] = id 151 return id 152 153 154 def print_doc(index): 155 print( 156 f"""<div>{{{{SpiderMonkeySidebar("Internals")}}}}</div> 157 158 <h2 id="Bytecode_Listing">Bytecode Listing</h2> 159 160 <p>This document is automatically generated from 161 <a href="{SOURCE_BASE}/js/src/vm/Opcodes.h">Opcodes.h</a> by 162 <a href="{SOURCE_BASE}/js/src/vm/make_opcode_doc.py">make_opcode_doc.py</a>.</p> 163 """ 164 ) 165 166 for category_name, types in index: 167 print(f'<h3 id="{make_element_id(category_name)}">{category_name}</h3>') 168 for type_name, opcodes in types: 169 if type_name: 170 print( 171 f'<h4 id="{make_element_id(category_name, type_name)}">{type_name}</h4>' 172 ) 173 print("<dl>") 174 for opcode in opcodes: 175 print_opcode(opcode) 176 print("</dl>") 177 178 179 if __name__ == "__main__": 180 if len(sys.argv) != 1: 181 print("Usage: mach python make_opcode_doc.py", file=sys.stderr) 182 sys.exit(1) 183 js_src_vm_dir = os.path.dirname(os.path.realpath(__file__)) 184 root_dir = os.path.abspath(os.path.join(js_src_vm_dir, "..", "..", "..")) 185 186 index, _ = jsopcode.get_opcodes(root_dir) 187 print_doc(index)