duk_cmdline.c 36 KB

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