Skip to main content

luaur_compiler/functions/
fold_interp_string.rs

1use alloc::vec::Vec;
2use core::ffi::c_char;
3use luaur_ast::records::ast_expr::AstExpr;
4use luaur_ast::records::ast_expr_interp_string::AstExprInterpString;
5use luaur_ast::records::ast_name::AstName;
6use luaur_ast::records::ast_name_table::AstNameTable;
7use luaur_ast::records::ast_node::AstNode;
8use luaur_common::macros::luau_assert::LUAU_ASSERT;
9use luaur_common::records::dense_hash_map::DenseHashMap;
10
11use crate::enums::type_constant_folding::Type;
12use crate::records::constant::Constant;
13
14pub fn fold_interp_string(
15    result: &mut Constant,
16    expr: *mut AstExprInterpString,
17    constants: &mut DenseHashMap<*mut AstExpr, Constant>,
18    string_table: &mut AstNameTable,
19) {
20    let expr = unsafe { &*expr };
21    LUAU_ASSERT!(expr.strings.len() == expr.expressions.len() + 1);
22
23    let mut result_length: usize = 0;
24    for index in 0..expr.strings.len() {
25        let string = expr.strings.as_slice()[index];
26        result_length += string.len();
27        if index < expr.expressions.len() {
28            let expr_ptr = expr.expressions.as_slice()[index];
29            let c = constants.find(&expr_ptr);
30            LUAU_ASSERT!(c.is_some());
31            let c = c.unwrap();
32            LUAU_ASSERT!(c.r#type == Type::Type_String);
33            result_length += c.string_length as usize;
34        }
35    }
36
37    const K_CONSTANT_FOLD_STRING_LIMIT: usize = 4096;
38    if result_length > K_CONSTANT_FOLD_STRING_LIMIT {
39        return;
40    }
41
42    result.r#type = Type::Type_String;
43    result.string_length = result_length as u32;
44
45    if result_length == 0 {
46        // C++ `result.valueString = ""` — a non-null pointer to a static empty C-string.
47        // A null here later trips sref()'s `LUAU_ASSERT(data.begin())` when the folded
48        // empty interpolation (e.g. `{empty}`) is emitted as a string constant.
49        unsafe {
50            result.data.value_string = c"".as_ptr();
51        }
52        return;
53    }
54
55    let mut tmp = Vec::with_capacity(result_length);
56
57    for index in 0..expr.strings.len() {
58        let string = expr.strings.as_slice()[index];
59        let slice = unsafe {
60            core::slice::from_raw_parts(string.as_slice().as_ptr() as *const u8, string.len())
61        };
62        tmp.extend_from_slice(slice);
63        if index < expr.expressions.len() {
64            let expr_ptr = expr.expressions.as_slice()[index];
65            let c = constants.find(&expr_ptr);
66            LUAU_ASSERT!(c.is_some());
67            let c = c.unwrap();
68            let string_slice = c.get_string();
69            let string_bytes = unsafe {
70                core::slice::from_raw_parts(
71                    string_slice.as_slice().as_ptr() as *const u8,
72                    string_slice.len(),
73                )
74            };
75            tmp.extend_from_slice(string_bytes);
76        }
77    }
78
79    result.r#type = Type::Type_String;
80    result.string_length = result_length as u32;
81    let name = string_table.get_or_add(tmp.as_ptr() as *const c_char, tmp.len());
82    unsafe {
83        result.data.value_string = name.value;
84    }
85}