Coverage for /pythoncovmergedfiles/medio/medio/src/turbohtml/tools/fuzz/dom_lifecycle.py: 28%
Shortcuts on this page
r m x toggle line displays
j k next/prev highlighted chunk
0 (zero) top of page
1 (one) first highlighted chunk
Shortcuts on this page
r m x toggle line displays
j k next/prev highlighted chunk
0 (zero) top of page
1 (one) first highlighted chunk
1"""Lifecycle byte programs retain wrappers across callbacks and reentry, then check native tree invariants."""
3from __future__ import annotations
5import gc
6import threading
7from collections.abc import Callable
8from typing import TYPE_CHECKING, Final, NamedTuple, cast
10from typing_extensions import override
12from turbohtml import (
13 Element,
14 IncrementalParser,
15 Node,
16 NodeFilter,
17 NodeIterator,
18 Range,
19 Text,
20 Tokenizer,
21 TreeWalker,
22 parse,
23)
24from turbohtml._html import _tree_verify # ruff: ignore[import-private-name] - the fuzz-only native invariant hook
25from turbohtml.migration.markupsafe import escape
26from turbohtml.mutations import MutationObserver
27from turbohtml.rewrite import rewrite
28from turbohtml.saxparse import SaxHandler, sax_parse
30if TYPE_CHECKING:
31 import random
32 from collections.abc import Iterable, Iterator, Sequence
34 from turbohtml.mutations import MutationRecord
35 from turbohtml.rewrite import Element as RewriteHandle
37__all__: Final = [
38 "LifecycleStep",
39 "UnsupportedDomLifecycleError",
40 "dom_lifecycle_check",
41 "dom_lifecycle_controls",
42 "dom_lifecycle_generate",
43 "dom_lifecycle_race",
44 "dom_lifecycle_seeds",
45 "run_dom_lifecycle",
46]
48# libxml2's api.c bounds content and copies (MAX_CONTENT 100, MAX_COPY_NODES 50) so a short input cannot build an
49# unbounded tree; these keep the dom-program input bound and cap execution, nesting, registers and created nodes
50_MAX_BYTES: Final = 1024
51_MAX_STEPS: Final = 256
52_MAX_DEPTH: Final = 2
53_SLOTS: Final = 8
54_MAX_NODES: Final = 48
55_TAGS: Final = ("p", "div", "span", "template", "b")
56_WORDS: Final = ("", "alpha", "beta gamma")
57_MARKUP: Final = (
58 "<p>a<b>c</b></p>",
59 "<div id=x><span class=y>t</span><!--c--></div>",
60 "<table><td>x<td>y</table>",
61 "<template><p>t</p></template><i>z</i>",
62)
63_EXPECTED: Final = (ValueError, TypeError, IndexError, RuntimeError)
66class UnsupportedDomLifecycleError(ValueError):
67 """Reject unsupported encodings before allocating register state."""
70class LifecycleStep(NamedTuple):
71 """One executed instruction: its nesting depth, operation, outcome and native invariant violations."""
73 depth: int
74 operation: str
75 outcome: str
76 violations: tuple[int, ...]
79def dom_lifecycle_check(
80 text: str, run: Callable[[bytes], tuple[tuple[LifecycleStep, ...], tuple[str, ...]]] | None = None
81) -> str | None:
82 """Require clean native invariants after every step and an identical replay."""
83 if len(text) > _MAX_BYTES * 2:
84 msg: Final = "DOM lifecycle program exceeds the input limit"
85 raise UnsupportedDomLifecycleError(msg)
86 try:
87 program: Final = bytes.fromhex(text)
88 except ValueError as error:
89 msg: Final = "DOM lifecycle program must contain hexadecimal bytes"
90 raise UnsupportedDomLifecycleError(msg) from error
91 runner: Final = run or run_dom_lifecycle
92 first: Final = runner(program)
93 if broken := next((step for step in first[0] if any(step.violations)), None):
94 return f"tree invariant broken after {broken.operation}"
95 return None if runner(program) == first else "DOM lifecycle program replays differently"
98def dom_lifecycle_generate(rng: random.Random) -> str:
99 """Seed every register ring before random instructions."""
100 return (_PREFIX + bytes(rng.randrange(256) for _ in range(rng.randrange(1, 65) * 4))).hex()
103def dom_lifecycle_seeds() -> list[str]:
104 """Run every opcode on each slot with a mutating nested program, then fire the callbacks it registered."""
105 return [
106 (
107 _PREFIX + bytes((opcode, slot, slot + 9, len(body) // 4 | slot % 3 << 2 | slot % 2 << 4)) + body + _TRAILER
108 ).hex()
109 for opcode in range(len(_OPERATIONS))
110 for slot in range(_SLOTS)
111 for body in (_BODIES[slot // 3 % len(_BODIES)],)
112 ]
115def dom_lifecycle_controls() -> dict[str, bool]:
116 """Require a reported violation and a drifting replay to fail the registered checker."""
117 seed: Final = dom_lifecycle_seeds()[0]
118 broken: Final = ((LifecycleStep(0, "element", "ok", (1, 0, 0, 0)),), ())
119 replays: Final = iter((((), ("<p></p>",)), ((), ("<div></div>",))))
120 return {
121 "missing violation": dom_lifecycle_check(seed, lambda _program: broken) is not None,
122 "replay drift": dom_lifecycle_check(seed, lambda _program: next(replays)) is not None,
123 }
126def run_dom_lifecycle(program: bytes) -> tuple[tuple[LifecycleStep, ...], tuple[str, ...]]:
127 """Execute the program and return each step plus the final markup of every node register."""
128 machine: Final = _Machine(_instructions(program))
129 machine.block(range(len(machine.code)))
130 return tuple(machine.trace), tuple("" if node is None else node.serialize() for node in machine.nodes)
133def dom_lifecycle_race(seeds: Sequence[str], *, shared: bool = False) -> list[tuple[LifecycleStep, ...]]:
134 """
135 Run every seed in its own thread at once, so the programs contend on the module and interpreter state.
137 With ``shared`` set, every program reads and writes one set of node registers, so the threads also edit, move and
138 walk the same trees.
139 """
140 nodes: Final[list[Node | None] | None] = [None] * _SLOTS if shared else None
141 machines: Final = [_Machine(_instructions(bytes.fromhex(seed)), nodes) for seed in seeds]
142 start: Final = threading.Barrier(len(machines))
144 def run(machine: _Machine) -> None:
145 start.wait()
146 machine.block(range(len(machine.code)))
148 threads: Final = [threading.Thread(target=run, args=(machine,)) for machine in machines]
149 for thread in threads:
150 thread.start()
151 for thread in threads:
152 thread.join()
153 return [tuple(machine.trace) for machine in machines]
156def _instructions(program: bytes) -> list[tuple[int, int, int, int]]:
157 bounded: Final = program[:_MAX_BYTES]
158 return [
159 (bounded[index], bounded[index + 1], bounded[index + 2], bounded[index + 3])
160 for index in range(0, len(bounded) - len(bounded) % 4, 4)
161 ]
164class _UnwindError(Exception):
165 """Unwind a nested program through the native caller that invoked its callback."""
168class _Machine:
169 """Typed register rings over one program; operands pick slots, so overwriting a slot drops its reference."""
171 def __init__(self, code: list[tuple[int, int, int, int]], nodes: list[Node | None] | None = None) -> None:
172 self.code: Final = code
173 self.nodes: Final[list[Node | None]] = [None] * _SLOTS if nodes is None else nodes
174 self.walkers: Final[list[TreeWalker | NodeIterator | Iterator[object] | None]] = [None] * _SLOTS
175 self.parsers: Final[list[Tokenizer | IncrementalParser | None]] = [None] * _SLOTS
176 self.observers: Final[list[MutationObserver | None]] = [None] * _SLOTS
177 self.trace: Final[list[LifecycleStep]] = []
178 self.depth = 0
179 self.created = 0
181 def block(self, span: range) -> None:
182 index = span.start
183 while index < span.stop and len(self.trace) < _MAX_STEPS:
184 opcode, first, second, third = self.code[index]
185 name, operation, nested = _OPERATIONS[opcode % len(_OPERATIONS)]
186 body = range(index + 1, min(index + 1 + third % 4, span.stop)) if nested else range(index + 1, index + 1)
187 if operation is None and self.depth:
188 raise _UnwindError
189 try:
190 outcome = "abort" if operation is None else operation(self, first % _SLOTS, second, third, body)
191 except (*_EXPECTED, _UnwindError) as error:
192 outcome = type(error).__name__
193 self.trace.append(LifecycleStep(self.depth, name, outcome, self.verify()))
194 index = body.stop
196 def verify(self) -> tuple[int, ...]:
197 roots: Final = [
198 *(node for node in self.nodes if node is not None),
199 *(walker.root for walker in self.walkers if isinstance(walker, (TreeWalker, NodeIterator))),
200 ]
201 return tuple(map(sum, zip((0, 0, 0, 0), *(_tree_verify(node) for node in roots), strict=True)))
203 def nested(self, body: range) -> None:
204 """Run a callback's program one level deeper, so reentry stays bounded by depth instead of the stack."""
205 if self.depth < _MAX_DEPTH:
206 self.depth += 1
207 try:
208 self.block(body)
209 finally:
210 self.depth -= 1
213def _element(machine: _Machine, slot: int, second: int, _third: int, _body: range) -> str:
214 if machine.created >= _MAX_NODES:
215 return "cap"
216 machine.created += 1
217 machine.nodes[slot] = Element(_TAGS[second % len(_TAGS)])
218 return "ok"
221def _text(machine: _Machine, slot: int, second: int, _third: int, _body: range) -> str:
222 if machine.created >= _MAX_NODES:
223 return "cap"
224 machine.created += 1
225 machine.nodes[slot] = Text(_WORDS[second % len(_WORDS)])
226 return "ok"
229def _parse(machine: _Machine, slot: int, second: int, _third: int, _body: range) -> str:
230 if machine.created >= _MAX_NODES:
231 return "cap"
232 machine.created += 8
233 machine.nodes[slot] = parse(_MARKUP[second % len(_MARKUP)])
234 return "ok"
237def _navigate(machine: _Machine, slot: int, second: int, third: int, _body: range) -> str:
238 if (source := machine.nodes[second % _SLOTS]) is None:
239 return "empty"
240 children: Final = source.children
241 relatives: Final = (
242 source.parent,
243 children[0] if children else None,
244 children[-1] if children else None,
245 source.next_sibling,
246 source.previous_sibling,
247 )
248 machine.nodes[slot] = relatives[third % len(relatives)]
249 return "ok"
252def _append(machine: _Machine, slot: int, second: int, _third: int, _body: range) -> str:
253 target: Final = machine.nodes[slot]
254 if not isinstance(target, Element) or (child := machine.nodes[second % _SLOTS]) is None:
255 return "empty"
256 target.append(child)
257 return "ok"
260def _insert(machine: _Machine, slot: int, second: int, third: int, body: range) -> str:
261 target: Final = machine.nodes[slot]
262 if not isinstance(target, Element) or (child := machine.nodes[second % _SLOTS]) is None:
263 return "empty"
264 target.insert(cast("int", _Index(machine, body, third, second % 3)), child)
265 return "ok"
268def _extract(machine: _Machine, slot: int, _second: int, _third: int, _body: range) -> str:
269 if (node := machine.nodes[slot]) is None:
270 return "empty"
271 node.extract()
272 return "ok"
275def _decompose(machine: _Machine, slot: int, _second: int, _third: int, _body: range) -> str:
276 if (node := machine.nodes[slot]) is None:
277 return "empty"
278 node.decompose()
279 return "ok"
282def _unwrap(machine: _Machine, slot: int, _second: int, _third: int, _body: range) -> str:
283 if (node := machine.nodes[slot]) is None:
284 return "empty"
285 node.unwrap()
286 return "ok"
289def _replace(machine: _Machine, slot: int, second: int, _third: int, _body: range) -> str:
290 if (node := machine.nodes[slot]) is None or (other := machine.nodes[second % _SLOTS]) is None:
291 return "empty"
292 node.replace_with(other)
293 return "ok"
296def _normalize(machine: _Machine, slot: int, _second: int, _third: int, _body: range) -> str:
297 if not isinstance(node := machine.nodes[slot], Element):
298 return "empty"
299 node.normalize()
300 return "ok"
303def _range_extract(machine: _Machine, slot: int, second: int, _third: int, _body: range) -> str:
304 if (node := machine.nodes[slot]) is None:
305 return "empty"
306 selection: Final = Range(node)
307 selection.select_node_contents(node)
308 machine.nodes[second % _SLOTS] = selection.extract_contents()
309 return "ok"
312def _collect(machine: _Machine, slot: int, second: int, _third: int, _body: range) -> str:
313 if second % 4 == 0:
314 machine.nodes[slot] = None
315 elif second % 4 == 1:
316 machine.walkers[slot] = None
317 elif second % 4 == 2:
318 machine.parsers[slot] = None
319 else:
320 machine.observers[slot] = None
321 gc.collect()
322 return "ok"
325def _tree_walker(machine: _Machine, slot: int, second: int, third: int, body: range) -> str:
326 if (root := machine.nodes[second % _SLOTS]) is None:
327 return "empty"
328 machine.walkers[slot] = TreeWalker(root, filter=_filter(machine, body, third))
329 return "ok"
332def _node_iterator(machine: _Machine, slot: int, second: int, third: int, body: range) -> str:
333 if (root := machine.nodes[second % _SLOTS]) is None:
334 return "empty"
335 machine.walkers[slot] = NodeIterator(root, filter=_filter(machine, body, third))
336 return "ok"
339def _filter(machine: _Machine, body: range, third: int) -> Callable[[Node], int]:
340 verdicts: Final = (NodeFilter.FILTER_ACCEPT, NodeFilter.FILTER_REJECT, NodeFilter.FILTER_SKIP)
342 def verdict(_node: Node) -> int:
343 machine.nested(body)
344 # the walker converts the verdict inside its step, so an __index__ program runs mid-traversal
345 return (
346 cast("int", _Index(machine, body, third, (third >> 2) % 3))
347 if third >> 4 & 1
348 else verdicts[(third >> 2) % 3]
349 )
351 return verdict
354def _iterate(machine: _Machine, slot: int, second: int, third: int, _body: range) -> str:
355 if (node := machine.nodes[second % _SLOTS]) is None:
356 return "empty"
357 iterators: Final = (node.descendants, node.iter_elements(), node.serialize_iter())
358 machine.walkers[slot] = iter(iterators[third % len(iterators)])
359 return "ok"
362def _step(machine: _Machine, slot: int, second: int, third: int, _body: range) -> str:
363 walker: Final = machine.walkers[slot]
364 if isinstance(walker, TreeWalker):
365 moves: Final = (
366 walker.next_node,
367 walker.previous_node,
368 walker.parent_node,
369 walker.first_child,
370 walker.last_child,
371 walker.next_sibling,
372 walker.previous_sibling,
373 )
374 found: object = moves[second % len(moves)]()
375 elif isinstance(walker, NodeIterator):
376 found = walker.next_node() if second % 2 else walker.previous_node()
377 elif walker is None:
378 return "empty"
379 else:
380 found = next(walker, None)
381 if isinstance(found, Node):
382 machine.nodes[third % _SLOTS] = found
383 return "ok" if found is not None else "end"
386def _observe(machine: _Machine, slot: int, second: int, third: int, body: range) -> str:
387 if (target := machine.nodes[second % _SLOTS]) is None:
388 return "empty"
390 def callback(_records: list[MutationRecord], _observer: MutationObserver) -> None:
391 machine.nested(body)
393 observer: Final = MutationObserver(callback)
394 machine.observers[slot] = observer
395 observer.observe(
396 target,
397 child_list=True,
398 attributes=True,
399 character_data=True,
400 subtree=True,
401 attribute_filter=cast("Sequence[str]", _Items(machine, body, third, second % 3, ["id"])),
402 )
403 return "ok"
406def _deliver(machine: _Machine, slot: int, second: int, _third: int, _body: range) -> str:
407 if (observer := machine.observers[slot]) is None:
408 return "empty"
409 actions: Final = (observer.deliver, observer.take_records, observer.disconnect)
410 actions[second % len(actions)]()
411 return "ok"
414def _rewrite(machine: _Machine, slot: int, second: int, _third: int, body: range) -> str:
415 def handler(handle: RewriteHandle) -> None:
416 machine.nested(body)
417 # the handle is callback-scoped, so the program edits it here and never stores it in a register
418 if second % 3 == 0:
419 handle.remove()
420 elif second % 3 == 1:
421 handle.after("<i>z</i>", html=True)
422 else:
423 handle.before("y")
425 rewrite(_MARKUP[slot % len(_MARKUP)], elements=[("*", handler)], text=handler, comments=handler)
426 return "ok"
429def _sax(machine: _Machine, slot: int, _second: int, _third: int, body: range) -> str:
430 sax_parse(_MARKUP[slot % len(_MARKUP)], _Sax(machine, body))
431 return "ok"
434def _tokenizer(machine: _Machine, slot: int, _second: int, _third: int, _body: range) -> str:
435 machine.parsers[slot] = Tokenizer()
436 return "ok"
439def _incremental(machine: _Machine, slot: int, _second: int, _third: int, _body: range) -> str:
440 machine.parsers[slot] = IncrementalParser()
441 return "ok"
444def _parser_call(machine: _Machine, slot: int, second: int, third: int, body: range) -> str:
445 parser: Final = machine.parsers[slot]
446 markup: Final = _MARKUP[third % len(_MARKUP)]
447 if isinstance(parser, Tokenizer):
448 if second % 4 == 0:
449 machine.walkers[third % _SLOTS] = parser.feed(markup)
450 elif second % 4 == 1:
451 machine.walkers[third % _SLOTS] = parser.close()
452 elif second % 4 == 2:
453 parser.reset()
454 else:
455 # a handler whose program feeds, closes or resets this tokenizer reenters it mid-dispatch
456 parser.dispatch(_Dispatch(machine, body))
457 elif isinstance(parser, IncrementalParser):
458 if second % 2:
459 parser.feed(markup)
460 else:
461 machine.nodes[third % _SLOTS] = parser.close()
462 else:
463 return "empty"
464 return "ok"
467def _attribute(machine: _Machine, slot: int, second: int, third: int, body: range) -> str:
468 if not isinstance(node := machine.nodes[slot], Element):
469 return "empty"
470 if second % 3 == 0:
471 node.attrs["id"] = _WORDS[third % len(_WORDS)]
472 elif second % 3 == 1:
473 node.attrs.pop("id", None)
474 else:
475 # dict(mapping) hashes each key and the comparison calls __eq__, both from inside the attrs view
476 return str(node.attrs == _Items(machine, body, third, second // 3 % 3, ["id"]))
477 return "ok"
480def _text_conversion(machine: _Machine, slot: int, second: int, third: int, body: range) -> str:
481 if not isinstance(node := machine.nodes[slot], Element):
482 return "empty"
483 node.text = escape(_Text(machine, body, third, second % 3))
484 return "ok"
487def _extend(machine: _Machine, slot: int, second: int, third: int, body: range) -> str:
488 if not isinstance(node := machine.nodes[slot], Element) or (child := machine.nodes[second % _SLOTS]) is None:
489 return "empty"
490 node.extend(cast("Iterable[Node]", _Items(machine, body, third, second % 3, [child])))
491 return "ok"
494def _compare(machine: _Machine, slot: int, second: int, third: int, body: range) -> str:
495 if (node := machine.nodes[slot]) is None:
496 return "empty"
497 # a Node compares only with a Node, so Python falls back to the reflected __eq__ of the other operand
498 return str(node == _Key(machine, body, third, second % 3))
501def _select(machine: _Machine, slot: int, second: int, _third: int, _body: range) -> str:
502 if not isinstance(node := machine.nodes[slot], Element):
503 return "empty"
504 # both populate per-handle caches stamped with tree versions, which the verifier then cross-checks
505 return str(len(node.select("b, span")) if second % 2 else node.css_path())
508class _Conversion:
509 """An argument whose conversion method runs a nested program, then answers, raises or behaves plainly."""
511 def __init__(self, machine: _Machine, body: range, third: int, mode: int) -> None:
512 self.machine: Final = machine
513 self.body: Final = body
514 self.third: Final = third
515 self.mode: Final = mode
517 def convert(self) -> None:
518 if self.mode:
519 self.machine.nested(self.body)
520 if self.mode == 2:
521 raise _UnwindError
524class _Index(_Conversion):
525 def __index__(self) -> int:
526 self.convert()
527 return self.third
530class _Text(_Conversion):
531 def __str__(self) -> str:
532 self.convert()
533 return _WORDS[self.third % len(_WORDS)]
536class _Key(_Conversion):
537 def __hash__(self) -> int:
538 self.convert()
539 return hash("id")
541 def __eq__(self, other: object) -> bool:
542 self.convert()
543 return other == "id"
546class _Items(_Conversion):
547 """Serve as an iterable of fixed items, and as a mapping keyed by a conversion key."""
549 def __init__(self, machine: _Machine, body: range, third: int, mode: int, items: list[Node] | list[str]) -> None:
550 super().__init__(machine, body, third, mode)
551 self.items: Final = items
553 def __iter__(self) -> Iterator[Node | str]:
554 self.convert()
555 return iter(self.items)
557 def keys(self) -> list[_Key]:
558 return [_Key(self.machine, self.body, self.third, self.mode)]
560 def __getitem__(self, _key: object) -> str:
561 return _WORDS[self.third % len(_WORDS)]
564class _Sax(SaxHandler):
565 """A sax_parse handler whose start and text events run the nested program."""
567 def __init__(self, machine: _Machine, body: range) -> None:
568 self.machine: Final = machine
569 self.body: Final = body
571 @override
572 def start_element(self, tag: str, attrs: tuple[tuple[str, str | None], ...]) -> None:
573 self.machine.nested(self.body)
575 @override
576 def characters(self, data: str) -> None:
577 self.machine.nested(self.body)
580class _Dispatch:
581 """A Tokenizer.dispatch handler whose every html.parser event runs the nested program."""
583 def __init__(self, machine: _Machine, body: range) -> None:
584 self.machine: Final = machine
585 self.body: Final = body
587 def __getattr__(self, _name: str) -> Callable[..., None]:
588 return lambda *_args: self.machine.nested(self.body)
591_Operation = Callable[[_Machine, int, int, int, range], str]
592_OPERATIONS: Final[tuple[tuple[str, _Operation | None, bool], ...]] = (
593 ("element", _element, False),
594 ("text", _text, False),
595 ("parse", _parse, False),
596 ("navigate", _navigate, False),
597 ("append", _append, False),
598 ("insert", _insert, True),
599 ("extract", _extract, False),
600 ("decompose", _decompose, False),
601 ("unwrap", _unwrap, False),
602 ("replace", _replace, False),
603 ("collect", _collect, False),
604 ("tree-walker", _tree_walker, True),
605 ("node-iterator", _node_iterator, True),
606 ("iterate", _iterate, False),
607 ("step", _step, False),
608 ("observe", _observe, True),
609 ("deliver", _deliver, False),
610 ("rewrite", _rewrite, True),
611 ("sax", _sax, True),
612 ("tokenizer", _tokenizer, False),
613 ("incremental", _incremental, False),
614 ("parser-call", _parser_call, True),
615 ("attribute", _attribute, True),
616 ("text-conversion", _text_conversion, True),
617 ("extend", _extend, True),
618 ("compare", _compare, True),
619 ("select", _select, False),
620 ("raise", None, False),
621 ("normalize", _normalize, False),
622 ("range-extract", _range_extract, False),
623)
625# element p, element div, text, parsed document, then html/body/p/b of that document, a tokenizer and a fed parser
626_PREFIX: Final = bytes((
627 *(0, 0, 0, 0),
628 *(0, 1, 1, 0),
629 *(1, 2, 1, 0),
630 *(2, 3, 0, 0),
631 *(4, 0, 1, 0),
632 *(4, 0, 2, 0),
633 *(3, 4, 3, 1),
634 *(3, 5, 4, 2),
635 *(3, 6, 5, 1),
636 *(3, 7, 6, 2),
637 *(19, 0, 0, 0),
638 *(20, 1, 0, 0),
639 *(21, 1, 1, 0),
640))
641# a nested program edits across trees, reenters the tokenizer and incremental parser, or runs a program of its own
642_BODIES: Final = (
643 bytes((*(6, 1, 0, 0), *(4, 6, 1, 0), *(7, 7, 0, 0))),
644 bytes((*(21, 0, 0, 0), *(21, 0, 2, 0), *(21, 1, 0, 2))),
645 bytes((*(18, 0, 0, 2), *(7, 7, 0, 0), *(8, 6, 0, 0))),
646)
647# step every walker, mutate the observed trees, deliver every observer, then dispatch the tokenizer into a program
648# that feeds and resets it
649_TRAILER: Final = bytes((
650 *(byte for slot in range(_SLOTS) for byte in (14, slot, slot, slot)),
651 *(22, 0, 0, 1),
652 *(5, 6, 2, 0),
653 *(byte for slot in range(_SLOTS) for byte in (16, slot, 0, 0)),
654 *(21, 0, 3, 3),
655 *_BODIES[1],
656))