Skip to main content

tvm_ffi/
error.rs

1/*
2 * Licensed to the Apache Software Foundation (ASF) under one
3 * or more contributor license agreements.  See the NOTICE file
4 * distributed with this work for additional information
5 * regarding copyright ownership.  The ASF licenses this file
6 * to you under the Apache License, Version 2.0 (the
7 * "License"); you may not use this file except in compliance
8 * with the License.  You may obtain a copy of the License at
9 *
10 *   http://www.apache.org/licenses/LICENSE-2.0
11 *
12 * Unless required by applicable law or agreed to in writing,
13 * software distributed under the License is distributed on an
14 * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
15 * KIND, either express or implied.  See the License for the
16 * specific language governing permissions and limitations
17 * under the License.
18 */
19use crate::derive::{Object, ObjectRef};
20use crate::object::{self, Object, ObjectArc, ObjectRefCore};
21use std::ffi::c_void;
22use tvm_ffi_sys::TVMFFIBacktraceUpdateMode::kTVMFFIBacktraceUpdateModeAppend;
23use tvm_ffi_sys::{
24    TVMFFIByteArray, TVMFFIErrorCell, TVMFFIErrorCreateWithCauseAndExtraContext,
25    TVMFFIErrorMoveFromRaised, TVMFFIErrorSetRaised, TVMFFIObjectHandle, TVMFFITypeIndex,
26};
27
28/// Error kind, wraps in a struct to be explicit
29#[derive(Debug, Clone, PartialEq, Eq)]
30pub struct ErrorKind<'a>(&'a str);
31
32impl<'a> ErrorKind<'a> {
33    pub fn as_str(&self) -> &str {
34        self.0
35    }
36}
37
38impl<'a> std::fmt::Display for ErrorKind<'a> {
39    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
40        write!(f, "{}", self.0)
41    }
42}
43
44pub const VALUE_ERROR: ErrorKind = ErrorKind("ValueError");
45pub const TYPE_ERROR: ErrorKind = ErrorKind("TypeError");
46pub const RUNTIME_ERROR: ErrorKind = ErrorKind("RuntimeError");
47pub const ATTRIBUTE_ERROR: ErrorKind = ErrorKind("AttributeError");
48pub const KEY_ERROR: ErrorKind = ErrorKind("KeyError");
49pub const INDEX_ERROR: ErrorKind = ErrorKind("IndexError");
50pub const INTERNAL_ERROR: ErrorKind = ErrorKind("InternalError");
51
52/// error object
53#[repr(C)]
54#[derive(Object)]
55#[type_key = "ffi.Error"]
56#[type_index(TVMFFITypeIndex::kTVMFFIError)]
57pub struct ErrorObj {
58    object: Object,
59    cell: TVMFFIErrorCell,
60}
61
62/// Error reference class
63#[derive(Clone, ObjectRef)]
64pub struct Error {
65    data: ObjectArc<ErrorObj>,
66}
67
68/// Default result that uses Error as the error type
69pub type Result<T, E = Error> = std::result::Result<T, E>;
70
71impl Error {
72    pub fn new(kind: ErrorKind<'_>, message: &str, traceback: &str) -> Self {
73        Self::new_with_cause_and_extra_context(kind, message, traceback, None, None)
74    }
75
76    /// Create an error retaining its cause and any application-specific context.
77    pub fn new_with_cause_and_extra_context(
78        kind: ErrorKind<'_>,
79        message: &str,
80        traceback: &str,
81        cause_chain: Option<&Error>,
82        extra_context: Option<&object::ObjectRef>,
83    ) -> Self {
84        unsafe {
85            let kind_data = TVMFFIByteArray::from_str(kind.as_str());
86            let message_data = TVMFFIByteArray::from_str(message);
87            let traceback_data = TVMFFIByteArray::from_str(traceback);
88            let mut error_handle: TVMFFIObjectHandle = std::ptr::null_mut();
89            let ret = TVMFFIErrorCreateWithCauseAndExtraContext(
90                &kind_data,
91                &message_data,
92                &traceback_data,
93                cause_chain.map_or(std::ptr::null_mut(), |cause| {
94                    ObjectArc::as_raw(&cause.data) as TVMFFIObjectHandle
95                }),
96                extra_context.map_or(std::ptr::null_mut(), |context| {
97                    ObjectArc::as_raw(object::ObjectRef::data(context)) as TVMFFIObjectHandle
98                }),
99                &mut error_handle,
100            );
101            assert_eq!(ret, 0, "Failed to create error object");
102            let error_obj = ObjectArc::from_raw(error_handle as *const ErrorObj);
103            Self { data: error_obj }
104        }
105    }
106
107    /// Create a new error by moving from raised error
108    ///
109    /// # Returns
110    /// The error from the raised error
111    pub fn from_raised() -> Self {
112        unsafe {
113            let mut error_handle: TVMFFIObjectHandle = std::ptr::null_mut();
114            TVMFFIErrorMoveFromRaised(&mut error_handle as *mut TVMFFIObjectHandle);
115            assert!(
116                !error_handle.is_null(),
117                "Calling Error::from_raised but no error was raised"
118            );
119            let error_obj = ObjectArc::from_raw(error_handle as *const ErrorObj);
120            Self { data: error_obj }
121        }
122    }
123
124    /// Set the error as raised
125    ///
126    /// # Arguments
127    /// * `error` - The error to set as raised
128    pub fn set_raised(error: &Self) {
129        unsafe {
130            TVMFFIErrorSetRaised(ObjectArc::as_raw(&error.data) as TVMFFIObjectHandle);
131        }
132    }
133
134    /// Get the kind of the error
135    ///
136    /// # Returns
137    /// The kind of the error
138    pub fn kind(&self) -> ErrorKind<'_> {
139        ErrorKind(&self.data.cell.kind.as_str())
140    }
141
142    /// Get the message of the error
143    ///
144    /// # Returns
145    /// The message of the error
146    pub fn message(&self) -> &str {
147        self.data.cell.message.as_str()
148    }
149
150    /// Get the backtrace of the error
151    ///
152    /// # Returns
153    /// The backtrace of the error
154    pub fn backtrace(&self) -> &str {
155        self.data.cell.backtrace.as_str()
156    }
157
158    /// Return the cause, if one was attached to this error.
159    pub fn cause_chain(&self) -> Option<Error> {
160        let handle = self.data.cell.cause_chain;
161        if handle.is_null() {
162            return None;
163        }
164        // The cell owns the handle; acquire a reference for the returned wrapper.
165        unsafe {
166            object::unsafe_::inc_ref(handle.cast());
167            Some(Self {
168                data: ObjectArc::from_raw(handle.cast()),
169            })
170        }
171    }
172
173    /// Return the application-specific context, if present.
174    pub fn extra_context(&self) -> Option<object::ObjectRef> {
175        let handle = self.data.cell.extra_context;
176        if handle.is_null() {
177            return None;
178        }
179        unsafe {
180            object::unsafe_::inc_ref(handle.cast());
181            Some(object::ObjectRef::from_data(ObjectArc::from_raw(
182                handle.cast(),
183            )))
184        }
185    }
186
187    /// Get the traceback of the error in the order of most recent call last
188    ///
189    /// # Returns
190    /// The traceback of the error
191    pub fn traceback_most_recent_call_last(&self) -> String {
192        let backtrace = self.backtrace();
193        let backtrace_lines = backtrace.split('\n');
194        let mut traceback = String::new();
195        for line in backtrace_lines.rev() {
196            traceback.push_str(line);
197            traceback.push('\n');
198        }
199        traceback
200    }
201
202    /// Append the backtrace to the error
203    ///
204    /// # Arguments
205    /// * `this` - The error to append the backtrace to
206    /// * `backtrace` - The backtrace to append
207    ///
208    /// # Returns
209    /// The error with the appended backtrace
210    pub fn with_appended_backtrace(this: Self, backtrace: &str) -> Self {
211        if ObjectArc::strong_count(&this.data) == 1 {
212            // this is the only reference to the error
213            // we can safely mutate the error
214            unsafe {
215                let backtrace_data = TVMFFIByteArray::from_str(backtrace);
216                (this.data.cell.update_backtrace)(
217                    ObjectArc::as_raw(&this.data) as *mut ErrorObj as *mut c_void,
218                    &backtrace_data,
219                    kTVMFFIBacktraceUpdateModeAppend as i32,
220                );
221                this
222            }
223        } else {
224            // we need to create a new error because there is more than one unique reference
225            // to the error
226            let mut new_backtrace = String::new();
227            new_backtrace.push_str(this.backtrace());
228            new_backtrace.push_str(backtrace);
229            Self::new_with_cause_and_extra_context(
230                this.kind(),
231                this.message(),
232                &new_backtrace,
233                this.cause_chain().as_ref(),
234                this.extra_context().as_ref(),
235            )
236        }
237    }
238}
239
240impl std::fmt::Display for Error {
241    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
242        write!(
243            f,
244            "Traceback (most recent call last):\n{}{}: {}",
245            self.traceback_most_recent_call_last(),
246            self.kind().as_str(),
247            self.message()
248        )
249    }
250}
251
252impl std::fmt::Debug for Error {
253    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
254        std::fmt::Display::fmt(self, f)
255    }
256}
257
258impl std::error::Error for Error {}