Skip to content

Commit

Permalink
Interpret C++ exceptions in C bindings (#955)
Browse files Browse the repository at this point in the history
This PR amends the interface of the simplification routines in the C
bindings to allow for error information to be safely returned to calling
clients without crashing the host process.

The changes are as follows:
- Introduce a new opaque `kore_error` type to the C bindings, with
appropriate bindings.
- **Breaking change** to the interfaces of `kore_simplify`,
`kore_simplify_bool` and `kore_simplify_binary` to take an optional
additional `kore_error` parameter. If a C++ exception is thrown during
these bindings, it will be caught and used to populate the error
parameter's fields. The booster will need to be updated as part of its K
release job down the line.
- Add a test that catches an error that imitates the original reporting
of this issue (invalid numeric value in the hooked arithmetic library).

Closes #953
  • Loading branch information
Baltoli authored Jan 17, 2024
1 parent ae78dab commit 8bdf2a5
Show file tree
Hide file tree
Showing 13 changed files with 2,307 additions and 27 deletions.
50 changes: 47 additions & 3 deletions bindings/c/include/kllvm-c/kllvm-c.h
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,48 @@
extern "C" {
#endif

/**
* Error Handling
* ==============
*
* Some API functions in these bindings can bubble up internal errors from the
* LLVM backend to avoid crashing the host process.
*
* These functions take an initial parameter of type `kore_error *`; if that
* parameter is `NULL` then any C++ exceptions thrown by the backend will
* simply be rethrown and the host process will crash with the relevant error
* message.
*
* If the input is not NULL, then the object will have call-specific information
* filled in that can be accessed using the getter functions in this section.
*/

typedef struct kore_error kore_error;

/**
* Create an empty error object. Initially, the created object will report
* success (not failure), and will return `NULL` if its message is accessed.
*/
kore_error *kore_error_new(void);

/**
* Return true if no error occurred when this object was passed to an API call.
*/
bool kore_error_is_success(kore_error const *);

/**
* Return any error-specific message that has been added to this object. The
* returned string is a reference to the error's internal state and should not
* be freed separately. If no error has occurred (i.e. is_success returns true),
* then return NULL.
*/
char const *kore_error_message(kore_error const *);

/**
* Deallocate an error and its associated message.
*/
void kore_error_free(kore_error *);

/*
* Binary KORE Outputs
* ===================
Expand Down Expand Up @@ -78,12 +120,14 @@ kore_pattern *kore_pattern_from_block(block *);
*/
bool kore_block_get_bool(block *);

bool kore_simplify_bool(kore_pattern const *);
bool kore_simplify_bool(kore_error *, kore_pattern const *);

void kore_simplify(
kore_pattern const *pattern, kore_sort const *sort, char **, size_t *);
kore_error *err, kore_pattern const *pattern, kore_sort const *sort,
char **, size_t *);

void kore_simplify_binary(char *, size_t, kore_sort const *, char **, size_t *);
void kore_simplify_binary(
kore_error *, char *, size_t, kore_sort const *, char **, size_t *);

block *take_steps(int64_t depth, block *term);

Expand Down
88 changes: 74 additions & 14 deletions bindings/c/lib.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -69,6 +69,24 @@ char kore_definition_macros __attribute__((weak)) = -1;

/* Completed types */

struct kore_error {
bool success_ = true;
std::optional<std::string> message_ = std::nullopt;

[[nodiscard]] char const *c_str() const {
if (!success_ && message_.has_value()) {
return message_->c_str();
}

return nullptr;
}

void set_error(const std::string &msg) {
success_ = false;
message_ = msg;
}
};

struct kore_pattern {
std::shared_ptr<kllvm::KOREPattern> ptr_;
};
Expand All @@ -81,6 +99,24 @@ struct kore_symbol {
std::shared_ptr<kllvm::KORESymbol> ptr_;
};

/* Error handling */

kore_error *kore_error_new(void) {
return new kore_error;
}

bool kore_error_is_success(kore_error const *err) {
return err->success_;
}

char const *kore_error_message(kore_error const *err) {
return err->c_str();
}

void kore_error_free(kore_error *err) {
delete err;
}

/* KOREPattern */

char *kore_pattern_dump(kore_pattern const *pat) {
Expand Down Expand Up @@ -257,26 +293,50 @@ bool kore_block_get_bool(block *term) {
return kllvm::bindings::get_bool(term);
}

bool kore_simplify_bool(kore_pattern const *pattern) {
return kllvm::bindings::simplify_to_bool(pattern->ptr_);
bool kore_simplify_bool(kore_error *err, kore_pattern const *pattern) {
try {
return kllvm::bindings::simplify_to_bool(pattern->ptr_);
} catch (std::exception &e) {
if (err == nullptr) {
throw;
}

err->set_error(e.what());
return false;
}
}

void kore_simplify(
kore_pattern const *pattern, kore_sort const *sort, char **data_out,
size_t *size_out) {
auto *block = kllvm::bindings::simplify_to_term(pattern->ptr_, sort->ptr_);
serializeConfiguration(block, "SortKItem{}", data_out, size_out, true);
kore_error *err, kore_pattern const *pattern, kore_sort const *sort,
char **data_out, size_t *size_out) {
try {
auto *block = kllvm::bindings::simplify_to_term(pattern->ptr_, sort->ptr_);
serializeConfiguration(block, "SortKItem{}", data_out, size_out, true);
} catch (std::exception &e) {
if (err == nullptr) {
throw;
}

err->set_error(e.what());
}
}

void kore_simplify_binary(
char *data_in, size_t size_in, kore_sort const *sort, char **data_out,
size_t *size_out) {
auto *sort_str = kore_sort_dump(sort);

auto *block = deserializeConfiguration(data_in, size_in);
serializeConfiguration(block, sort_str, data_out, size_out, true);

free(sort_str);
kore_error *err, char *data_in, size_t size_in, kore_sort const *sort,
char **data_out, size_t *size_out) {
try {
auto sort_str = std::unique_ptr<char, decltype(std::free) *>(
kore_sort_dump(sort), std::free);

auto *block = deserializeConfiguration(data_in, size_in);
serializeConfiguration(block, sort_str.get(), data_out, size_out, true);
} catch (std::exception &e) {
if (err == nullptr) {
throw;
}

err->set_error(e.what());
}
}

/* KORECompositePattern */
Expand Down
4 changes: 4 additions & 0 deletions test/c/Inputs/api.c
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,10 @@ struct kllvm_c_api load_c_api(char const *path) {

struct kllvm_c_api api;

API_FUNCTION(kore_error_new);
API_FUNCTION(kore_error_is_success);
API_FUNCTION(kore_error_message);
API_FUNCTION(kore_error_free);
API_FUNCTION(kore_pattern_dump);
API_FUNCTION(kore_pattern_pretty_print);
API_FUNCTION(kore_pattern_serialize);
Expand Down
11 changes: 8 additions & 3 deletions test/c/Inputs/api.h
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,10 @@
#include <kllvm-c/kllvm-c.h>

struct kllvm_c_api {
kore_error *(*kore_error_new)(void);
bool (*kore_error_is_success)(kore_error const *);
char const *(*kore_error_message)(kore_error const *);
void (*kore_error_free)(kore_error *);
char *(*kore_pattern_dump)(kore_pattern const *);
char *(*kore_pattern_pretty_print)(kore_pattern const *);
void (*kore_pattern_serialize)(kore_pattern const *, char **, size_t *);
Expand All @@ -28,11 +32,12 @@ struct kllvm_c_api {
char *(*kore_block_dump)(block *);
kore_pattern *(*kore_pattern_from_block)(block *);
bool (*kore_block_get_bool)(block *);
bool (*kore_simplify_bool)(kore_pattern const *);
bool (*kore_simplify_bool)(kore_error *err, kore_pattern const *);
void (*kore_simplify)(
kore_pattern const *pattern, kore_sort const *sort, char **, size_t *);
kore_error *err, kore_pattern const *pattern, kore_sort const *sort,
char **, size_t *);
void (*kore_simplify_binary)(
char *, size_t, kore_sort const *, char **, size_t *);
kore_error *, char *, size_t, kore_sort const *, char **, size_t *);
block *(*take_steps)(int64_t depth, block *term);
char *(*kore_sort_dump)(kore_sort const *);
void (*kore_sort_free)(kore_sort const *);
Expand Down
7 changes: 6 additions & 1 deletion test/c/Inputs/binary_simplify.c
Original file line number Diff line number Diff line change
Expand Up @@ -30,7 +30,11 @@ int main(int argc, char **argv) {
size_t size_in, size_out;

api.kore_pattern_serialize(pat, &data_in, &size_in);
api.kore_simplify_binary(data_in, size_in, sort_foo, &data_out, &size_out);

kore_error *err = api.kore_error_new();
api.kore_simplify_binary(
err, data_in, size_in, sort_foo, &data_out, &size_out);
assert(api.kore_error_is_success(err));

FILE *f = fopen(argv[2], "wb");
if (!f) {
Expand All @@ -40,6 +44,7 @@ int main(int argc, char **argv) {
fwrite(data_out, size_out, 1, f);
fclose(f);

api.kore_error_free(err);
api.kore_pattern_free(pat);
api.kore_sort_free(sort_int);
api.kore_sort_free(sort_foo);
Expand Down
6 changes: 5 additions & 1 deletion test/c/Inputs/bool.c
Original file line number Diff line number Diff line change
Expand Up @@ -11,8 +11,12 @@
char *buf = (char *)malloc(len); \
snprintf(buf, len, "Lbl%s", (c)); \
kore_pattern *pat = api.kore_composite_pattern_new(buf); \
assert(api.kore_simplify_bool(pat) == (v) && c "failed!"); \
kore_error *err = api.kore_error_new(); \
bool result = api.kore_simplify_bool(err, pat); \
assert(api.kore_error_is_success(err)); \
assert(result == (v) && c "failed!"); \
api.kore_pattern_free(pat); \
api.kore_error_free(err); \
free(buf); \
} while (false);

Expand Down
48 changes: 48 additions & 0 deletions test/c/Inputs/div_by_zero.c
Original file line number Diff line number Diff line change
@@ -0,0 +1,48 @@
#include "api.h"

#include <assert.h>
#include <stdio.h>

int main(int argc, char **argv) {
if (argc <= 2) {
return 1;
}

struct kllvm_c_api api = load_c_api(argv[1]);

api.kllvm_init();

kore_sort *sort_int = api.kore_composite_sort_new("SortInt");

kore_pattern *good_call = api.kore_pattern_parse(
"Lblfoo{}(\\dv{SortInt{}}(\"2\"), \\dv{SortInt{}}(\"2\"))");

kore_pattern *bad_call = api.kore_pattern_parse(
"Lblfoo{}(\\dv{SortInt{}}(\"2\"), \\dv{SortInt{}}(\"0\"))");

kore_error *err = api.kore_error_new();

char *data;
size_t size;

api.kore_simplify(err, good_call, sort_int, &data, &size);
assert(api.kore_error_is_success(err));

FILE *f = fopen(argv[2], "wb");
if (!f) {
return 4;
}

fwrite(data, size, 1, f);
fclose(f);

api.kore_simplify(err, bad_call, sort_int, &data, &size);
assert(!api.kore_error_is_success(err));

printf("%s\n", api.kore_error_message(err));

api.kore_error_free(err);
api.kore_pattern_free(good_call);
api.kore_pattern_free(bad_call);
api.kore_sort_free(sort_int);
}
2 changes: 1 addition & 1 deletion test/c/Inputs/handle_bytes.c
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,7 @@ int main(int argc, char **argv) {

char *data;
size_t size;
api.kore_simplify(pat, bytes, &data, &size);
api.kore_simplify(NULL, pat, bytes, &data, &size);

FILE *f = fopen(argv[2], "wb");
if (!f) {
Expand Down
2 changes: 1 addition & 1 deletion test/c/Inputs/k-prod.c
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@ int main(int argc, char **argv) {

char *data;
size_t size;
api.kore_simplify(pat, k_sort, &data, &size);
api.kore_simplify(NULL, pat, k_sort, &data, &size);

FILE *f = fopen(argv[2], "wb");
if (!f) {
Expand Down
2 changes: 1 addition & 1 deletion test/c/Inputs/kitem.c
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@ int main(int argc, char **argv) {

char *data;
size_t size;
api.kore_simplify(pat, sort, &data, &size);
api.kore_simplify(NULL, pat, sort, &data, &size);

FILE *f = fopen(argv[2], "wb");
if (!f) {
Expand Down
4 changes: 2 additions & 2 deletions test/c/Inputs/simplify.c
Original file line number Diff line number Diff line change
Expand Up @@ -18,12 +18,12 @@ int main(int argc, char **argv) {

char *data;
size_t size;
api.kore_simplify(pat, sort, &data, &size);
api.kore_simplify(NULL, pat, sort, &data, &size);

// Do the simplification twice to make sure GC works
api.kllvm_free_all_memory();

api.kore_simplify(pat, sort, &data, &size);
api.kore_simplify(NULL, pat, sort, &data, &size);

FILE *f = fopen(argv[2], "wb");
if (!f) {
Expand Down
Loading

0 comments on commit 8bdf2a5

Please sign in to comment.