]> git.proxmox.com Git - ceph.git/blob - ceph/src/jaegertracing/opentelemetry-cpp/third_party/prometheus-cpp/3rdparty/civetweb/src/third_party/duktape-1.5.2/examples/cmdline/duk_cmdline.c
update ceph source to reef 18.1.2
[ceph.git] / ceph / src / jaegertracing / opentelemetry-cpp / third_party / prometheus-cpp / 3rdparty / civetweb / src / third_party / duktape-1.5.2 / examples / cmdline / duk_cmdline.c
1 /*
2 * Command line execution tool. Useful for test cases and manual testing.
3 *
4 * To enable linenoise and other fancy stuff, compile with -DDUK_CMDLINE_FANCY.
5 * It is not the default to maximize portability. You can also compile in
6 * support for example allocators, grep for DUK_CMDLINE_*.
7 */
8
9 /* Helper define to enable a feature set; can also use separate defines. */
10 #if defined(DUK_CMDLINE_FANCY)
11 #define DUK_CMDLINE_LINENOISE
12 #define DUK_CMDLINE_LINENOISE_COMPLETION
13 #define DUK_CMDLINE_RLIMIT
14 #define DUK_CMDLINE_SIGNAL
15 #endif
16
17 #if defined(WIN32) || defined(_WIN32) || defined(__WIN32__) || \
18 defined(WIN64) || defined(_WIN64) || defined(__WIN64__)
19 /* Suppress warnings about plain fopen() etc. */
20 #define _CRT_SECURE_NO_WARNINGS
21 #if defined(_MSC_VER) && (_MSC_VER < 1900)
22 /* Workaround for snprintf() missing in older MSVC versions.
23 * Note that _snprintf() may not NUL terminate the string, but
24 * this difference does not matter here as a NUL terminator is
25 * always explicitly added.
26 */
27 #define snprintf _snprintf
28 #endif
29 #endif
30
31 #define GREET_CODE(variant) \
32 "print('((o) Duktape" variant " ' + " \
33 "Math.floor(Duktape.version / 10000) + '.' + " \
34 "Math.floor(Duktape.version / 100) % 100 + '.' + " \
35 "Duktape.version % 100" \
36 ", '(" DUK_GIT_DESCRIBE ")');"
37
38 #include <stdio.h>
39 #include <stdlib.h>
40 #include <string.h>
41 #if defined(DUK_CMDLINE_SIGNAL)
42 #include <signal.h>
43 #endif
44 #if defined(DUK_CMDLINE_RLIMIT)
45 #include <sys/resource.h>
46 #endif
47 #if defined(DUK_CMDLINE_LINENOISE)
48 #include "linenoise.h"
49 #endif
50 #if defined(DUK_CMDLINE_FILEIO)
51 #include <errno.h>
52 #endif
53 #if defined(EMSCRIPTEN)
54 #include <emscripten.h>
55 #endif
56 #if defined(DUK_CMDLINE_ALLOC_LOGGING)
57 #include "duk_alloc_logging.h"
58 #endif
59 #if defined(DUK_CMDLINE_ALLOC_TORTURE)
60 #include "duk_alloc_torture.h"
61 #endif
62 #if defined(DUK_CMDLINE_ALLOC_HYBRID)
63 #include "duk_alloc_hybrid.h"
64 #endif
65 #include "duktape.h"
66
67 #if defined(DUK_CMDLINE_AJSHEAP)
68 /* Defined in duk_cmdline_ajduk.c or alljoyn.js headers. */
69 void ajsheap_init(void);
70 void ajsheap_free(void);
71 void ajsheap_dump(void);
72 void ajsheap_register(duk_context *ctx);
73 void ajsheap_start_exec_timeout(void);
74 void ajsheap_clear_exec_timeout(void);
75 void *ajsheap_alloc_wrapped(void *udata, duk_size_t size);
76 void *ajsheap_realloc_wrapped(void *udata, void *ptr, duk_size_t size);
77 void ajsheap_free_wrapped(void *udata, void *ptr);
78 void *AJS_Alloc(void *udata, duk_size_t size);
79 void *AJS_Realloc(void *udata, void *ptr, duk_size_t size);
80 void AJS_Free(void *udata, void *ptr);
81 #endif
82
83 #if defined(DUK_CMDLINE_DEBUGGER_SUPPORT)
84 #include "duk_trans_socket.h"
85 #endif
86
87 #define MEM_LIMIT_NORMAL (128*1024*1024) /* 128 MB */
88 #define MEM_LIMIT_HIGH (2047*1024*1024) /* ~2 GB */
89 #define LINEBUF_SIZE 65536
90
91 static int main_argc = 0;
92 static char **main_argv = NULL;
93 static int interactive_mode = 0;
94 #if defined(DUK_CMDLINE_DEBUGGER_SUPPORT)
95 static int debugger_reattach = 0;
96 #endif
97
98 /*
99 * Misc helpers
100 */
101
102 #if defined(DUK_CMDLINE_RLIMIT)
103 static void set_resource_limits(rlim_t mem_limit_value) {
104 int rc;
105 struct rlimit lim;
106
107 rc = getrlimit(RLIMIT_AS, &lim);
108 if (rc != 0) {
109 fprintf(stderr, "Warning: cannot read RLIMIT_AS\n");
110 return;
111 }
112
113 if (lim.rlim_max < mem_limit_value) {
114 fprintf(stderr, "Warning: rlim_max < mem_limit_value (%d < %d)\n", (int) lim.rlim_max, (int) mem_limit_value);
115 return;
116 }
117
118 lim.rlim_cur = mem_limit_value;
119 lim.rlim_max = mem_limit_value;
120
121 rc = setrlimit(RLIMIT_AS, &lim);
122 if (rc != 0) {
123 fprintf(stderr, "Warning: setrlimit failed\n");
124 return;
125 }
126
127 #if 0
128 fprintf(stderr, "Set RLIMIT_AS to %d\n", (int) mem_limit_value);
129 #endif
130 }
131 #endif /* DUK_CMDLINE_RLIMIT */
132
133 #if defined(DUK_CMDLINE_SIGNAL)
134 static void my_sighandler(int x) {
135 fprintf(stderr, "Got signal %d\n", x);
136 fflush(stderr);
137 }
138 static void set_sigint_handler(void) {
139 (void) signal(SIGINT, my_sighandler);
140 (void) signal(SIGPIPE, SIG_IGN); /* avoid SIGPIPE killing process */
141 }
142 #endif /* DUK_CMDLINE_SIGNAL */
143
144 static int get_stack_raw(duk_context *ctx) {
145 if (!duk_is_object(ctx, -1)) {
146 return 1;
147 }
148 if (!duk_has_prop_string(ctx, -1, "stack")) {
149 return 1;
150 }
151 if (!duk_is_error(ctx, -1)) {
152 /* Not an Error instance, don't read "stack". */
153 return 1;
154 }
155
156 duk_get_prop_string(ctx, -1, "stack"); /* caller coerces */
157 duk_remove(ctx, -2);
158 return 1;
159 }
160
161 /* Print error to stderr and pop error. */
162 static void print_pop_error(duk_context *ctx, FILE *f) {
163 /* Print error objects with a stack trace specially.
164 * Note that getting the stack trace may throw an error
165 * so this also needs to be safe call wrapped.
166 */
167 (void) duk_safe_call(ctx, get_stack_raw, 1 /*nargs*/, 1 /*nrets*/);
168 fprintf(f, "%s\n", duk_safe_to_string(ctx, -1));
169 fflush(f);
170 duk_pop(ctx);
171 }
172
173 static int wrapped_compile_execute(duk_context *ctx) {
174 const char *src_data;
175 duk_size_t src_len;
176 int comp_flags;
177
178 /* XXX: Here it'd be nice to get some stats for the compilation result
179 * when a suitable command line is given (e.g. code size, constant
180 * count, function count. These are available internally but not through
181 * the public API.
182 */
183
184 /* Use duk_compile_lstring_filename() variant which avoids interning
185 * the source code. This only really matters for low memory environments.
186 */
187
188 /* [ ... bytecode_filename src_data src_len filename ] */
189
190 src_data = (const char *) duk_require_pointer(ctx, -3);
191 src_len = (duk_size_t) duk_require_uint(ctx, -2);
192
193 if (src_data != NULL && src_len >= 2 && src_data[0] == (char) 0xff) {
194 /* Bytecode. */
195 duk_push_lstring(ctx, src_data, src_len);
196 duk_to_buffer(ctx, -1, NULL);
197 duk_load_function(ctx);
198 } else {
199 /* Source code. */
200 comp_flags = 0;
201 duk_compile_lstring_filename(ctx, comp_flags, src_data, src_len);
202 }
203
204 /* [ ... bytecode_filename src_data src_len function ] */
205
206 /* Optional bytecode dump. */
207 if (duk_is_string(ctx, -4)) {
208 FILE *f;
209 void *bc_ptr;
210 duk_size_t bc_len;
211 size_t wrote;
212 char fnbuf[256];
213 const char *filename;
214
215 duk_dup_top(ctx);
216 duk_dump_function(ctx);
217 bc_ptr = duk_require_buffer(ctx, -1, &bc_len);
218 filename = duk_require_string(ctx, -5);
219 #if defined(EMSCRIPTEN)
220 if (filename[0] == '/') {
221 snprintf(fnbuf, sizeof(fnbuf), "%s", filename);
222 } else {
223 snprintf(fnbuf, sizeof(fnbuf), "/working/%s", filename);
224 }
225 #else
226 snprintf(fnbuf, sizeof(fnbuf), "%s", filename);
227 #endif
228 fnbuf[sizeof(fnbuf) - 1] = (char) 0;
229
230 f = fopen(fnbuf, "wb");
231 if (!f) {
232 duk_error(ctx, DUK_ERR_ERROR, "failed to open bytecode output file");
233 }
234 wrote = fwrite(bc_ptr, 1, (size_t) bc_len, f); /* XXX: handle partial writes */
235 (void) fclose(f);
236 if (wrote != bc_len) {
237 duk_error(ctx, DUK_ERR_ERROR, "failed to write all bytecode");
238 }
239
240 return 0; /* duk_safe_call() cleans up */
241 }
242
243 #if 0
244 /* Manual test for bytecode dump/load cycle: dump and load before
245 * execution. Enable manually, then run "make qecmatest" for a
246 * reasonably good coverage of different functions and programs.
247 */
248 duk_dump_function(ctx);
249 duk_load_function(ctx);
250 #endif
251
252 #if defined(DUK_CMDLINE_AJSHEAP)
253 ajsheap_start_exec_timeout();
254 #endif
255
256 duk_push_global_object(ctx); /* 'this' binding */
257 duk_call_method(ctx, 0);
258
259 #if defined(DUK_CMDLINE_AJSHEAP)
260 ajsheap_clear_exec_timeout();
261 #endif
262
263 if (interactive_mode) {
264 /*
265 * In interactive mode, write to stdout so output won't
266 * interleave as easily.
267 *
268 * NOTE: the ToString() coercion may fail in some cases;
269 * for instance, if you evaluate:
270 *
271 * ( {valueOf: function() {return {}},
272 * toString: function() {return {}}});
273 *
274 * The error is:
275 *
276 * TypeError: failed to coerce with [[DefaultValue]]
277 * duk_api.c:1420
278 *
279 * These are handled now by the caller which also has stack
280 * trace printing support. User code can print out errors
281 * safely using duk_safe_to_string().
282 */
283
284 fprintf(stdout, "= %s\n", duk_to_string(ctx, -1));
285 fflush(stdout);
286 } else {
287 /* In non-interactive mode, success results are not written at all.
288 * It is important that the result value is not string coerced,
289 * as the string coercion may cause an error in some cases.
290 */
291 }
292
293 return 0; /* duk_safe_call() cleans up */
294 }
295
296 /*
297 * Minimal Linenoise completion support
298 */
299
300 #if defined(DUK_CMDLINE_LINENOISE_COMPLETION)
301 static duk_context *completion_ctx;
302
303 static int completion_idpart(unsigned char c) {
304 /* Very simplified "is identifier part" check. */
305 if ((c >= (unsigned char) 'a' && c <= (unsigned char) 'z') ||
306 (c >= (unsigned char) 'A' && c <= (unsigned char) 'Z') ||
307 (c >= (unsigned char) '0' && c <= (unsigned char) '9') ||
308 c == (unsigned char) '$' || c == (unsigned char) '_') {
309 return 1;
310 }
311 return 0;
312 }
313
314 static int completion_digit(unsigned char c) {
315 return (c >= (unsigned char) '0' && c <= (unsigned char) '9');
316 }
317
318 static duk_ret_t linenoise_completion_lookup(duk_context *ctx) {
319 duk_size_t len;
320 const char *orig;
321 const unsigned char *p;
322 const unsigned char *p_curr;
323 const unsigned char *p_end;
324 const char *key;
325 const char *prefix;
326 linenoiseCompletions *lc;
327 duk_idx_t idx_obj;
328
329 orig = duk_require_string(ctx, -3);
330 p_curr = (const unsigned char *) duk_require_lstring(ctx, -2, &len);
331 p_end = p_curr + len;
332 lc = duk_require_pointer(ctx, -1);
333
334 duk_push_global_object(ctx);
335 idx_obj = duk_require_top_index(ctx);
336
337 while (p_curr <= p_end) {
338 /* p_curr == p_end allowed on purpose, to handle 'Math.' for example. */
339 p = p_curr;
340 while (p < p_end && p[0] != (unsigned char) '.') {
341 p++;
342 }
343 /* 'p' points to a NUL (p == p_end) or a period. */
344 prefix = duk_push_lstring(ctx, (const char *) p_curr, (duk_size_t) (p - p_curr));
345
346 #if 0
347 fprintf(stderr, "Completion check: '%s'\n", prefix);
348 fflush(stderr);
349 #endif
350
351 if (p == p_end) {
352 /* 'idx_obj' points to the object matching the last
353 * full component, use [p_curr,p[ as a filter for
354 * that object.
355 */
356
357 duk_enum(ctx, idx_obj, DUK_ENUM_INCLUDE_NONENUMERABLE);
358 while (duk_next(ctx, -1, 0 /*get_value*/)) {
359 key = duk_get_string(ctx, -1);
360 #if 0
361 fprintf(stderr, "Key: %s\n", key ? key : "");
362 fflush(stderr);
363 #endif
364 if (!key) {
365 /* Should never happen, just in case. */
366 goto next;
367 }
368
369 /* Ignore array index keys: usually not desirable, and would
370 * also require ['0'] quoting.
371 */
372 if (completion_digit(key[0])) {
373 goto next;
374 }
375
376 /* XXX: There's no key quoting now, it would require replacing the
377 * last component with a ['foo\nbar'] style lookup when appropriate.
378 */
379
380 if (strlen(prefix) == 0) {
381 /* Partial ends in a period, e.g. 'Math.' -> complete all Math properties. */
382 duk_push_string(ctx, orig); /* original, e.g. 'Math.' */
383 duk_push_string(ctx, key);
384 duk_concat(ctx, 2);
385 linenoiseAddCompletion(lc, duk_require_string(ctx, -1));
386 duk_pop(ctx);
387 } else if (prefix && strcmp(key, prefix) == 0) {
388 /* Full completion, add a period, e.g. input 'Math' -> 'Math.'. */
389 duk_push_string(ctx, orig); /* original, including partial last component */
390 duk_push_string(ctx, ".");
391 duk_concat(ctx, 2);
392 linenoiseAddCompletion(lc, duk_require_string(ctx, -1));
393 duk_pop(ctx);
394 } else if (prefix && strncmp(key, prefix, strlen(prefix)) == 0) {
395 /* Last component is partial, complete. */
396 duk_push_string(ctx, orig); /* original, including partial last component */
397 duk_push_string(ctx, key + strlen(prefix)); /* completion to last component */
398 duk_concat(ctx, 2);
399 linenoiseAddCompletion(lc, duk_require_string(ctx, -1));
400 duk_pop(ctx);
401 }
402
403 next:
404 duk_pop(ctx);
405 }
406 return 0;
407 } else {
408 if (duk_get_prop(ctx, idx_obj)) {
409 duk_to_object(ctx, -1); /* for properties of plain strings etc */
410 duk_replace(ctx, idx_obj);
411 p_curr = p + 1;
412 } else {
413 /* Not found. */
414 return 0;
415 }
416 }
417 }
418
419 return 0;
420 }
421
422 static void linenoise_completion(const char *buf, linenoiseCompletions *lc) {
423 duk_context *ctx;
424 const unsigned char *p_start;
425 const unsigned char *p_end;
426 const unsigned char *p;
427 duk_int_t rc;
428
429 if (!buf) {
430 return;
431 }
432 ctx = completion_ctx;
433 if (!ctx) {
434 return;
435 }
436
437 p_start = (const unsigned char *) buf;
438 p_end = (const unsigned char *) (buf + strlen(buf));
439 p = p_end;
440
441 /* Scan backwards for a maximal string which looks like a property
442 * chain (e.g. foo.bar.quux).
443 */
444
445 while (--p >= p_start) {
446 if (p[0] == (unsigned char) '.') {
447 if (p <= p_start) {
448 break;
449 }
450 if (!completion_idpart(p[-1])) {
451 /* Catches e.g. 'foo..bar' -> we want 'bar' only. */
452 break;
453 }
454 } else if (!completion_idpart(p[0])) {
455 break;
456 }
457 }
458 /* 'p' will either be p_start - 1 (ran out of buffer) or point to
459 * the first offending character.
460 */
461 p++;
462 if (p < p_start || p >= p_end) {
463 return; /* should never happen, but just in case */
464 }
465
466 /* 'p' now points to a string of the form 'foo.bar.quux'. Look up
467 * all the components except the last; treat the last component as
468 * a partial name which is used as a filter for the previous full
469 * component. All lookups are from the global object now.
470 */
471
472 #if 0
473 fprintf(stderr, "Completion starting point: '%s'\n", p);
474 fflush(stderr);
475 #endif
476
477 duk_push_string(ctx, (const char *) buf);
478 duk_push_lstring(ctx, (const char *) p, (duk_size_t) (p_end - p));
479 duk_push_pointer(ctx, (void *) lc);
480
481 rc = duk_safe_call(ctx, linenoise_completion_lookup, 3 /*nargs*/, 1 /*nrets*/);
482 if (rc != DUK_EXEC_SUCCESS) {
483 fprintf(stderr, "Completion handling failure: %s\n", duk_safe_to_string(ctx, -1));
484 }
485 duk_pop(ctx);
486 }
487 #endif /* DUK_CMDLINE_LINENOISE_COMPLETION */
488
489 /*
490 * Execute from file handle etc
491 */
492
493 static int handle_fh(duk_context *ctx, FILE *f, const char *filename, const char *bytecode_filename) {
494 char *buf = NULL;
495 size_t bufsz;
496 size_t bufoff;
497 size_t got;
498 int rc;
499 int retval = -1;
500
501 buf = (char *) malloc(1024);
502 if (!buf) {
503 goto error;
504 }
505 bufsz = 1024;
506 bufoff = 0;
507
508 /* Read until EOF, avoid fseek/stat because it won't work with stdin. */
509 for (;;) {
510 size_t avail;
511
512 avail = bufsz - bufoff;
513 if (avail < 1024) {
514 size_t newsz;
515 char *buf_new;
516 #if 0
517 fprintf(stderr, "resizing read buffer: %ld -> %ld\n", (long) bufsz, (long) (bufsz * 2));
518 #endif
519 newsz = bufsz + (bufsz >> 2) + 1024; /* +25% and some extra */
520 buf_new = (char *) realloc(buf, newsz);
521 if (!buf_new) {
522 goto error;
523 }
524 buf = buf_new;
525 bufsz = newsz;
526 }
527
528 avail = bufsz - bufoff;
529 #if 0
530 fprintf(stderr, "reading input: buf=%p bufsz=%ld bufoff=%ld avail=%ld\n",
531 (void *) buf, (long) bufsz, (long) bufoff, (long) avail);
532 #endif
533
534 got = fread((void *) (buf + bufoff), (size_t) 1, avail, f);
535 #if 0
536 fprintf(stderr, "got=%ld\n", (long) got);
537 #endif
538 if (got == 0) {
539 break;
540 }
541 bufoff += got;
542
543 /* Emscripten specific: stdin EOF doesn't work as expected.
544 * Instead, when 'emduk' is executed using Node.js, a file
545 * piped to stdin repeats (!). Detect that repeat and cut off
546 * the stdin read. Ensure the loop repeats enough times to
547 * avoid detecting spurious loops.
548 *
549 * This only seems to work for inputs up to 256 bytes long.
550 */
551 #if defined(EMSCRIPTEN)
552 if (bufoff >= 16384) {
553 size_t i, j, nloops;
554 int looped = 0;
555
556 for (i = 16; i < bufoff / 8; i++) {
557 int ok;
558
559 nloops = bufoff / i;
560 ok = 1;
561 for (j = 1; j < nloops; j++) {
562 if (memcmp((void *) buf, (void *) (buf + i * j), i) != 0) {
563 ok = 0;
564 break;
565 }
566 }
567 if (ok) {
568 fprintf(stderr, "emscripten workaround: detect looping at index %ld, verified with %ld loops\n", (long) i, (long) (nloops - 1));
569 bufoff = i;
570 looped = 1;
571 break;
572 }
573 }
574
575 if (looped) {
576 break;
577 }
578 }
579 #endif
580 }
581
582 duk_push_string(ctx, bytecode_filename);
583 duk_push_pointer(ctx, (void *) buf);
584 duk_push_uint(ctx, (duk_uint_t) bufoff);
585 duk_push_string(ctx, filename);
586
587 interactive_mode = 0; /* global */
588
589 rc = duk_safe_call(ctx, wrapped_compile_execute, 4 /*nargs*/, 1 /*nret*/);
590
591 #if defined(DUK_CMDLINE_AJSHEAP)
592 ajsheap_clear_exec_timeout();
593 #endif
594
595 free(buf);
596 buf = NULL;
597
598 if (rc != DUK_EXEC_SUCCESS) {
599 print_pop_error(ctx, stderr);
600 goto error;
601 } else {
602 duk_pop(ctx);
603 retval = 0;
604 }
605 /* fall thru */
606
607 cleanup:
608 if (buf) {
609 free(buf);
610 buf = NULL;
611 }
612 return retval;
613
614 error:
615 fprintf(stderr, "error in executing file %s\n", filename);
616 fflush(stderr);
617 goto cleanup;
618 }
619
620 static int handle_file(duk_context *ctx, const char *filename, const char *bytecode_filename) {
621 FILE *f = NULL;
622 int retval;
623 char fnbuf[256];
624
625 /* Example of sending an application specific debugger notification. */
626 duk_push_string(ctx, "DebuggerHandleFile");
627 duk_push_string(ctx, filename);
628 duk_debugger_notify(ctx, 2);
629
630 #if defined(EMSCRIPTEN)
631 if (filename[0] == '/') {
632 snprintf(fnbuf, sizeof(fnbuf), "%s", filename);
633 } else {
634 snprintf(fnbuf, sizeof(fnbuf), "/working/%s", filename);
635 }
636 #else
637 snprintf(fnbuf, sizeof(fnbuf), "%s", filename);
638 #endif
639 fnbuf[sizeof(fnbuf) - 1] = (char) 0;
640
641 f = fopen(fnbuf, "rb");
642 if (!f) {
643 fprintf(stderr, "failed to open source file: %s\n", filename);
644 fflush(stderr);
645 goto error;
646 }
647
648 retval = handle_fh(ctx, f, filename, bytecode_filename);
649
650 fclose(f);
651 return retval;
652
653 error:
654 return -1;
655 }
656
657 static int handle_eval(duk_context *ctx, char *code) {
658 int rc;
659 int retval = -1;
660
661 duk_push_pointer(ctx, (void *) code);
662 duk_push_uint(ctx, (duk_uint_t) strlen(code));
663 duk_push_string(ctx, "eval");
664
665 interactive_mode = 0; /* global */
666
667 rc = duk_safe_call(ctx, wrapped_compile_execute, 3 /*nargs*/, 1 /*nret*/);
668
669 #if defined(DUK_CMDLINE_AJSHEAP)
670 ajsheap_clear_exec_timeout();
671 #endif
672
673 if (rc != DUK_EXEC_SUCCESS) {
674 print_pop_error(ctx, stderr);
675 } else {
676 duk_pop(ctx);
677 retval = 0;
678 }
679
680 return retval;
681 }
682
683 #if defined(DUK_CMDLINE_LINENOISE)
684 static int handle_interactive(duk_context *ctx) {
685 const char *prompt = "duk> ";
686 char *buffer = NULL;
687 int retval = 0;
688 int rc;
689
690 duk_eval_string(ctx, GREET_CODE(" [linenoise]"));
691 duk_pop(ctx);
692
693 linenoiseSetMultiLine(1);
694 linenoiseHistorySetMaxLen(64);
695 #if defined(DUK_CMDLINE_LINENOISE_COMPLETION)
696 linenoiseSetCompletionCallback(linenoise_completion);
697 #endif
698
699 for (;;) {
700 if (buffer) {
701 linenoiseFree(buffer);
702 buffer = NULL;
703 }
704
705 #if defined(DUK_CMDLINE_LINENOISE_COMPLETION)
706 completion_ctx = ctx;
707 #endif
708 buffer = linenoise(prompt);
709 #if defined(DUK_CMDLINE_LINENOISE_COMPLETION)
710 completion_ctx = NULL;
711 #endif
712
713 if (!buffer) {
714 break;
715 }
716
717 if (buffer && buffer[0] != (char) 0) {
718 linenoiseHistoryAdd(buffer);
719 }
720
721 duk_push_pointer(ctx, (void *) buffer);
722 duk_push_uint(ctx, (duk_uint_t) strlen(buffer));
723 duk_push_string(ctx, "input");
724
725 interactive_mode = 1; /* global */
726
727 rc = duk_safe_call(ctx, wrapped_compile_execute, 3 /*nargs*/, 1 /*nret*/);
728
729 #if defined(DUK_CMDLINE_AJSHEAP)
730 ajsheap_clear_exec_timeout();
731 #endif
732
733 if (buffer) {
734 linenoiseFree(buffer);
735 buffer = NULL;
736 }
737
738 if (rc != DUK_EXEC_SUCCESS) {
739 /* in interactive mode, write to stdout */
740 print_pop_error(ctx, stdout);
741 retval = -1; /* an error 'taints' the execution */
742 } else {
743 duk_pop(ctx);
744 }
745 }
746
747 if (buffer) {
748 linenoiseFree(buffer);
749 buffer = NULL;
750 }
751
752 return retval;
753 }
754 #else /* DUK_CMDLINE_LINENOISE */
755 static int handle_interactive(duk_context *ctx) {
756 const char *prompt = "duk> ";
757 char *buffer = NULL;
758 int retval = 0;
759 int rc;
760 int got_eof = 0;
761
762 duk_eval_string(ctx, GREET_CODE(""));
763 duk_pop(ctx);
764
765 buffer = (char *) malloc(LINEBUF_SIZE);
766 if (!buffer) {
767 fprintf(stderr, "failed to allocated a line buffer\n");
768 fflush(stderr);
769 retval = -1;
770 goto done;
771 }
772
773 while (!got_eof) {
774 size_t idx = 0;
775
776 fwrite(prompt, 1, strlen(prompt), stdout);
777 fflush(stdout);
778
779 for (;;) {
780 int c = fgetc(stdin);
781 if (c == EOF) {
782 got_eof = 1;
783 break;
784 } else if (c == '\n') {
785 break;
786 } else if (idx >= LINEBUF_SIZE) {
787 fprintf(stderr, "line too long\n");
788 fflush(stderr);
789 retval = -1;
790 goto done;
791 } else {
792 buffer[idx++] = (char) c;
793 }
794 }
795
796 duk_push_pointer(ctx, (void *) buffer);
797 duk_push_uint(ctx, (duk_uint_t) idx);
798 duk_push_string(ctx, "input");
799
800 interactive_mode = 1; /* global */
801
802 rc = duk_safe_call(ctx, wrapped_compile_execute, 3 /*nargs*/, 1 /*nret*/);
803
804 #if defined(DUK_CMDLINE_AJSHEAP)
805 ajsheap_clear_exec_timeout();
806 #endif
807
808 if (rc != DUK_EXEC_SUCCESS) {
809 /* in interactive mode, write to stdout */
810 print_pop_error(ctx, stdout);
811 retval = -1; /* an error 'taints' the execution */
812 } else {
813 duk_pop(ctx);
814 }
815 }
816
817 done:
818 if (buffer) {
819 free(buffer);
820 buffer = NULL;
821 }
822
823 return retval;
824 }
825 #endif /* DUK_CMDLINE_LINENOISE */
826
827 /*
828 * Simple file read/write bindings
829 */
830
831 #if defined(DUK_CMDLINE_FILEIO)
832 static duk_ret_t fileio_read_file(duk_context *ctx) {
833 const char *fn;
834 char *buf;
835 size_t len;
836 size_t off;
837 int rc;
838 FILE *f;
839
840 fn = duk_require_string(ctx, 0);
841 f = fopen(fn, "rb");
842 if (!f) {
843 duk_error(ctx, DUK_ERR_TYPE_ERROR, "cannot open file %s for reading, errno %ld: %s",
844 fn, (long) errno, strerror(errno));
845 }
846
847 rc = fseek(f, 0, SEEK_END);
848 if (rc < 0) {
849 (void) fclose(f);
850 duk_error(ctx, DUK_ERR_TYPE_ERROR, "fseek() failed for %s, errno %ld: %s",
851 fn, (long) errno, strerror(errno));
852 }
853 len = (size_t) ftell(f);
854 rc = fseek(f, 0, SEEK_SET);
855 if (rc < 0) {
856 (void) fclose(f);
857 duk_error(ctx, DUK_ERR_TYPE_ERROR, "fseek() failed for %s, errno %ld: %s",
858 fn, (long) errno, strerror(errno));
859 }
860
861 buf = (char *) duk_push_fixed_buffer(ctx, (duk_size_t) len);
862 for (off = 0; off < len;) {
863 size_t got;
864 got = fread((void *) (buf + off), 1, len - off, f);
865 if (ferror(f)) {
866 (void) fclose(f);
867 duk_error(ctx, DUK_ERR_TYPE_ERROR, "error while reading %s", fn);
868 }
869 if (got == 0) {
870 if (feof(f)) {
871 break;
872 } else {
873 (void) fclose(f);
874 duk_error(ctx, DUK_ERR_TYPE_ERROR, "error while reading %s", fn);
875 }
876 }
877 off += got;
878 }
879
880 if (f) {
881 (void) fclose(f);
882 }
883
884 return 1;
885 }
886
887 static duk_ret_t fileio_write_file(duk_context *ctx) {
888 const char *fn;
889 const char *buf;
890 size_t len;
891 size_t off;
892 FILE *f;
893
894 fn = duk_require_string(ctx, 0);
895 f = fopen(fn, "wb");
896 if (!f) {
897 duk_error(ctx, DUK_ERR_TYPE_ERROR, "cannot open file %s for writing, errno %ld: %s",
898 fn, (long) errno, strerror(errno));
899 }
900
901 len = 0;
902 buf = (char *) duk_to_buffer(ctx, 1, &len);
903 for (off = 0; off < len;) {
904 size_t got;
905 got = fwrite((const void *) (buf + off), 1, len - off, f);
906 if (ferror(f)) {
907 (void) fclose(f);
908 duk_error(ctx, DUK_ERR_TYPE_ERROR, "error while writing %s", fn);
909 }
910 if (got == 0) {
911 (void) fclose(f);
912 duk_error(ctx, DUK_ERR_TYPE_ERROR, "error while writing %s", fn);
913 }
914 off += got;
915 }
916
917 if (f) {
918 (void) fclose(f);
919 }
920
921 return 0;
922 }
923 #endif /* DUK_CMDLINE_FILEIO */
924
925 /*
926 * Duktape heap lifecycle
927 */
928
929 #if defined(DUK_CMDLINE_DEBUGGER_SUPPORT)
930 static duk_idx_t debugger_request(duk_context *ctx, void *udata, duk_idx_t nvalues) {
931 const char *cmd;
932 int i;
933
934 (void) udata;
935
936 if (nvalues < 1) {
937 duk_push_string(ctx, "missing AppRequest argument(s)");
938 return -1;
939 }
940
941 cmd = duk_get_string(ctx, -nvalues + 0);
942
943 if (cmd && strcmp(cmd, "CommandLine") == 0) {
944 if (!duk_check_stack(ctx, main_argc)) {
945 /* Callback should avoid errors for now, so use
946 * duk_check_stack() rather than duk_require_stack().
947 */
948 duk_push_string(ctx, "failed to extend stack");
949 return -1;
950 }
951 for (i = 0; i < main_argc; i++) {
952 duk_push_string(ctx, main_argv[i]);
953 }
954 return main_argc;
955 }
956 duk_push_sprintf(ctx, "command not supported");
957 return -1;
958 }
959
960 static void debugger_detached(void *udata) {
961 duk_context *ctx = (duk_context *) udata;
962 (void) ctx;
963 fprintf(stderr, "Debugger detached, udata: %p\n", (void *) udata);
964 fflush(stderr);
965
966 /* Ensure socket is closed even when detach is initiated by Duktape
967 * rather than debug client.
968 */
969 duk_trans_socket_finish();
970
971 if (debugger_reattach) {
972 /* For automatic reattach testing. */
973 duk_trans_socket_init();
974 duk_trans_socket_waitconn();
975 fprintf(stderr, "Debugger reconnected, call duk_debugger_attach()\n");
976 fflush(stderr);
977 #if 0
978 /* This is not necessary but should be harmless. */
979 duk_debugger_detach(ctx);
980 #endif
981 duk_debugger_attach_custom(ctx,
982 duk_trans_socket_read_cb,
983 duk_trans_socket_write_cb,
984 duk_trans_socket_peek_cb,
985 duk_trans_socket_read_flush_cb,
986 duk_trans_socket_write_flush_cb,
987 debugger_request,
988 debugger_detached,
989 (void *) ctx);
990 }
991 }
992 #endif
993
994 #define ALLOC_DEFAULT 0
995 #define ALLOC_LOGGING 1
996 #define ALLOC_TORTURE 2
997 #define ALLOC_HYBRID 3
998 #define ALLOC_AJSHEAP 4
999
1000 static duk_context *create_duktape_heap(int alloc_provider, int debugger, int ajsheap_log) {
1001 duk_context *ctx;
1002
1003 (void) ajsheap_log; /* suppress warning */
1004
1005 ctx = NULL;
1006 if (!ctx && alloc_provider == ALLOC_LOGGING) {
1007 #if defined(DUK_CMDLINE_ALLOC_LOGGING)
1008 ctx = duk_create_heap(duk_alloc_logging,
1009 duk_realloc_logging,
1010 duk_free_logging,
1011 (void *) 0xdeadbeef,
1012 NULL);
1013 #else
1014 fprintf(stderr, "Warning: option --alloc-logging ignored, no logging allocator support\n");
1015 fflush(stderr);
1016 #endif
1017 }
1018 if (!ctx && alloc_provider == ALLOC_TORTURE) {
1019 #if defined(DUK_CMDLINE_ALLOC_TORTURE)
1020 ctx = duk_create_heap(duk_alloc_torture,
1021 duk_realloc_torture,
1022 duk_free_torture,
1023 (void *) 0xdeadbeef,
1024 NULL);
1025 #else
1026 fprintf(stderr, "Warning: option --alloc-torture ignored, no torture allocator support\n");
1027 fflush(stderr);
1028 #endif
1029 }
1030 if (!ctx && alloc_provider == ALLOC_HYBRID) {
1031 #if defined(DUK_CMDLINE_ALLOC_HYBRID)
1032 void *udata = duk_alloc_hybrid_init();
1033 if (!udata) {
1034 fprintf(stderr, "Failed to init hybrid allocator\n");
1035 fflush(stderr);
1036 } else {
1037 ctx = duk_create_heap(duk_alloc_hybrid,
1038 duk_realloc_hybrid,
1039 duk_free_hybrid,
1040 udata,
1041 NULL);
1042 }
1043 #else
1044 fprintf(stderr, "Warning: option --alloc-hybrid ignored, no hybrid allocator support\n");
1045 fflush(stderr);
1046 #endif
1047 }
1048 if (!ctx && alloc_provider == ALLOC_AJSHEAP) {
1049 #if defined(DUK_CMDLINE_AJSHEAP)
1050 ajsheap_init();
1051
1052 ctx = duk_create_heap(
1053 ajsheap_log ? ajsheap_alloc_wrapped : AJS_Alloc,
1054 ajsheap_log ? ajsheap_realloc_wrapped : AJS_Realloc,
1055 ajsheap_log ? ajsheap_free_wrapped : AJS_Free,
1056 (void *) 0xdeadbeef, /* heap_udata: ignored by AjsHeap, use as marker */
1057 NULL
1058 ); /* fatal_handler */
1059 #else
1060 fprintf(stderr, "Warning: option --alloc-ajsheap ignored, no ajsheap allocator support\n");
1061 fflush(stderr);
1062 #endif
1063 }
1064 if (!ctx && alloc_provider == ALLOC_DEFAULT) {
1065 ctx = duk_create_heap_default();
1066 }
1067
1068 if (!ctx) {
1069 fprintf(stderr, "Failed to create Duktape heap\n");
1070 fflush(stderr);
1071 exit(-1);
1072 }
1073
1074 #if defined(DUK_CMDLINE_AJSHEAP)
1075 if (alloc_provider == ALLOC_AJSHEAP) {
1076 fprintf(stdout, "Pool dump after heap creation\n");
1077 ajsheap_dump();
1078 }
1079 #endif
1080
1081 #if defined(DUK_CMDLINE_AJSHEAP)
1082 if (alloc_provider == ALLOC_AJSHEAP) {
1083 ajsheap_register(ctx);
1084 }
1085 #endif
1086
1087 #if defined(DUK_CMDLINE_FILEIO)
1088 duk_push_c_function(ctx, fileio_read_file, 1 /*nargs*/);
1089 duk_put_global_string(ctx, "readFile");
1090 duk_push_c_function(ctx, fileio_write_file, 2 /*nargs*/);
1091 duk_put_global_string(ctx, "writeFile");
1092 #endif
1093
1094 if (debugger) {
1095 #if defined(DUK_CMDLINE_DEBUGGER_SUPPORT)
1096 fprintf(stderr, "Debugger enabled, create socket and wait for connection\n");
1097 fflush(stderr);
1098 duk_trans_socket_init();
1099 duk_trans_socket_waitconn();
1100 fprintf(stderr, "Debugger connected, call duk_debugger_attach() and then execute requested file(s)/eval\n");
1101 fflush(stderr);
1102 duk_debugger_attach_custom(ctx,
1103 duk_trans_socket_read_cb,
1104 duk_trans_socket_write_cb,
1105 duk_trans_socket_peek_cb,
1106 duk_trans_socket_read_flush_cb,
1107 duk_trans_socket_write_flush_cb,
1108 debugger_request,
1109 debugger_detached,
1110 (void *) ctx);
1111 #else
1112 fprintf(stderr, "Warning: option --debugger ignored, no debugger support\n");
1113 fflush(stderr);
1114 #endif
1115 }
1116
1117 #if 0
1118 /* Manual test for duk_debugger_cooperate() */
1119 {
1120 for (i = 0; i < 60; i++) {
1121 printf("cooperate: %d\n", i);
1122 usleep(1000000);
1123 duk_debugger_cooperate(ctx);
1124 }
1125 }
1126 #endif
1127
1128 return ctx;
1129 }
1130
1131 static void destroy_duktape_heap(duk_context *ctx, int alloc_provider) {
1132 (void) alloc_provider;
1133
1134 #if defined(DUK_CMDLINE_AJSHEAP)
1135 if (alloc_provider == ALLOC_AJSHEAP) {
1136 fprintf(stdout, "Pool dump before duk_destroy_heap(), before forced gc\n");
1137 ajsheap_dump();
1138
1139 duk_gc(ctx, 0);
1140
1141 fprintf(stdout, "Pool dump before duk_destroy_heap(), after forced gc\n");
1142 ajsheap_dump();
1143 }
1144 #endif
1145
1146 if (ctx) {
1147 duk_destroy_heap(ctx);
1148 }
1149
1150 #if defined(DUK_CMDLINE_AJSHEAP)
1151 if (alloc_provider == ALLOC_AJSHEAP) {
1152 fprintf(stdout, "Pool dump after duk_destroy_heap() (should have zero allocs)\n");
1153 ajsheap_dump();
1154 }
1155 ajsheap_free();
1156 #endif
1157 }
1158
1159 /*
1160 * Main
1161 */
1162
1163 int main(int argc, char *argv[]) {
1164 duk_context *ctx = NULL;
1165 int retval = 0;
1166 int have_files = 0;
1167 int have_eval = 0;
1168 int interactive = 0;
1169 int memlimit_high = 1;
1170 int alloc_provider = ALLOC_DEFAULT;
1171 int ajsheap_log = 0;
1172 int debugger = 0;
1173 int recreate_heap = 0;
1174 int no_heap_destroy = 0;
1175 int verbose = 0;
1176 int run_stdin = 0;
1177 const char *compile_filename = NULL;
1178 int i;
1179
1180 main_argc = argc;
1181 main_argv = (char **) argv;
1182
1183 #if defined(EMSCRIPTEN)
1184 /* Try to use NODEFS to provide access to local files. Mount the
1185 * CWD as /working, and then prepend "/working/" to relative native
1186 * paths in file calls to get something that works reasonably for
1187 * relative paths. Emscripten doesn't support replacing virtual
1188 * "/" with host "/" (the default MEMFS at "/" can't be unmounted)
1189 * but we can mount "/tmp" as host "/tmp" to allow testcase runs.
1190 *
1191 * https://kripken.github.io/emscripten-site/docs/api_reference/Filesystem-API.html#filesystem-api-nodefs
1192 * https://github.com/kripken/emscripten/blob/master/tests/fs/test_nodefs_rw.c
1193 */
1194 EM_ASM(
1195 /* At the moment it's not possible to replace the default MEMFS mounted at '/':
1196 * https://github.com/kripken/emscripten/issues/2040
1197 * https://github.com/kripken/emscripten/blob/incoming/src/library_fs.js#L1341-L1358
1198 */
1199 /*
1200 try {
1201 FS.unmount("/");
1202 } catch (e) {
1203 console.log("Failed to unmount default '/' MEMFS mount: " + e);
1204 }
1205 */
1206 try {
1207 FS.mkdir("/working");
1208 FS.mount(NODEFS, { root: "." }, "/working");
1209 } catch (e) {
1210 console.log("Failed to mount NODEFS /working: " + e);
1211 }
1212 /* A virtual '/tmp' exists by default:
1213 * https://gist.github.com/evanw/e6be28094f34451bd5bd#file-temp-js-L3806-L3809
1214 */
1215 /*
1216 try {
1217 FS.mkdir("/tmp");
1218 } catch (e) {
1219 console.log("Failed to create virtual /tmp: " + e);
1220 }
1221 */
1222 try {
1223 FS.mount(NODEFS, { root: "/tmp" }, "/tmp");
1224 } catch (e) {
1225 console.log("Failed to mount NODEFS /tmp: " + e);
1226 }
1227 );
1228 #endif /* EMSCRIPTEN */
1229
1230 #if defined(DUK_CMDLINE_AJSHEAP)
1231 alloc_provider = ALLOC_AJSHEAP;
1232 #endif
1233 (void) ajsheap_log;
1234
1235 /*
1236 * Signal handling setup
1237 */
1238
1239 #if defined(DUK_CMDLINE_SIGNAL)
1240 set_sigint_handler();
1241
1242 /* This is useful at the global level; libraries should avoid SIGPIPE though */
1243 /*signal(SIGPIPE, SIG_IGN);*/
1244 #endif
1245
1246 /*
1247 * Parse options
1248 */
1249
1250 for (i = 1; i < argc; i++) {
1251 char *arg = argv[i];
1252 if (!arg) {
1253 goto usage;
1254 }
1255 if (strcmp(arg, "--restrict-memory") == 0) {
1256 memlimit_high = 0;
1257 } else if (strcmp(arg, "-i") == 0) {
1258 interactive = 1;
1259 } else if (strcmp(arg, "-c") == 0) {
1260 if (i == argc - 1) {
1261 goto usage;
1262 }
1263 i++;
1264 compile_filename = argv[i];
1265 } else if (strcmp(arg, "-e") == 0) {
1266 have_eval = 1;
1267 if (i == argc - 1) {
1268 goto usage;
1269 }
1270 i++; /* skip code */
1271 } else if (strcmp(arg, "--alloc-default") == 0) {
1272 alloc_provider = ALLOC_DEFAULT;
1273 } else if (strcmp(arg, "--alloc-logging") == 0) {
1274 alloc_provider = ALLOC_LOGGING;
1275 } else if (strcmp(arg, "--alloc-torture") == 0) {
1276 alloc_provider = ALLOC_TORTURE;
1277 } else if (strcmp(arg, "--alloc-hybrid") == 0) {
1278 alloc_provider = ALLOC_HYBRID;
1279 } else if (strcmp(arg, "--alloc-ajsheap") == 0) {
1280 alloc_provider = ALLOC_AJSHEAP;
1281 } else if (strcmp(arg, "--ajsheap-log") == 0) {
1282 ajsheap_log = 1;
1283 } else if (strcmp(arg, "--debugger") == 0) {
1284 debugger = 1;
1285 #if defined(DUK_CMDLINE_DEBUGGER_SUPPORT)
1286 } else if (strcmp(arg, "--reattach") == 0) {
1287 debugger_reattach = 1;
1288 #endif
1289 } else if (strcmp(arg, "--recreate-heap") == 0) {
1290 recreate_heap = 1;
1291 } else if (strcmp(arg, "--no-heap-destroy") == 0) {
1292 no_heap_destroy = 1;
1293 } else if (strcmp(arg, "--verbose") == 0) {
1294 verbose = 1;
1295 } else if (strcmp(arg, "--run-stdin") == 0) {
1296 run_stdin = 1;
1297 } else if (strlen(arg) >= 1 && arg[0] == '-') {
1298 goto usage;
1299 } else {
1300 have_files = 1;
1301 }
1302 }
1303 if (!have_files && !have_eval && !run_stdin) {
1304 interactive = 1;
1305 }
1306
1307 /*
1308 * Memory limit
1309 */
1310
1311 #if defined(DUK_CMDLINE_RLIMIT)
1312 set_resource_limits(memlimit_high ? MEM_LIMIT_HIGH : MEM_LIMIT_NORMAL);
1313 #else
1314 if (memlimit_high == 0) {
1315 fprintf(stderr, "Warning: option --restrict-memory ignored, no rlimit support\n");
1316 fflush(stderr);
1317 }
1318 #endif
1319
1320 /*
1321 * Create heap
1322 */
1323
1324 ctx = create_duktape_heap(alloc_provider, debugger, ajsheap_log);
1325
1326 /*
1327 * Execute any argument file(s)
1328 */
1329
1330 for (i = 1; i < argc; i++) {
1331 char *arg = argv[i];
1332 if (!arg) {
1333 continue;
1334 } else if (strlen(arg) == 2 && strcmp(arg, "-e") == 0) {
1335 /* Here we know the eval arg exists but check anyway */
1336 if (i == argc - 1) {
1337 retval = 1;
1338 goto cleanup;
1339 }
1340 if (handle_eval(ctx, argv[i + 1]) != 0) {
1341 retval = 1;
1342 goto cleanup;
1343 }
1344 i++; /* skip code */
1345 continue;
1346 } else if (strlen(arg) == 2 && strcmp(arg, "-c") == 0) {
1347 i++; /* skip filename */
1348 continue;
1349 } else if (strlen(arg) >= 1 && arg[0] == '-') {
1350 continue;
1351 }
1352
1353 if (verbose) {
1354 fprintf(stderr, "*** Executing file: %s\n", arg);
1355 fflush(stderr);
1356 }
1357
1358 if (handle_file(ctx, arg, compile_filename) != 0) {
1359 retval = 1;
1360 goto cleanup;
1361 }
1362
1363 if (recreate_heap) {
1364 if (verbose) {
1365 fprintf(stderr, "*** Recreating heap...\n");
1366 fflush(stderr);
1367 }
1368
1369 destroy_duktape_heap(ctx, alloc_provider);
1370 ctx = create_duktape_heap(alloc_provider, debugger, ajsheap_log);
1371 }
1372 }
1373
1374 if (run_stdin) {
1375 /* Running stdin like a full file (reading all lines before
1376 * compiling) is useful with emduk:
1377 * cat test.js | ./emduk --run-stdin
1378 */
1379 if (handle_fh(ctx, stdin, "stdin", compile_filename) != 0) {
1380 retval = 1;
1381 goto cleanup;
1382 }
1383
1384 if (recreate_heap) {
1385 if (verbose) {
1386 fprintf(stderr, "*** Recreating heap...\n");
1387 fflush(stderr);
1388 }
1389
1390 destroy_duktape_heap(ctx, alloc_provider);
1391 ctx = create_duktape_heap(alloc_provider, debugger, ajsheap_log);
1392 }
1393 }
1394
1395 /*
1396 * Enter interactive mode if options indicate it
1397 */
1398
1399 if (interactive) {
1400 if (handle_interactive(ctx) != 0) {
1401 retval = 1;
1402 goto cleanup;
1403 }
1404 }
1405
1406 /*
1407 * Cleanup and exit
1408 */
1409
1410 cleanup:
1411 if (interactive) {
1412 fprintf(stderr, "Cleaning up...\n");
1413 fflush(stderr);
1414 }
1415
1416 if (ctx && no_heap_destroy) {
1417 duk_gc(ctx, 0);
1418 }
1419 if (ctx && !no_heap_destroy) {
1420 destroy_duktape_heap(ctx, alloc_provider);
1421 }
1422 ctx = NULL;
1423
1424 return retval;
1425
1426 /*
1427 * Usage
1428 */
1429
1430 usage:
1431 fprintf(stderr, "Usage: duk [options] [<filenames>]\n"
1432 "\n"
1433 " -i enter interactive mode after executing argument file(s) / eval code\n"
1434 " -e CODE evaluate code\n"
1435 " -c FILE compile into bytecode (use with only one file argument)\n"
1436 " --run-stdin treat stdin like a file, i.e. compile full input (not line by line)\n"
1437 " --verbose verbose messages to stderr\n"
1438 " --restrict-memory use lower memory limit (used by test runner)\n"
1439 " --alloc-default use Duktape default allocator\n"
1440 #if defined(DUK_CMDLINE_ALLOC_LOGGING)
1441 " --alloc-logging use logging allocator (writes to /tmp)\n"
1442 #endif
1443 #if defined(DUK_CMDLINE_ALLOC_TORTURE)
1444 " --alloc-torture use torture allocator\n"
1445 #endif
1446 #if defined(DUK_CMDLINE_ALLOC_HYBRID)
1447 " --alloc-hybrid use hybrid allocator\n"
1448 #endif
1449 #if defined(DUK_CMDLINE_AJSHEAP)
1450 " --alloc-ajsheap use ajsheap allocator (enabled by default with 'ajduk')\n"
1451 " --ajsheap-log write alloc log to /tmp/ajduk-alloc-log.txt\n"
1452 #endif
1453 #if defined(DUK_CMDLINE_DEBUGGER_SUPPORT)
1454 " --debugger start example debugger\n"
1455 " --reattach automatically reattach debugger on detach\n"
1456 #endif
1457 " --recreate-heap recreate heap after every file\n"
1458 " --no-heap-destroy force GC, but don't destroy heap at end (leak testing)\n"
1459 "\n"
1460 "If <filename> is omitted, interactive mode is started automatically.\n");
1461 fflush(stderr);
1462 exit(1);
1463 }