Skip to main content

luaur_code_gen/functions/
append_vm_constant.rs

1extern crate alloc;
2
3use crate::functions::append::append;
4use crate::functions::format_g::format_g;
5use crate::functions::is_printable_string_constant::is_printable_string_constant;
6use alloc::string::String;
7use core::ffi::{c_char, c_uint};
8use luaur_vm::macros::gco_2_ts::gco2ts;
9use luaur_vm::macros::getstr::getstr;
10use luaur_vm::macros::lua_vector_size::LUA_VECTOR_SIZE;
11use luaur_vm::records::g_cheader::GCheader;
12use luaur_vm::records::t_string::TString;
13use luaur_vm::type_aliases::proto::Proto;
14
15// Lua value type tags (lobject.h)
16const LUA_TNIL: i32 = 0;
17const LUA_TBOOLEAN: i32 = 1;
18const LUA_TNUMBER: i32 = 3;
19const LUA_TINTEGER: i32 = 4;
20const LUA_TVECTOR: i32 = 5;
21const LUA_TSTRING: i32 = 6;
22
23const K_MAX_STRING_CONSTANT_PRINT_LENGTH: c_uint = 16;
24
25// TString's `len`/`data` are crate-private in luau-vm; mirror the layout so we
26// can read the length (the `getstr` macro already exposes the data pointer).
27#[repr(C)]
28struct TStringHeader {
29    hdr: GCheader,
30    _padding1: [c_char; 1],
31    atom: i16,
32    _padding2: [c_char; 2],
33    next: *mut TString,
34    hash: c_uint,
35    len: c_uint,
36    data: [c_char; 1],
37}
38
39pub fn append_vm_constant(result: &mut String, proto: *mut Proto, index: i32) {
40    unsafe {
41        let constant = *(*proto).k.add(index as usize);
42
43        if constant.tt == LUA_TNIL {
44            append(result, format_args!("nil"));
45        } else if constant.tt == LUA_TBOOLEAN {
46            append(
47                result,
48                format_args!(
49                    "{}",
50                    if constant.value.b != 0 {
51                        "true"
52                    } else {
53                        "false"
54                    }
55                ),
56            );
57        } else if constant.tt == LUA_TNUMBER {
58            let n = constant.value.n;
59            if n != n {
60                append(result, format_args!("nan"));
61            } else {
62                // C++ uses "%.17g"; Rust `{}` prints the shortest round-tripping form.
63                result.push_str(&format_g(n, 17));
64            }
65        } else if constant.tt == LUA_TINTEGER {
66            append(result, format_args!("{}i", constant.value.l as i64));
67        } else if constant.tt == LUA_TSTRING {
68            let str_ts = gco2ts!(constant.value.gc) as *const _ as *const TString;
69            let data = getstr(str_ts);
70            let len = (*(str_ts as *const TStringHeader)).len;
71
72            if is_printable_string_constant(data, len as usize) {
73                let n = if len < K_MAX_STRING_CONSTANT_PRINT_LENGTH {
74                    len
75                } else {
76                    K_MAX_STRING_CONSTANT_PRINT_LENGTH
77                } as usize;
78                let bytes = core::slice::from_raw_parts(data as *const u8, n);
79                let text = String::from_utf8_lossy(bytes);
80
81                if len < K_MAX_STRING_CONSTANT_PRINT_LENGTH {
82                    append(result, format_args!("'{}'", text));
83                } else {
84                    append(result, format_args!("'{}'...", text));
85                }
86            }
87        } else if constant.tt == LUA_TVECTOR {
88            // value.v is float[2] in the union; v[2]/v[3] index into the trailing
89            // TValue storage, mirroring the C++ `const float* v = constant.value.v`.
90            let v = &constant.value as *const _ as *const f32;
91
92            if LUA_VECTOR_SIZE == 4 {
93                if *v.add(3) != 0.0 {
94                    append(
95                        result,
96                        format_args!(
97                            "{}, {}, {}, {}",
98                            format_g(*v.add(0) as f64, 9),
99                            format_g(*v.add(1) as f64, 9),
100                            format_g(*v.add(2) as f64, 9),
101                            format_g(*v.add(3) as f64, 9)
102                        ),
103                    );
104                } else {
105                    append(
106                        result,
107                        format_args!(
108                            "{}, {}, {}",
109                            format_g(*v.add(0) as f64, 9),
110                            format_g(*v.add(1) as f64, 9),
111                            format_g(*v.add(2) as f64, 9)
112                        ),
113                    );
114                }
115            } else {
116                append(
117                    result,
118                    format_args!(
119                        "{}, {}, {}",
120                        format_g(*v.add(0) as f64, 9),
121                        format_g(*v.add(1) as f64, 9),
122                        format_g(*v.add(2) as f64, 9)
123                    ),
124                );
125            }
126        }
127    }
128}