diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 95f0c67..f094852 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -17,7 +17,7 @@ jobs: matrix: rust: [stable, "1.85"] steps: - - uses: actions/checkout@v4 + - uses: actions/checkout@v7 - uses: dtolnay/rust-toolchain@master with: toolchain: ${{ matrix.rust }} @@ -27,7 +27,7 @@ jobs: name: Clippy runs-on: ubuntu-latest steps: - - uses: actions/checkout@v4 + - uses: actions/checkout@v7 - uses: dtolnay/rust-toolchain@stable with: components: clippy @@ -37,7 +37,7 @@ jobs: name: Format runs-on: ubuntu-latest steps: - - uses: actions/checkout@v4 + - uses: actions/checkout@v7 - uses: dtolnay/rust-toolchain@stable with: components: rustfmt @@ -47,7 +47,7 @@ jobs: name: Docs runs-on: ubuntu-latest steps: - - uses: actions/checkout@v4 + - uses: actions/checkout@v7 - uses: dtolnay/rust-toolchain@stable - run: cargo doc --no-deps env: diff --git a/.github/workflows/publish.yml b/.github/workflows/publish.yml index ab6da95..2c15968 100644 --- a/.github/workflows/publish.yml +++ b/.github/workflows/publish.yml @@ -18,7 +18,7 @@ jobs: contents: read id-token: write # required to mint the crates.io OIDC token steps: - - uses: actions/checkout@v4 + - uses: actions/checkout@v7 with: fetch-depth: 0 # merge-base needs real history diff --git a/CHANGELOG.md b/CHANGELOG.md index e2a6bcf..0368041 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -2,9 +2,14 @@ All notable changes to this crate are documented here. +## 0.1.3 + +- Replaced em-dashes and en-dashes in rustdoc comments and the changelog with plain ASCII hyphens, so generated documentation renders consistently. +- Updated CI and publish workflows from `actions/checkout@v4` to `actions/checkout@v7` (Node 24 runtime). The crate still has no external Rust dependencies. + ## 0.1.2 -- Fixed PC-relative branch resolution for backward branches. The IFPS VM adds the branch delta to the current position in wrapping `Cardinal` arithmetic, so the compiler encodes a backward jump as a large unsigned delta. `Instruction::branch_target()` / `branch_targets()` resolved `condgoto` / `condnotgoto` with a checked add and therefore returned `None` for every backward conditional branch — losing the back edge of every loop — while the disassembly display saturated and printed `0xffffffff`. +- Fixed PC-relative branch resolution for backward branches. The IFPS VM adds the branch delta to the current position in wrapping `Cardinal` arithmetic, so the compiler encodes a backward jump as a large unsigned delta. `Instruction::branch_target()` / `branch_targets()` resolved `condgoto` / `condnotgoto` with a checked add and therefore returned `None` for every backward conditional branch - losing the back edge of every loop - while the disassembly display saturated and printed `0xffffffff`. - Unified the four separate copies of the branch-delta arithmetic (two in `bytecode`, two in `disasm`) onto a single `relative_target` helper, so `goto` and `condgoto` can no longer disagree about the same wire delta. - Fixed the coordinate system of absolute branch targets. `flaggoto` and the four `PushExceptionHandler` regions store *procedure-local* positions, but relative branch targets and `Instruction::offset` are IFPS-blob offsets, so a listing mixed two coordinate systems and handler targets did not resolve against the listing they appeared in for any proc but the first. Disassembly output now rebases them onto the proc's `bytecode_offset`, and new `ProcDisasm::branch_target()` / `ProcDisasm::branch_targets()` return every form in blob coordinates. The `Instruction`-level helpers keep their raw behaviour and now document the caveat. diff --git a/Cargo.toml b/Cargo.toml index 2cff04b..1d48ea3 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "pascalscript" -version = "0.1.2" +version = "0.1.3" edition = "2024" rust-version = "1.85" description = "Read-only parser + disassembler for the RemObjects PascalScript III binary container format (IFPS)" diff --git a/src/attribute.rs b/src/attribute.rs index 0706e94..9c4aa1b 100644 --- a/src/attribute.rs +++ b/src/attribute.rs @@ -1,8 +1,8 @@ -//! Attribute-block walker — `ReadAttributes` in +//! Attribute-block walker - `ReadAttributes` in //! `uPSRuntime.pas:2333-2532`. //! //! Build-21+ attaches an inline attribute block at the end of -//! every type and proc entry (vars do not carry attributes — see +//! every type and proc entry (vars do not carry attributes - see //! `LoadVars`, `uPSRuntime.pas:2936-2989`). The block layout is: //! //! ```text @@ -15,7 +15,7 @@ //! payload: per-BaseType (see `AttributeFieldValue`) //! ``` //! -//! Reading an attribute requires the type table parsed so far — +//! Reading an attribute requires the type table parsed so far - //! the per-field payload's size depends on the referenced type's //! `BaseType` (and its body, in the `Set` case). @@ -36,7 +36,7 @@ pub type AttributeFieldValue<'a> = Literal<'a>; /// One parsed attribute. #[derive(Clone, Debug)] pub struct Attribute<'a> { - /// ASCII identifier — the attribute "name" the script + /// ASCII identifier - the attribute "name" the script /// declared, e.g. `Description`. pub name: &'a [u8], /// Field values in declaration order. @@ -46,7 +46,7 @@ pub struct Attribute<'a> { /// One field of an [`Attribute`]. #[derive(Clone, Debug)] pub struct AttributeField<'a> { - /// Index into the type table — describes the field's type. + /// Index into the type table - describes the field's type. pub type_no: u32, /// Decoded wire value. pub value: Literal<'a>, @@ -57,7 +57,7 @@ const MAX_NAME_LEN: u32 = 0x4000_0000; /// Parses the attribute block at `reader`'s cursor into a vec of /// [`Attribute`]s. /// -/// `types` is the type table parsed so far — including the +/// `types` is the type table parsed so far - including the /// current entry, since upstream pushes the type onto `FTypes` /// before reading its attribute block (`uPSRuntime.pas:2789-2798`). /// Empty blocks (count == 0) return `Ok(vec![])`. diff --git a/src/bytecode.rs b/src/bytecode.rs index ab6babc..bfeb8ce 100644 --- a/src/bytecode.rs +++ b/src/bytecode.rs @@ -20,7 +20,7 @@ use crate::{ #[derive(Clone, Debug, PartialEq)] pub struct Instruction<'a> { /// Offset of this instruction's leading byte within the - /// IFPS blob — same coordinate system as + /// IFPS blob - same coordinate system as /// [`crate::InternalProc::bytecode_offset`]. /// Useful when chasing PC-relative branches: a `Goto` /// instruction's `offset` is relative to the byte @@ -41,7 +41,7 @@ impl Instruction<'_> { /// setup opcodes carry multiple targets, so callers that need all of /// them should use [`Self::branch_targets`]. /// - /// Carries the same coordinate caveat as [`Self::branch_targets`] — prefer + /// Carries the same coordinate caveat as [`Self::branch_targets`] - prefer /// [`ProcDisasm::branch_target`], which normalizes every form to IFPS-blob /// coordinates. pub fn branch_target(&self) -> Option { @@ -57,7 +57,7 @@ impl Instruction<'_> { /// /// - Relative branches (`goto`, `condgoto`, `condnotgoto`, the pop-and-goto /// forms) resolve against [`Self::next_offset`], which is an IFPS-blob - /// offset — so these come back **blob-absolute**. A delta is + /// offset - so these come back **blob-absolute**. A delta is /// coordinate-independent, so this is exact. /// - `flaggoto` and the four `PushExceptionHandler` regions store an /// *absolute* position, and IFPS positions are **procedure-local** (the VM @@ -106,7 +106,7 @@ impl Instruction<'_> { /// pop-and-goto forms) encodes the same thing: a four-byte little-endian delta /// added to the position after the instruction. The VM performs that addition /// on Delphi `Cardinal`s (`CurrentPosition := CurrentPosition + NewPosition`), -/// which wraps — so the compiler emits a **backward** branch as a large +/// which wraps - so the compiler emits a **backward** branch as a large /// unsigned delta that wraps back around. Modelling the addition as /// checked or saturating therefore loses exactly the backward branches, i.e. /// every loop. @@ -116,7 +116,7 @@ impl Instruction<'_> { /// both reach this function as a raw `u32` delta. /// /// The result can land outside the owning procedure when the input is -/// malformed — IFPS producers are untrusted. Validating that is the caller's +/// malformed - IFPS producers are untrusted. Validating that is the caller's /// job (the VM itself bounds-checks the new position against the proc length), /// which is why this returns a plain `u32` rather than an `Option`. pub(crate) fn relative_target(base: u32, delta: u32) -> u32 { @@ -130,7 +130,7 @@ pub struct ProcDisasm<'a> { /// disassembled proc. pub proc_index: u32, /// Byte offset where the proc's bytecode starts inside the - /// IFPS blob — copied verbatim from + /// IFPS blob - copied verbatim from /// [`crate::InternalProc::bytecode_offset`] for /// cross-reference. pub bytecode_offset: u32, @@ -152,7 +152,7 @@ impl ProcDisasm<'_> { } /// Returns every control-flow target `instruction` carries, all of them in - /// **IFPS-blob coordinates** — the same coordinate system as + /// **IFPS-blob coordinates** - the same coordinate system as /// [`Instruction::offset`], so a target can be compared directly against /// the instruction stream. /// @@ -181,14 +181,14 @@ impl ProcDisasm<'_> { /// `blob` is the entire IFPS byte buffer; `bytecode_offset` and /// `bytecode_len` are the proc's window into that buffer /// (validated up-front by [`crate::proc::parse_proc`]). -/// `types` is the parsed type table — needed for typed-literal +/// `types` is the parsed type table - needed for typed-literal /// operand payloads. /// /// # Errors /// /// - [`Error::BytecodeOutOfRange`] when the window falls outside /// `blob`. -/// - Any error from the per-opcode decoder — bad opcode byte, +/// - Any error from the per-opcode decoder - bad opcode byte, /// malformed operand, truncated payload. pub(crate) fn disassemble_proc<'a>( blob: &'a [u8], @@ -267,7 +267,7 @@ mod tests { } /// A backward conditional branch is encoded as a wrapping `Cardinal` - /// delta, exactly like the unconditional `goto` — resolving it with + /// delta, exactly like the unconditional `goto` - resolving it with /// checked or saturating arithmetic loses every loop in the program. #[test] fn backward_conditional_branch_wraps_to_a_target_behind_the_instruction() { @@ -306,7 +306,7 @@ mod tests { assert_eq!(unconditional.branch_target(), conditional.branch_target()); } - /// `flaggoto` carries an absolute position, not a delta — and on the bare + /// `flaggoto` carries an absolute position, not a delta - and on the bare /// instruction it comes back procedure-local, as stored. #[test] fn flag_goto_target_is_absolute() { diff --git a/src/container.rs b/src/container.rs index 257d128..ee2dc09 100644 --- a/src/container.rs +++ b/src/container.rs @@ -20,7 +20,7 @@ use crate::{ /// Parsed IFPS blob. /// -/// Borrows from the original byte slice — names, decls, and +/// Borrows from the original byte slice - names, decls, and /// bytecode regions all reference the original buffer rather /// than allocating. #[derive(Clone, Debug)] @@ -38,7 +38,7 @@ impl<'a> Container<'a> { /// # Errors /// /// Returns whatever [`Header::parse`] or any of the per-table - /// walkers (types, procs, vars) surface — see [`Error`] for + /// walkers (types, procs, vars) surface - see [`Error`] for /// the full set. pub fn parse(bytes: &'a [u8]) -> Result { let header = Header::parse(bytes)?; @@ -151,8 +151,8 @@ impl<'a> Container<'a> { DisasmDisplay::new(self, disasm) } - /// Returns a [`ContainerSummary`] — single-line - /// `fmt::Display`-ready triage view (`IFPS build N — A + /// Returns a [`ContainerSummary`] - single-line + /// `fmt::Display`-ready triage view (`IFPS build N - A /// types, B procs (X internal / Y external), C vars, main=…`). pub fn display_summary(&self) -> ContainerSummary<'a, '_> { ContainerSummary::new(self) diff --git a/src/disasm.rs b/src/disasm.rs index 98fe1f4..2f56df2 100644 --- a/src/disasm.rs +++ b/src/disasm.rs @@ -1,4 +1,4 @@ -//! Symbolic disassembly — render a [`ProcDisasm`] as text with +//! Symbolic disassembly - render a [`ProcDisasm`] as text with //! operand indices resolved against the container's symbol //! tables. //! @@ -10,12 +10,12 @@ //! offsets via [`Instruction::next_offset`]; absolute branches //! (`FlagGoto`, the exception-handler regions) are stored //! procedure-locally and are rebased onto the proc's -//! `bytecode_offset`, so every offset in a listing — the leading -//! instruction offset and every printed target alike — is in one +//! `bytecode_offset`, so every offset in a listing - the leading +//! instruction offset and every printed target alike - is in one //! coordinate system. //! //! Control-flow reconstruction (loop / if-else detection from -//! goto patterns) is deferred — the raw target offsets in the +//! goto patterns) is deferred - the raw target offsets in the //! output are sufficient for tracing manually, and the //! basic-block analysis required for structural recovery is its //! own substantial layer. @@ -31,7 +31,7 @@ use crate::{ proc::{Proc, ProcKind}, }; -/// One-line summary of a [`Container`] — counts of types, procs +/// One-line summary of a [`Container`] - counts of types, procs /// (split into internal vs. external), vars, plus the wire build /// number. Suitable for triage / log output. /// @@ -72,7 +72,7 @@ impl fmt::Display for ContainerSummary<'_, '_> { }; write!( f, - "IFPS build {} — {} types, {} procs ({} internal / {} external), {} vars, {main_proc}", + "IFPS build {} - {} types, {} procs ({} internal / {} external), {} vars, {main_proc}", header.build_no, header.type_count, header.proc_count, @@ -87,7 +87,7 @@ impl fmt::Display for ContainerSummary<'_, '_> { /// [`Container`] it came from for symbol resolution. /// /// Construct via [`Container::display`] or directly via -/// [`Self::new`]. Implements [`fmt::Display`] — drop into +/// [`Self::new`]. Implements [`fmt::Display`] - drop into /// `println!`, `format!`, or any other formatting context. #[derive(Clone, Copy)] pub struct DisasmDisplay<'a, 'c> { @@ -146,8 +146,8 @@ fn format_proc_self_label(proc: &Proc<'_>) -> String { /// /// `bytecode_offset` is the owning proc's base in the IFPS blob. It is needed /// because `flaggoto` and the exception-handler regions store *procedure-local* -/// absolute positions, whereas the leading `inst.offset` on every line — and -/// every relative branch target — is blob-absolute. Printing them as stored +/// absolute positions, whereas the leading `inst.offset` on every line - and +/// every relative branch target - is blob-absolute. Printing them as stored /// would make a handler target unresolvable against the very listing it sits in /// for every proc but the first. fn write_instruction( diff --git a/src/error.rs b/src/error.rs index 42e8f55..2b386e4 100644 --- a/src/error.rs +++ b/src/error.rs @@ -16,7 +16,7 @@ use std::fmt; #[non_exhaustive] pub enum Error { /// `bytes` were too short to contain the field named by - /// `what` — typically the 28-byte header, or a fixed payload + /// `what` - typically the 28-byte header, or a fixed payload /// past the header. Truncated { /// Static label for the truncated structure. @@ -60,7 +60,7 @@ pub enum Error { length: u32, }, /// An integer overflow occurred while computing a buffer - /// offset or size — the input contains a value too large to + /// offset or size - the input contains a value too large to /// represent on this platform. Overflow { /// Static label identifying the overflowing computation. diff --git a/src/header.rs b/src/header.rs index 22644bc..12411b3 100644 --- a/src/header.rs +++ b/src/header.rs @@ -1,4 +1,4 @@ -//! `TPSHeader` — the fixed 28-byte preamble of every IFPS blob. +//! `TPSHeader` - the fixed 28-byte preamble of every IFPS blob. //! //! Wire layout (`uPSRuntime.pas:1204-1212`): //! @@ -16,12 +16,12 @@ //! //! The `ImportTableSize` field is included for forward parity with //! the upstream layout but `LoadData` (`uPSRuntime.pas:3030`) does -//! not actually walk an import table — externals are stored +//! not actually walk an import table - externals are stored //! inline on the proc table, with their `Flags & 1` bit set. use crate::{error::Error, reader::Reader}; -/// Canonical magic byte sequence — first 4 bytes of every IFPS +/// Canonical magic byte sequence - first 4 bytes of every IFPS /// blob. Mirrors [`PS_VALID_HEADER`]. pub const IFPS_MAGIC: [u8; 4] = *b"IFPS"; @@ -43,7 +43,7 @@ pub const PS_CURRENT_BUILD_NO: u32 = 23; /// <> InvalidVal`). pub const INVALID_VAL: u32 = u32::MAX; -/// Total header size in bytes — `SizeOf(TPSHeader)` in upstream. +/// Total header size in bytes - `SizeOf(TPSHeader)` in upstream. pub const HEADER_SIZE: usize = 28; /// Parsed [`TPSHeader`](https://github.com/remobjects/pascalscript) @@ -66,7 +66,7 @@ pub struct Header { pub main_proc_no: u32, /// Trailing `ImportTableSize` field. Retained for layout /// parity but has no functional role at the supported build - /// range — the proc table carries import data inline. + /// range - the proc table carries import data inline. pub import_table_size: u32, } diff --git a/src/lib.rs b/src/lib.rs index 7f8b772..be65666 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -4,8 +4,8 @@ //! RemObjects PascalScript is a Pascal-syntax scripting library //! widely embedded in Delphi applications (Inno Setup's `[Code]` //! sections being one of the most prominent users). This crate -//! parses the on-disk binary form — header, type / proc / var -//! / attribute tables, and the bytecode stream — into typed +//! parses the on-disk binary form - header, type / proc / var +//! / attribute tables, and the bytecode stream - into typed //! Rust views, plus surfaces a symbolic disassembly via //! [`Container::display`]. //! diff --git a/src/literal.rs b/src/literal.rs index c12e1d2..708d4f6 100644 --- a/src/literal.rs +++ b/src/literal.rs @@ -25,7 +25,7 @@ use crate::{ #[derive(Clone, Debug, PartialEq)] #[non_exhaustive] pub enum Literal<'a> { - /// `btSet` — `ceil(bit_size / 8)` bytes (size derived from + /// `btSet` - `ceil(bit_size / 8)` bytes (size derived from /// the referenced type's [`TypeBody::Set`]). Set(&'a [u8]), /// `btU8` (1 byte). @@ -44,7 +44,7 @@ pub enum Literal<'a> { U32(u32), /// `btS32` (4 bytes, signed). S32(i32), - /// `btProcPtr` (4 bytes — proc-table index). + /// `btProcPtr` (4 bytes - proc-table index). ProcPtr(u32), /// `btSingle` (4 bytes IEEE-754 binary32, raw). Single([u8; 4]), @@ -54,18 +54,18 @@ pub enum Literal<'a> { S64(i64), /// `btDouble` (8 bytes IEEE-754 binary64, raw). Double([u8; 8]), - /// `btCurrency` (8 bytes — Delphi `Currency`, scaled by 1e4). + /// `btCurrency` (8 bytes - Delphi `Currency`, scaled by 1e4). Currency([u8; 8]), - /// `btExtended` (10 bytes — Delphi `Extended`, x86 80-bit). + /// `btExtended` (10 bytes - Delphi `Extended`, x86 80-bit). Extended([u8; 10]), - /// `btString` / `btPChar` — ANSI / Windows-1252 bytes + /// `btString` / `btPChar` - ANSI / Windows-1252 bytes /// (length-prefixed on the wire; we surface the bytes only). String(&'a [u8]), - /// `btWideString` — UTF-16LE bytes. The on-disk length is + /// `btWideString` - UTF-16LE bytes. The on-disk length is /// the count of UTF-16 code units; the byte slice has length /// `2 * count`. WideString(&'a [u8]), - /// `btUnicodeString` — UTF-16LE bytes. Same encoding as + /// `btUnicodeString` - UTF-16LE bytes. Same encoding as /// `WideString`; PascalScript distinguishes the type but the /// wire form is identical. UnicodeString(&'a [u8]), diff --git a/src/opcode.rs b/src/opcode.rs index 7cf6146..b2d7ff3 100644 --- a/src/opcode.rs +++ b/src/opcode.rs @@ -129,42 +129,42 @@ pub enum FlowType { #[non_exhaustive] #[allow(missing_docs)] pub enum Opcode<'a> { - /// `CM_A = 0` — `dest := src`. Two operands. + /// `CM_A = 0` - `dest := src`. Two operands. Assign { dest: Operand<'a>, src: Operand<'a> }, - /// `CM_CA = 1` — `dest := dest src`. CalcType byte + + /// `CM_CA = 1` - `dest := dest src`. CalcType byte + /// two operands. CalculateAssign { op: CalcOp, dest: Operand<'a>, src: Operand<'a>, }, - /// `CM_P = 2` — push variable. + /// `CM_P = 2` - push variable. Push { var: Operand<'a> }, - /// `CM_PV = 3` — push var-by-reference. + /// `CM_PV = 3` - push var-by-reference. PushVar { var: Operand<'a> }, - /// `CM_PO = 4` — pop top of stack. No operands. + /// `CM_PO = 4` - pop top of stack. No operands. Pop, - /// `Cm_C = 5` — call procedure by index. + /// `Cm_C = 5` - call procedure by index. Call { proc_no: u32 }, - /// `Cm_G = 6` — unconditional goto, signed PC-relative + /// `Cm_G = 6` - unconditional goto, signed PC-relative /// offset from end-of-instruction. Goto { offset: i32 }, - /// `Cm_CG = 7` — `if pop() then goto`. Unsigned PC-relative + /// `Cm_CG = 7` - `if pop() then goto`. Unsigned PC-relative /// offset + condition operand. CondGoto { offset: u32, cond: Operand<'a> }, - /// `Cm_CNG = 8` — `if not pop() then goto`. Same shape as + /// `Cm_CNG = 8` - `if not pop() then goto`. Same shape as /// `CondGoto`. CondNotGoto { offset: u32, cond: Operand<'a> }, - /// `Cm_R = 9` — return from procedure. + /// `Cm_R = 9` - return from procedure. Return, - /// `Cm_ST = 10` — set stack type at offset. + /// `Cm_ST = 10` - set stack type at offset. SetStackType { new_type: u32, offset_from_base: u32, }, - /// `Cm_Pt = 11` — push a typed temporary onto the stack. + /// `Cm_Pt = 11` - push a typed temporary onto the stack. PushType { type_no: u32 }, - /// `CM_CO = 12` — compare two operands and store the + /// `CM_CO = 12` - compare two operands and store the /// boolean result into `into`. Compare { op: CompareOp, @@ -172,22 +172,22 @@ pub enum Opcode<'a> { lhs: Operand<'a>, rhs: Operand<'a>, }, - /// `Cm_cv = 13` — call procedure whose index is the + /// `Cm_cv = 13` - call procedure whose index is the /// runtime value of `var`. CallVar { var: Operand<'a> }, - /// `cm_sp = 14` — `dest := @src` (set pointer). + /// `cm_sp = 14` - `dest := @src` (set pointer). SetPointer { dest: Operand<'a>, src: Operand<'a> }, - /// `cm_bn = 15` — boolean NOT in place. + /// `cm_bn = 15` - boolean NOT in place. BoolNot { var: Operand<'a> }, - /// `cm_vm = 16` — arithmetic negation in place. + /// `cm_vm = 16` - arithmetic negation in place. Negate { var: Operand<'a> }, - /// `cm_sf = 17` — set the saved compare flag from `var`, + /// `cm_sf = 17` - set the saved compare flag from `var`, /// optionally inverted. SetFlag { var: Operand<'a>, invert: bool }, - /// `cm_fg = 18` — unconditional goto if the saved compare + /// `cm_fg = 18` - unconditional goto if the saved compare /// flag is set. Absolute target offset. FlagGoto { target: u32 }, - /// `cm_puexh = 19` — push a four-section exception handler + /// `cm_puexh = 19` - push a four-section exception handler /// frame (try/finally/except/finally2). PushExceptionHandler { finally_offset: u32, @@ -195,22 +195,22 @@ pub enum Opcode<'a> { finally2_offset: u32, end_of_block: u32, }, - /// `cm_poexh = 20` — pop one section of an exception + /// `cm_poexh = 20` - pop one section of an exception /// handler frame. PopExceptionHandler { position: ExceptionHandlerEnd }, - /// `cm_in = 21` — bitwise NOT in place. + /// `cm_in = 21` - bitwise NOT in place. IntegerNot { var: Operand<'a> }, - /// `cm_spc = 22` — copy stack-pointer state. + /// `cm_spc = 22` - copy stack-pointer state. SetStackPointerToCopy { target: u32 }, - /// `cm_inc = 23` — increment in place. + /// `cm_inc = 23` - increment in place. Inc { var: Operand<'a> }, - /// `cm_dec = 24` — decrement in place. + /// `cm_dec = 24` - decrement in place. Dec { var: Operand<'a> }, - /// `Cm_PG = 25` — pop one stack slot then goto. + /// `Cm_PG = 25` - pop one stack slot then goto. PopAndGoto { offset: i32 }, - /// `Cm_P2G = 26` — pop two stack slots then goto. + /// `Cm_P2G = 26` - pop two stack slots then goto. Pop2AndGoto { offset: i32 }, - /// `cm_nop = 255` — no operation. + /// `cm_nop = 255` - no operation. Nop, } diff --git a/src/operand.rs b/src/operand.rs index 9d536e2..c018960 100644 --- a/src/operand.rs +++ b/src/operand.rs @@ -4,25 +4,25 @@ //! Wire form is `VarType: u8 + Param: u32` followed by extra //! bytes that vary per `VarType`: //! -//! - **0** — direct global / stack variable reference. 5 bytes. -//! - **1** — typed temporary or literal. 5 bytes (type ref) plus +//! - **0** - direct global / stack variable reference. 5 bytes. +//! - **1** - typed temporary or literal. 5 bytes (type ref) plus //! per-`BaseType` payload (see [`crate::Literal`]). -//! - **2** — `base[const_index]` for record fields and +//! - **2** - `base[const_index]` for record fields and //! compile-time-fixed array indices. 9 bytes (5 + 4). -//! - **3** — `base[var_index]` for dynamic array indices that +//! - **3** - `base[var_index]` for dynamic array indices that //! read another variable's value at runtime. 9 bytes (5 + 4). //! //! The `Param` value of a `VarType=0`/`2`/`3` outer reference //! splits at `PSAddrStackStart = 0x60000000` //! (`uPSUtils.pas:22`): //! -//! - `Param < 0x40000000` (`PSAddrNegativeStackStart`) — global +//! - `Param < 0x40000000` (`PSAddrNegativeStackStart`) - global //! var index. -//! - `Param >= 0x60000000` — stack-relative offset, with the +//! - `Param >= 0x60000000` - stack-relative offset, with the //! actual offset = `Param - 0x60000000` (the runtime adds //! `FCurrStackBase` and subtracts `PSAddrStackStart`). //! - The `0x40000000..0x60000000` window is treated as -//! stack-relative by upstream too — same formula. We surface +//! stack-relative by upstream too - same formula. We surface //! anything `>= 0x40000000` as [`VarRef::Stack`]. use crate::{ @@ -46,7 +46,7 @@ const PS_ADDR_NEGATIVE_STACK_START: u32 = 0x4000_0000; #[derive(Clone, Copy, Debug, PartialEq, Eq)] pub enum VarRef { /// Index into the global-variable table. The runtime - /// validates `index < FGlobalVars.Count` at execute time — + /// validates `index < FGlobalVars.Count` at execute time - /// we don't validate at parse time because the global count /// is fixed by the `VarCount` header field, not the operand /// stream. @@ -89,18 +89,18 @@ pub enum Operand<'a> { /// Decoded payload. value: Literal<'a>, }, - /// `base[const_index]` (`VarType=2`) — compile-time-fixed + /// `base[const_index]` (`VarType=2`) - compile-time-fixed /// record-field or static-array index. Indexed { /// Base var being indexed. base: VarRef, /// Constant index into the base's record fields or /// array elements, depending on the base's resolved - /// type. We don't resolve which kind here — the index + /// type. We don't resolve which kind here - the index /// is always 4 wire bytes. sub_index: u32, }, - /// `base[var_index]` (`VarType=3`) — dynamic array index + /// `base[var_index]` (`VarType=3`) - dynamic array index /// read at runtime from another variable. DynamicIndexed { /// Base var being indexed. diff --git a/src/proc.rs b/src/proc.rs index 3f10827..74dab19 100644 --- a/src/proc.rs +++ b/src/proc.rs @@ -1,16 +1,16 @@ -//! Proc-table walker — `LoadProcs` in +//! Proc-table walker - `LoadProcs` in //! `uPSRuntime.pas:2802-2933`. //! //! Each `ProcCount` slot starts with `TPSProc = packed record //! Flags: Byte end;` (`uPSRuntime.pas:1223-1225`). The flag bits //! are: //! -//! - `Flags & 1` — external proc (name + optional decl). -//! - `Flags & 2` — exported (per-kind: internal procs gain +//! - `Flags & 1` - external proc (name + optional decl). +//! - `Flags & 2` - exported (per-kind: internal procs gain //! export-name + decl; external procs aren't gated by this bit). -//! - `Flags & 3 == 3` (external + exported) — external procs add +//! - `Flags & 3 == 3` (external + exported) - external procs add //! a 4-byte-prefixed decl after the name. -//! - `Flags & 4` — has a build-21+ attribute block. +//! - `Flags & 4` - has a build-21+ attribute block. //! //! External procs serialize as `(name_len: u8, name, [decl_len: //! u32, decl])`. Internal procs serialize as @@ -25,7 +25,7 @@ const FLAG_EXPORTED: u8 = 0x02; const FLAG_HAS_ATTRIBUTES: u8 = 0x04; /// Address-space sentinel from upstream (`uPSUtils.pas:24`). -/// `PSAddrNegativeStackStart = 0x40000000` — name/decl lengths +/// `PSAddrNegativeStackStart = 0x40000000` - name/decl lengths /// must stay below this to be addressable. const MAX_NAME_LEN: u32 = 0x4000_0000; @@ -35,7 +35,7 @@ pub struct Proc<'a> { /// Raw flag byte (preserves bits beyond the documented set /// for forward compatibility). pub flags_raw: u8, - /// Variant-specific data — external import vs. internal + /// Variant-specific data - external import vs. internal /// script-defined proc. pub kind: ProcKind<'a>, /// Build-21+ attributes attached to this proc, populated @@ -52,7 +52,7 @@ pub enum ProcKind<'a> { /// script references the host by `name`; the host's runtime /// resolves and dispatches. External(ExternalProc<'a>), - /// Script-defined procedure — has bytecode at + /// Script-defined procedure - has bytecode at /// `(bytecode_offset, bytecode_len)` inside the IFPS blob. Internal(InternalProc<'a>), } @@ -72,7 +72,7 @@ pub struct ExternalProc<'a> { #[derive(Clone, Debug)] pub struct InternalProc<'a> { /// Byte offset of the proc's bytecode within the IFPS blob. - /// 1-based in upstream — `Move(s[L2 + 1], …)` — but we + /// 1-based in upstream - `Move(s[L2 + 1], …)` - but we /// surface it as the 0-based offset that's actually useful. pub bytecode_offset: u32, /// Length of the bytecode body in bytes. @@ -87,7 +87,7 @@ pub struct InternalProc<'a> { /// advancing `reader`. The caller is expected to push the /// resulting [`Proc`] onto the proc table and **then** invoke /// [`crate::attribute::parse_block`] when the -/// returned `flags_raw` has [`FLAG_HAS_ATTRIBUTES`] set — +/// returned `flags_raw` has [`FLAG_HAS_ATTRIBUTES`] set - /// matching upstream's `LoadProcs` push-then-attr order /// (`uPSRuntime.pas:2922-2932`). pub(crate) fn parse_proc<'a>(reader: &mut Reader<'a>, blob_len: usize) -> Result, Error> { @@ -231,7 +231,7 @@ mod tests { #[test] fn rejects_internal_proc_past_blob() { - // offset=0x10, length=0x100 — exceeds 0x40-byte blob. + // offset=0x10, length=0x100 - exceeds 0x40-byte blob. let mut buf = vec![0x00]; put_le32(&mut buf, 0x10); put_le32(&mut buf, 0x100); diff --git a/src/ty.rs b/src/ty.rs index 86639f6..7591b81 100644 --- a/src/ty.rs +++ b/src/ty.rs @@ -1,11 +1,11 @@ -//! Type-table walker — `LoadTypes` in +//! Type-table walker - `LoadTypes` in //! `uPSRuntime.pas:2538-2799`. //! //! Each `TypeCount` slot starts with a 1-byte //! `BaseType`-plus-flag byte. Bit 7 of the byte signals "has //! export name"; the low 7 bits are the actual `BaseType` //! constant (`uPSUtils.pas:46-118`). The on-disk constants only -//! reach `btU64 = 29` — `btType = 130`, `btEnum = 129`, and +//! reach `btU64 = 29` - `btType = 130`, `btEnum = 129`, and //! `btExtClass = 131` are runtime-only sentinels and never appear //! in serialized data. //! @@ -19,7 +19,7 @@ use crate::{attribute::Attribute, error::Error, reader::Reader}; /// /// Source: `uPSUtils.pas:46-112` (constants 0-29). The runtime /// sentinels `btType = 130`, `btEnum = 129`, `btExtClass = 131` -/// are excluded — they never appear in serialized data per +/// are excluded - they never appear in serialized data per /// `LoadTypes`. #[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)] #[non_exhaustive] @@ -105,34 +105,34 @@ pub enum TypeBody<'a> { /// No type-specific payload (numeric primitives, strings, /// pointers, variants). Bare, - /// `btClass` — class-name string (≤ 255 bytes). + /// `btClass` - class-name string (≤ 255 bytes). Class { /// Raw class-name bytes (ASCII / Windows-1252 in /// upstream). name: &'a [u8], }, - /// `btProcPtr` — variable-length parameter-info string. + /// `btProcPtr` - variable-length parameter-info string. ProcPtr { /// Raw param-info bytes. param_info: &'a [u8], }, - /// `btInterface` — 16-byte interface GUID. + /// `btInterface` - 16-byte interface GUID. Interface { - /// Raw GUID bytes (Microsoft byte order — this struct + /// Raw GUID bytes (Microsoft byte order - this struct /// does NOT canonicalize to RFC 4122). guid: [u8; 16], }, - /// `btSet` — bit count (≤ 256). + /// `btSet` - bit count (≤ 256). Set { /// Number of bits the set carries on the wire. bit_size: u32, }, - /// `btArray` — dynamic array; element-type index. + /// `btArray` - dynamic array; element-type index. Array { /// Index into the type table for the element type. element_type: u32, }, - /// `btStaticArray` — fixed-length array. + /// `btStaticArray` - fixed-length array. StaticArray { /// Index into the type table for the element type. element_type: u32, @@ -142,7 +142,7 @@ pub enum TypeBody<'a> { /// at non-zero indices). start_offset: u32, }, - /// `btRecord` — list of field-type indices. + /// `btRecord` - list of field-type indices. Record { /// One type-table index per field, in declaration order. field_types: Vec, @@ -173,7 +173,7 @@ const MAX_SET_BIT_SIZE: u32 = 256; /// to push the resulting [`Type`] onto the type table and /// **then** invoke /// [`crate::attribute::parse_block`] for builds -/// ≥ 21 — matching upstream's `LoadTypes` push-then-attr order +/// ≥ 21 - matching upstream's `LoadTypes` push-then-attr order /// (`uPSRuntime.pas:2789-2798`). pub(crate) fn parse_type<'a>(reader: &mut Reader<'a>, so_far: u32) -> Result, Error> { let raw = reader.u8("type BaseType")?; @@ -284,7 +284,7 @@ fn parse_body<'a>( } fn check_type_ref(idx: u32, so_far: u32) -> Result<(), Error> { - // Upstream rejects forward references too — a type can only + // Upstream rejects forward references too - a type can only // refer to entries that have already been defined. if idx >= so_far { return Err(Error::TypeIndexOutOfRange { diff --git a/src/var.rs b/src/var.rs index 34173fd..7a7f7a9 100644 --- a/src/var.rs +++ b/src/var.rs @@ -1,4 +1,4 @@ -//! Var-table walker — `LoadVars` in +//! Var-table walker - `LoadVars` in //! `uPSRuntime.pas:2936-2989`. //! //! Each `VarCount` slot is a `TPSVar = packed record TypeNo: