1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
//! Backtrace implementation to track script errors.
//!
//! As of the moment, doesn't support predicates.

use alloc::{
    borrow::ToOwned,
    vec::Vec,
};

use crate::{
    call::CallFrame,
    consts::*,
    interpreter::{
        InitialBalances,
        Interpreter,
    },
};
use derivative::Derivative;

use crate::interpreter::{
    Memory,
    MemoryInstance,
};
use fuel_tx::ScriptExecutionResult;
use fuel_types::{
    ContractId,
    Word,
};

#[derive(Derivative)]
#[derivative(Debug)]
/// Runtime description derived from a VM error.
pub struct Backtrace {
    call_stack: Vec<CallFrame>,
    contract: ContractId,
    registers: [Word; VM_REGISTER_COUNT],
    memory: MemoryInstance,
    result: ScriptExecutionResult,
    initial_balances: InitialBalances,
}

impl Backtrace {
    /// Create a backtrace from a vm instance and instruction result.
    ///
    /// This isn't copy-free and shouldn't be provided by default.
    pub fn from_vm_error<M, S, Tx, Ecal>(
        vm: &Interpreter<M, S, Tx, Ecal>,
        result: ScriptExecutionResult,
    ) -> Self
    where
        M: Memory,
    {
        let call_stack = vm.call_stack().to_owned();
        let contract = vm.internal_contract().unwrap_or_default();
        let memory = vm.memory().clone();
        let initial_balances = vm.initial_balances().clone();
        let mut registers = [0; VM_REGISTER_COUNT];

        registers.copy_from_slice(vm.registers());

        Self {
            call_stack,
            contract,
            registers,
            memory,
            result,
            initial_balances,
        }
    }

    /// Call stack of the VM when the error occurred.
    pub fn call_stack(&self) -> &[CallFrame] {
        self.call_stack.as_slice()
    }

    /// Last contract of the context when the error occurred.
    pub const fn contract(&self) -> &ContractId {
        &self.contract
    }

    /// Register set when the error occurred.
    pub const fn registers(&self) -> &[Word] {
        &self.registers
    }

    /// Memory of the VM when the error occurred.
    pub fn memory(&self) -> &MemoryInstance {
        &self.memory
    }

    /// [`ScriptExecutionResult`] of the error that caused this backtrace.
    pub const fn result(&self) -> &ScriptExecutionResult {
        &self.result
    }

    /// The initial balances.
    pub const fn initial_balances(&self) -> &InitialBalances {
        &self.initial_balances
    }

    /// Expose the internal attributes of the backtrace.
    pub fn into_inner(
        self,
    ) -> (
        Vec<CallFrame>,
        ContractId,
        [Word; VM_REGISTER_COUNT],
        MemoryInstance,
        ScriptExecutionResult,
        InitialBalances,
    ) {
        let Self {
            call_stack,
            contract,
            registers,
            memory,
            result,
            initial_balances,
        } = self;

        (
            call_stack,
            contract,
            registers,
            memory,
            result,
            initial_balances,
        )
    }
}