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
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
use crate::LuaValue;
// Port of expdesc from lcode.h
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum ExpKind {
VVOID, // when 'expdesc' describes the last expression of a list, this kind means an empty list
VNIL, // constant nil
VTRUE, // constant true
VFALSE, // constant false
VK, // constant in 'k'; info = index of constant in 'k'
VKFLT, // floating constant; nval = numerical float value
VKINT, // integer constant; ival = numerical integer value
VKSTR, // string constant; strval = TString address
VNONRELOC, // expression has its value in a fixed register; info = result register
VLOCAL, // local variable; var.ridx = register index; var.vidx = relative index in 'actvar.arr'
VVARGVAR, // vararg parameter; var.ridx = register index; var.vidx = relative index in 'actvar.arr'
VUPVAL, // upvalue variable; info = index of upvalue in 'upvalues'
VCONST, // compile-time <const> variable; info = absolute index in 'actvar.arr'
VGLOBAL, // Lua 5.5: global variable declaration; info = index in actvar for global name
VINDEXED, // indexed variable; ind.t = table register; ind.idx = key's R index
VINDEXUP, // indexed upvalue; ind.t = upvalue; ind.idx = key's K index
VINDEXI, // indexed variable with constant integer; ind.t = table register; ind.idx = key's value
VINDEXSTR, // indexed variable with literal string; ind.t = table register; ind.idx = key's K index
VVARGIND, // indexed vararg parameter; ind.t = vararg register; ind.idx = key's R index (Lua 5.5)
VJMP, // expression is a test/comparison; info = pc of corresponding jump instruction
VRELOC, // expression can put result in any register; info = instruction pc
VCALL, // expression is a function call; info = instruction pc
VVARARG, // vararg expression; info = instruction pc
}
#[derive(Clone)]
pub struct ExpDesc {
pub kind: ExpKind,
pub u: ExpUnion,
pub t: isize, // patch list of 'exit when true'
pub f: isize, // patch list of 'exit when false'
}
#[derive(Clone, Copy)]
pub enum ExpUnion {
// for generic use
Info(i32),
// for VKSTR
Str(LuaValue),
// for VKINT
IVal(i64),
// for VKFLT
NVal(f64),
// for indexed variables
Ind(IndVars),
// for local/upvalue variables
Var(VarVals),
}
impl ExpUnion {
pub fn info(&self) -> i32 {
match self {
ExpUnion::Info(info) => *info,
_ => panic!("ExpUnion does not contain info"),
}
}
pub fn str(&self) -> &LuaValue {
match self {
ExpUnion::Str(s_value) => s_value,
_ => panic!("ExpUnion does not contain str"),
}
}
pub fn ival(&self) -> i64 {
match self {
ExpUnion::IVal(ival) => *ival,
_ => panic!("ExpUnion does not contain ival"),
}
}
pub fn nval(&self) -> f64 {
match self {
ExpUnion::NVal(nval) => *nval,
_ => panic!("ExpUnion does not contain nval"),
}
}
pub fn ind(&self) -> IndVars {
match self {
ExpUnion::Ind(ind) => *ind,
_ => panic!("ExpUnion does not contain ind"),
}
}
pub fn ind_mut(&mut self) -> &mut IndVars {
match self {
ExpUnion::Ind(ind) => ind,
_ => {
*self = ExpUnion::Ind(IndVars {
t: -1,
idx: -1,
ro: false,
keystr: -1,
});
if let ExpUnion::Ind(ind) = self {
ind
} else {
unreachable!()
}
}
}
}
pub fn var(&self) -> VarVals {
match self {
ExpUnion::Var(var) => *var,
_ => panic!("ExpUnion does not contain var"),
}
}
}
#[derive(Clone, Copy)]
pub struct IndVars {
pub t: i16, // table (register or upvalue)
pub idx: i16, // index (R or "long" K)
pub ro: bool, // true if variable is read-only
pub keystr: i32, // index in 'k' of string key, or -1 if not a string
}
#[derive(Clone, Copy)]
pub struct VarVals {
pub ridx: i16, // register holding the variable
pub vidx: u16, // compiler index (in 'actvar.arr' or 'upvalues')
}
impl ExpDesc {
pub fn new_void() -> Self {
ExpDesc {
kind: ExpKind::VVOID,
u: ExpUnion::Info(0),
t: -1,
f: -1,
}
}
pub fn new_nil() -> Self {
ExpDesc {
kind: ExpKind::VNIL,
u: ExpUnion::Info(0),
t: -1,
f: -1,
}
}
pub fn new_int(val: i64) -> Self {
ExpDesc {
kind: ExpKind::VKINT,
u: ExpUnion::IVal(val),
t: -1,
f: -1,
}
}
pub fn new_float(val: f64) -> Self {
ExpDesc {
kind: ExpKind::VKFLT,
u: ExpUnion::NVal(val),
t: -1,
f: -1,
}
}
pub fn new_bool(val: bool) -> Self {
ExpDesc {
kind: if val { ExpKind::VTRUE } else { ExpKind::VFALSE },
u: ExpUnion::Info(0),
t: -1,
f: -1,
}
}
pub fn new_k(info: usize) -> Self {
ExpDesc {
kind: ExpKind::VK,
u: ExpUnion::Info(info as i32),
t: -1,
f: -1,
}
}
pub fn new_vkstr(s_value: LuaValue) -> Self {
debug_assert!(s_value.is_string(), "Expected LuaValue to be a string");
ExpDesc {
kind: ExpKind::VKSTR,
u: ExpUnion::Str(s_value),
t: -1,
f: -1,
}
}
pub fn new_nonreloc(reg: u8) -> Self {
ExpDesc {
kind: ExpKind::VNONRELOC,
u: ExpUnion::Info(reg as i32),
t: -1,
f: -1,
}
}
pub fn new_local(ridx: u8, vidx: u16) -> Self {
ExpDesc {
kind: ExpKind::VLOCAL,
u: ExpUnion::Var(VarVals {
ridx: ridx as i16,
vidx,
}),
t: -1,
f: -1,
}
}
pub fn new_upval(idx: u8) -> Self {
ExpDesc {
kind: ExpKind::VUPVAL,
u: ExpUnion::Info(idx as i32),
t: -1,
f: -1,
}
}
pub fn new_indexed(t: u8, idx: u8) -> Self {
ExpDesc {
kind: ExpKind::VINDEXED,
u: ExpUnion::Ind(IndVars {
t: t as i16,
idx: idx as i16,
ro: false,
keystr: -1,
}),
t: -1,
f: -1,
}
}
pub fn new_reloc(pc: usize) -> Self {
ExpDesc {
kind: ExpKind::VRELOC,
u: ExpUnion::Info(pc as i32),
t: -1,
f: -1,
}
}
pub fn new_call(pc: usize) -> Self {
ExpDesc {
kind: ExpKind::VCALL,
u: ExpUnion::Info(pc as i32),
t: -1,
f: -1,
}
}
pub fn has_jumps(&self) -> bool {
// Port of hasjumps macro from lcode.c:58
// #define hasjumps(e) ((e)->t != (e)->f)
self.t != self.f
}
pub fn is_const(&self) -> bool {
matches!(
self.kind,
ExpKind::VNIL
| ExpKind::VTRUE
| ExpKind::VFALSE
| ExpKind::VK
| ExpKind::VKFLT
| ExpKind::VKINT
| ExpKind::VKSTR
)
}
pub fn is_numeral(&self) -> bool {
matches!(self.kind, ExpKind::VKINT | ExpKind::VKFLT) && !self.has_jumps()
}
}