use crate::common::{compile_dumped_function_with_options, compile_function0_source};
use luau_bytecode::builder::BytecodeDumpFlags;
use luau_common::{BString, flags};
use luau_compiler::{CompileOptions, EmptyCompileHost, StaticCompileHost};
fn compile_debug_locals(source: &str, id: usize) -> BString {
let _feedback = flags::LuauEmitCallFeedback.scoped(true);
let options = CompileOptions {
debug_level: 2,
optimization_level: 1,
..CompileOptions::default()
};
let mut dump_flags = BytecodeDumpFlags::default();
dump_flags.set_code(true);
dump_flags.set_lines(true);
dump_flags.set_locals(true);
compile_dumped_function_with_options(
source,
id,
options,
&EmptyCompileHost,
dump_flags,
Some(source.as_bytes()),
)
}
fn compile_debug_types(source: &str) -> BString {
let _feedback = flags::LuauEmitCallFeedback.scoped(true);
let host = StaticCompileHost::new()
.with_vector_type_name(b"Vector3".as_slice())
.with_userdata_type(b"vec2".as_slice())
.with_userdata_type(b"color".as_slice())
.with_userdata_type(b"mat3".as_slice());
let options = CompileOptions {
debug_level: 2,
optimization_level: 1,
type_info_level: 1,
..CompileOptions::default()
};
let mut dump_flags = BytecodeDumpFlags::default();
dump_flags.set_code(true);
dump_flags.set_types(true);
compile_dumped_function_with_options(
source,
0,
options,
&host,
dump_flags,
Some(source.as_bytes()),
)
}
#[test]
fn debug_source_dump_interleaves_source_lines_and_bytecode() {
assert_eq!(
compile_function0_source(
r#"
local kSelectedBiomes = {
['Mountains'] = true,
['Canyons'] = true,
['Dunes'] = true,
['Arctic'] = true,
['Lavaflow'] = true,
['Hills'] = true,
['Plains'] = true,
['Marsh'] = true,
['Water'] = true,
}
local result = ""
for k in pairs(kSelectedBiomes) do
result = result .. k
end
return result
"#
),
br#" 2: local kSelectedBiomes = {
NEWTABLE R0 16 0
3: ['Mountains'] = true,
LOADB R1 1
SETTABLEKS R1 R0 K0 ['Mountains']
4: ['Canyons'] = true,
LOADB R1 1
SETTABLEKS R1 R0 K1 ['Canyons']
5: ['Dunes'] = true,
LOADB R1 1
SETTABLEKS R1 R0 K2 ['Dunes']
6: ['Arctic'] = true,
LOADB R1 1
SETTABLEKS R1 R0 K3 ['Arctic']
7: ['Lavaflow'] = true,
LOADB R1 1
SETTABLEKS R1 R0 K4 ['Lavaflow']
8: ['Hills'] = true,
LOADB R1 1
SETTABLEKS R1 R0 K5 ['Hills']
9: ['Plains'] = true,
LOADB R1 1
SETTABLEKS R1 R0 K6 ['Plains']
10: ['Marsh'] = true,
LOADB R1 1
SETTABLEKS R1 R0 K7 ['Marsh']
11: ['Water'] = true,
LOADB R1 1
SETTABLEKS R1 R0 K8 ['Water']
13: local result = ""
LOADK R1 K9 ['']
14: for k in pairs(kSelectedBiomes) do
GETIMPORT R2 11 [pairs]
MOVE R3 R0
CALL R2 1 3
FORGPREP_NEXT R2 L1
15: result = result .. k
L0: MOVE R7 R1
MOVE R8 R5
CONCAT R1 R7 R8
14: for k in pairs(kSelectedBiomes) do
L1: FORGLOOP R2 L0 1
17: return result
RETURN R1 1
"#
);
}
#[test]
fn debug_locals_record_registers_and_live_ranges() {
assert_eq!(
compile_debug_locals(
r#"
function foo(e, f)
local a = 1
for i=1,3 do
print(i)
end
for k,v in pairs() do
print(k, v)
end
do
local b = 2
print(b)
end
do
local c = 2
print(b)
end
local function inner()
return inner, a
end
return a
end
"#,
1,
),
b"\
local 0: reg 5, start pc 5 line 5, end pc 9 line 5
local 1: reg 6, start pc 16 line 8, end pc 21 line 8
local 2: reg 7, start pc 16 line 8, end pc 21 line 8
local 3: reg 3, start pc 25 line 12, end pc 29 line 12
local 4: reg 3, start pc 31 line 16, end pc 36 line 16
local 5: reg 0, start pc 0 line 3, end pc 40 line 21
local 6: reg 1, start pc 0 line 3, end pc 40 line 21
local 7: reg 2, start pc 1 line 4, end pc 40 line 21
local 8: reg 3, start pc 40 line 21, end pc 40 line 21
3: LOADN R2 1
4: LOADN R5 1
4: LOADN R3 3
4: LOADN R4 1
4: FORNPREP R3 L1
5: L0: GETIMPORT R6 1 [print]
5: MOVE R7 R5
5: CALLFB R6 1 0 [0]
4: FORNLOOP R3 L0
7: L1: GETIMPORT R3 3 [pairs]
7: CALLFB R3 0 3 [1]
7: FORGPREP_NEXT R3 L3
8: L2: GETIMPORT R8 1 [print]
8: MOVE R9 R6
8: MOVE R10 R7
8: CALLFB R8 2 0 [2]
7: L3: FORGLOOP R3 L2 2
11: LOADN R3 2
12: GETIMPORT R4 1 [print]
12: LOADN R5 2
12: CALLFB R4 1 0 [3]
15: LOADN R3 2
16: GETIMPORT R4 1 [print]
16: GETIMPORT R5 5 [b]
16: CALLFB R4 1 0 [4]
18: NEWCLOSURE R3 P0
18: CAPTURE VAL R3
18: CAPTURE VAL R2
21: RETURN R2 1
"
);
}
#[test]
fn debug_locals_record_repeat_until_ranges() {
assert_eq!(
compile_debug_locals(
r#"
function foo(x)
repeat
local a, b
until true
end
"#,
0,
),
b"\
local 0: reg 1, start pc 2 line 6, no live range
local 1: reg 2, start pc 2 line 6, no live range
local 2: reg 0, start pc 0 line 4, end pc 2 line 6
4: LOADNIL R1
4: LOADNIL R2
6: RETURN R0 0
"
);
}
#[test]
fn debug_locals_record_repeat_until_continue_ranges() {
assert_eq!(
compile_debug_locals(
r#"
function foo(x)
repeat
local a, b
do continue end
local c, d = 2
until true
end
"#,
0,
),
b"\
local 0: reg 3, start pc 5 line 8, no live range
local 1: reg 4, start pc 5 line 8, no live range
local 2: reg 1, start pc 2 line 5, end pc 4 line 6
local 3: reg 2, start pc 2 line 5, end pc 4 line 6
local 4: reg 0, start pc 0 line 4, end pc 5 line 8
4: LOADNIL R1
4: LOADNIL R2
5: RETURN R0 0
6: LOADN R3 2
6: LOADNIL R4
8: RETURN R0 0
"
);
}
#[test]
fn debug_type_dump_records_argument_upvalue_and_register_types() {
assert_eq!(
compile_debug_types(
r#"
local up: number = 2
function foo(e: vector, f: mat3, g: sequence)
local h = e * e
for i=1,3 do
print(i)
end
print(e * f)
print(g)
print(h)
up += a
return a
end
"#
),
"\
R0: vector [argument]
R1: mat3 [argument]
R2: userdata [argument]
U0: number
R6: number from 1 to 10
R3: vector from 0 to 34
MUL R3 R0 R0
LOADN R6 1
LOADN R4 3
LOADN R5 1
FORNPREP R4 L1
L0: GETIMPORT R7 1 [print]
MOVE R8 R6
CALLFB R7 1 0 [0]
FORNLOOP R4 L0
L1: GETIMPORT R4 1 [print]
MUL R5 R0 R1
CALLFB R4 1 0 [1]
GETIMPORT R4 1 [print]
MOVE R5 R2
CALLFB R4 1 0 [2]
GETIMPORT R4 1 [print]
MOVE R5 R3
CALLFB R4 1 0 [3]
GETUPVAL R4 0
GETIMPORT R5 3 [a]
ADD R4 R4 R5
SETUPVAL R4 0
GETIMPORT R4 3 [a]
RETURN R4 1
"
);
}