Skip to content

Error handling without UB #12

@gnzlbg

Description

@gnzlbg

IIUC, you can pass mozjpeg a function that gets called when an error happens. That function currently panic!. Is that the only source of panics from Rust callbacks ?

If so, it would be easy to change that into a C++ function that calls some Rust function and then throws some C++ exception containing whatever information would be interesting for Rust code (e.g. an error string):

extern "C" ErrorType error_function_rust();
extern "C" void error_function_cpp() {
    ErrorType e = error_function_rust();
    if (ErrorType.error()) { throw e.exception(); }
}

Otherwise, we could provide a small proc macro that does this automatically.

To catch that exception before unwinding into Rust and invoking UB, we would need to write one wrapper per extern "C" function declaration in the library, with one or two exceptions.

It wouldn't be hard to write a small proc macro that does this automatically. You'd just put it in the top-level of the crate #![try_catch_extern_decls]. This proc macro would do two things. First, for all functions within a extern "C" { ... } block. It would change a function like:

extern "C" {
    pub fn jpeg_destroy_compress(cinfo: &mut jpeg_compress_struct);
}

into:

pub unsafe fn jpeg_destroy_compress(cinfo: &mut jpeg_compress_struct) {
     extern "C" { 
        pub fn jpeg_destroy_compress_rust_shim(cinfo: &mut jpeg_compress_struct) -> ErrorType;
     } 
    if let Some(string) = jpeg_destroy_compress_rust_shim(cinfo) {
        panic!(string)
    }
}

These functions map an optional error message received from C into a Rust panic. Most functions in the jpeg API don't have a return type, so we can use that, but we could also use an out parameter.

Simultaneously, the proc macro would generate a rust_shim.cpp file containing the Rust shims:

extern "C" CErrorType jpeg_destroy_compress_rust_shim(jpeg_compress_struct* cinfo) {
     try {
           jpeg_destroy_compress(cinfo);
           return CErrorType::ok();
     } catch (const runtime_error& e) {
            return CErrorType::error(e);
     }
}

We'd compile this shim using the cc crate and link it on the fly to the current crate.

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions