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