lua_types/proto.rs
1//! `LuaProto` — compiled function prototype. Mirrors C-Lua's `Proto` struct
2//! but uses Rust idioms (Vec instead of pointer+size pairs).
3
4use crate::closure::LuaLClosure;
5use crate::gc::GcRef;
6use crate::opcode::Instruction;
7use crate::string::LuaString;
8use crate::value::LuaValue;
9use core::cell::RefCell;
10
11#[derive(Debug)]
12pub struct LuaProto {
13 pub numparams: u8,
14 pub is_vararg: bool,
15 pub maxstacksize: u8,
16 pub upvalues: Vec<UpvalDesc>,
17 pub k: Vec<LuaValue>,
18 pub code: Vec<Instruction>,
19 pub p: Vec<GcRef<LuaProto>>,
20 pub lineinfo: Vec<i8>,
21 pub abslineinfo: Vec<AbsLineInfo>,
22 pub locvars: Vec<LocalVar>,
23 pub linedefined: i32,
24 pub lastlinedefined: i32,
25 pub source: Option<GcRef<LuaString>>,
26 /// Last closure instantiated from this proto, reused by `OP_CLOSURE` when a
27 /// new instantiation would capture the identical upvalues. Mirrors C-Lua's
28 /// `Proto.cache` (5.2/5.3 only — added in 5.2, removed in 5.4), which is why
29 /// loop-built closures with shared upvalues compare `==` on those versions.
30 /// Populated only under 5.2/5.3 in `push_closure`; `None` otherwise. Traced
31 /// (so it cannot dangle); unlike C's GC-cleared weak cache this pins the one
32 /// cached closure to the proto's lifetime, which is bounded and safe.
33 pub cache: RefCell<Option<GcRef<LuaLClosure>>>,
34 /// Lua 5.5 named varargs (`function f(...t)`): the register holding the
35 /// packed vararg table `t`. When set, `...` unpacks live from that table
36 /// (count = its `n` field) rather than the frame's extra-arg slots, so
37 /// mutating `t` is observable through a later `...` (shared storage). `None`
38 /// for ordinary `...` and all pre-5.5 functions. Mirrors upstream's
39 /// `needvatab` proto flag + the vararg-table register.
40 pub vararg_table_reg: Option<u8>,
41}
42
43impl LuaProto {
44 pub fn placeholder() -> Self {
45 LuaProto {
46 numparams: 0,
47 is_vararg: false,
48 maxstacksize: 2,
49 upvalues: Vec::new(),
50 k: Vec::new(),
51 code: Vec::new(),
52 p: Vec::new(),
53 lineinfo: Vec::new(),
54 abslineinfo: Vec::new(),
55 locvars: Vec::new(),
56 linedefined: 0,
57 lastlinedefined: 0,
58 source: None,
59 cache: RefCell::new(None),
60 vararg_table_reg: None,
61 }
62 }
63
64 /// Bytes owned outside the `GcBox` header/object allocation.
65 ///
66 /// C allocates these arrays through Lua's allocator. The Rust port stores
67 /// them as `Vec`s, so GC byte accounting charges their backing capacity
68 /// explicitly when a populated proto is wrapped in `GcRef`.
69 pub fn buffer_bytes(&self) -> usize {
70 self.upvalues.capacity() * std::mem::size_of::<UpvalDesc>()
71 + self.k.capacity() * std::mem::size_of::<LuaValue>()
72 + self.code.capacity() * std::mem::size_of::<Instruction>()
73 + self.p.capacity() * std::mem::size_of::<GcRef<LuaProto>>()
74 + self.lineinfo.capacity() * std::mem::size_of::<i8>()
75 + self.abslineinfo.capacity() * std::mem::size_of::<AbsLineInfo>()
76 + self.locvars.capacity() * std::mem::size_of::<LocalVar>()
77 }
78}
79
80#[derive(Debug, Clone)]
81pub struct UpvalDesc {
82 pub name: Option<GcRef<LuaString>>,
83 pub instack: bool,
84 pub idx: u8,
85 pub kind: u8,
86}
87
88#[derive(Debug, Clone)]
89pub struct LocalVar {
90 pub varname: GcRef<LuaString>,
91 pub startpc: i32,
92 pub endpc: i32,
93}
94
95#[derive(Debug, Clone, Copy)]
96pub struct AbsLineInfo {
97 pub pc: i32,
98 pub line: i32,
99}
100
101// ──────────────────────────────────────────────────────────────────────────────
102// PORT STATUS
103// source: src/lobject.h (Proto struct)
104// target_crate: lua-types
105// confidence: high
106// todos: 0
107// port_notes: 0
108// unsafe_blocks: 0
109// notes: Function prototype: bytecode, constants, line info, debug info,
110// upvalue descriptors. Faithful layout of C's Proto struct using
111// Vec<T> in place of T*+size pairs.
112// ──────────────────────────────────────────────────────────────────────────────