diff --git a/Makefile b/Makefile index 109fcb2..85e1314 100644 --- a/Makefile +++ b/Makefile @@ -75,17 +75,27 @@ ifdef CONFIG_ARM32 MQJS_BUILD_FLAGS=-m32 endif -PROGS=mqjs$(EXE) example$(EXE) +PROGS=mqjs$(EXE) mqjsc$(EXE) example$(EXE) TEST_PROGS=dtoa_test libm_test all: $(PROGS) -MQJS_OBJS=mqjs.o readline_tty.o readline.o mquickjs.o dtoa.o libm.o cutils.o +MQJS_OBJS=mqjs.o readline_tty.o readline.o mqjs_runtime.o mquickjs.o dtoa.o libm.o cutils.o +MQUICKJS_LIB_OBJS=mquickjs.o mqjs_runtime.o dtoa.o libm.o cutils.o LIBS=-lm -mqjs$(EXE): $(MQJS_OBJS) +mqjs$(EXE): mqjs.o readline_tty.o readline.o libmquickjs.a $(CC) $(LDFLAGS) -o $@ $^ $(LIBS) +mqjsc$(EXE): mqjsc.o libmquickjs.a + $(CC) $(LDFLAGS) -o $@ $^ $(LIBS) + +mqjs_runtime.o: mqjs.c + $(CC) $(CFLAGS) -DMQJS_RUNTIME_ONLY -c -o $@ $< + +libmquickjs.a: $(MQUICKJS_LIB_OBJS) + $(AR) rcs $@ $^ + mquickjs.o: mquickjs_atom.h mqjs_stdlib: mqjs_stdlib.host.o mquickjs_build.host.o @@ -98,11 +108,12 @@ mqjs_stdlib.h: mqjs_stdlib ./mqjs_stdlib $(MQJS_BUILD_FLAGS) > $@ mqjs.o: mqjs_stdlib.h +mqjsc.o: mqjs_stdlib.h # C API example example.o: example_stdlib.h -example$(EXE): example.o mquickjs.o dtoa.o libm.o cutils.o +example$(EXE): example.o libmquickjs.a $(CC) $(LDFLAGS) -o $@ $^ $(LIBS) example_stdlib: example_stdlib.host.o mquickjs_build.host.o @@ -117,7 +128,7 @@ example_stdlib.h: example_stdlib %.host.o: %.c $(HOST_CC) $(HOST_CFLAGS) -c -o $@ $< -test: mqjs example +test: mqjs mqjsc example ./mqjs tests/test_closure.js ./mqjs tests/test_language.js ./mqjs tests/test_loop.js @@ -127,6 +138,10 @@ test: mqjs example # @sha256sum -c test_builtin.sha256 ./mqjs -b test_builtin.bin ./example tests/test_rect.js +# test standalone compiler + ./mqjsc -o test_standalone tests/test_builtin.js + ./test_standalone + rm -f test_standalone microbench: mqjs ./mqjs tests/microbench.js @@ -147,6 +162,6 @@ rempio2_test: tests/rempio2_test.o libm.o $(CC) $(LDFLAGS) -o $@ $^ $(LIBS) clean: - rm -f *.o *.d *~ tests/*.o tests/*.d tests/*~ test_builtin.bin mqjs_stdlib mqjs_stdlib.h mquickjs_build_atoms mquickjs_atom.h mqjs_example example_stdlib example_stdlib.h $(PROGS) $(TEST_PROGS) + rm -f *.o *.a *.d *~ tests/*.o tests/*.d tests/*~ test_builtin.bin mqjs_stdlib mqjs_stdlib.h mquickjs_build_atoms mquickjs_atom.h mqjs_example example_stdlib example_stdlib.h $(PROGS) $(TEST_PROGS) -include $(wildcard *.d) diff --git a/example.c b/example.c index 5385ede..338c1e7 100644 --- a/example.c +++ b/example.c @@ -37,6 +37,7 @@ #include "cutils.h" #include "mquickjs.h" +#include "mqjs_runtime.h" #define JS_CLASS_RECTANGLE (JS_CLASS_USER + 0) #define JS_CLASS_FILLED_RECTANGLE (JS_CLASS_USER + 1) @@ -168,57 +169,6 @@ static JSValue js_filled_rectangle_get_color(JSContext *ctx, JSValue *this_val, return JS_NewInt32(ctx, d->color); } -static JSValue js_print(JSContext *ctx, JSValue *this_val, int argc, JSValue *argv) -{ - int i; - JSValue v; - - for(i = 0; i < argc; i++) { - if (i != 0) - putchar(' '); - v = argv[i]; - if (JS_IsString(ctx, v)) { - JSCStringBuf buf; - const char *str; - size_t len; - str = JS_ToCStringLen(ctx, &len, v, &buf); - fwrite(str, 1, len, stdout); - } else { - JS_PrintValueF(ctx, argv[i], JS_DUMP_LONG); - } - } - putchar('\n'); - return JS_UNDEFINED; -} - -#if defined(__linux__) || defined(__APPLE__) -static int64_t get_time_ms(void) -{ - struct timespec ts; - clock_gettime(CLOCK_MONOTONIC, &ts); - return (uint64_t)ts.tv_sec * 1000 + (ts.tv_nsec / 1000000); -} -#else -static int64_t get_time_ms(void) -{ - struct timeval tv; - gettimeofday(&tv, NULL); - return (int64_t)tv.tv_sec * 1000 + (tv.tv_usec / 1000); -} -#endif - -static JSValue js_date_now(JSContext *ctx, JSValue *this_val, int argc, JSValue *argv) -{ - struct timeval tv; - gettimeofday(&tv, NULL); - return JS_NewInt64(ctx, (int64_t)tv.tv_sec * 1000 + (tv.tv_usec / 1000)); -} - -static JSValue js_performance_now(JSContext *ctx, JSValue *this_val, int argc, JSValue *argv) -{ - return JS_NewInt64(ctx, get_time_ms()); -} - #include "example_stdlib.h" static void js_log_func(void *opaque, const void *buf, size_t buf_len) @@ -226,29 +176,6 @@ static void js_log_func(void *opaque, const void *buf, size_t buf_len) fwrite(buf, 1, buf_len, stdout); } -static uint8_t *load_file(const char *filename, int *plen) -{ - FILE *f; - uint8_t *buf; - int buf_len; - - f = fopen(filename, "rb"); - if (!f) { - perror(filename); - exit(1); - } - fseek(f, 0, SEEK_END); - buf_len = ftell(f); - fseek(f, 0, SEEK_SET); - buf = malloc(buf_len + 1); - fread(buf, 1, buf_len, f); - buf[buf_len] = '\0'; - fclose(f); - if (plen) - *plen = buf_len; - return buf; -} - int main(int argc, const char **argv) { size_t mem_size; diff --git a/mqjs.c b/mqjs.c index 46ad953..1acb871 100644 --- a/mqjs.c +++ b/mqjs.c @@ -34,19 +34,51 @@ #include #include #include +#include #include "cutils.h" +#ifndef MQJS_RUNTIME_ONLY #include "readline_tty.h" +#endif #include "mquickjs.h" +#include "mqjs_runtime.h" -static uint8_t *load_file(const char *filename, int *plen); -static void dump_error(JSContext *ctx); +uint8_t *load_file(const char *filename, int *plen) +{ + FILE *f; + uint8_t *buf; + int buf_len; -static JSValue js_print(JSContext *ctx, JSValue *this_val, int argc, JSValue *argv) + f = fopen(filename, "rb"); + if (!f) { + perror(filename); + exit(1); + } + fseek(f, 0, SEEK_END); + buf_len = ftell(f); + fseek(f, 0, SEEK_SET); + buf = malloc(buf_len + 1); + if (!buf) { + fclose(f); + return NULL; + } + if (fread(buf, 1, buf_len, f) != (size_t)buf_len) { + free(buf); + fclose(f); + return NULL; + } + buf[buf_len] = '\0'; + fclose(f); + if (plen) + *plen = buf_len; + return buf; +} + +JSValue js_print(JSContext *ctx, JSValue *this_val, int argc, JSValue *argv) { int i; JSValue v; - + for(i = 0; i < argc; i++) { if (i != 0) putchar(' '); @@ -65,7 +97,7 @@ static JSValue js_print(JSContext *ctx, JSValue *this_val, int argc, JSValue *ar return JS_UNDEFINED; } -static JSValue js_gc(JSContext *ctx, JSValue *this_val, int argc, JSValue *argv) +JSValue js_gc(JSContext *ctx, JSValue *this_val, int argc, JSValue *argv) { JS_GC(ctx); return JS_UNDEFINED; @@ -87,31 +119,33 @@ static int64_t get_time_ms(void) } #endif -static JSValue js_date_now(JSContext *ctx, JSValue *this_val, int argc, JSValue *argv) +JSValue js_date_now(JSContext *ctx, JSValue *this_val, int argc, JSValue *argv) { struct timeval tv; gettimeofday(&tv, NULL); return JS_NewInt64(ctx, (int64_t)tv.tv_sec * 1000 + (tv.tv_usec / 1000)); } -static JSValue js_performance_now(JSContext *ctx, JSValue *this_val, int argc, JSValue *argv) +JSValue js_performance_now(JSContext *ctx, JSValue *this_val, int argc, JSValue *argv) { return JS_NewInt64(ctx, get_time_ms()); } /* load a script */ -static JSValue js_load(JSContext *ctx, JSValue *this_val, int argc, JSValue *argv) +JSValue js_load(JSContext *ctx, JSValue *this_val, int argc, JSValue *argv) { const char *filename; JSCStringBuf buf_str; uint8_t *buf; int buf_len; JSValue ret; - + filename = JS_ToCString(ctx, argv[0], &buf_str); if (!filename) return JS_EXCEPTION; buf = load_file(filename, &buf_len); + if (!buf) + return JS_ThrowInternalError(ctx, "could not load file"); ret = JS_Eval(ctx, (const char *)buf, buf_len, filename, 0); free(buf); @@ -119,22 +153,14 @@ static JSValue js_load(JSContext *ctx, JSValue *this_val, int argc, JSValue *arg } /* timers */ -typedef struct { - BOOL allocated; - JSGCRef func; - int64_t timeout; /* in ms */ -} JSTimer; - -#define MAX_TIMERS 16 - static JSTimer js_timer_list[MAX_TIMERS]; -static JSValue js_setTimeout(JSContext *ctx, JSValue *this_val, int argc, JSValue *argv) +JSValue js_setTimeout(JSContext *ctx, JSValue *this_val, int argc, JSValue *argv) { JSTimer *th; int delay, i; JSValue *pfunc; - + if (!JS_IsFunction(ctx, argv[0])) return JS_ThrowTypeError(ctx, "not a function"); if (JS_ToInt32(ctx, &delay, argv[1])) @@ -152,7 +178,7 @@ static JSValue js_setTimeout(JSContext *ctx, JSValue *this_val, int argc, JSValu return JS_ThrowInternalError(ctx, "too many timers"); } -static JSValue js_clearTimeout(JSContext *ctx, JSValue *this_val, int argc, JSValue *argv) +JSValue js_clearTimeout(JSContext *ctx, JSValue *this_val, int argc, JSValue *argv) { int timer_id; JSTimer *th; @@ -169,7 +195,7 @@ static JSValue js_clearTimeout(JSContext *ctx, JSValue *this_val, int argc, JSVa return JS_UNDEFINED; } -static void run_timers(JSContext *ctx) +void run_timers(JSContext *ctx) { int64_t min_delay, delay, cur_time; BOOL has_timer; @@ -193,14 +219,14 @@ static void run_timers(JSContext *ctx) goto fail; JS_PushArg(ctx, th->func.val); /* func name */ JS_PushArg(ctx, JS_NULL); /* this */ - + JS_DeleteGCRef(ctx, &th->func); th->allocated = FALSE; - + ret = JS_Call(ctx, 0); if (JS_IsException(ret)) { fail: - dump_error(ctx); + /* Error message should be dumped by the caller if needed */ exit(1); } min_delay = 0; @@ -220,6 +246,8 @@ static void run_timers(JSContext *ctx) } } +#ifndef MQJS_RUNTIME_ONLY + #include "mqjs_stdlib.h" #define STYLE_DEFAULT COLOR_BRIGHT_GREEN @@ -235,28 +263,6 @@ static void run_timers(JSContext *ctx) #define STYLE_RESULT COLOR_BRIGHT_WHITE #define STYLE_ERROR_MSG COLOR_BRIGHT_RED -static uint8_t *load_file(const char *filename, int *plen) -{ - FILE *f; - uint8_t *buf; - int buf_len; - - f = fopen(filename, "rb"); - if (!f) { - perror(filename); - exit(1); - } - fseek(f, 0, SEEK_END); - buf_len = ftell(f); - fseek(f, 0, SEEK_SET); - buf = malloc(buf_len + 1); - fread(buf, 1, buf_len, f); - buf[buf_len] = '\0'; - fclose(f); - if (plen) - *plen = buf_len; - return buf; -} static int js_log_err_flag; @@ -772,3 +778,4 @@ int main(int argc, const char **argv) free(mem_buf); return 1; } +#endif /* !MQJS_RUNTIME_ONLY */ diff --git a/mqjs_runtime.h b/mqjs_runtime.h new file mode 100644 index 0000000..6b19a61 --- /dev/null +++ b/mqjs_runtime.h @@ -0,0 +1,56 @@ +/* + * Micro QuickJS Runtime + * + * Copyright (c) 2017-2025 Fabrice Bellard + * Copyright (c) 2017-2025 Charlie Gordon + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ +#ifndef MQJS_RUNTIME_H +#define MQJS_RUNTIME_H + +#include "mquickjs.h" + +/* defined in cutils.h, but we don't want to include it everywhere */ +#ifndef BOOL +typedef int BOOL; +#define FALSE 0 +#define TRUE 1 +#endif + +#define MAX_TIMERS 16 + +typedef struct { + BOOL allocated; + JSGCRef func; + int64_t timeout; /* in ms */ +} JSTimer; + +JSValue js_print(JSContext *ctx, JSValue *this_val, int argc, JSValue *argv); +JSValue js_gc(JSContext *ctx, JSValue *this_val, int argc, JSValue *argv); +JSValue js_date_now(JSContext *ctx, JSValue *this_val, int argc, JSValue *argv); +JSValue js_performance_now(JSContext *ctx, JSValue *this_val, int argc, JSValue *argv); +JSValue js_load(JSContext *ctx, JSValue *this_val, int argc, JSValue *argv); +JSValue js_setTimeout(JSContext *ctx, JSValue *this_val, int argc, JSValue *argv); +JSValue js_clearTimeout(JSContext *ctx, JSValue *this_val, int argc, JSValue *argv); +void run_timers(JSContext *ctx); + +uint8_t *load_file(const char *filename, int *plen); + +#endif /* MQJS_RUNTIME_H */ diff --git a/mqjsc.c b/mqjsc.c new file mode 100644 index 0000000..57edf29 --- /dev/null +++ b/mqjsc.c @@ -0,0 +1,308 @@ +/* + * Micro QuickJS compiler + * + * Copyright (c) 2017-2025 Fabrice Bellard + * Copyright (c) 2017-2025 Charlie Gordon + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "cutils.h" +#include "mquickjs.h" +#include "mqjs_runtime.h" + +/* we include mqjs_stdlib.h so we have the atoms defined for compilation */ +#include "mqjs_stdlib.h" + +static void dump_error(JSContext *ctx) +{ + JSValue obj; + obj = JS_GetException(ctx); + JS_PrintValueF(ctx, obj, JS_DUMP_LONG); + printf("\n"); +} + +static const char main_c_template[] = + "/* automatically generated by mqjsc */\n" + "#include \n" + "#include \n" + "#include \n" + "#include \"mquickjs.h\"\n" + "#include \"mqjs_runtime.h\"\n" + "#include \"mqjs_stdlib.h\"\n" + "\n" + "static const uint8_t bytecode[%u] = {"; + +static const char main_c_template2[] = + "};\n" + "\n" + "int main(int argc, const char **argv)\n" + "{\n" + " uint8_t *mem_buf;\n" + " JSContext *ctx;\n" + " JSValue val;\n" + " size_t mem_size = %u;\n" + " mem_buf = malloc(mem_size);\n" + " if (!mem_buf) { fprintf(stderr, \"Could not allocate memory\\n\"); return 1; }\n" + " ctx = JS_NewContext(mem_buf, mem_size, &js_stdlib);\n" + " if (!ctx) { fprintf(stderr, \"Could not create JS context\\n\"); return 1; }\n" + "\n" + " if (argc > 1) {\n" + " JSValue obj;\n" + " JSGCRef arr_ref;\n" + " int i;\n" + " JS_AddGCRef(ctx, &arr_ref);\n" + " arr_ref.val = JS_NewArray(ctx, argc - 1);\n" + " for(i = 1; i < argc; i++) {\n" + " JSValue str = JS_NewString(ctx, argv[i]);\n" + " JS_SetPropertyUint32(ctx, arr_ref.val, i - 1, str);\n" + " }\n" + " obj = JS_GetGlobalObject(ctx);\n" + " JS_SetPropertyStr(ctx, obj, \"scriptArgs\", arr_ref.val);\n" + " JS_DeleteGCRef(ctx, &arr_ref);\n" + " }\n" + "\n" + " uint8_t *bytecode_copy = malloc(%u);\n" + " if (!bytecode_copy) { fprintf(stderr, \"Could not allocate bytecode copy\\n\"); return 1; }\n" + " memcpy(bytecode_copy, bytecode, %u);\n" + " if (JS_RelocateBytecode(ctx, bytecode_copy, %u)) {\n" + " fprintf(stderr, \"Could not relocate bytecode\\n\");\n" + " exit(1);\n" + " }\n" + " val = JS_LoadBytecode(ctx, bytecode_copy);\n" + " val = JS_Run(ctx, val);\n" + " if (JS_IsException(val)) {\n" + " JSValue obj = JS_GetException(ctx);\n" + " JS_PrintValueF(ctx, obj, JS_DUMP_LONG);\n" + " fprintf(stderr, \"\\n\");\n" + " exit(1);\n" + " }\n" + " run_timers(ctx);\n" + " JS_FreeContext(ctx);\n" + " free(bytecode_copy);\n" + " free(mem_buf);\n" + " return 0;\n" + "}\n"; + +static void output_c_code(FILE *f, const uint8_t *bytecode, uint32_t bytecode_len, + size_t mem_size) +{ + uint32_t i; + + fprintf(f, main_c_template, bytecode_len); + for (i = 0; i < bytecode_len; i++) { + if (i % 16 == 0) + fprintf(f, "\n "); + fprintf(f, "0x%02x,", bytecode[i]); + } + fprintf(f, "\n"); + fprintf(f, main_c_template2, (unsigned int)mem_size, bytecode_len, bytecode_len, bytecode_len); +} + +static void help(void) +{ + printf("MicroQuickJS compiler\n" + "usage: mqjsc [options] [file]\n" + "-h list options\n" + "-o FILE set the output filename\n" + "-c only output the C source file\n" + "-m32 force 32 bit bytecode output\n" + "--memory-limit n limit the memory usage to 'n' bytes\n"); + exit(1); +} + +int main(int argc, char **argv) +{ + int optind; + const char *out_filename = "out.exe"; + const char *in_filename; + BOOL output_c_only = FALSE; + BOOL force_32bit = FALSE; + size_t mem_size = 16 << 20; + uint8_t *mem_buf; + JSContext *ctx; + uint8_t *js_buf; + int js_buf_len; + JSValue val; + union { + JSBytecodeHeader hdr; +#if JSW == 8 + JSBytecodeHeader32 hdr32; +#endif + } hdr_buf; + int hdr_len; + const uint8_t *data_buf; + uint32_t data_len; + FILE *f; + char c_filename[1024]; + + optind = 1; + while (optind < argc && *argv[optind] == '-') { + const char *arg = argv[optind] + 1; + const char *longopt = ""; + if (!*arg) + break; + optind++; + if (*arg == '-') { + longopt = arg + 1; + arg += strlen(arg); + if (!*longopt) + break; + } + for (; *arg || *longopt; longopt = "") { + char opt = *arg; + if (opt) + arg++; + if (opt == 'h' || !strcmp(longopt, "help")) { + help(); + } else if (opt == 'o') { + if (*arg) { + out_filename = arg; + break; + } + if (optind < argc) { + out_filename = argv[optind++]; + break; + } + } else if (opt == 'c') { + output_c_only = TRUE; + } else if (opt == 'm' && !strcmp(arg, "32")) { + force_32bit = TRUE; + arg += strlen(arg); + } else if (!strcmp(longopt, "memory-limit")) { + if (optind < argc) { + char *p; + double count = strtod(argv[optind++], &p); + switch (tolower((unsigned char)*p)) { + case 'g': count *= 1024; + case 'm': count *= 1024; + case 'k': count *= 1024; + default: mem_size = (size_t)count; break; + } + } + } else { + fprintf(stderr, "unknown option\n"); + help(); + } + } + } + + if (optind >= argc) + help(); + + in_filename = argv[optind]; + + mem_buf = malloc(mem_size); + ctx = JS_NewContext2(mem_buf, mem_size, &js_stdlib, TRUE); + + js_buf = load_file(in_filename, &js_buf_len); + val = JS_Parse(ctx, (char *)js_buf, js_buf_len, in_filename, 0); + free(js_buf); + + if (JS_IsException(val)) { + dump_error(ctx); + exit(1); + } + +#if JSW == 8 + if (force_32bit) { + if (JS_PrepareBytecode64to32(ctx, &hdr_buf.hdr32, &data_buf, &data_len, val)) { + fprintf(stderr, "Could not convert bytecode to 32 bits\n"); + exit(1); + } + hdr_len = sizeof(JSBytecodeHeader32); + } else +#endif + { + JS_PrepareBytecode(ctx, &hdr_buf.hdr, &data_buf, &data_len, val); + JS_RelocateBytecode2(ctx, &hdr_buf.hdr, (uint8_t *)data_buf, data_len, 0, FALSE); + hdr_len = sizeof(JSBytecodeHeader); + } + + if (output_c_only) { + f = fopen(out_filename, "w"); + } else { + snprintf(c_filename, sizeof(c_filename), "%s.c", out_filename); + f = fopen(c_filename, "w"); + } + if (!f) { + perror("fopen"); + exit(1); + } + + uint8_t *full_bc = malloc(hdr_len + data_len); + memcpy(full_bc, &hdr_buf, hdr_len); + memcpy(full_bc + hdr_len, data_buf, data_len); + + output_c_code(f, full_bc, hdr_len + data_len, mem_size); + fclose(f); + free(full_bc); + + if (!output_c_only) { + pid_t pid; + int status; + const char *argv_cc[16]; + int i; + + i = 0; + argv_cc[i++] = "gcc"; + argv_cc[i++] = "-Os"; + argv_cc[i++] = "-Wall"; + argv_cc[i++] = "-o"; + argv_cc[i++] = out_filename; + argv_cc[i++] = c_filename; + argv_cc[i++] = "libmquickjs.a"; + argv_cc[i++] = "-lm"; + if (force_32bit) + argv_cc[i++] = "-m32"; + argv_cc[i++] = NULL; + + pid = fork(); + if (pid == 0) { + execvp(argv_cc[0], (char **)argv_cc); + perror("execvp"); + exit(1); + } else if (pid < 0) { + perror("fork"); + exit(1); + } else { + waitpid(pid, &status, 0); + if (!WIFEXITED(status) || WEXITSTATUS(status) != 0) { + fprintf(stderr, "C compiler failed\n"); + exit(1); + } + } + unlink(c_filename); + } + + JS_FreeContext(ctx); + free(mem_buf); + + return 0; +}