summaryrefslogtreecommitdiff
path: root/src/runtime/c/gu/exn.h
blob: 66ca107e35e2c49bcfa4bbdd5ddc5d1a102288c9 (plain)
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
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
#ifndef GU_EXN_H_
#define GU_EXN_H_

#include <gu/mem.h>
#include <gu/type.h>

/** @file
 *
 * @defgroup GuExn Exceptions
 * Defined in <gu/exn.h>.
 * @{
 */

/// An exception frame.
typedef struct GuExn GuExn;

/// @private
typedef enum {
	GU_EXN_RAISED,
	GU_EXN_OK,
	GU_EXN_BLOCKED
} GuExnState;

typedef struct GuExnData GuExnData;

/// A structure for storing exception values.
struct GuExnData
/**
 * When an exception is raised, if there is an associated value, it
 * must be allocated from a pool that still exists when control
 * returns to the handler of that exception. This structure is used to
 * communicate the exception from the raiser to the handler: the
 * handler sets #pool when setting up the exception frame, and the
 * raiser uses that pool to allocate the value and stores that in
 * #data. When control returns to the handler, it reads the value from
 * there.
 */
{
	
	/// The pool that the exception value should be allocated from.
	GuPool* const pool;

	/// The exception value.
	const void* data;
};

struct GuExn {
	/// @privatesection
	GuExnState state;
	GuExn* parent;
	GuKind* catch;
	GuType* caught;
	GuExnData data;
};


/// @name Creating exception frames
//@{


/// Allocate a new local exception frame.
#define gu_exn(parent_, catch_, pool_) &(GuExn){	\
	.state = GU_EXN_OK,	\
	.parent = parent_,	\
	.catch = gu_kind(catch_),	\
	.caught = NULL,	\
	.data.pool = pool_,		\
	.data.data = NULL \
}


/// Allocate a new exception frame.
GuExn*
gu_new_exn(GuExn* parent, GuKind* catch_kind, GuPool* pool);



static inline bool
gu_exn_is_raised(GuExn* err) {
	return err && (err->state == GU_EXN_RAISED);
}

static inline void
gu_exn_clear(GuExn* err) {
	err->caught = NULL;
	err->state = GU_EXN_OK;
}



GuType*
gu_exn_caught(GuExn* err);

const void*
gu_exn_caught_data(GuExn* err);

/// Temporarily block a raised exception.
void
gu_exn_block(GuExn* err);

/// Show again a blocked exception.
void
gu_exn_unblock(GuExn* err);

//@private
GuExnData*
gu_exn_raise_(GuExn* err, GuType* type);

//@private
GuExnData*
gu_exn_raise_debug_(GuExn* err, GuType* type,
		      const char* filename, const char* func, int lineno);

#ifdef NDEBUG
#define gu_exn_raise(err_, type_)	\
	gu_exn_raise_(err_, type_)
#else
#define gu_exn_raise(err_, type_)	\
	gu_exn_raise_debug_(err_, type_, \
			      __FILE__, __func__, __LINE__)
#endif

/// Raise an exception.
#define gu_raise(exn, T)		\
	gu_exn_raise(exn, gu_type(T))
/**< 
 * @param exn The current exception frame.
 *
 * @param T   The C type of the exception to raise.
 *
 * @return    A #GuExnData object that can be used to store the exception value, or
 * \c NULL if no value is required.
 * 
 * @note The associated #GuType object for type \p T must be visible.
 */

#define gu_raise_new(error_, t_, pool_, expr_)				\
	GU_BEGIN							\
	GuExnData* gu_raise_err_ = gu_raise(error_, t_);		\
	if (gu_raise_err_) {						\
		GuPool* pool_ = gu_raise_err_->pool;			\
		gu_raise_err_->data = expr_;				\
	}								\
	GU_END

#define gu_raise_i(error_, t_, ...) \
	gu_raise_new(error_, t_, gu_raise_pool_, gu_new_i(gu_raise_pool_, t_, __VA_ARGS__))


/// Check the status of the current exception frame
static inline bool
gu_ok(GuExn* exn) {
	return !GU_UNLIKELY(gu_exn_is_raised(exn));
}
/**<
 * @return \c false if an exception has been raised in the frame \p exn
 * and it has not been blocked, \c true otherwise.
 */


/// Return from current function if an exception has been raised.
#define gu_return_on_exn(exn_, retval_)		\
	GU_BEGIN					\
	if (gu_exn_is_raised(exn_)) return retval_;	\
	GU_END
/**<
 * @showinitializer
 */


#include <errno.h>

typedef int GuErrno;

extern GU_DECLARE_TYPE(GuErrno, signed);



#define gu_raise_errno(error_) \
	gu_raise_i(error_, GuErrno, errno)

#if 0

typedef void (*GuExnPrintFn)(GuFn* clo, void* err, FILE* out);

extern GuTypeTable gu_exn_default_printer;

void
gu_exn_print(GuExn* err, FILE* out, GuTypeMap printer_map);

#endif

/** @} */

#endif // GU_EXN_H_