mirror of
https://github.com/LadybirdBrowser/ladybird.git
synced 2025-12-05 01:10:24 +00:00
This commit adds a new Bytecode.def file that describes all the LibJS bytecode instructions. From this, we are able to generate the full declarations for all C++ bytecode instruction classes, as well as their serialization code. Note that some of the bytecode compiler was updated since instructions no longer have default constructor arguments. The big immediate benefit here is that we lose a couple thousand lines of hand-written C++ code. Going forward, this also allows us to do more tooling for the bytecode VM, now that we have an authoritative description of its instructions. Key things to know about: - Instructions can inherit from one another. At the moment, everything simply inherits from the base "Instruction". - @terminator means the instruction terminates a basic block. - @nothrow means the instruction cannot throw. This affects how the interpreter interacts with it. - Variable-length instructions are automatically supported. Just put an array of something as the last field of the instruction. - The m_length field is magical. If present, it will be populated with the full length of the instruction. This is used for variable-length instructions.
98 lines
2.5 KiB
Python
98 lines
2.5 KiB
Python
#!/usr/bin/env python3
|
|
from __future__ import annotations
|
|
|
|
from dataclasses import dataclass
|
|
from dataclasses import field
|
|
from typing import List
|
|
from typing import Optional
|
|
|
|
|
|
@dataclass
|
|
class Field:
|
|
name: str
|
|
type: str
|
|
is_array: bool = False
|
|
|
|
|
|
@dataclass
|
|
class OpDef:
|
|
name: str
|
|
base: str
|
|
fields: List[Field] = field(default_factory=list)
|
|
is_terminator: bool = False # @terminator
|
|
is_nothrow: bool = False # @nothrow
|
|
|
|
|
|
def parse_bytecode_def(path: str) -> List[OpDef]:
|
|
with open(path, "r", encoding="utf-8") as f:
|
|
lines = f.readlines()
|
|
|
|
ops: List[OpDef] = []
|
|
current: Optional[OpDef] = None
|
|
in_op = False
|
|
|
|
for raw_line in lines:
|
|
stripped = raw_line.strip()
|
|
if not stripped:
|
|
continue
|
|
if stripped.startswith("//") or stripped.startswith("#"):
|
|
continue
|
|
|
|
if stripped.startswith("op "):
|
|
if in_op:
|
|
raise RuntimeError("Nested op blocks are not allowed")
|
|
in_op = True
|
|
|
|
rest = stripped[len("op ") :].strip()
|
|
if "<" in rest:
|
|
name_part, base_part = rest.split("<", 1)
|
|
name = name_part.strip()
|
|
base = base_part.strip()
|
|
else:
|
|
name = rest.strip()
|
|
base = "Instruction"
|
|
|
|
current = OpDef(name=name, base=base)
|
|
continue
|
|
|
|
if stripped == "endop":
|
|
if not in_op or current is None:
|
|
raise RuntimeError("endop without corresponding op")
|
|
ops.append(current)
|
|
current = None
|
|
in_op = False
|
|
continue
|
|
|
|
if not in_op:
|
|
continue
|
|
|
|
if stripped.startswith("@"):
|
|
if stripped == "@terminator":
|
|
current.is_terminator = True
|
|
elif stripped == "@nothrow":
|
|
current.is_nothrow = True
|
|
continue
|
|
|
|
if ":" not in stripped:
|
|
raise RuntimeError(f"Malformed field line: {stripped!r}")
|
|
lhs, rhs = stripped.split(":", 1)
|
|
field_name = lhs.strip()
|
|
field_type = rhs.strip()
|
|
is_array = False
|
|
if field_type.endswith("[]"):
|
|
is_array = True
|
|
field_type = field_type[:-2].strip()
|
|
current.fields.append(Field(name=field_name, type=field_type, is_array=is_array))
|
|
|
|
if in_op or current is not None:
|
|
raise RuntimeError("Unclosed op block at end of file")
|
|
|
|
return ops
|
|
|
|
|
|
__all__ = [
|
|
"Field",
|
|
"OpDef",
|
|
"parse_bytecode_def",
|
|
]
|