1use crate::bytecode::value::NativeFn;
2use crate::bytecode::Instruction;
3use crate::bytecode::{Register, Value};
4use crate::LustError;
5use std::fmt;
6use std::rc::Rc;
7
8#[derive(Clone)]
9pub struct TracedNativeFn {
10 function: NativeFn,
11}
12
13impl TracedNativeFn {
14 pub fn new(function: NativeFn) -> Self {
15 Self { function }
16 }
17
18 pub fn pointer(&self) -> *const () {
19 Rc::as_ptr(&self.function) as *const ()
20 }
21}
22
23impl fmt::Debug for TracedNativeFn {
24 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
25 write!(f, "NativeFn({:p})", Rc::as_ptr(&self.function))
26 }
27}
28
29#[derive(Debug, Clone)]
30pub struct Trace {
31 pub function_idx: usize,
32 pub start_ip: usize,
33 pub ops: Vec<TraceOp>,
34 pub inputs: Vec<Register>,
35 pub outputs: Vec<Register>,
36}
37
38#[derive(Debug, Clone)]
39pub enum TraceOp {
40 LoadConst {
41 dest: Register,
42 value: Value,
43 },
44 Move {
45 dest: Register,
46 src: Register,
47 },
48 Add {
49 dest: Register,
50 lhs: Register,
51 rhs: Register,
52 lhs_type: ValueType,
53 rhs_type: ValueType,
54 },
55 Sub {
56 dest: Register,
57 lhs: Register,
58 rhs: Register,
59 lhs_type: ValueType,
60 rhs_type: ValueType,
61 },
62 Mul {
63 dest: Register,
64 lhs: Register,
65 rhs: Register,
66 lhs_type: ValueType,
67 rhs_type: ValueType,
68 },
69 Div {
70 dest: Register,
71 lhs: Register,
72 rhs: Register,
73 lhs_type: ValueType,
74 rhs_type: ValueType,
75 },
76 Mod {
77 dest: Register,
78 lhs: Register,
79 rhs: Register,
80 lhs_type: ValueType,
81 rhs_type: ValueType,
82 },
83 Neg {
84 dest: Register,
85 src: Register,
86 },
87 Eq {
88 dest: Register,
89 lhs: Register,
90 rhs: Register,
91 },
92 Ne {
93 dest: Register,
94 lhs: Register,
95 rhs: Register,
96 },
97 Lt {
98 dest: Register,
99 lhs: Register,
100 rhs: Register,
101 },
102 Le {
103 dest: Register,
104 lhs: Register,
105 rhs: Register,
106 },
107 Gt {
108 dest: Register,
109 lhs: Register,
110 rhs: Register,
111 },
112 Ge {
113 dest: Register,
114 lhs: Register,
115 rhs: Register,
116 },
117 And {
118 dest: Register,
119 lhs: Register,
120 rhs: Register,
121 },
122 Or {
123 dest: Register,
124 lhs: Register,
125 rhs: Register,
126 },
127 Not {
128 dest: Register,
129 src: Register,
130 },
131 Concat {
132 dest: Register,
133 lhs: Register,
134 rhs: Register,
135 },
136 GetIndex {
137 dest: Register,
138 array: Register,
139 index: Register,
140 },
141 ArrayLen {
142 dest: Register,
143 array: Register,
144 },
145 GuardNativeFunction {
146 register: Register,
147 function: TracedNativeFn,
148 },
149 CallNative {
150 dest: Register,
151 callee: Register,
152 function: TracedNativeFn,
153 first_arg: Register,
154 arg_count: u8,
155 },
156 CallMethod {
157 dest: Register,
158 object: Register,
159 method_name: String,
160 first_arg: Register,
161 arg_count: u8,
162 },
163 GetField {
164 dest: Register,
165 object: Register,
166 field_name: String,
167 field_index: Option<usize>,
168 value_type: Option<ValueType>,
169 is_weak: bool,
170 },
171 SetField {
172 object: Register,
173 field_name: String,
174 value: Register,
175 field_index: Option<usize>,
176 value_type: Option<ValueType>,
177 is_weak: bool,
178 },
179 NewStruct {
180 dest: Register,
181 struct_name: String,
182 field_names: Vec<String>,
183 field_registers: Vec<Register>,
184 },
185 Guard {
186 register: Register,
187 expected_type: ValueType,
188 },
189 GuardLoopContinue {
190 condition_register: Register,
191 bailout_ip: usize,
192 },
193 NestedLoopCall {
194 function_idx: usize,
195 loop_start_ip: usize,
196 bailout_ip: usize,
197 },
198 Return {
199 value: Option<Register>,
200 },
201}
202
203#[derive(Debug, Clone, Copy, PartialEq, Eq)]
204pub enum ValueType {
205 Int,
206 Float,
207 Bool,
208 String,
209 Array,
210 Tuple,
211 Struct,
212}
213
214pub struct TraceRecorder {
215 pub trace: Trace,
216 max_length: usize,
217 recording: bool,
218 guarded_registers: std::collections::HashSet<Register>,
219}
220
221impl TraceRecorder {
222 pub fn new(function_idx: usize, start_ip: usize, max_length: usize) -> Self {
223 Self {
224 trace: Trace {
225 function_idx,
226 start_ip,
227 ops: Vec::new(),
228 inputs: Vec::new(),
229 outputs: Vec::new(),
230 },
231 max_length,
232 recording: true,
233 guarded_registers: std::collections::HashSet::new(),
234 }
235 }
236
237 pub fn record_instruction(
238 &mut self,
239 instruction: Instruction,
240 current_ip: usize,
241 registers: &[Value; 256],
242 function: &crate::bytecode::Function,
243 function_idx: usize,
244 ) -> Result<(), LustError> {
245 if !self.recording {
246 return Ok(());
247 }
248
249 if function_idx != self.trace.function_idx {
250 return Ok(());
251 }
252
253 let trace_op = match instruction {
254 Instruction::LoadConst(dest, _) => {
255 if let Some(_ty) = Self::get_value_type(®isters[dest as usize]) {
256 self.guarded_registers.insert(dest);
257 }
258
259 TraceOp::LoadConst {
260 dest,
261 value: registers[dest as usize].clone(),
262 }
263 }
264
265 Instruction::LoadGlobal(dest, _) => {
266 if let Some(_ty) = Self::get_value_type(®isters[dest as usize]) {
267 self.guarded_registers.insert(dest);
268 }
269
270 TraceOp::LoadConst {
271 dest,
272 value: registers[dest as usize].clone(),
273 }
274 }
275
276 Instruction::StoreGlobal(_, _) => {
277 return Ok(());
278 }
279
280 Instruction::Move(dest, src) => TraceOp::Move { dest, src },
281 Instruction::Add(dest, lhs, rhs) => {
282 self.add_type_guards(lhs, rhs, registers, function)?;
283 let lhs_type =
284 Self::get_value_type(®isters[lhs as usize]).unwrap_or(ValueType::Int);
285 let rhs_type =
286 Self::get_value_type(®isters[rhs as usize]).unwrap_or(ValueType::Int);
287 TraceOp::Add {
288 dest,
289 lhs,
290 rhs,
291 lhs_type,
292 rhs_type,
293 }
294 }
295
296 Instruction::Sub(dest, lhs, rhs) => {
297 self.add_type_guards(lhs, rhs, registers, function)?;
298 let lhs_type =
299 Self::get_value_type(®isters[lhs as usize]).unwrap_or(ValueType::Int);
300 let rhs_type =
301 Self::get_value_type(®isters[rhs as usize]).unwrap_or(ValueType::Int);
302 TraceOp::Sub {
303 dest,
304 lhs,
305 rhs,
306 lhs_type,
307 rhs_type,
308 }
309 }
310
311 Instruction::Mul(dest, lhs, rhs) => {
312 self.add_type_guards(lhs, rhs, registers, function)?;
313 let lhs_type =
314 Self::get_value_type(®isters[lhs as usize]).unwrap_or(ValueType::Int);
315 let rhs_type =
316 Self::get_value_type(®isters[rhs as usize]).unwrap_or(ValueType::Int);
317 TraceOp::Mul {
318 dest,
319 lhs,
320 rhs,
321 lhs_type,
322 rhs_type,
323 }
324 }
325
326 Instruction::Div(dest, lhs, rhs) => {
327 self.add_type_guards(lhs, rhs, registers, function)?;
328 let lhs_type =
329 Self::get_value_type(®isters[lhs as usize]).unwrap_or(ValueType::Int);
330 let rhs_type =
331 Self::get_value_type(®isters[rhs as usize]).unwrap_or(ValueType::Int);
332 TraceOp::Div {
333 dest,
334 lhs,
335 rhs,
336 lhs_type,
337 rhs_type,
338 }
339 }
340
341 Instruction::Mod(dest, lhs, rhs) => {
342 self.add_type_guards(lhs, rhs, registers, function)?;
343 let lhs_type =
344 Self::get_value_type(®isters[lhs as usize]).unwrap_or(ValueType::Int);
345 let rhs_type =
346 Self::get_value_type(®isters[rhs as usize]).unwrap_or(ValueType::Int);
347 TraceOp::Mod {
348 dest,
349 lhs,
350 rhs,
351 lhs_type,
352 rhs_type,
353 }
354 }
355
356 Instruction::Neg(dest, src) => TraceOp::Neg { dest, src },
357 Instruction::Eq(dest, lhs, rhs) => TraceOp::Eq { dest, lhs, rhs },
358 Instruction::Ne(dest, lhs, rhs) => TraceOp::Ne { dest, lhs, rhs },
359 Instruction::Lt(dest, lhs, rhs) => TraceOp::Lt { dest, lhs, rhs },
360 Instruction::Le(dest, lhs, rhs) => TraceOp::Le { dest, lhs, rhs },
361 Instruction::Gt(dest, lhs, rhs) => TraceOp::Gt { dest, lhs, rhs },
362 Instruction::Ge(dest, lhs, rhs) => TraceOp::Ge { dest, lhs, rhs },
363 Instruction::And(dest, lhs, rhs) => TraceOp::And { dest, lhs, rhs },
364 Instruction::Or(dest, lhs, rhs) => TraceOp::Or { dest, lhs, rhs },
365 Instruction::Not(dest, src) => TraceOp::Not { dest, src },
366 Instruction::Concat(dest, lhs, rhs) => {
367 if let Some(ty) = Self::get_value_type(®isters[lhs as usize]) {
368 if !self.guarded_registers.contains(&lhs) {
369 self.trace.ops.push(TraceOp::Guard {
370 register: lhs,
371 expected_type: ty,
372 });
373 self.guarded_registers.insert(lhs);
374 }
375 }
376
377 if let Some(ty) = Self::get_value_type(®isters[rhs as usize]) {
378 if !self.guarded_registers.contains(&rhs) {
379 self.trace.ops.push(TraceOp::Guard {
380 register: rhs,
381 expected_type: ty,
382 });
383 self.guarded_registers.insert(rhs);
384 }
385 }
386
387 TraceOp::Concat { dest, lhs, rhs }
388 }
389
390 Instruction::GetIndex(dest, array, index) => {
391 if let Some(ty) = Self::get_value_type(®isters[array as usize]) {
392 if !self.guarded_registers.contains(&array) {
393 self.trace.ops.push(TraceOp::Guard {
394 register: array,
395 expected_type: ty,
396 });
397 self.guarded_registers.insert(array);
398 }
399 }
400
401 if let Some(ty) = Self::get_value_type(®isters[index as usize]) {
402 if !self.guarded_registers.contains(&index) {
403 self.trace.ops.push(TraceOp::Guard {
404 register: index,
405 expected_type: ty,
406 });
407 self.guarded_registers.insert(index);
408 }
409 }
410
411 TraceOp::GetIndex { dest, array, index }
412 }
413
414 Instruction::ArrayLen(dest, array) => {
415 if let Some(ty) = Self::get_value_type(®isters[array as usize]) {
416 if !self.guarded_registers.contains(&array) {
417 self.trace.ops.push(TraceOp::Guard {
418 register: array,
419 expected_type: ty,
420 });
421 self.guarded_registers.insert(array);
422 }
423 }
424
425 TraceOp::ArrayLen { dest, array }
426 }
427
428 Instruction::CallMethod(obj_reg, method_name_idx, first_arg, arg_count, dest_reg) => {
429 let method_name = function.chunk.constants[method_name_idx as usize]
430 .as_string()
431 .unwrap_or("unknown")
432 .to_string();
433 if let Some(ty) = Self::get_value_type(®isters[obj_reg as usize]) {
434 if !self.guarded_registers.contains(&obj_reg) {
435 self.trace.ops.push(TraceOp::Guard {
436 register: obj_reg,
437 expected_type: ty,
438 });
439 self.guarded_registers.insert(obj_reg);
440 }
441 }
442
443 for i in 0..arg_count {
444 let arg_reg = first_arg + i;
445 if let Some(ty) = Self::get_value_type(®isters[arg_reg as usize]) {
446 if !self.guarded_registers.contains(&arg_reg) {
447 self.trace.ops.push(TraceOp::Guard {
448 register: arg_reg,
449 expected_type: ty,
450 });
451 self.guarded_registers.insert(arg_reg);
452 }
453 }
454 }
455
456 TraceOp::CallMethod {
457 dest: dest_reg,
458 object: obj_reg,
459 method_name,
460 first_arg,
461 arg_count,
462 }
463 }
464
465 Instruction::GetField(dest, obj_reg, field_name_idx) => {
466 let field_name = function.chunk.constants[field_name_idx as usize]
467 .as_string()
468 .unwrap_or("unknown")
469 .to_string();
470 let (field_index, is_weak_field) = match ®isters[obj_reg as usize] {
471 Value::Struct { layout, .. } => {
472 let idx = layout.index_of_str(&field_name);
473 let is_weak = idx.map(|i| layout.is_weak(i)).unwrap_or(false);
474 (idx, is_weak)
475 }
476
477 _ => (None, false),
478 };
479 if let Some(ty) = Self::get_value_type(®isters[obj_reg as usize]) {
480 if !self.guarded_registers.contains(&obj_reg) {
481 self.trace.ops.push(TraceOp::Guard {
482 register: obj_reg,
483 expected_type: ty,
484 });
485 self.guarded_registers.insert(obj_reg);
486 }
487 }
488
489 let value_type = Self::get_value_type(®isters[dest as usize]);
490 TraceOp::GetField {
491 dest,
492 object: obj_reg,
493 field_name,
494 field_index,
495 value_type,
496 is_weak: is_weak_field,
497 }
498 }
499
500 Instruction::SetField(obj_reg, field_name_idx, value_reg) => {
501 let field_name = function.chunk.constants[field_name_idx as usize]
502 .as_string()
503 .unwrap_or("unknown")
504 .to_string();
505 let (field_index, is_weak_field) = match ®isters[obj_reg as usize] {
506 Value::Struct { layout, .. } => {
507 let idx = layout.index_of_str(&field_name);
508 let is_weak = idx.map(|i| layout.is_weak(i)).unwrap_or(false);
509 (idx, is_weak)
510 }
511
512 _ => (None, false),
513 };
514 if let Some(ty) = Self::get_value_type(®isters[obj_reg as usize]) {
515 if !self.guarded_registers.contains(&obj_reg) {
516 self.trace.ops.push(TraceOp::Guard {
517 register: obj_reg,
518 expected_type: ty,
519 });
520 self.guarded_registers.insert(obj_reg);
521 }
522 }
523
524 let value_type = Self::get_value_type(®isters[value_reg as usize]);
525 if let Some(ty) = value_type {
526 if !self.guarded_registers.contains(&value_reg) {
527 self.trace.ops.push(TraceOp::Guard {
528 register: value_reg,
529 expected_type: ty,
530 });
531 self.guarded_registers.insert(value_reg);
532 }
533 }
534
535 TraceOp::SetField {
536 object: obj_reg,
537 field_name,
538 value: value_reg,
539 field_index,
540 value_type,
541 is_weak: is_weak_field,
542 }
543 }
544
545 Instruction::NewStruct(
546 dest,
547 struct_name_idx,
548 first_field_name_idx,
549 first_field_reg,
550 field_count,
551 ) => {
552 let struct_name = function.chunk.constants[struct_name_idx as usize]
553 .as_string()
554 .unwrap_or("unknown")
555 .to_string();
556 let mut field_names = Vec::new();
557 for i in 0..field_count {
558 let field_name_idx = first_field_name_idx + (i as u16);
559 let field_name = function.chunk.constants[field_name_idx as usize]
560 .as_string()
561 .unwrap_or("unknown")
562 .to_string();
563 field_names.push(field_name);
564 }
565
566 let mut field_registers = Vec::new();
567 for i in 0..field_count {
568 let field_reg = first_field_reg + i;
569 field_registers.push(field_reg);
570 if let Some(ty) = Self::get_value_type(®isters[field_reg as usize]) {
571 if !self.guarded_registers.contains(&field_reg) {
572 self.trace.ops.push(TraceOp::Guard {
573 register: field_reg,
574 expected_type: ty,
575 });
576 self.guarded_registers.insert(field_reg);
577 }
578 }
579 }
580
581 TraceOp::NewStruct {
582 dest,
583 struct_name,
584 field_names,
585 field_registers,
586 }
587 }
588
589 Instruction::Call(func_reg, first_arg, arg_count, dest_reg) => {
590 match ®isters[func_reg as usize] {
591 Value::NativeFunction(native_fn) => {
592 let traced = TracedNativeFn::new(native_fn.clone());
593 if !self.guarded_registers.contains(&func_reg) {
594 self.trace.ops.push(TraceOp::GuardNativeFunction {
595 register: func_reg,
596 function: traced.clone(),
597 });
598 self.guarded_registers.insert(func_reg);
599 }
600
601 self.trace.ops.push(TraceOp::CallNative {
602 dest: dest_reg,
603 callee: func_reg,
604 function: traced,
605 first_arg,
606 arg_count,
607 });
608 return Ok(());
609 }
610
611 _ => {
612 self.recording = false;
613 return Err(LustError::RuntimeError {
614 message: "Trace aborted: unsupported operation".to_string(),
615 });
616 }
617 }
618 }
619
620 Instruction::NewArray(_, _, _)
621 | Instruction::NewMap(_)
622 | Instruction::SetIndex(_, _, _) => {
623 self.recording = false;
624 return Err(LustError::RuntimeError {
625 message: "Trace aborted: unsupported operation".to_string(),
626 });
627 }
628
629 Instruction::Return(value_reg) => {
630 if function_idx == self.trace.function_idx {
631 self.recording = false;
632 return Ok(());
633 } else {
634 TraceOp::Return {
635 value: if value_reg == 255 {
636 None
637 } else {
638 Some(value_reg)
639 },
640 }
641 }
642 }
643
644 Instruction::Jump(offset) => {
645 if offset < 0 {
646 let target_calc = (current_ip as isize) + (offset as isize);
647 if target_calc < 0 {
648 self.recording = false;
649 return Err(LustError::RuntimeError {
650 message: format!(
651 "Invalid jump target: offset={}, current_ip={}, target={}",
652 offset, current_ip, target_calc
653 ),
654 });
655 }
656
657 let jump_target = target_calc as usize;
658 if function_idx == self.trace.function_idx && jump_target == self.trace.start_ip
659 {
660 self.recording = false;
661 return Ok(());
662 } else {
663 let bailout_ip = current_ip.saturating_sub(1);
664 TraceOp::NestedLoopCall {
665 function_idx,
666 loop_start_ip: jump_target,
667 bailout_ip,
668 }
669 }
670 } else {
671 return Ok(());
672 }
673 }
674
675 Instruction::JumpIf(cond, _) | Instruction::JumpIfNot(cond, _) => {
676 return Ok(());
677 }
678
679 _ => {
680 self.recording = false;
681 return Err(LustError::RuntimeError {
682 message: "Trace aborted: unsupported instruction".to_string(),
683 });
684 }
685 };
686 self.trace.ops.push(trace_op);
687 if self.trace.ops.len() >= self.max_length {
688 self.recording = false;
689 return Err(LustError::RuntimeError {
690 message: "Trace too long".to_string(),
691 });
692 }
693
694 Ok(())
695 }
696
697 fn add_type_guards(
698 &mut self,
699 lhs: Register,
700 rhs: Register,
701 registers: &[Value; 256],
702 function: &crate::bytecode::Function,
703 ) -> Result<(), LustError> {
704 if let Some(ty) = Self::get_value_type(®isters[lhs as usize]) {
705 let needs_guard = if self.guarded_registers.contains(&lhs) {
706 false
707 } else if let Some(static_type) = function.register_types.get(&lhs) {
708 !Self::type_kind_matches_value_type(static_type, ty)
709 } else {
710 true
711 };
712 if needs_guard {
713 self.trace.ops.push(TraceOp::Guard {
714 register: lhs,
715 expected_type: ty,
716 });
717 self.guarded_registers.insert(lhs);
718 } else {
719 self.guarded_registers.insert(lhs);
720 }
721 }
722
723 if let Some(ty) = Self::get_value_type(®isters[rhs as usize]) {
724 let needs_guard = if self.guarded_registers.contains(&rhs) {
725 false
726 } else if let Some(static_type) = function.register_types.get(&rhs) {
727 !Self::type_kind_matches_value_type(static_type, ty)
728 } else {
729 true
730 };
731 if needs_guard {
732 self.trace.ops.push(TraceOp::Guard {
733 register: rhs,
734 expected_type: ty,
735 });
736 self.guarded_registers.insert(rhs);
737 } else {
738 self.guarded_registers.insert(rhs);
739 }
740 }
741
742 Ok(())
743 }
744
745 fn type_kind_matches_value_type(
746 type_kind: &crate::ast::TypeKind,
747 value_type: ValueType,
748 ) -> bool {
749 use crate::ast::TypeKind;
750 match (type_kind, value_type) {
751 (TypeKind::Int, ValueType::Int) => true,
752 (TypeKind::Float, ValueType::Float) => true,
753 (TypeKind::Bool, ValueType::Bool) => true,
754 (TypeKind::String, ValueType::String) => true,
755 (TypeKind::Array(_), ValueType::Array) => true,
756 (TypeKind::Tuple(_), ValueType::Tuple) => true,
757 _ => false,
758 }
759 }
760
761 fn get_value_type(value: &Value) -> Option<ValueType> {
762 match value {
763 Value::Int(_) => Some(ValueType::Int),
764 Value::Float(_) => Some(ValueType::Float),
765 Value::Bool(_) => Some(ValueType::Bool),
766 Value::String(_) => Some(ValueType::String),
767 Value::Array(_) => Some(ValueType::Array),
768 Value::Tuple(_) => Some(ValueType::Tuple),
769 Value::Struct { .. } => Some(ValueType::Struct),
770 _ => None,
771 }
772 }
773
774 pub fn finish(self) -> Trace {
775 self.trace
776 }
777
778 pub fn is_recording(&self) -> bool {
779 self.recording
780 }
781
782 pub fn abort(&mut self) {
783 self.recording = false;
784 }
785}