Skip to main content

luaur_vm/functions/
loadsafe.rs

1use crate::enums::feedback_vector_slot_kind::FeedbackVectorSlotKind;
2use crate::enums::lua_status::lua_Status;
3use crate::functions::lua_a_toobject::luaA_toobject;
4use crate::functions::lua_c_barrierback::lua_c_barrierback;
5use crate::functions::lua_d_pcall::luaD_pcall;
6use crate::functions::lua_f_new_lclosure::lua_f_new_lclosure;
7use crate::functions::lua_f_newproto::lua_f_newproto;
8use crate::functions::lua_gettop::lua_gettop;
9use crate::functions::lua_h_new::lua_h_new;
10use crate::functions::lua_h_set::luaH_set;
11use crate::functions::lua_h_setstr::lua_h_setstr;
12use crate::functions::lua_o_chunkid::lua_o_chunkid;
13use crate::functions::lua_pushlstring::lua_pushlstring;
14use crate::functions::lua_r_newclass::lua_r_newclass;
15use crate::functions::lua_s_newlstr::luaS_newlstr;
16use crate::functions::read::read;
17use crate::functions::read_string::read_string;
18use crate::functions::read_var_int::read_var_int;
19use crate::functions::read_var_int_64::read_var_int_64;
20use crate::functions::remap_userdata_types::remap_userdata_types;
21use crate::macros::getstr::getstr;
22use crate::macros::hvalue::hvalue;
23use crate::macros::incr_top::incr_top;
24use crate::macros::isblack::isblack;
25use crate::macros::lua_c_barriert::luaC_barriert;
26use crate::macros::lua_idsize::LUA_IDSIZE;
27use crate::macros::lua_m_newarray::luaM_newarray;
28use crate::macros::lua_s_new::luaS_new;
29use crate::macros::lua_s_updateatom::luaS_updateatom;
30use crate::macros::savestack::savestack;
31use crate::macros::setbvalue::setbvalue;
32use crate::macros::setclassvalue::setclassvalue;
33use crate::macros::setclvalue::setclvalue;
34use crate::macros::sethvalue::sethvalue;
35use crate::macros::setlvalue::setlvalue;
36use crate::macros::setnilvalue::setnilvalue;
37use crate::macros::setnvalue::setnvalue;
38use crate::macros::setobj::setobj;
39use crate::macros::setobj_2_t::setobj2t;
40use crate::macros::setsvalue::setsvalue;
41use crate::macros::setvvalue::setvvalue;
42use crate::macros::tsvalue::tsvalue;
43use crate::macros::ttisnil::ttisnil;
44use crate::macros::ttisstring::ttisstring;
45use crate::records::feedback_vector_slot::FeedbackVectorSlot;
46use crate::records::gc_object::GCObject;
47use crate::records::loc_var::LocVar;
48use crate::records::proto::Proto;
49use crate::records::resolve_import::ResolveImport;
50use crate::records::t_string::TString;
51use crate::records::temp_buffer::TempBuffer;
52use crate::type_aliases::instruction::Instruction;
53use crate::type_aliases::lua_state::lua_State;
54use crate::type_aliases::lua_table::LuaTable;
55use crate::type_aliases::t_value::TValue;
56use core::ffi::{c_char, c_int};
57use luaur_common::enums::luau_bytecode_tag::{
58    LBC_CONSTANT_BOOLEAN, LBC_CONSTANT_CLASS_SHAPE, LBC_CONSTANT_CLOSURE, LBC_CONSTANT_IMPORT,
59    LBC_CONSTANT_INTEGER, LBC_CONSTANT_NIL, LBC_CONSTANT_NUMBER, LBC_CONSTANT_STRING,
60    LBC_CONSTANT_TABLE, LBC_CONSTANT_TABLE_WITH_CONSTANTS, LBC_CONSTANT_VECTOR,
61    LBC_TYPE_VERSION_MAX, LBC_TYPE_VERSION_MIN, LBC_VERSION_MAX, LBC_VERSION_MIN,
62};
63use luaur_common::enums::luau_bytecode_type::{
64    LBC_TYPE_FUNCTION, LBC_TYPE_TAGGED_USERDATA_BASE, LBC_TYPE_TAGGED_USERDATA_END,
65    LBC_TYPE_USERDATA,
66};
67use luaur_common::enums::luau_feedback_type::LuauFeedbackType;
68use luaur_common::enums::luau_opcode::LuauOpcode;
69use luaur_common::functions::get_op_length::getOpLength;
70use luaur_common::macros::luau_assert::LUAU_ASSERT;
71use luaur_common::macros::luau_insn_op::LUAU_INSN_OP;
72
73const LBC_CONSTANT_NIL_U8: u8 = LBC_CONSTANT_NIL.0 as u8;
74const LBC_CONSTANT_BOOLEAN_U8: u8 = LBC_CONSTANT_BOOLEAN.0 as u8;
75const LBC_CONSTANT_NUMBER_U8: u8 = LBC_CONSTANT_NUMBER.0 as u8;
76const LBC_CONSTANT_STRING_U8: u8 = LBC_CONSTANT_STRING.0 as u8;
77const LBC_CONSTANT_IMPORT_U8: u8 = LBC_CONSTANT_IMPORT.0 as u8;
78const LBC_CONSTANT_TABLE_U8: u8 = LBC_CONSTANT_TABLE.0 as u8;
79const LBC_CONSTANT_CLOSURE_U8: u8 = LBC_CONSTANT_CLOSURE.0 as u8;
80const LBC_CONSTANT_VECTOR_U8: u8 = LBC_CONSTANT_VECTOR.0 as u8;
81const LBC_CONSTANT_TABLE_WITH_CONSTANTS_U8: u8 = LBC_CONSTANT_TABLE_WITH_CONSTANTS.0 as u8;
82const LBC_CONSTANT_INTEGER_U8: u8 = LBC_CONSTANT_INTEGER.0 as u8;
83const LBC_CONSTANT_CLASS_SHAPE_U8: u8 = LBC_CONSTANT_CLASS_SHAPE.0 as u8;
84const USERDATA_TYPE_LIMIT: usize =
85    (LBC_TYPE_TAGGED_USERDATA_END.0 - LBC_TYPE_TAGGED_USERDATA_BASE.0) as usize;
86
87#[allow(non_snake_case)]
88pub unsafe fn loadsafe(
89    L: *mut lua_State,
90    strings: &mut TempBuffer<*mut TString>,
91    protos: &mut TempBuffer<*mut Proto>,
92    chunkname: *const c_char,
93    data: *const c_char,
94    size: usize,
95    env: c_int,
96) -> c_int {
97    let mut offset: usize = 0;
98
99    let version: u8 = read(data, size, &mut offset);
100
101    // 0 means the rest of the bytecode is the error message
102    if version == 0 {
103        let mut chunkbuf = [0 as c_char; LUA_IDSIZE as usize];
104        let chunkid = lua_o_chunkid(
105            chunkbuf.as_mut_ptr(),
106            chunkbuf.len(),
107            chunkname,
108            c_strlen(chunkname),
109        );
110        push_chunk_prefixed_slice(L, chunkid, data.add(offset), size - offset);
111        return 1;
112    }
113
114    if version < LBC_VERSION_MIN.0 as u8 || version > LBC_VERSION_MAX.0 as u8 {
115        let mut chunkbuf = [0 as c_char; LUA_IDSIZE as usize];
116        let chunkid = lua_o_chunkid(
117            chunkbuf.as_mut_ptr(),
118            chunkbuf.len(),
119            chunkname,
120            c_strlen(chunkname),
121        );
122        let message = format!(
123            "{}: bytecode version mismatch (expected [{}..{}], got {})",
124            c_string_lossy(chunkid),
125            LBC_VERSION_MIN.0,
126            LBC_VERSION_MAX.0,
127            version
128        );
129        push_rust_string(L, &message);
130        return 1;
131    }
132
133    let mut typesversion: u8 = 0;
134
135    if version >= 4 {
136        typesversion = read(data, size, &mut offset);
137
138        if typesversion < LBC_TYPE_VERSION_MIN.0 as u8
139            || typesversion > LBC_TYPE_VERSION_MAX.0 as u8
140        {
141            let mut chunkbuf = [0 as c_char; LUA_IDSIZE as usize];
142            let chunkid = lua_o_chunkid(
143                chunkbuf.as_mut_ptr(),
144                chunkbuf.len(),
145                chunkname,
146                c_strlen(chunkname),
147            );
148            let message = format!(
149                "{}: bytecode type version mismatch (expected [{}..{}], got {})",
150                c_string_lossy(chunkid),
151                LBC_TYPE_VERSION_MIN.0,
152                LBC_TYPE_VERSION_MAX.0,
153                typesversion
154            );
155            push_rust_string(L, &message);
156            return 1;
157        }
158    }
159
160    // env is 0 for current environment and a stack index otherwise
161    let envt: *mut LuaTable = if env == 0 {
162        (*L).gt
163    } else {
164        hvalue!(luaA_toobject(L, env))
165    };
166
167    let source: *mut TString = luaS_new(L, chunkname);
168
169    // string table
170    let string_count = read_var_int(data, size, &mut offset);
171    strings.allocate(L, string_count as usize);
172
173    for i in 0..string_count {
174        let length = read_var_int(data, size, &mut offset);
175
176        *strings.data.add(i as usize) = luaS_newlstr(L, data.add(offset), length as usize);
177        offset += length as usize;
178    }
179
180    // userdata type remapping table
181    // for unknown userdata types, the entry will remap to common 'userdata' type
182    let mut userdata_remapping = [LBC_TYPE_USERDATA.0 as u8; USERDATA_TYPE_LIMIT];
183
184    if typesversion == 3 {
185        let mut index: u8 = read(data, size, &mut offset);
186
187        while index != 0 {
188            let name = read_string(strings, data, size, &mut offset);
189
190            if ((index - 1) as usize) < USERDATA_TYPE_LIMIT {
191                if let Some(cb) = (*(*L).global).ecb.gettypemapping {
192                    userdata_remapping[(index - 1) as usize] =
193                        cb(L, getstr(name), (*name).len as usize);
194                }
195            }
196
197            index = read(data, size, &mut offset);
198        }
199    }
200
201    // proto table
202    let proto_count = read_var_int(data, size, &mut offset);
203    protos.allocate(L, proto_count as usize);
204
205    for i in 0..proto_count {
206        let p = lua_f_newproto(L);
207        (*p).source = source;
208        (*p).bytecodeid = i as c_int;
209        (*p).funid = if (*(*L).global).lastprotoid == 0 {
210            0
211        } else {
212            let id = (*(*L).global).lastprotoid;
213            (*(*L).global).lastprotoid = (*(*L).global).lastprotoid.wrapping_add(1);
214            id
215        };
216
217        (*p).maxstacksize = read(data, size, &mut offset);
218        (*p).numparams = read(data, size, &mut offset);
219        (*p).nups = read(data, size, &mut offset);
220        (*p).is_vararg = read(data, size, &mut offset);
221
222        if version >= 4 {
223            (*p).flags = read(data, size, &mut offset);
224
225            if typesversion == 1 {
226                let typesize = read_var_int(data, size, &mut offset);
227
228                if typesize != 0 {
229                    let types = data.add(offset) as *mut u8;
230
231                    LUAU_ASSERT!(typesize == 2 + (*p).numparams as u32);
232                    LUAU_ASSERT!(*types.add(0) == LBC_TYPE_FUNCTION.0 as u8);
233                    LUAU_ASSERT!(*types.add(1) == (*p).numparams);
234
235                    // transform v1 into v2 format
236                    let headersize = if typesize > 127 { 4usize } else { 3usize };
237
238                    (*p).typeinfo =
239                        luaM_newarray!(L, headersize + typesize as usize, u8, (*p).hdr.memcat);
240                    (*p).sizetypeinfo = (headersize + typesize as usize) as c_int;
241
242                    if headersize == 4 {
243                        *(*p).typeinfo.add(0) = ((typesize & 127) | (1 << 7)) as u8;
244                        *(*p).typeinfo.add(1) = (typesize >> 7) as u8;
245                        *(*p).typeinfo.add(2) = 0;
246                        *(*p).typeinfo.add(3) = 0;
247                    } else {
248                        *(*p).typeinfo.add(0) = typesize as u8;
249                        *(*p).typeinfo.add(1) = 0;
250                        *(*p).typeinfo.add(2) = 0;
251                    }
252
253                    core::ptr::copy_nonoverlapping(
254                        types,
255                        (*p).typeinfo.add(headersize),
256                        typesize as usize,
257                    );
258                }
259
260                offset += typesize as usize;
261            } else if typesversion == 2 || typesversion == 3 {
262                let typesize = read_var_int(data, size, &mut offset);
263
264                if typesize != 0 {
265                    let types = data.add(offset) as *mut u8;
266
267                    (*p).typeinfo = luaM_newarray!(L, typesize as usize, u8, (*p).hdr.memcat);
268                    (*p).sizetypeinfo = typesize as c_int;
269                    core::ptr::copy_nonoverlapping(types, (*p).typeinfo, typesize as usize);
270                    offset += typesize as usize;
271
272                    if typesversion == 3 {
273                        remap_userdata_types(
274                            (*p).typeinfo as *mut c_char,
275                            (*p).sizetypeinfo as usize,
276                            userdata_remapping.as_mut_ptr(),
277                            USERDATA_TYPE_LIMIT as u32,
278                        );
279                    }
280                }
281            }
282        }
283
284        let sizecode = read_var_int(data, size, &mut offset) as c_int;
285        (*p).code = luaM_newarray!(L, sizecode as usize, Instruction, (*p).hdr.memcat);
286        (*p).sizecode = sizecode;
287
288        for j in 0..(*p).sizecode {
289            *(*p).code.add(j as usize) = read::<u32>(data, size, &mut offset);
290        }
291
292        (*p).codeentry = (*p).code;
293
294        let sizek = read_var_int(data, size, &mut offset) as c_int;
295        (*p).k = luaM_newarray!(L, sizek as usize, TValue, (*p).hdr.memcat);
296        (*p).sizek = sizek;
297
298        // Initialize the constants to nil to ensure they have a valid state
299        // in the event that some operation in the following loop fails with
300        // an exception.
301        for j in 0..(*p).sizek {
302            setnilvalue!((*p).k.add(j as usize));
303        }
304
305        for j in 0..(*p).sizek {
306            let k = (*p).k.add(j as usize);
307
308            match read::<u8>(data, size, &mut offset) {
309                LBC_CONSTANT_NIL_U8 => {
310                    // All constants have already been pre-initialized to nil
311                }
312
313                LBC_CONSTANT_BOOLEAN_U8 => {
314                    let v: u8 = read(data, size, &mut offset);
315                    setbvalue!(k, v);
316                }
317
318                LBC_CONSTANT_NUMBER_U8 => {
319                    let v: f64 = read(data, size, &mut offset);
320                    setnvalue!(k, v);
321                }
322
323                LBC_CONSTANT_VECTOR_U8 => {
324                    let x: f32 = read(data, size, &mut offset);
325                    let y: f32 = read(data, size, &mut offset);
326                    let z: f32 = read(data, size, &mut offset);
327                    let w: f32 = read(data, size, &mut offset);
328                    setvvalue!(k, x, y, z, w);
329                }
330
331                LBC_CONSTANT_STRING_U8 => {
332                    let v = read_string(strings, data, size, &mut offset);
333                    setsvalue!(L, k, v);
334                }
335
336                LBC_CONSTANT_IMPORT_U8 => {
337                    let iid: u32 = read(data, size, &mut offset);
338                    resolve_import_safe(L, envt, (*p).k, iid);
339                    setobj!(L, k, (*L).top.sub(1));
340                    (*L).top = (*L).top.sub(1);
341                }
342
343                LBC_CONSTANT_TABLE_U8 => {
344                    let keys = read_var_int(data, size, &mut offset) as c_int;
345                    let h = lua_h_new(L, 0, keys);
346                    for _ in 0..keys {
347                        let key = read_var_int(data, size, &mut offset) as c_int;
348                        let val = luaH_set(L, h, (*p).k.add(key as usize) as *const TValue);
349                        setnvalue!(val, 0.0);
350                    }
351                    sethvalue!(L, k, h);
352                }
353
354                LBC_CONSTANT_TABLE_WITH_CONSTANTS_U8 => {
355                    let keys = read_var_int(data, size, &mut offset);
356                    let h = lua_h_new(L, 0, keys as c_int);
357
358                    let mut nil_keys: TempBuffer<i32> = TempBuffer::temp_buffer();
359                    nil_keys.allocate(L, keys as usize);
360                    let mut nil_keys_size: usize = 0;
361
362                    for _ in 0..keys {
363                        let key = read_var_int(data, size, &mut offset) as i32;
364                        let val = luaH_set(L, h, (*p).k.add(key as usize) as *const TValue);
365                        let constant_idx: i32 = read(data, size, &mut offset);
366                        if constant_idx >= 0 {
367                            let constant = (*p).k.add(constant_idx as usize);
368                            if ttisnil!(constant) {
369                                *nil_keys.data.add(nil_keys_size) = key;
370                                nil_keys_size += 1;
371                            } else {
372                                setobj2t!(L, val, constant);
373                                luaC_barriert!(L, h, constant);
374                                continue;
375                            }
376                        }
377                        setnvalue!(val, 0.0);
378                    }
379
380                    for idx in 0..nil_keys_size {
381                        let key = *nil_keys.data.add(idx);
382                        let val = luaH_set(L, h, (*p).k.add(key as usize) as *const TValue);
383                        setnilvalue!(val);
384                    }
385
386                    sethvalue!(L, k, h);
387                }
388
389                LBC_CONSTANT_CLOSURE_U8 => {
390                    let fid = read_var_int(data, size, &mut offset);
391                    let proto = *protos.data.add(fid as usize);
392                    let cl = lua_f_new_lclosure(L, (*proto).nups as c_int, envt, proto);
393                    (*cl).preload = if (*cl).nupvalues > 0 { 1 } else { 0 };
394                    setclvalue!(L, k, cl);
395                }
396
397                LBC_CONSTANT_CLASS_SHAPE_U8 => {
398                    let cnid = read_var_int(data, size, &mut offset);
399                    let classname = (*p).k.add(cnid as usize);
400                    LUAU_ASSERT!(ttisstring!(classname));
401                    let num_properties = read_var_int(data, size, &mut offset);
402                    let num_methods = read_var_int(data, size, &mut offset);
403                    let num_members = num_methods + num_properties;
404                    let offset_to_member =
405                        luaM_newarray!(L, num_members as usize, *mut TString, (*L).activememcat);
406                    let members_to_offset = lua_h_new(L, 0, num_members as c_int);
407
408                    for idx in 0..num_members {
409                        let mid = read_var_int(data, size, &mut offset);
410                        let member_name = (*p).k.add(mid as usize);
411                        LUAU_ASSERT!(ttisstring!(member_name));
412                        *offset_to_member.add(idx as usize) = tsvalue!(member_name) as *mut TString;
413                        let val = lua_h_setstr(
414                            L,
415                            members_to_offset,
416                            tsvalue!(member_name) as *mut TString,
417                        );
418                        setnvalue!(val, idx as f64);
419                    }
420
421                    (*members_to_offset).readonly = 1;
422
423                    let lco = lua_r_newclass(
424                        L,
425                        tsvalue!(classname) as *mut TString,
426                        members_to_offset,
427                        offset_to_member,
428                        num_properties as c_int,
429                        num_methods as c_int,
430                    );
431                    setclassvalue!(L, k, lco);
432                }
433
434                LBC_CONSTANT_INTEGER_U8 => {
435                    let is_negative: u8 = read(data, size, &mut offset);
436                    let magnitude = read_var_int_64(data, size, &mut offset);
437                    let value = if is_negative != 0 {
438                        (!magnitude).wrapping_add(1) as i64
439                    } else {
440                        magnitude as i64
441                    };
442                    setlvalue!(k, value);
443                }
444
445                _ => {
446                    LUAU_ASSERT!(false);
447                }
448            }
449        }
450
451        if luaur_common::FFlag::LuauUdataDirectAccess6.get() {
452            let mut instruction = (*p).code;
453            let end = (*p).code.add((*p).sizecode as usize);
454
455            while (instruction as usize) < (end as usize) {
456                let mut target_op = -1i32;
457
458                match LuauOpcode::from(LUAU_INSN_OP(*instruction) as u8) {
459                    LuauOpcode::LOP_GETTABLEKS => {
460                        target_op = LuauOpcode::LOP_GETUDATAKS as u8 as i32;
461                    }
462
463                    LuauOpcode::LOP_SETTABLEKS => {
464                        target_op = LuauOpcode::LOP_SETUDATAKS as u8 as i32;
465                    }
466
467                    LuauOpcode::LOP_NAMECALL => {
468                        target_op = LuauOpcode::LOP_NAMECALLUDATA as u8 as i32;
469                    }
470
471                    _ => {}
472                }
473
474                if target_op != -1 {
475                    LUAU_ASSERT!(*instruction.add(1) < sizek as u32);
476
477                    // We take over the upper 16 bits of AUX - so no constants with big indices.
478                    if *instruction.add(1) < 0x10000 {
479                        let k = (*p).k.add(*instruction.add(1) as usize);
480                        let s = tsvalue!(k) as *mut TString;
481
482                        luaS_updateatom!(L, s);
483
484                        if (*s).atom >= 0 {
485                            *instruction = (*instruction & 0xffffff00) | target_op as u32;
486                        }
487                    }
488                }
489
490                instruction = instruction
491                    .add(getOpLength(LuauOpcode::from(LUAU_INSN_OP(*instruction) as u8)) as usize);
492            }
493        }
494
495        let sizep = read_var_int(data, size, &mut offset) as c_int;
496        (*p).p = luaM_newarray!(L, sizep as usize, *mut Proto, (*p).hdr.memcat);
497        (*p).sizep = sizep;
498
499        for j in 0..(*p).sizep {
500            let fid = read_var_int(data, size, &mut offset);
501            *(*p).p.add(j as usize) = *protos.data.add(fid as usize);
502        }
503
504        (*p).linedefined = read_var_int(data, size, &mut offset) as c_int;
505        (*p).debugname = read_string(strings, data, size, &mut offset);
506
507        let lineinfo: u8 = read(data, size, &mut offset);
508
509        if lineinfo != 0 {
510            (*p).linegaplog2 = read::<u8>(data, size, &mut offset) as c_int;
511
512            let intervals = (((*p).sizecode - 1) >> (*p).linegaplog2) + 1;
513            let absoffset = ((*p).sizecode + 3) & !3;
514
515            let sizelineinfo = absoffset + intervals * core::mem::size_of::<c_int>() as c_int;
516            (*p).lineinfo = luaM_newarray!(L, sizelineinfo as usize, u8, (*p).hdr.memcat);
517            (*p).sizelineinfo = sizelineinfo;
518
519            (*p).abslineinfo = (*p).lineinfo.add(absoffset as usize) as *mut c_int;
520
521            let mut lastoffset: u8 = 0;
522            for j in 0..(*p).sizecode {
523                lastoffset = lastoffset.wrapping_add(read::<u8>(data, size, &mut offset));
524                *(*p).lineinfo.add(j as usize) = lastoffset;
525            }
526
527            let mut lastline: c_int = 0;
528            for j in 0..intervals {
529                lastline = lastline.wrapping_add(read::<i32>(data, size, &mut offset));
530                *(*p).abslineinfo.add(j as usize) = lastline;
531            }
532        }
533
534        let debuginfo: u8 = read(data, size, &mut offset);
535
536        if debuginfo != 0 {
537            let sizelocvars = read_var_int(data, size, &mut offset) as c_int;
538            (*p).locvars = luaM_newarray!(L, sizelocvars as usize, LocVar, (*p).hdr.memcat);
539            (*p).sizelocvars = sizelocvars;
540
541            for j in 0..(*p).sizelocvars {
542                let locvar = (*p).locvars.add(j as usize);
543                (*locvar).varname = read_string(strings, data, size, &mut offset);
544                (*locvar).startpc = read_var_int(data, size, &mut offset) as c_int;
545                (*locvar).endpc = read_var_int(data, size, &mut offset) as c_int;
546                (*locvar).reg = read(data, size, &mut offset);
547            }
548
549            let sizeupvalues = read_var_int(data, size, &mut offset) as c_int;
550            LUAU_ASSERT!(sizeupvalues == (*p).nups as c_int);
551
552            (*p).upvalues = luaM_newarray!(L, sizeupvalues as usize, *mut TString, (*p).hdr.memcat);
553            (*p).sizeupvalues = sizeupvalues;
554
555            for j in 0..(*p).sizeupvalues {
556                *(*p).upvalues.add(j as usize) = read_string(strings, data, size, &mut offset);
557            }
558        }
559
560        if version >= 11 {
561            LUAU_ASSERT!(luaur_common::FFlag::LuauCallFeedback.get());
562            (*p).feedbackvecsize = read_var_int(data, size, &mut offset);
563
564            if (*p).feedbackvecsize > 0 {
565                (*p).feedbackvec = luaM_newarray!(
566                    L,
567                    (*p).feedbackvecsize as usize,
568                    FeedbackVectorSlot,
569                    (*p).hdr.memcat
570                );
571            }
572            for j in 0..(*p).feedbackvecsize {
573                let slottype: u8 = read(data, size, &mut offset);
574                LUAU_ASSERT!(slottype == LuauFeedbackType::LFT_CALLTARGET as u8);
575                let slot = (*p).feedbackvec.add(j as usize);
576                (*slot).kind = FeedbackVectorSlotKind::CALL_TARGET;
577                (*slot).data.call_target.pc = read_var_int(data, size, &mut offset);
578                (*slot).data.call_target.proto = 0;
579                (*slot).data.call_target.hits = 0;
580            }
581        }
582
583        *protos.data.add(i as usize) = p;
584    }
585
586    // "main" proto is pushed to Lua stack
587    let mainid = read_var_int(data, size, &mut offset);
588    let main = *protos.data.add(mainid as usize);
589
590    let thread_obj = L as *mut GCObject;
591    if isblack!(thread_obj) {
592        lua_c_barrierback(L, thread_obj, &mut (*L).gclist);
593    }
594
595    let cl = lua_f_new_lclosure(L, 0, envt, main);
596    setclvalue!(L, (*L).top, cl);
597    incr_top!(L);
598
599    0
600}
601
602unsafe fn resolve_import_safe(L: *mut lua_State, _env: *mut LuaTable, k: *mut TValue, id: u32) {
603    let mut ri = ResolveImport { k, id };
604
605    if (*(*L).gt).safeenv != 0 {
606        // luaD_pcall will make sure that if any C/Lua calls during import resolution fail, the thread state is restored back
607        let old_top = lua_gettop(L);
608        let status = luaD_pcall(
609            L,
610            Some(ResolveImport::run),
611            &mut ri as *mut ResolveImport as *mut core::ffi::c_void,
612            savestack!(L, (*L).top) as isize,
613            0,
614        );
615        LUAU_ASSERT!(old_top + 1 == lua_gettop(L)); // if an error occurred, luaD_pcall saves it on stack
616
617        if status != lua_Status::LUA_OK as c_int {
618            // replace error object with nil
619            setnilvalue!((*L).top.sub(1));
620        }
621    } else {
622        setnilvalue!((*L).top);
623        (*L).top = (*L).top.add(1);
624    }
625}
626
627unsafe fn c_strlen(s: *const c_char) -> usize {
628    let mut len = 0usize;
629    while *s.add(len) != 0 {
630        len += 1;
631    }
632    len
633}
634
635unsafe fn c_string_lossy(s: *const c_char) -> String {
636    String::from_utf8_lossy(core::slice::from_raw_parts(s as *const u8, c_strlen(s))).into_owned()
637}
638
639unsafe fn push_chunk_prefixed_slice(
640    L: *mut lua_State,
641    chunkid: *const c_char,
642    bytes: *const c_char,
643    len: usize,
644) {
645    let prefix = core::slice::from_raw_parts(chunkid as *const u8, c_strlen(chunkid));
646    let payload = core::slice::from_raw_parts(bytes as *const u8, len);
647    let mut message = Vec::with_capacity(prefix.len() + payload.len());
648    message.extend_from_slice(prefix);
649    message.extend_from_slice(payload);
650    lua_pushlstring(L, message.as_ptr() as *const c_char, message.len());
651}
652
653unsafe fn push_rust_string(L: *mut lua_State, message: &str) {
654    lua_pushlstring(L, message.as_ptr() as *const c_char, message.len());
655}