@@ -227,6 +227,12 @@ pub struct CompiledProgram {
227227 pub functions : Vec < FunctionDecl > ,
228228}
229229
230+ #[ derive( Clone , Copy , Debug , Default ) ]
231+ struct LocalDebugRange {
232+ declared_line : Option < u32 > ,
233+ last_line : Option < u32 > ,
234+ }
235+
230236impl CompiledProgram {
231237 #[ cfg( feature = "runtime" ) ]
232238 pub fn into_vm ( self ) -> Vm {
@@ -308,6 +314,7 @@ fn compile_parsed_output(
308314 . filter ( |func| !is_compiler_primitive_import ( & func. name ) )
309315 . cloned ( )
310316 . collect :: < Vec < _ > > ( ) ;
317+ let local_debug_ranges = collect_local_debug_ranges ( & stmts, & function_impls) ;
311318
312319 let mut compiler = Compiler :: new ( ) ;
313320 compiler. set_source ( source) ;
@@ -317,8 +324,9 @@ fn compile_parsed_output(
317324 compiler. add_function_debug ( func) ;
318325 }
319326 for ( name, index) in local_bindings {
327+ let range = local_debug_ranges. get ( & index) . copied ( ) . unwrap_or_default ( ) ;
320328 compiler
321- . add_local_debug ( name, index)
329+ . add_local_debug ( name, index, range . declared_line , range . last_line )
322330 . map_err ( SourceError :: Compile ) ?;
323331 }
324332 let mut program = compiler
@@ -469,8 +477,19 @@ impl Compiler {
469477 . add_function ( func. name . clone ( ) , func. args . clone ( ) ) ;
470478 }
471479
472- pub fn add_local_debug ( & mut self , name : String , index : LocalSlot ) -> Result < ( ) , CompileError > {
473- self . assembler . add_local ( name, local_slot_operand ( index) ?) ;
480+ pub fn add_local_debug (
481+ & mut self ,
482+ name : String ,
483+ index : LocalSlot ,
484+ declared_line : Option < u32 > ,
485+ last_line : Option < u32 > ,
486+ ) -> Result < ( ) , CompileError > {
487+ self . assembler . add_local_with_range (
488+ name,
489+ local_slot_operand ( index) ?,
490+ declared_line,
491+ last_line,
492+ ) ;
474493 Ok ( ( ) )
475494 }
476495
@@ -1066,6 +1085,212 @@ fn local_slot_operand(index: LocalSlot) -> Result<u8, CompileError> {
10661085 u8:: try_from ( index) . map_err ( |_| CompileError :: LocalSlotOverflow ( index) )
10671086}
10681087
1088+ fn collect_local_debug_ranges (
1089+ stmts : & [ Stmt ] ,
1090+ function_impls : & HashMap < u16 , FunctionImpl > ,
1091+ ) -> HashMap < LocalSlot , LocalDebugRange > {
1092+ let mut ranges = HashMap :: < LocalSlot , LocalDebugRange > :: new ( ) ;
1093+ for stmt in stmts {
1094+ record_stmt_local_debug_ranges ( stmt, & mut ranges) ;
1095+ }
1096+ for function_impl in function_impls. values ( ) {
1097+ for stmt in & function_impl. body_stmts {
1098+ record_stmt_local_debug_ranges ( stmt, & mut ranges) ;
1099+ }
1100+ let fallback_line = function_impl
1101+ . body_stmts
1102+ . last ( )
1103+ . map ( stmt_source_line)
1104+ . unwrap_or ( 1 ) ;
1105+ record_expr_local_debug_ranges ( & function_impl. body_expr , fallback_line, & mut ranges) ;
1106+ }
1107+ ranges
1108+ }
1109+
1110+ fn record_stmt_local_debug_ranges (
1111+ stmt : & Stmt ,
1112+ ranges : & mut HashMap < LocalSlot , LocalDebugRange > ,
1113+ ) {
1114+ match stmt {
1115+ Stmt :: Noop { .. } | Stmt :: FuncDecl { .. } | Stmt :: Break { .. } | Stmt :: Continue { .. } => { }
1116+ Stmt :: Let { index, expr, line } => {
1117+ note_local_decl ( ranges, * index, * line) ;
1118+ record_expr_local_debug_ranges ( expr, * line, ranges) ;
1119+ }
1120+ Stmt :: Assign { index, expr, line } => {
1121+ note_local_use ( ranges, * index, * line) ;
1122+ record_expr_local_debug_ranges ( expr, * line, ranges) ;
1123+ }
1124+ Stmt :: ClosureLet { line, closure } => {
1125+ for ( source_slot, captured_slot) in & closure. capture_copies {
1126+ note_local_use ( ranges, * source_slot, * line) ;
1127+ note_local_use ( ranges, * captured_slot, * line) ;
1128+ }
1129+ record_expr_local_debug_ranges ( & closure. body , * line, ranges) ;
1130+ }
1131+ Stmt :: Expr { expr, line } => {
1132+ record_expr_local_debug_ranges ( expr, * line, ranges) ;
1133+ }
1134+ Stmt :: IfElse {
1135+ condition,
1136+ then_branch,
1137+ else_branch,
1138+ line,
1139+ } => {
1140+ record_expr_local_debug_ranges ( condition, * line, ranges) ;
1141+ for nested in then_branch {
1142+ record_stmt_local_debug_ranges ( nested, ranges) ;
1143+ }
1144+ for nested in else_branch {
1145+ record_stmt_local_debug_ranges ( nested, ranges) ;
1146+ }
1147+ }
1148+ Stmt :: For {
1149+ init,
1150+ condition,
1151+ post,
1152+ body,
1153+ line,
1154+ } => {
1155+ record_stmt_local_debug_ranges ( init, ranges) ;
1156+ record_expr_local_debug_ranges ( condition, * line, ranges) ;
1157+ record_stmt_local_debug_ranges ( post, ranges) ;
1158+ for nested in body {
1159+ record_stmt_local_debug_ranges ( nested, ranges) ;
1160+ }
1161+ }
1162+ Stmt :: While {
1163+ condition,
1164+ body,
1165+ line,
1166+ } => {
1167+ record_expr_local_debug_ranges ( condition, * line, ranges) ;
1168+ for nested in body {
1169+ record_stmt_local_debug_ranges ( nested, ranges) ;
1170+ }
1171+ }
1172+ }
1173+ }
1174+
1175+ fn record_expr_local_debug_ranges (
1176+ expr : & Expr ,
1177+ line : u32 ,
1178+ ranges : & mut HashMap < LocalSlot , LocalDebugRange > ,
1179+ ) {
1180+ match expr {
1181+ Expr :: Null
1182+ | Expr :: Int ( _)
1183+ | Expr :: Float ( _)
1184+ | Expr :: Bool ( _)
1185+ | Expr :: String ( _)
1186+ | Expr :: FunctionRef ( _) => { }
1187+ Expr :: Var ( index) => {
1188+ note_local_use ( ranges, * index, line) ;
1189+ }
1190+ Expr :: Call ( _, args) => {
1191+ for arg in args {
1192+ record_expr_local_debug_ranges ( arg, line, ranges) ;
1193+ }
1194+ }
1195+ Expr :: LocalCall ( index, args) => {
1196+ note_local_use ( ranges, * index, line) ;
1197+ for arg in args {
1198+ record_expr_local_debug_ranges ( arg, line, ranges) ;
1199+ }
1200+ }
1201+ Expr :: Closure ( closure) => {
1202+ for ( source_slot, captured_slot) in & closure. capture_copies {
1203+ note_local_use ( ranges, * source_slot, line) ;
1204+ note_local_use ( ranges, * captured_slot, line) ;
1205+ }
1206+ record_expr_local_debug_ranges ( & closure. body , line, ranges) ;
1207+ }
1208+ Expr :: ClosureCall ( closure, args) => {
1209+ for arg in args {
1210+ record_expr_local_debug_ranges ( arg, line, ranges) ;
1211+ }
1212+ for ( source_slot, captured_slot) in & closure. capture_copies {
1213+ note_local_use ( ranges, * source_slot, line) ;
1214+ note_local_use ( ranges, * captured_slot, line) ;
1215+ }
1216+ record_expr_local_debug_ranges ( & closure. body , line, ranges) ;
1217+ }
1218+ Expr :: Add ( lhs, rhs)
1219+ | Expr :: Sub ( lhs, rhs)
1220+ | Expr :: Mul ( lhs, rhs)
1221+ | Expr :: Div ( lhs, rhs)
1222+ | Expr :: Mod ( lhs, rhs)
1223+ | Expr :: And ( lhs, rhs)
1224+ | Expr :: Or ( lhs, rhs)
1225+ | Expr :: Eq ( lhs, rhs)
1226+ | Expr :: Lt ( lhs, rhs)
1227+ | Expr :: Gt ( lhs, rhs) => {
1228+ record_expr_local_debug_ranges ( lhs, line, ranges) ;
1229+ record_expr_local_debug_ranges ( rhs, line, ranges) ;
1230+ }
1231+ Expr :: Neg ( inner) | Expr :: Not ( inner) => {
1232+ record_expr_local_debug_ranges ( inner, line, ranges) ;
1233+ }
1234+ Expr :: IfElse {
1235+ condition,
1236+ then_expr,
1237+ else_expr,
1238+ } => {
1239+ record_expr_local_debug_ranges ( condition, line, ranges) ;
1240+ record_expr_local_debug_ranges ( then_expr, line, ranges) ;
1241+ record_expr_local_debug_ranges ( else_expr, line, ranges) ;
1242+ }
1243+ Expr :: Match {
1244+ value_slot,
1245+ result_slot,
1246+ value,
1247+ arms,
1248+ default,
1249+ } => {
1250+ note_local_use ( ranges, * value_slot, line) ;
1251+ note_local_use ( ranges, * result_slot, line) ;
1252+ record_expr_local_debug_ranges ( value, line, ranges) ;
1253+ for ( _, arm_expr) in arms {
1254+ record_expr_local_debug_ranges ( arm_expr, line, ranges) ;
1255+ }
1256+ record_expr_local_debug_ranges ( default, line, ranges) ;
1257+ }
1258+ Expr :: Block { stmts, expr } => {
1259+ for stmt in stmts {
1260+ record_stmt_local_debug_ranges ( stmt, ranges) ;
1261+ }
1262+ record_expr_local_debug_ranges ( expr, line, ranges) ;
1263+ }
1264+ }
1265+ }
1266+
1267+ fn note_local_decl ( ranges : & mut HashMap < LocalSlot , LocalDebugRange > , slot : LocalSlot , line : u32 ) {
1268+ let entry = ranges. entry ( slot) . or_default ( ) ;
1269+ entry. declared_line = Some ( entry. declared_line . map_or ( line, |current| current. min ( line) ) ) ;
1270+ entry. last_line = Some ( entry. last_line . map_or ( line, |current| current. max ( line) ) ) ;
1271+ }
1272+
1273+ fn note_local_use ( ranges : & mut HashMap < LocalSlot , LocalDebugRange > , slot : LocalSlot , line : u32 ) {
1274+ let entry = ranges. entry ( slot) . or_default ( ) ;
1275+ entry. last_line = Some ( entry. last_line . map_or ( line, |current| current. max ( line) ) ) ;
1276+ }
1277+
1278+ fn stmt_source_line ( stmt : & Stmt ) -> u32 {
1279+ match stmt {
1280+ Stmt :: Noop { line }
1281+ | Stmt :: Let { line, .. }
1282+ | Stmt :: Assign { line, .. }
1283+ | Stmt :: ClosureLet { line, .. }
1284+ | Stmt :: FuncDecl { line, .. }
1285+ | Stmt :: Expr { line, .. }
1286+ | Stmt :: IfElse { line, .. }
1287+ | Stmt :: For { line, .. }
1288+ | Stmt :: While { line, .. }
1289+ | Stmt :: Break { line }
1290+ | Stmt :: Continue { line } => * line,
1291+ }
1292+ }
1293+
10691294fn shift_amount_for_power_of_two ( value : i64 ) -> Option < u32 > {
10701295 if value <= 0 {
10711296 return None ;
0 commit comments