1 | /* |
2 | * QuickJS command line compiler |
3 | * |
4 | * Copyright (c) 2018-2021 Fabrice Bellard |
5 | * |
6 | * Permission is hereby granted, free of charge, to any person obtaining a copy |
7 | * of this software and associated documentation files (the "Software"), to deal |
8 | * in the Software without restriction, including without limitation the rights |
9 | * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell |
10 | * copies of the Software, and to permit persons to whom the Software is |
11 | * furnished to do so, subject to the following conditions: |
12 | * |
13 | * The above copyright notice and this permission notice shall be included in |
14 | * all copies or substantial portions of the Software. |
15 | * |
16 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR |
17 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, |
18 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL |
19 | * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER |
20 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, |
21 | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN |
22 | * THE SOFTWARE. |
23 | */ |
24 | #include <stdlib.h> |
25 | #include <stdio.h> |
26 | #include <stdarg.h> |
27 | #include <inttypes.h> |
28 | #include <string.h> |
29 | #include <assert.h> |
30 | #include <unistd.h> |
31 | #include <errno.h> |
32 | #if !defined(_WIN32) |
33 | #include <sys/wait.h> |
34 | #endif |
35 | |
36 | #include "cutils.h" |
37 | #include "quickjs-libc.h" |
38 | |
39 | typedef struct { |
40 | char *name; |
41 | char *short_name; |
42 | int flags; |
43 | } namelist_entry_t; |
44 | |
45 | typedef struct namelist_t { |
46 | namelist_entry_t *array; |
47 | int count; |
48 | int size; |
49 | } namelist_t; |
50 | |
51 | typedef struct { |
52 | const char *option_name; |
53 | const char *init_name; |
54 | } FeatureEntry; |
55 | |
56 | static namelist_t cname_list; |
57 | static namelist_t cmodule_list; |
58 | static namelist_t init_module_list; |
59 | static uint64_t feature_bitmap; |
60 | static FILE *outfile; |
61 | static BOOL byte_swap; |
62 | static BOOL dynamic_export; |
63 | static const char *c_ident_prefix = "qjsc_" ; |
64 | |
65 | #define FE_ALL (-1) |
66 | |
67 | static const FeatureEntry feature_list[] = { |
68 | { "date" , "Date" }, |
69 | { "eval" , "Eval" }, |
70 | { "string-normalize" , "StringNormalize" }, |
71 | { "regexp" , "RegExp" }, |
72 | { "json" , "JSON" }, |
73 | { "proxy" , "Proxy" }, |
74 | { "map" , "MapSet" }, |
75 | { "typedarray" , "TypedArrays" }, |
76 | { "promise" , "Promise" }, |
77 | #define FE_MODULE_LOADER 9 |
78 | { "module-loader" , NULL }, |
79 | { "weakref" , "WeakRef" }, |
80 | }; |
81 | |
82 | void namelist_add(namelist_t *lp, const char *name, const char *short_name, |
83 | int flags) |
84 | { |
85 | namelist_entry_t *e; |
86 | if (lp->count == lp->size) { |
87 | size_t newsize = lp->size + (lp->size >> 1) + 4; |
88 | namelist_entry_t *a = |
89 | realloc(lp->array, sizeof(lp->array[0]) * newsize); |
90 | /* XXX: check for realloc failure */ |
91 | lp->array = a; |
92 | lp->size = newsize; |
93 | } |
94 | e = &lp->array[lp->count++]; |
95 | e->name = strdup(name); |
96 | if (short_name) |
97 | e->short_name = strdup(short_name); |
98 | else |
99 | e->short_name = NULL; |
100 | e->flags = flags; |
101 | } |
102 | |
103 | void namelist_free(namelist_t *lp) |
104 | { |
105 | while (lp->count > 0) { |
106 | namelist_entry_t *e = &lp->array[--lp->count]; |
107 | free(e->name); |
108 | free(e->short_name); |
109 | } |
110 | free(lp->array); |
111 | lp->array = NULL; |
112 | lp->size = 0; |
113 | } |
114 | |
115 | namelist_entry_t *namelist_find(namelist_t *lp, const char *name) |
116 | { |
117 | int i; |
118 | for(i = 0; i < lp->count; i++) { |
119 | namelist_entry_t *e = &lp->array[i]; |
120 | if (!strcmp(e->name, name)) |
121 | return e; |
122 | } |
123 | return NULL; |
124 | } |
125 | |
126 | static void get_c_name(char *buf, size_t buf_size, const char *file) |
127 | { |
128 | const char *p, *r; |
129 | size_t len, i; |
130 | int c; |
131 | char *q; |
132 | |
133 | p = strrchr(file, '/'); |
134 | if (!p) |
135 | p = file; |
136 | else |
137 | p++; |
138 | r = strrchr(p, '.'); |
139 | if (!r) |
140 | len = strlen(p); |
141 | else |
142 | len = r - p; |
143 | pstrcpy(buf, buf_size, c_ident_prefix); |
144 | q = buf + strlen(buf); |
145 | for(i = 0; i < len; i++) { |
146 | c = p[i]; |
147 | if (!((c >= '0' && c <= '9') || |
148 | (c >= 'A' && c <= 'Z') || |
149 | (c >= 'a' && c <= 'z'))) { |
150 | c = '_'; |
151 | } |
152 | if ((q - buf) < buf_size - 1) |
153 | *q++ = c; |
154 | } |
155 | *q = '\0'; |
156 | } |
157 | |
158 | static void dump_hex(FILE *f, const uint8_t *buf, size_t len) |
159 | { |
160 | size_t i, col; |
161 | col = 0; |
162 | for(i = 0; i < len; i++) { |
163 | fprintf(f, " 0x%02x," , buf[i]); |
164 | if (++col == 8) { |
165 | fprintf(f, "\n" ); |
166 | col = 0; |
167 | } |
168 | } |
169 | if (col != 0) |
170 | fprintf(f, "\n" ); |
171 | } |
172 | |
173 | static void output_object_code(JSContext *ctx, |
174 | FILE *fo, JSValueConst obj, const char *c_name, |
175 | BOOL load_only) |
176 | { |
177 | uint8_t *out_buf; |
178 | size_t out_buf_len; |
179 | int flags; |
180 | flags = JS_WRITE_OBJ_BYTECODE; |
181 | if (byte_swap) |
182 | flags |= JS_WRITE_OBJ_BSWAP; |
183 | out_buf = JS_WriteObject(ctx, &out_buf_len, obj, flags); |
184 | if (!out_buf) { |
185 | js_std_dump_error(ctx); |
186 | exit(1); |
187 | } |
188 | |
189 | namelist_add(&cname_list, c_name, NULL, load_only); |
190 | |
191 | fprintf(fo, "const uint32_t %s_size = %u;\n\n" , |
192 | c_name, (unsigned int)out_buf_len); |
193 | fprintf(fo, "const uint8_t %s[%u] = {\n" , |
194 | c_name, (unsigned int)out_buf_len); |
195 | dump_hex(fo, out_buf, out_buf_len); |
196 | fprintf(fo, "};\n\n" ); |
197 | |
198 | js_free(ctx, out_buf); |
199 | } |
200 | |
201 | static int js_module_dummy_init(JSContext *ctx, JSModuleDef *m) |
202 | { |
203 | /* should never be called when compiling JS code */ |
204 | abort(); |
205 | } |
206 | |
207 | static void find_unique_cname(char *cname, size_t cname_size) |
208 | { |
209 | char cname1[1024]; |
210 | int suffix_num; |
211 | size_t len, max_len; |
212 | assert(cname_size >= 32); |
213 | /* find a C name not matching an existing module C name by |
214 | adding a numeric suffix */ |
215 | len = strlen(cname); |
216 | max_len = cname_size - 16; |
217 | if (len > max_len) |
218 | cname[max_len] = '\0'; |
219 | suffix_num = 1; |
220 | for(;;) { |
221 | snprintf(cname1, sizeof(cname1), "%s_%d" , cname, suffix_num); |
222 | if (!namelist_find(&cname_list, cname1)) |
223 | break; |
224 | suffix_num++; |
225 | } |
226 | pstrcpy(cname, cname_size, cname1); |
227 | } |
228 | |
229 | JSModuleDef *jsc_module_loader(JSContext *ctx, |
230 | const char *module_name, void *opaque) |
231 | { |
232 | JSModuleDef *m; |
233 | namelist_entry_t *e; |
234 | |
235 | /* check if it is a declared C or system module */ |
236 | e = namelist_find(&cmodule_list, module_name); |
237 | if (e) { |
238 | /* add in the static init module list */ |
239 | namelist_add(&init_module_list, e->name, e->short_name, 0); |
240 | /* create a dummy module */ |
241 | m = JS_NewCModule(ctx, module_name, js_module_dummy_init); |
242 | } else if (has_suffix(module_name, ".so" )) { |
243 | fprintf(stderr, "Warning: binary module '%s' will be dynamically loaded\n" , module_name); |
244 | /* create a dummy module */ |
245 | m = JS_NewCModule(ctx, module_name, js_module_dummy_init); |
246 | /* the resulting executable will export its symbols for the |
247 | dynamic library */ |
248 | dynamic_export = TRUE; |
249 | } else { |
250 | size_t buf_len; |
251 | uint8_t *buf; |
252 | JSValue func_val; |
253 | char cname[1024]; |
254 | |
255 | buf = js_load_file(ctx, &buf_len, module_name); |
256 | if (!buf) { |
257 | JS_ThrowReferenceError(ctx, "could not load module filename '%s'" , |
258 | module_name); |
259 | return NULL; |
260 | } |
261 | |
262 | /* compile the module */ |
263 | func_val = JS_Eval(ctx, (char *)buf, buf_len, module_name, |
264 | JS_EVAL_TYPE_MODULE | JS_EVAL_FLAG_COMPILE_ONLY); |
265 | js_free(ctx, buf); |
266 | if (JS_IsException(func_val)) |
267 | return NULL; |
268 | get_c_name(cname, sizeof(cname), module_name); |
269 | if (namelist_find(&cname_list, cname)) { |
270 | find_unique_cname(cname, sizeof(cname)); |
271 | } |
272 | output_object_code(ctx, outfile, func_val, cname, TRUE); |
273 | |
274 | /* the module is already referenced, so we must free it */ |
275 | m = JS_VALUE_GET_PTR(func_val); |
276 | JS_FreeValue(ctx, func_val); |
277 | } |
278 | return m; |
279 | } |
280 | |
281 | static void compile_file(JSContext *ctx, FILE *fo, |
282 | const char *filename, |
283 | const char *c_name1, |
284 | int module) |
285 | { |
286 | uint8_t *buf; |
287 | char c_name[1024]; |
288 | int eval_flags; |
289 | JSValue obj; |
290 | size_t buf_len; |
291 | |
292 | buf = js_load_file(ctx, &buf_len, filename); |
293 | if (!buf) { |
294 | fprintf(stderr, "Could not load '%s'\n" , filename); |
295 | exit(1); |
296 | } |
297 | eval_flags = JS_EVAL_FLAG_COMPILE_ONLY; |
298 | if (module < 0) { |
299 | module = (has_suffix(filename, ".mjs" ) || |
300 | JS_DetectModule((const char *)buf, buf_len)); |
301 | } |
302 | if (module) |
303 | eval_flags |= JS_EVAL_TYPE_MODULE; |
304 | else |
305 | eval_flags |= JS_EVAL_TYPE_GLOBAL; |
306 | obj = JS_Eval(ctx, (const char *)buf, buf_len, filename, eval_flags); |
307 | if (JS_IsException(obj)) { |
308 | js_std_dump_error(ctx); |
309 | exit(1); |
310 | } |
311 | js_free(ctx, buf); |
312 | if (c_name1) { |
313 | pstrcpy(c_name, sizeof(c_name), c_name1); |
314 | } else { |
315 | get_c_name(c_name, sizeof(c_name), filename); |
316 | } |
317 | output_object_code(ctx, fo, obj, c_name, FALSE); |
318 | JS_FreeValue(ctx, obj); |
319 | } |
320 | |
321 | static const char main_c_template1[] = |
322 | "int main(int argc, char **argv)\n" |
323 | "{\n" |
324 | " JSRuntime *rt;\n" |
325 | " JSContext *ctx;\n" |
326 | " rt = JS_NewRuntime();\n" |
327 | " js_std_set_worker_new_context_func(JS_NewCustomContext);\n" |
328 | " js_std_init_handlers(rt);\n" |
329 | ; |
330 | |
331 | static const char main_c_template2[] = |
332 | " js_std_loop(ctx);\n" |
333 | " js_std_free_handlers(rt);\n" |
334 | " JS_FreeContext(ctx);\n" |
335 | " JS_FreeRuntime(rt);\n" |
336 | " return 0;\n" |
337 | "}\n" ; |
338 | |
339 | #define PROG_NAME "qjsc" |
340 | |
341 | void help(void) |
342 | { |
343 | printf("QuickJS Compiler version " CONFIG_VERSION "\n" |
344 | "usage: " PROG_NAME " [options] [files]\n" |
345 | "\n" |
346 | "options are:\n" |
347 | "-c only output bytecode to a C file\n" |
348 | "-e output main() and bytecode to a C file (default = executable output)\n" |
349 | "-o output set the output filename\n" |
350 | "-N cname set the C name of the generated data\n" |
351 | "-m compile as Javascript module (default=autodetect)\n" |
352 | "-D module_name compile a dynamically loaded module or worker\n" |
353 | "-M module_name[,cname] add initialization code for an external C module\n" |
354 | "-x byte swapped output\n" |
355 | "-p prefix set the prefix of the generated C names\n" |
356 | "-S n set the maximum stack size to 'n' bytes (default=%d)\n" |
357 | "-s strip all the debug info\n" |
358 | "--keep-source keep the source code\n" , |
359 | JS_DEFAULT_STACK_SIZE); |
360 | #ifdef CONFIG_LTO |
361 | { |
362 | int i; |
363 | printf("-flto use link time optimization\n" ); |
364 | printf("-fno-[" ); |
365 | for(i = 0; i < countof(feature_list); i++) { |
366 | if (i != 0) |
367 | printf("|" ); |
368 | printf("%s" , feature_list[i].option_name); |
369 | } |
370 | printf("]\n" |
371 | " disable selected language features (smaller code size)\n" ); |
372 | } |
373 | #endif |
374 | exit(1); |
375 | } |
376 | |
377 | #if defined(CONFIG_CC) && !defined(_WIN32) |
378 | |
379 | int exec_cmd(char **argv) |
380 | { |
381 | int pid, status, ret; |
382 | |
383 | pid = fork(); |
384 | if (pid == 0) { |
385 | execvp(argv[0], argv); |
386 | exit(1); |
387 | } |
388 | |
389 | for(;;) { |
390 | ret = waitpid(pid, &status, 0); |
391 | if (ret == pid && WIFEXITED(status)) |
392 | break; |
393 | } |
394 | return WEXITSTATUS(status); |
395 | } |
396 | |
397 | static int output_executable(const char *out_filename, const char *cfilename, |
398 | BOOL use_lto, BOOL verbose, const char *exename) |
399 | { |
400 | const char *argv[64]; |
401 | const char **arg, *bn_suffix, *lto_suffix; |
402 | char libjsname[1024]; |
403 | char exe_dir[1024], inc_dir[1024], lib_dir[1024], buf[1024], *p; |
404 | int ret; |
405 | |
406 | /* get the directory of the executable */ |
407 | pstrcpy(exe_dir, sizeof(exe_dir), exename); |
408 | p = strrchr(exe_dir, '/'); |
409 | if (p) { |
410 | *p = '\0'; |
411 | } else { |
412 | pstrcpy(exe_dir, sizeof(exe_dir), "." ); |
413 | } |
414 | |
415 | /* if 'quickjs.h' is present at the same path as the executable, we |
416 | use it as include and lib directory */ |
417 | snprintf(buf, sizeof(buf), "%s/quickjs.h" , exe_dir); |
418 | if (access(buf, R_OK) == 0) { |
419 | pstrcpy(inc_dir, sizeof(inc_dir), exe_dir); |
420 | pstrcpy(lib_dir, sizeof(lib_dir), exe_dir); |
421 | } else { |
422 | snprintf(inc_dir, sizeof(inc_dir), "%s/include/quickjs" , CONFIG_PREFIX); |
423 | snprintf(lib_dir, sizeof(lib_dir), "%s/lib/quickjs" , CONFIG_PREFIX); |
424 | } |
425 | |
426 | lto_suffix = "" ; |
427 | bn_suffix = "" ; |
428 | |
429 | arg = argv; |
430 | *arg++ = CONFIG_CC; |
431 | *arg++ = "-O2" ; |
432 | #ifdef CONFIG_LTO |
433 | if (use_lto) { |
434 | *arg++ = "-flto" ; |
435 | lto_suffix = ".lto" ; |
436 | } |
437 | #endif |
438 | /* XXX: use the executable path to find the includes files and |
439 | libraries */ |
440 | *arg++ = "-D" ; |
441 | *arg++ = "_GNU_SOURCE" ; |
442 | *arg++ = "-I" ; |
443 | *arg++ = inc_dir; |
444 | *arg++ = "-o" ; |
445 | *arg++ = out_filename; |
446 | if (dynamic_export) |
447 | *arg++ = "-rdynamic" ; |
448 | *arg++ = cfilename; |
449 | snprintf(libjsname, sizeof(libjsname), "%s/libquickjs%s%s.a" , |
450 | lib_dir, bn_suffix, lto_suffix); |
451 | *arg++ = libjsname; |
452 | *arg++ = "-lm" ; |
453 | *arg++ = "-ldl" ; |
454 | *arg++ = "-lpthread" ; |
455 | *arg = NULL; |
456 | |
457 | if (verbose) { |
458 | for(arg = argv; *arg != NULL; arg++) |
459 | printf("%s " , *arg); |
460 | printf("\n" ); |
461 | } |
462 | |
463 | ret = exec_cmd((char **)argv); |
464 | unlink(cfilename); |
465 | return ret; |
466 | } |
467 | #else |
468 | static int output_executable(const char *out_filename, const char *cfilename, |
469 | BOOL use_lto, BOOL verbose, const char *exename) |
470 | { |
471 | fprintf(stderr, "Executable output is not supported for this target\n" ); |
472 | exit(1); |
473 | return 0; |
474 | } |
475 | #endif |
476 | |
477 | static size_t get_suffixed_size(const char *str) |
478 | { |
479 | char *p; |
480 | size_t v; |
481 | v = (size_t)strtod(str, &p); |
482 | switch(*p) { |
483 | case 'G': |
484 | v <<= 30; |
485 | break; |
486 | case 'M': |
487 | v <<= 20; |
488 | break; |
489 | case 'k': |
490 | case 'K': |
491 | v <<= 10; |
492 | break; |
493 | default: |
494 | if (*p != '\0') { |
495 | fprintf(stderr, "qjs: invalid suffix: %s\n" , p); |
496 | exit(1); |
497 | } |
498 | break; |
499 | } |
500 | return v; |
501 | } |
502 | |
503 | typedef enum { |
504 | OUTPUT_C, |
505 | OUTPUT_C_MAIN, |
506 | OUTPUT_EXECUTABLE, |
507 | } OutputTypeEnum; |
508 | |
509 | static const char *get_short_optarg(int *poptind, int opt, |
510 | const char *arg, int argc, char **argv) |
511 | { |
512 | const char *optarg; |
513 | if (*arg) { |
514 | optarg = arg; |
515 | } else if (*poptind < argc) { |
516 | optarg = argv[(*poptind)++]; |
517 | } else { |
518 | fprintf(stderr, "qjsc: expecting parameter for -%c\n" , opt); |
519 | exit(1); |
520 | } |
521 | return optarg; |
522 | } |
523 | |
524 | int main(int argc, char **argv) |
525 | { |
526 | int i, verbose, strip_flags; |
527 | const char *out_filename, *cname; |
528 | char cfilename[1024]; |
529 | FILE *fo; |
530 | JSRuntime *rt; |
531 | JSContext *ctx; |
532 | BOOL use_lto; |
533 | int module; |
534 | OutputTypeEnum output_type; |
535 | size_t stack_size; |
536 | namelist_t dynamic_module_list; |
537 | |
538 | out_filename = NULL; |
539 | output_type = OUTPUT_EXECUTABLE; |
540 | cname = NULL; |
541 | feature_bitmap = FE_ALL; |
542 | module = -1; |
543 | byte_swap = FALSE; |
544 | verbose = 0; |
545 | strip_flags = JS_STRIP_SOURCE; |
546 | use_lto = FALSE; |
547 | stack_size = 0; |
548 | memset(&dynamic_module_list, 0, sizeof(dynamic_module_list)); |
549 | |
550 | /* add system modules */ |
551 | namelist_add(&cmodule_list, "std" , "std" , 0); |
552 | namelist_add(&cmodule_list, "os" , "os" , 0); |
553 | |
554 | optind = 1; |
555 | while (optind < argc && *argv[optind] == '-') { |
556 | char *arg = argv[optind] + 1; |
557 | const char *longopt = "" ; |
558 | const char *optarg; |
559 | /* a single - is not an option, it also stops argument scanning */ |
560 | if (!*arg) |
561 | break; |
562 | optind++; |
563 | if (*arg == '-') { |
564 | longopt = arg + 1; |
565 | arg += strlen(arg); |
566 | /* -- stops argument scanning */ |
567 | if (!*longopt) |
568 | break; |
569 | } |
570 | for (; *arg || *longopt; longopt = "" ) { |
571 | char opt = *arg; |
572 | if (opt) |
573 | arg++; |
574 | if (opt == 'h' || opt == '?' || !strcmp(longopt, "help" )) { |
575 | help(); |
576 | continue; |
577 | } |
578 | if (opt == 'o') { |
579 | out_filename = get_short_optarg(&optind, opt, arg, argc, argv); |
580 | break; |
581 | } |
582 | if (opt == 'c') { |
583 | output_type = OUTPUT_C; |
584 | continue; |
585 | } |
586 | if (opt == 'e') { |
587 | output_type = OUTPUT_C_MAIN; |
588 | continue; |
589 | } |
590 | if (opt == 'N') { |
591 | cname = get_short_optarg(&optind, opt, arg, argc, argv); |
592 | break; |
593 | } |
594 | if (opt == 'f') { |
595 | const char *p; |
596 | optarg = get_short_optarg(&optind, opt, arg, argc, argv); |
597 | p = optarg; |
598 | if (!strcmp(p, "lto" )) { |
599 | use_lto = TRUE; |
600 | } else if (strstart(p, "no-" , &p)) { |
601 | use_lto = TRUE; |
602 | for(i = 0; i < countof(feature_list); i++) { |
603 | if (!strcmp(p, feature_list[i].option_name)) { |
604 | feature_bitmap &= ~((uint64_t)1 << i); |
605 | break; |
606 | } |
607 | } |
608 | if (i == countof(feature_list)) |
609 | goto bad_feature; |
610 | } else { |
611 | bad_feature: |
612 | fprintf(stderr, "unsupported feature: %s\n" , optarg); |
613 | exit(1); |
614 | } |
615 | break; |
616 | } |
617 | if (opt == 'm') { |
618 | module = 1; |
619 | continue; |
620 | } |
621 | if (opt == 'M') { |
622 | char *p; |
623 | char path[1024]; |
624 | char cname[1024]; |
625 | |
626 | optarg = get_short_optarg(&optind, opt, arg, argc, argv); |
627 | pstrcpy(path, sizeof(path), optarg); |
628 | p = strchr(path, ','); |
629 | if (p) { |
630 | *p = '\0'; |
631 | pstrcpy(cname, sizeof(cname), p + 1); |
632 | } else { |
633 | get_c_name(cname, sizeof(cname), path); |
634 | } |
635 | namelist_add(&cmodule_list, path, cname, 0); |
636 | break; |
637 | } |
638 | if (opt == 'D') { |
639 | optarg = get_short_optarg(&optind, opt, arg, argc, argv); |
640 | namelist_add(&dynamic_module_list, optarg, NULL, 0); |
641 | break; |
642 | } |
643 | if (opt == 'x') { |
644 | byte_swap = 1; |
645 | continue; |
646 | } |
647 | if (opt == 'v') { |
648 | verbose++; |
649 | continue; |
650 | } |
651 | if (opt == 'p') { |
652 | c_ident_prefix = get_short_optarg(&optind, opt, arg, argc, argv); |
653 | break; |
654 | } |
655 | if (opt == 'S') { |
656 | optarg = get_short_optarg(&optind, opt, arg, argc, argv); |
657 | stack_size = get_suffixed_size(optarg); |
658 | break; |
659 | } |
660 | if (opt == 's') { |
661 | strip_flags = JS_STRIP_DEBUG; |
662 | continue; |
663 | } |
664 | if (!strcmp(longopt, "keep-source" )) { |
665 | strip_flags = 0; |
666 | continue; |
667 | } |
668 | if (opt) { |
669 | fprintf(stderr, "qjsc: unknown option '-%c'\n" , opt); |
670 | } else { |
671 | fprintf(stderr, "qjsc: unknown option '--%s'\n" , longopt); |
672 | } |
673 | help(); |
674 | } |
675 | } |
676 | |
677 | if (optind >= argc) |
678 | help(); |
679 | |
680 | if (!out_filename) { |
681 | if (output_type == OUTPUT_EXECUTABLE) { |
682 | out_filename = "a.out" ; |
683 | } else { |
684 | out_filename = "out.c" ; |
685 | } |
686 | } |
687 | |
688 | if (output_type == OUTPUT_EXECUTABLE) { |
689 | #if defined(_WIN32) || defined(__ANDROID__) |
690 | /* XXX: find a /tmp directory ? */ |
691 | snprintf(cfilename, sizeof(cfilename), "out%d.c" , getpid()); |
692 | #else |
693 | snprintf(cfilename, sizeof(cfilename), "/tmp/out%d.c" , getpid()); |
694 | #endif |
695 | } else { |
696 | pstrcpy(cfilename, sizeof(cfilename), out_filename); |
697 | } |
698 | |
699 | fo = fopen(cfilename, "w" ); |
700 | if (!fo) { |
701 | perror(cfilename); |
702 | exit(1); |
703 | } |
704 | outfile = fo; |
705 | |
706 | rt = JS_NewRuntime(); |
707 | ctx = JS_NewContext(rt); |
708 | |
709 | JS_SetStripInfo(rt, strip_flags); |
710 | |
711 | /* loader for ES6 modules */ |
712 | JS_SetModuleLoaderFunc(rt, NULL, jsc_module_loader, NULL); |
713 | |
714 | fprintf(fo, "/* File generated automatically by the QuickJS compiler. */\n" |
715 | "\n" |
716 | ); |
717 | |
718 | if (output_type != OUTPUT_C) { |
719 | fprintf(fo, "#include \"quickjs-libc.h\"\n" |
720 | "\n" |
721 | ); |
722 | } else { |
723 | fprintf(fo, "#include <inttypes.h>\n" |
724 | "\n" |
725 | ); |
726 | } |
727 | |
728 | for(i = optind; i < argc; i++) { |
729 | const char *filename = argv[i]; |
730 | compile_file(ctx, fo, filename, cname, module); |
731 | cname = NULL; |
732 | } |
733 | |
734 | for(i = 0; i < dynamic_module_list.count; i++) { |
735 | if (!jsc_module_loader(ctx, dynamic_module_list.array[i].name, NULL)) { |
736 | fprintf(stderr, "Could not load dynamic module '%s'\n" , |
737 | dynamic_module_list.array[i].name); |
738 | exit(1); |
739 | } |
740 | } |
741 | |
742 | if (output_type != OUTPUT_C) { |
743 | fprintf(fo, |
744 | "static JSContext *JS_NewCustomContext(JSRuntime *rt)\n" |
745 | "{\n" |
746 | " JSContext *ctx = JS_NewContextRaw(rt);\n" |
747 | " if (!ctx)\n" |
748 | " return NULL;\n" ); |
749 | /* add the basic objects */ |
750 | fprintf(fo, " JS_AddIntrinsicBaseObjects(ctx);\n" ); |
751 | for(i = 0; i < countof(feature_list); i++) { |
752 | if ((feature_bitmap & ((uint64_t)1 << i)) && |
753 | feature_list[i].init_name) { |
754 | fprintf(fo, " JS_AddIntrinsic%s(ctx);\n" , |
755 | feature_list[i].init_name); |
756 | } |
757 | } |
758 | /* add the precompiled modules (XXX: could modify the module |
759 | loader instead) */ |
760 | for(i = 0; i < init_module_list.count; i++) { |
761 | namelist_entry_t *e = &init_module_list.array[i]; |
762 | /* initialize the static C modules */ |
763 | |
764 | fprintf(fo, |
765 | " {\n" |
766 | " extern JSModuleDef *js_init_module_%s(JSContext *ctx, const char *name);\n" |
767 | " js_init_module_%s(ctx, \"%s\");\n" |
768 | " }\n" , |
769 | e->short_name, e->short_name, e->name); |
770 | } |
771 | for(i = 0; i < cname_list.count; i++) { |
772 | namelist_entry_t *e = &cname_list.array[i]; |
773 | if (e->flags) { |
774 | fprintf(fo, " js_std_eval_binary(ctx, %s, %s_size, 1);\n" , |
775 | e->name, e->name); |
776 | } |
777 | } |
778 | fprintf(fo, |
779 | " return ctx;\n" |
780 | "}\n\n" ); |
781 | |
782 | fputs(main_c_template1, fo); |
783 | |
784 | if (stack_size != 0) { |
785 | fprintf(fo, " JS_SetMaxStackSize(rt, %u);\n" , |
786 | (unsigned int)stack_size); |
787 | } |
788 | |
789 | /* add the module loader if necessary */ |
790 | if (feature_bitmap & (1 << FE_MODULE_LOADER)) { |
791 | fprintf(fo, " JS_SetModuleLoaderFunc(rt, NULL, js_module_loader, NULL);\n" ); |
792 | } |
793 | |
794 | fprintf(fo, |
795 | " ctx = JS_NewCustomContext(rt);\n" |
796 | " js_std_add_helpers(ctx, argc, argv);\n" ); |
797 | |
798 | for(i = 0; i < cname_list.count; i++) { |
799 | namelist_entry_t *e = &cname_list.array[i]; |
800 | if (!e->flags) { |
801 | fprintf(fo, " js_std_eval_binary(ctx, %s, %s_size, 0);\n" , |
802 | e->name, e->name); |
803 | } |
804 | } |
805 | fputs(main_c_template2, fo); |
806 | } |
807 | |
808 | JS_FreeContext(ctx); |
809 | JS_FreeRuntime(rt); |
810 | |
811 | fclose(fo); |
812 | |
813 | if (output_type == OUTPUT_EXECUTABLE) { |
814 | return output_executable(out_filename, cfilename, use_lto, verbose, |
815 | argv[0]); |
816 | } |
817 | namelist_free(&cname_list); |
818 | namelist_free(&cmodule_list); |
819 | namelist_free(&init_module_list); |
820 | return 0; |
821 | } |
822 | |