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