js/src/vm/make_opcode_doc.py

Sat, 03 Jan 2015 20:18:00 +0100

author
Michael Schloh von Bennewitz <michael@schloh.com>
date
Sat, 03 Jan 2015 20:18:00 +0100
branch
TOR_BUG_3246
changeset 7
129ffea94266
permissions
-rw-r--r--

Conditionally enable double key logic according to:
private browsing mode or privacy.thirdparty.isolate preference and
implement in GetCookieStringCommon and FindCookie where it counts...
With some reservations of how to convince FindCookie users to test
condition and pass a nullptr when disabling double key logic.

michael@0 1 #!/usr/bin/python -B
michael@0 2
michael@0 3 """ Usage: make_opcode_doc.py PATH_TO_MOZILLA_CENTRAL
michael@0 4
michael@0 5 This script generates SpiderMonkey bytecode documentation
michael@0 6 from js/src/vm/Opcodes.h and js/src/vm/Xdr.h.
michael@0 7
michael@0 8 Output is written to stdout and should be pasted into the following
michael@0 9 MDN page:
michael@0 10 https://developer.mozilla.org/en-US/docs/SpiderMonkey/Internals/Bytecode
michael@0 11 """
michael@0 12
michael@0 13 from __future__ import print_function
michael@0 14 import re
michael@0 15 import sys
michael@0 16 from xml.sax.saxutils import escape
michael@0 17
michael@0 18 SOURCE_BASE = 'http://mxr.mozilla.org/mozilla-central/source'
michael@0 19
michael@0 20 def error(message):
michael@0 21 print("Error: {message}".format(message=message), file=sys.stderr)
michael@0 22 #sys.exit(1) # uncomment when all opcodes documented
michael@0 23
michael@0 24 def get_xdr_version(dir):
michael@0 25 version_pat = re.compile('XDR_BYTECODE_VERSION = uint32_t\(0xb973c0de - (\d+)\);')
michael@0 26
michael@0 27 version = ''
michael@0 28 with open('{dir}/js/src/vm/Xdr.h'.format(dir=dir), 'r') as f:
michael@0 29 data = f.read()
michael@0 30
michael@0 31 m = version_pat.search(data)
michael@0 32 if m:
michael@0 33 version = int(m.group(1))
michael@0 34
michael@0 35 return version
michael@0 36
michael@0 37 quoted_pat = re.compile(r"([^A-Za-z0-9]|^)'([^']+)'")
michael@0 38 js_pat = re.compile(r"([^A-Za-z0-9]|^)(JS[A-Z0-9_\*]+)")
michael@0 39 def codify(text):
michael@0 40 text = re.sub(quoted_pat, '\\1<code>\\2</code>', text)
michael@0 41 text = re.sub(js_pat, '\\1<code>\\2</code>', text)
michael@0 42
michael@0 43 return text
michael@0 44
michael@0 45 space_star_space_pat = re.compile('^\s*\* ?', re.M)
michael@0 46 def get_comment_body(comment):
michael@0 47 return re.sub(space_star_space_pat, '', comment).split('\n')
michael@0 48
michael@0 49 def parse_index(comment):
michael@0 50 index = []
michael@0 51 current_types = None
michael@0 52 category_name = ''
michael@0 53 category_pat = re.compile('\[([^\]]+)\]')
michael@0 54 for line in get_comment_body(comment):
michael@0 55 m = category_pat.search(line)
michael@0 56 if m:
michael@0 57 category_name = m.group(1)
michael@0 58 if category_name == 'Index':
michael@0 59 continue
michael@0 60 current_types = []
michael@0 61 index.append((category_name, current_types))
michael@0 62 else:
michael@0 63 type_name = line.strip()
michael@0 64 if type_name and current_types is not None:
michael@0 65 current_types.append((type_name, []))
michael@0 66
michael@0 67 return index
michael@0 68
michael@0 69 class OpcodeInfo:
michael@0 70 def __init__(self):
michael@0 71 self.name = ''
michael@0 72 self.value = ''
michael@0 73 self.length = ''
michael@0 74 self.length_override = ''
michael@0 75 self.nuses = ''
michael@0 76 self.nuses_override = ''
michael@0 77 self.ndefs = ''
michael@0 78 self.ndefs_override = ''
michael@0 79 self.flags = ''
michael@0 80 self.operands = ''
michael@0 81 self.stack_uses = ''
michael@0 82 self.stack_defs = ''
michael@0 83
michael@0 84 self.desc = ''
michael@0 85
michael@0 86 self.category_name = ''
michael@0 87 self.type_name = ''
michael@0 88
michael@0 89 self.group = []
michael@0 90 self.sort_key = ''
michael@0 91
michael@0 92 def find_by_name(list, name):
michael@0 93 for (n, body) in list:
michael@0 94 if n == name:
michael@0 95 return body
michael@0 96
michael@0 97 return None
michael@0 98
michael@0 99 def add_to_index(index, opcode):
michael@0 100 types = find_by_name(index, opcode.category_name)
michael@0 101 if types is None:
michael@0 102 error("Category is not listed in index: "
michael@0 103 "{name}".format(name=opcode.category_name))
michael@0 104 opcodes = find_by_name(types, opcode.type_name)
michael@0 105 if opcodes is None:
michael@0 106 if opcode.type_name:
michael@0 107 error("Type is not listed in {category}: "
michael@0 108 "{name}".format(category=opcode.category_name,
michael@0 109 name=opcode.type_name))
michael@0 110 types.append((opcode.type_name, [opcode]))
michael@0 111 return
michael@0 112
michael@0 113 opcodes.append(opcode)
michael@0 114
michael@0 115 def format_desc(descs):
michael@0 116 current_type = ''
michael@0 117 desc = ''
michael@0 118 for (type, line) in descs:
michael@0 119 if type != current_type:
michael@0 120 if current_type:
michael@0 121 desc += '</{name}>\n'.format(name=current_type)
michael@0 122 current_type = type
michael@0 123 if type:
michael@0 124 desc += '<{name}>'.format(name=current_type)
michael@0 125 if current_type:
michael@0 126 desc += line + "\n"
michael@0 127 if current_type:
michael@0 128 desc += '</{name}>'.format(name=current_type)
michael@0 129
michael@0 130 return desc
michael@0 131
michael@0 132 tag_pat = re.compile('^\s*[A-Za-z]+:\s*|\s*$')
michael@0 133 def get_tag_value(line):
michael@0 134 return re.sub(tag_pat, '', line)
michael@0 135
michael@0 136 def get_opcodes(dir):
michael@0 137 iter_pat = re.compile(r"/\*(.*?)\*/" # either a documentation comment...
michael@0 138 r"|"
michael@0 139 r"macro\(" # or a macro(...) call
michael@0 140 r"([^,]+),\s*" # op
michael@0 141 r"([0-9]+),\s*" # val
michael@0 142 r"[^,]+,\s*" # name
michael@0 143 r"[^,]+,\s*" # image
michael@0 144 r"([0-9\-]+),\s*" # length
michael@0 145 r"([0-9\-]+),\s*" # nuses
michael@0 146 r"([0-9\-]+),\s*" # ndefs
michael@0 147 r"([^\)]+)" # format
michael@0 148 r"\)", re.S)
michael@0 149 stack_pat = re.compile('^(.*?)\s*=>\s*(.*?)$')
michael@0 150
michael@0 151 index = []
michael@0 152
michael@0 153 opcode = OpcodeInfo()
michael@0 154 merged = opcode
michael@0 155
michael@0 156 with open('{dir}/js/src/vm/Opcodes.h'.format(dir=dir), 'r') as f:
michael@0 157 data = f.read()
michael@0 158
michael@0 159 for m in re.finditer(iter_pat, data):
michael@0 160 comment = m.group(1)
michael@0 161 name = m.group(2)
michael@0 162
michael@0 163 if comment:
michael@0 164 if '[Index]' in comment:
michael@0 165 index = parse_index(comment)
michael@0 166 continue
michael@0 167
michael@0 168 if 'Operands:' not in comment:
michael@0 169 continue
michael@0 170
michael@0 171 state = 'desc'
michael@0 172 stack = ''
michael@0 173 descs = []
michael@0 174
michael@0 175 for line in get_comment_body(comment):
michael@0 176 if line.startswith(' Category:'):
michael@0 177 state = 'category'
michael@0 178 opcode.category_name = get_tag_value(line)
michael@0 179 elif line.startswith(' Type:'):
michael@0 180 state = 'type'
michael@0 181 opcode.type_name = get_tag_value(line)
michael@0 182 elif line.startswith(' Operands:'):
michael@0 183 state = 'operands'
michael@0 184 opcode.operands = get_tag_value(line)
michael@0 185 elif line.startswith(' Stack:'):
michael@0 186 state = 'stack'
michael@0 187 stack = get_tag_value(line)
michael@0 188 elif line.startswith(' len:'):
michael@0 189 state = 'len'
michael@0 190 opcode.length_override = get_tag_value(line)
michael@0 191 elif line.startswith(' nuses:'):
michael@0 192 state = 'nuses'
michael@0 193 opcode.nuses_override = get_tag_value(line)
michael@0 194 elif line.startswith(' ndefs:'):
michael@0 195 state = 'ndefs'
michael@0 196 opcode.ndefs_override = get_tag_value(line)
michael@0 197 elif state == 'desc':
michael@0 198 if line.startswith(' '):
michael@0 199 descs.append(('pre', escape(line[1:])))
michael@0 200 else:
michael@0 201 line = line.strip()
michael@0 202 if line == '':
michael@0 203 descs.append(('', line))
michael@0 204 else:
michael@0 205 descs.append(('p', codify(escape(line))))
michael@0 206 elif line.startswith(' '):
michael@0 207 if state == 'operands':
michael@0 208 opcode.operands += line.strip()
michael@0 209 elif state == 'stack':
michael@0 210 stack += line.strip()
michael@0 211 elif state == 'len':
michael@0 212 opcode.length_override += line.strip()
michael@0 213 elif state == 'nuses':
michael@0 214 opcode.nuses_override += line.strip()
michael@0 215 elif state == 'ndefs':
michael@0 216 opcode.ndefs_override += line.strip()
michael@0 217
michael@0 218 opcode.desc = format_desc(descs)
michael@0 219
michael@0 220 m2 = stack_pat.search(stack)
michael@0 221 if m2:
michael@0 222 opcode.stack_uses = m2.group(1)
michael@0 223 opcode.stack_defs = m2.group(2)
michael@0 224
michael@0 225 merged = opcode
michael@0 226 elif name and not name.startswith('JSOP_UNUSED'):
michael@0 227 opcode.name = name
michael@0 228 opcode.value = int(m.group(3))
michael@0 229 opcode.length = m.group(4)
michael@0 230 opcode.nuses = m.group(5)
michael@0 231 opcode.ndefs = m.group(6)
michael@0 232
michael@0 233 flags = []
michael@0 234 for flag in m.group(7).split('|'):
michael@0 235 if flag != 'JOF_BYTE':
michael@0 236 flags.append(flag.replace('JOF_', ''))
michael@0 237 opcode.flags = ', '.join(flags)
michael@0 238
michael@0 239 if merged == opcode:
michael@0 240 opcode.sort_key = opcode.name
michael@0 241 if opcode.category_name == '':
michael@0 242 error("Category is not specified for "
michael@0 243 "{name}".format(name=opcode.name))
michael@0 244 add_to_index(index, opcode)
michael@0 245 else:
michael@0 246 if merged.length != opcode.length:
michael@0 247 error("length should be same for merged section: "
michael@0 248 "{value1}({name1}) != "
michael@0 249 "{value2}({name2})".format(name1=merged.name,
michael@0 250 value1=merged.length,
michael@0 251 name2=opcode.name,
michael@0 252 value2=opcode.length))
michael@0 253 if merged.nuses != opcode.nuses:
michael@0 254 error("nuses should be same for merged section: "
michael@0 255 "{value1}({name1}) != "
michael@0 256 "{value2}({name2})".format(name1=merged.name,
michael@0 257 value1=merged.nuses,
michael@0 258 name2=opcode.name,
michael@0 259 value2=opcode.nuses))
michael@0 260 if merged.ndefs != opcode.ndefs:
michael@0 261 error("ndefs should be same for merged section: "
michael@0 262 "{value1}({name1}) != "
michael@0 263 "{value2}({name2})".format(name1=merged.name,
michael@0 264 value1=merged.ndefs,
michael@0 265 name2=opcode.name,
michael@0 266 value2=opcode.ndefs))
michael@0 267 merged.group.append(opcode)
michael@0 268 if opcode.name < merged.name:
michael@0 269 merged.sort_key = opcode.name
michael@0 270
michael@0 271 opcode = OpcodeInfo()
michael@0 272
michael@0 273 return index
michael@0 274
michael@0 275 def override(value, override_value):
michael@0 276 if override_value != '':
michael@0 277 return override_value
michael@0 278
michael@0 279 return value
michael@0 280
michael@0 281 def format_flags(flags):
michael@0 282 if flags == '':
michael@0 283 return ''
michael@0 284
michael@0 285 return ' ({flags})'.format(flags=flags)
michael@0 286
michael@0 287 def print_opcode(opcode):
michael@0 288 names_template = '{name} [-{nuses}, +{ndefs}]{flags}'
michael@0 289 names = map(lambda code: names_template.format(name=escape(code.name),
michael@0 290 nuses=override(code.nuses,
michael@0 291 opcode.nuses_override),
michael@0 292 ndefs=override(code.ndefs,
michael@0 293 opcode.ndefs_override),
michael@0 294 flags=format_flags(code.flags)),
michael@0 295 sorted([opcode] + opcode.group,
michael@0 296 key=lambda opcode: opcode.name))
michael@0 297 if len(opcode.group) == 0:
michael@0 298 values = ['{value} (0x{value:02x})'.format(value=opcode.value)]
michael@0 299 else:
michael@0 300 values_template = '{name}: {value} (0x{value:02x})'
michael@0 301 values = map(lambda code: values_template.format(name=escape(code.name),
michael@0 302 value=code.value),
michael@0 303 sorted([opcode] + opcode.group,
michael@0 304 key=lambda opcode: opcode.name))
michael@0 305
michael@0 306 print("""<dt>{names}</dt>
michael@0 307 <dd>
michael@0 308 <table class="standard-table">
michael@0 309 <tr><th>Value</th><td><code>{values}</code></td></tr>
michael@0 310 <tr><th>Operands</th><td><code>{operands}</code></td></tr>
michael@0 311 <tr><th>Length</th><td><code>{length}</code></td></tr>
michael@0 312 <tr><th>Stack Uses</th><td><code>{stack_uses}</code></td></tr>
michael@0 313 <tr><th>Stack Defs</th><td><code>{stack_defs}</code></td></tr>
michael@0 314 </table>
michael@0 315
michael@0 316 {desc}
michael@0 317 </dd>
michael@0 318 """.format(names='<br>'.join(names),
michael@0 319 values='<br>'.join(values),
michael@0 320 operands=escape(opcode.operands),
michael@0 321 length=escape(override(opcode.length,
michael@0 322 opcode.length_override)),
michael@0 323 stack_uses=escape(opcode.stack_uses),
michael@0 324 stack_defs=escape(opcode.stack_defs),
michael@0 325 desc=opcode.desc)) # desc is already escaped
michael@0 326
michael@0 327 def make_element_id(name):
michael@0 328 return name.replace(' ', '-')
michael@0 329
michael@0 330 def print_doc(version, index):
michael@0 331 print("""<h2 id="Bytecode_Listing">Bytecode Listing</h2>
michael@0 332
michael@0 333 <p>This document is automatically generated from
michael@0 334 <a href="{source_base}/js/src/vm/Opcodes.h">Opcodes.h</a> and
michael@0 335 <a href="{source_base}/js/src/vm/Xdr.h">Xdr.h</a> by
michael@0 336 <a href="{source_base}/js/src/vm/make_opcode_doc.py">make_opcode_doc.py</a>.</p>
michael@0 337
michael@0 338 <p>Bytecode version: <code>{version}</code>
michael@0 339 (<code>0x{actual_version:08x}</code>).</p>
michael@0 340 """.format(source_base=SOURCE_BASE,
michael@0 341 version=version,
michael@0 342 actual_version=0xb973c0de - version))
michael@0 343
michael@0 344 for (category_name, types) in index:
michael@0 345 print('<h3 id="{id}">{name}</h3>'.format(name=category_name,
michael@0 346 id=make_element_id(category_name)))
michael@0 347 for (type_name, opcodes) in types:
michael@0 348 if type_name:
michael@0 349 print('<h4 id="{id}">{name}</h4>'.format(name=type_name,
michael@0 350 id=make_element_id(type_name)))
michael@0 351 print('<dl>')
michael@0 352 for opcode in sorted(opcodes,
michael@0 353 key=lambda opcode: opcode.sort_key):
michael@0 354 print_opcode(opcode)
michael@0 355 print('</dl>')
michael@0 356
michael@0 357 if __name__ == '__main__':
michael@0 358 if len(sys.argv) < 2:
michael@0 359 print("Usage: make_opcode_doc.py PATH_TO_MOZILLA_CENTRAL",
michael@0 360 file=sys.stderr)
michael@0 361 sys.exit(1)
michael@0 362 dir = sys.argv[1]
michael@0 363 version = get_xdr_version(dir)
michael@0 364 index = get_opcodes(dir)
michael@0 365 print_doc(version, index)

mercurial