1 /*
2  *  Example of how to use DUK_USE_CPP_EXCEPTIONS to support automatic
3  *  variables (e.g. destructor calls) in Duktape/C functions.
4  *
5  *  Compile with -DDUK_OPT_CPP_EXCEPTIONS:
6  *
7  *    $ g++ -otest -DDUK_OPT_CPP_EXCEPTIONS -I<duktape_dist>/src/ \
8  *      <duktape_dist>/src/duktape.c cpp_exceptions.cpp -lm
9  *
10  *  or ensure duk_config.h has DUK_USE_CPP_EXCEPTIONS enabled using
11  *  genconfig.  When executed you should see something like:
12  *
13  *    $ ./test
14  *    my_class instance created
15  *    my_class instance destroyed      <== destructor gets called
16  *    --> rc=1 (SyntaxError: parse error (line 1))
17  *    [...]
18  *
19  *  Duktape uses a custom exception class (duk_internal_exception) which
20  *  doesn't inherit from any base class, so that catching any base classes
21  *  in user code won't accidentally catch exceptions thrown by Duktape.
22  */
23 
24 #if !defined(__cplusplus)
25 #error compile using a c++ compiler
26 #endif
27 
28 #include <stdio.h>
29 #include <exception>
30 #include "duktape.h"
31 
32 #if defined(__cplusplus) && (__cplusplus >= 201103L)
33 #define NOEXCEPT noexcept
34 #else
35 #define NOEXCEPT throw()
36 #endif
37 
38 /*
39  *  Example class with a destructor
40  */
41 
42 class my_class {
43  public:
44 	my_class();
45 	~my_class();
46 };
47 
my_class()48 my_class::my_class() {
49 	printf("my_class instance created\n");
50 }
51 
~my_class()52 my_class::~my_class() {
53 	printf("my_class instance destroyed\n");
54 }
55 
56 /*
57  *  SyntaxError caused by eval exits Duktape/C function but destructors
58  *  are executed.
59  */
60 
test1(duk_context * ctx)61 duk_ret_t test1(duk_context *ctx) {
62 	my_class myclass;
63 
64 	duk_eval_string(ctx, "aiee=");
65 
66 	return 0;
67 }
68 
69 /*
70  *  You can use C++ exceptions inside Duktape/C functions for your own
71  *  purposes but you should catch them before they propagate to Duktape.
72  */
73 
test2(duk_context * ctx)74 duk_ret_t test2(duk_context *ctx) {
75 	my_class myclass;
76 
77 	try {
78 		throw 123;
79 	} catch (int myvalue) {
80 		printf("Caught: %d\n", myvalue);
81 	}
82 
83 	return 0;
84 }
85 
86 /*
87  *  If you let your own C++ exceptions propagate out of a Duktape/C function
88  *  it will be caught by Duktape and considered a programming error.  Duktape
89  *  will catch the exception and convert it to a Duktape error.
90  *
91  *  This may be allowed in a later version once all the implications have been
92  *  worked out.
93  */
94 
test3(duk_context * ctx)95 duk_ret_t test3(duk_context *ctx) {
96 	my_class myclass;
97 
98 	throw 123;  /* ERROR: exception propagated to Duktape */
99 
100 	return 0;
101 }
102 
103 /*
104  *  Same as above, but if the exception inherits from std::exception, it's
105  *  "what()" will be included in the error message.
106  */
107 
108 class my_exception : public std::exception {
what() const109 	virtual const char *what() const NOEXCEPT {
110 		return "my_exception";
111 	}
112 };
113 
test4(duk_context * ctx)114 duk_ret_t test4(duk_context *ctx) {
115 	my_class myclass;
116 	my_exception myexc;
117 
118 	throw myexc;  /* ERROR: exception propagated to Duktape */
119 
120 	return 0;
121 }
122 
123 /*
124  *  Same as above, but if the exception inherits from std::exception with
125  *  a NULL what().  Duktape will describe the error as 'unknown' if so.
126  */
127 
128 class my_exception2 : public std::exception {
what() const129 	virtual const char *what() const NOEXCEPT {
130 		return NULL;
131 	}
132 };
133 
test5(duk_context * ctx)134 duk_ret_t test5(duk_context *ctx) {
135 	my_class myclass;
136 	my_exception2 myexc;
137 
138 	throw myexc;  /* ERROR: exception propagated to Duktape */
139 
140 	return 0;
141 }
142 
main(int argc,char * argv[])143 int main(int argc, char *argv[]) {
144 	duk_context *ctx = duk_create_heap_default();
145 	duk_int_t rc;
146 
147 	(void) argc; (void) argv;  /* suppress warning */
148 
149 	printf("*** test1 - duk_pcall()\n");
150 	duk_push_c_function(ctx, test1, 0);
151 	rc = duk_pcall(ctx, 0);
152 	printf("--> rc=%ld (%s)\n", (long) rc, duk_safe_to_string(ctx, -1));
153 	duk_pop(ctx);
154 	printf("\n");
155 
156 	printf("*** test1 - duk_safe_call()\n");
157 	rc = duk_safe_call(ctx, test1, 0, 1);
158 	printf("--> rc=%ld (%s)\n", (long) rc, duk_safe_to_string(ctx, -1));
159 	duk_pop(ctx);
160 	printf("\n");
161 
162 	printf("*** test1 - ecmascript try-catch\n");
163 	duk_push_c_function(ctx, test1, 0);
164 	duk_put_global_string(ctx, "test1");
165 	duk_eval_string_noresult(ctx,
166 		"try {\n"
167 		"    test1();\n"
168 		"} catch (e) {\n"
169 		"    print(e.stack || e);\n"
170 		"}\n");
171 	printf("\n");
172 
173 	printf("*** test2 - duk_pcall()\n");
174 	duk_push_c_function(ctx, test2, 0);
175 	rc = duk_pcall(ctx, 0);
176 	printf("--> rc=%ld (%s)\n", (long) rc, duk_safe_to_string(ctx, -1));
177 	duk_pop(ctx);
178 	printf("\n");
179 
180 	printf("*** test2 - duk_safe_call()\n");
181 	rc = duk_safe_call(ctx, test2, 0, 1);
182 	printf("--> rc=%ld (%s)\n", (long) rc, duk_safe_to_string(ctx, -1));
183 	duk_pop(ctx);
184 	printf("\n");
185 
186 	printf("*** test2 - ecmascript try-catch\n");
187 	duk_push_c_function(ctx, test2, 0);
188 	duk_put_global_string(ctx, "test2");
189 	duk_eval_string_noresult(ctx,
190 		"try {\n"
191 		"    test2();\n"
192 		"} catch (e) {\n"
193 		"    print(e.stack || e);\n"
194 		"}\n");
195 	printf("\n");
196 
197 	printf("*** test3 - duk_pcall()\n");
198 	duk_push_c_function(ctx, test3, 0);
199 	rc = duk_pcall(ctx, 0);
200 	printf("--> rc=%ld (%s)\n", (long) rc, duk_safe_to_string(ctx, -1));
201 	duk_pop(ctx);
202 	printf("\n");
203 
204 	printf("*** test3 - duk_safe_call()\n");
205 	rc = duk_safe_call(ctx, test3, 0, 1);
206 	printf("--> rc=%ld (%s)\n", (long) rc, duk_safe_to_string(ctx, -1));
207 	duk_pop(ctx);
208 	printf("\n");
209 
210 	printf("*** test3 - ecmascript try-catch\n");
211 	duk_push_c_function(ctx, test3, 0);
212 	duk_put_global_string(ctx, "test3");
213 	duk_eval_string_noresult(ctx,
214 		"try {\n"
215 		"    test3();\n"
216 		"} catch (e) {\n"
217 		"    print(e.stack || e);\n"
218 		"}\n");
219 	printf("\n");
220 
221 	printf("*** test4 - duk_pcall()\n");
222 	duk_push_c_function(ctx, test4, 0);
223 	rc = duk_pcall(ctx, 0);
224 	printf("--> rc=%ld (%s)\n", (long) rc, duk_safe_to_string(ctx, -1));
225 	duk_pop(ctx);
226 	printf("\n");
227 
228 	printf("*** test4 - duk_safe_call()\n");
229 	rc = duk_safe_call(ctx, test4, 0, 1);
230 	printf("--> rc=%ld (%s)\n", (long) rc, duk_safe_to_string(ctx, -1));
231 	duk_pop(ctx);
232 	printf("\n");
233 
234 	printf("*** test4 - ecmascript try-catch\n");
235 	duk_push_c_function(ctx, test4, 0);
236 	duk_put_global_string(ctx, "test4");
237 	duk_eval_string_noresult(ctx,
238 		"try {\n"
239 		"    test4();\n"
240 		"} catch (e) {\n"
241 		"    print(e.stack || e);\n"
242 		"}\n");
243 	printf("\n");
244 
245 	printf("*** test5 - duk_pcall()\n");
246 	duk_push_c_function(ctx, test5, 0);
247 	rc = duk_pcall(ctx, 0);
248 	printf("--> rc=%ld (%s)\n", (long) rc, duk_safe_to_string(ctx, -1));
249 	duk_pop(ctx);
250 	printf("\n");
251 
252 	printf("*** test5 - duk_safe_call()\n");
253 	rc = duk_safe_call(ctx, test5, 0, 1);
254 	printf("--> rc=%ld (%s)\n", (long) rc, duk_safe_to_string(ctx, -1));
255 	duk_pop(ctx);
256 	printf("\n");
257 
258 	printf("*** test5 - ecmascript try-catch\n");
259 	duk_push_c_function(ctx, test5, 0);
260 	duk_put_global_string(ctx, "test5");
261 	duk_eval_string_noresult(ctx,
262 		"try {\n"
263 		"    test5();\n"
264 		"} catch (e) {\n"
265 		"    print(e.stack || e);\n"
266 		"}\n");
267 	printf("\n");
268 
269 	printf("*** done\n");
270 
271 	duk_destroy_heap(ctx);
272 
273 	return 0;
274 }
275