Last active
February 24, 2020 16:39
-
-
Save barrucadu/af9eea657c13de811f1843a6a0e8813a to your computer and use it in GitHub Desktop.
clang jmp-cflow.c -Weverything -Wno-shadow -Wno-unused-variable -Wno-missing-noreturn -Wno-reserved-id-macro -Wno-unused-macros -Werror
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
#include <setjmp.h> | |
#include <stdbool.h> | |
#include <stdint.h> | |
#include <stdio.h> | |
#include <stdlib.h> | |
#define BEGIN_BLOCK \ | |
{ \ | |
volatile uint8_t orig_exc_i = exc_i; \ | |
volatile uint8_t exc_i = orig_exc_i + 1; \ | |
switch(setjmp(exc[exc_i].catcher)) { \ | |
case 0: \ | |
case 1: | |
#define __E_CATCHER(off) exc[exc_i-off].catcher | |
#define __E_RESUMER(off) exc[exc_i-off].resumer | |
#define __E_THROW(n) if(setjmp(__E_RESUMER(0)) == 0) { longjmp(__E_CATCHER(0), n); } | |
#define __E_THROW_UP(n) if(setjmp(__E_RESUMER(1)) == 0) { longjmp(__E_CATCHER(1), n); } | |
#define __E_CATCH(n) break; case (n): | |
#define END_BLOCK \ | |
break; \ | |
default: \ | |
fprintf(stderr, "missing CATCH for THROW - did you mean to use THROW_UP? aborting...\n"); \ | |
abort(); \ | |
} \ | |
} | |
#define THROW(n) __E_THROW((n)+2) | |
#define CATCH(n) __E_CATCH((n)+2) | |
#define RESTART longjmp(__E_CATCHER(0), 1) | |
#define RESUME longjmp(__E_RESUMER(0), 1) | |
#define THROW_UP(n) __E_THROW_UP((n)+2) | |
#define RESTART_UP longjmp(__E_CATCHER(1), 1) | |
#define RESUME_UP longjmp(__E_RESUMER(1), 1) | |
#define BEGIN_E_FUNC(ty, name, ...) \ | |
__attribute__((always_inline)) \ | |
__attribute__((noreturn)) \ | |
static inline ty name(uint8_t exc_i, __VA_ARGS__) { | |
#define END_E_FUNC \ | |
fprintf(stderr, "control has reached end of E_FUNC. aborting...\n"); \ | |
abort(); \ | |
} | |
#define CALL_E_FUNC(name, ...) name(exc_i, __VA_ARGS__) | |
static struct { | |
jmp_buf catcher; | |
jmp_buf resumer; | |
} exc[255]; | |
/* THROWs true or false */ | |
BEGIN_E_FUNC(void, check_numbers, int a, int b) | |
THROW(a == b); | |
printf("\nTry again, but enter two equal numbers this time!\n\n"); | |
RESTART; | |
END_E_FUNC | |
BEGIN_E_FUNC(void, e_main, int *ret) | |
volatile int a, b; | |
BEGIN_BLOCK | |
printf("Enter a number:\n"); | |
scanf("%d", &a); | |
printf("Enter a second number:\n"); | |
scanf("%d", &b); | |
CALL_E_FUNC(check_numbers, a, b); | |
CATCH(true) | |
printf("%d == %d\n", a, b); | |
THROW_UP(256); | |
printf("yay!\n"); | |
*ret = 0; | |
THROW_UP(42); | |
CATCH(false) | |
printf("%d != %d\n", a, b); | |
RESUME; | |
END_BLOCK | |
END_E_FUNC | |
int main(void) { | |
uint8_t exc_i = 0; | |
int ret = 1; | |
switch(setjmp(__E_CATCHER(0))) { | |
case 0: | |
case 1: | |
e_main(exc_i, &ret); | |
case 42: | |
printf("returning\n"); | |
break; | |
default: | |
fprintf(stderr, "uncaught exception!\n"); | |
longjmp(__E_RESUMER(0), 1); | |
} | |
return ret; | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment