Sat, 03 Jan 2015 20:18:00 +0100
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) |