| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568 |
- /*
- * QuickJS stand alone interpreter
- *
- * Copyright (c) 2017-2021 Fabrice Bellard
- * Copyright (c) 2017-2021 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 <stdlib.h>
- #include <stdio.h>
- #include <stdarg.h>
- #include <inttypes.h>
- #include <string.h>
- #include <assert.h>
- #include <unistd.h>
- #include <errno.h>
- #include <fcntl.h>
- #include <time.h>
- #if defined(__APPLE__)
- #include <malloc/malloc.h>
- #elif defined(__linux__) || defined(__GLIBC__)
- #include <malloc.h>
- #elif defined(__FreeBSD__)
- #include <malloc_np.h>
- #endif
- #include "cutils.h"
- #include "quickjs-libc.h"
- extern const uint8_t qjsc_repl[];
- extern const uint32_t qjsc_repl_size;
- static int eval_buf(JSContext *ctx, const void *buf, int buf_len,
- const char *filename, int eval_flags)
- {
- JSValue val;
- int ret;
- if ((eval_flags & JS_EVAL_TYPE_MASK) == JS_EVAL_TYPE_MODULE) {
- /* for the modules, we compile then run to be able to set
- import.meta */
- val = JS_Eval(ctx, buf, buf_len, filename,
- eval_flags | JS_EVAL_FLAG_COMPILE_ONLY);
- if (!JS_IsException(val)) {
- js_module_set_import_meta(ctx, val, TRUE, TRUE);
- val = JS_EvalFunction(ctx, val);
- }
- val = js_std_await(ctx, val);
- } else {
- val = JS_Eval(ctx, buf, buf_len, filename, eval_flags);
- }
- if (JS_IsException(val)) {
- js_std_dump_error(ctx);
- ret = -1;
- } else {
- ret = 0;
- }
- JS_FreeValue(ctx, val);
- return ret;
- }
- static int eval_file(JSContext *ctx, const char *filename, int module, int strict)
- {
- uint8_t *buf;
- int ret, eval_flags;
- size_t buf_len;
- buf = js_load_file(ctx, &buf_len, filename);
- if (!buf) {
- perror(filename);
- exit(1);
- }
- if (module < 0) {
- module = (has_suffix(filename, ".mjs") ||
- JS_DetectModule((const char *)buf, buf_len));
- }
- if (module) {
- eval_flags = JS_EVAL_TYPE_MODULE;
- } else {
- eval_flags = JS_EVAL_TYPE_GLOBAL;
- if (strict)
- eval_flags |= JS_EVAL_FLAG_STRICT;
- }
- ret = eval_buf(ctx, buf, buf_len, filename, eval_flags);
- js_free(ctx, buf);
- return ret;
- }
- /* also used to initialize the worker context */
- static JSContext *JS_NewCustomContext(JSRuntime *rt)
- {
- JSContext *ctx;
- ctx = JS_NewContext(rt);
- if (!ctx)
- return NULL;
- /* system modules */
- js_init_module_std(ctx, "std");
- js_init_module_os(ctx, "os");
- return ctx;
- }
- #if defined(__APPLE__)
- #define MALLOC_OVERHEAD 0
- #else
- #define MALLOC_OVERHEAD 8
- #endif
- struct trace_malloc_data {
- uint8_t *base;
- };
- static inline unsigned long long js_trace_malloc_ptr_offset(uint8_t *ptr,
- struct trace_malloc_data *dp)
- {
- return ptr - dp->base;
- }
- /* default memory allocation functions with memory limitation */
- static size_t js_trace_malloc_usable_size(const void *ptr)
- {
- #if defined(__APPLE__)
- return malloc_size(ptr);
- #elif defined(_WIN32)
- return _msize((void *)ptr);
- #elif defined(__EMSCRIPTEN__)
- return 0;
- #elif defined(__linux__) || defined(__GLIBC__)
- return malloc_usable_size((void *)ptr);
- #else
- /* change this to `return 0;` if compilation fails */
- return malloc_usable_size((void *)ptr);
- #endif
- }
- static void
- #ifdef _WIN32
- /* mingw printf is used */
- __attribute__((format(gnu_printf, 2, 3)))
- #else
- __attribute__((format(printf, 2, 3)))
- #endif
- js_trace_malloc_printf(JSMallocState *s, const char *fmt, ...)
- {
- va_list ap;
- int c;
- va_start(ap, fmt);
- while ((c = *fmt++) != '\0') {
- if (c == '%') {
- /* only handle %p and %zd */
- if (*fmt == 'p') {
- uint8_t *ptr = va_arg(ap, void *);
- if (ptr == NULL) {
- printf("NULL");
- } else {
- printf("H%+06lld.%zd",
- js_trace_malloc_ptr_offset(ptr, s->opaque),
- js_trace_malloc_usable_size(ptr));
- }
- fmt++;
- continue;
- }
- if (fmt[0] == 'z' && fmt[1] == 'd') {
- size_t sz = va_arg(ap, size_t);
- printf("%zd", sz);
- fmt += 2;
- continue;
- }
- }
- putc(c, stdout);
- }
- va_end(ap);
- }
- static void js_trace_malloc_init(struct trace_malloc_data *s)
- {
- free(s->base = malloc(8));
- }
- static void *js_trace_malloc(JSMallocState *s, size_t size)
- {
- void *ptr;
- /* Do not allocate zero bytes: behavior is platform dependent */
- assert(size != 0);
- if (unlikely(s->malloc_size + size > s->malloc_limit))
- return NULL;
- ptr = malloc(size);
- js_trace_malloc_printf(s, "A %zd -> %p\n", size, ptr);
- if (ptr) {
- s->malloc_count++;
- s->malloc_size += js_trace_malloc_usable_size(ptr) + MALLOC_OVERHEAD;
- }
- return ptr;
- }
- static void js_trace_free(JSMallocState *s, void *ptr)
- {
- if (!ptr)
- return;
- js_trace_malloc_printf(s, "F %p\n", ptr);
- s->malloc_count--;
- s->malloc_size -= js_trace_malloc_usable_size(ptr) + MALLOC_OVERHEAD;
- free(ptr);
- }
- static void *js_trace_realloc(JSMallocState *s, void *ptr, size_t size)
- {
- size_t old_size;
- if (!ptr) {
- if (size == 0)
- return NULL;
- return js_trace_malloc(s, size);
- }
- old_size = js_trace_malloc_usable_size(ptr);
- if (size == 0) {
- js_trace_malloc_printf(s, "R %zd %p\n", size, ptr);
- s->malloc_count--;
- s->malloc_size -= old_size + MALLOC_OVERHEAD;
- free(ptr);
- return NULL;
- }
- if (s->malloc_size + size - old_size > s->malloc_limit)
- return NULL;
- js_trace_malloc_printf(s, "R %zd %p", size, ptr);
- ptr = realloc(ptr, size);
- js_trace_malloc_printf(s, " -> %p\n", ptr);
- if (ptr) {
- s->malloc_size += js_trace_malloc_usable_size(ptr) - old_size;
- }
- return ptr;
- }
- static const JSMallocFunctions trace_mf = {
- js_trace_malloc,
- js_trace_free,
- js_trace_realloc,
- js_trace_malloc_usable_size,
- };
- static size_t get_suffixed_size(const char *str)
- {
- char *p;
- size_t v;
- v = (size_t)strtod(str, &p);
- switch(*p) {
- case 'G':
- v <<= 30;
- break;
- case 'M':
- v <<= 20;
- break;
- case 'k':
- case 'K':
- v <<= 10;
- break;
- default:
- if (*p != '\0') {
- fprintf(stderr, "qjs: invalid suffix: %s\n", p);
- exit(1);
- }
- break;
- }
- return v;
- }
- #define PROG_NAME "qjs"
- void help(void)
- {
- printf("QuickJS version " CONFIG_VERSION "\n"
- "usage: " PROG_NAME " [options] [file [args]]\n"
- "-h --help list options\n"
- "-e --eval EXPR evaluate EXPR\n"
- "-i --interactive go to interactive mode\n"
- "-m --module load as ES6 module (default=autodetect)\n"
- " --script load as ES6 script (default=autodetect)\n"
- " --strict force strict mode\n"
- "-I --include file include an additional file\n"
- " --std make 'std' and 'os' available to the loaded script\n"
- "-T --trace trace memory allocation\n"
- "-d --dump dump the memory usage stats\n"
- " --memory-limit n limit the memory usage to 'n' bytes (SI suffixes allowed)\n"
- " --stack-size n limit the stack size to 'n' bytes (SI suffixes allowed)\n"
- " --no-unhandled-rejection ignore unhandled promise rejections\n"
- "-s strip all the debug info\n"
- " --strip-source strip the source code\n"
- "-q --quit just instantiate the interpreter and quit\n");
- exit(1);
- }
- int main(int argc, char **argv)
- {
- JSRuntime *rt;
- JSContext *ctx;
- struct trace_malloc_data trace_data = { NULL };
- int optind;
- char *expr = NULL;
- int interactive = 0;
- int dump_memory = 0;
- int trace_memory = 0;
- int empty_run = 0;
- int module = -1;
- int strict = 0;
- int load_std = 0;
- int dump_unhandled_promise_rejection = 1;
- size_t memory_limit = 0;
- char *include_list[32];
- int i, include_count = 0;
- int strip_flags = 0;
- size_t stack_size = 0;
- /* cannot use getopt because we want to pass the command line to
- the script */
- optind = 1;
- while (optind < argc && *argv[optind] == '-') {
- char *arg = argv[optind] + 1;
- const char *longopt = "";
- /* a single - is not an option, it also stops argument scanning */
- if (!*arg)
- break;
- optind++;
- if (*arg == '-') {
- longopt = arg + 1;
- arg += strlen(arg);
- /* -- stops argument scanning */
- if (!*longopt)
- break;
- }
- for (; *arg || *longopt; longopt = "") {
- char opt = *arg;
- if (opt)
- arg++;
- if (opt == 'h' || opt == '?' || !strcmp(longopt, "help")) {
- help();
- continue;
- }
- if (opt == 'e' || !strcmp(longopt, "eval")) {
- if (*arg) {
- expr = arg;
- break;
- }
- if (optind < argc) {
- expr = argv[optind++];
- break;
- }
- fprintf(stderr, "qjs: missing expression for -e\n");
- exit(2);
- }
- if (opt == 'I' || !strcmp(longopt, "include")) {
- if (optind >= argc) {
- fprintf(stderr, "expecting filename");
- exit(1);
- }
- if (include_count >= countof(include_list)) {
- fprintf(stderr, "too many included files");
- exit(1);
- }
- include_list[include_count++] = argv[optind++];
- continue;
- }
- if (opt == 'i' || !strcmp(longopt, "interactive")) {
- interactive++;
- continue;
- }
- if (opt == 'm' || !strcmp(longopt, "module")) {
- module = 1;
- continue;
- }
- if (!strcmp(longopt, "script")) {
- module = 0;
- continue;
- }
- if (!strcmp(longopt, "strict")) {
- strict = 1;
- continue;
- }
- if (opt == 'd' || !strcmp(longopt, "dump")) {
- dump_memory++;
- continue;
- }
- if (opt == 'T' || !strcmp(longopt, "trace")) {
- trace_memory++;
- continue;
- }
- if (!strcmp(longopt, "std")) {
- load_std = 1;
- continue;
- }
- if (!strcmp(longopt, "no-unhandled-rejection")) {
- dump_unhandled_promise_rejection = 0;
- continue;
- }
- if (opt == 'q' || !strcmp(longopt, "quit")) {
- empty_run++;
- continue;
- }
- if (!strcmp(longopt, "memory-limit")) {
- if (optind >= argc) {
- fprintf(stderr, "expecting memory limit");
- exit(1);
- }
- memory_limit = get_suffixed_size(argv[optind++]);
- continue;
- }
- if (!strcmp(longopt, "stack-size")) {
- if (optind >= argc) {
- fprintf(stderr, "expecting stack size");
- exit(1);
- }
- stack_size = get_suffixed_size(argv[optind++]);
- continue;
- }
- if (opt == 's') {
- strip_flags = JS_STRIP_DEBUG;
- continue;
- }
- if (!strcmp(longopt, "strip-source")) {
- strip_flags = JS_STRIP_SOURCE;
- continue;
- }
- if (opt) {
- fprintf(stderr, "qjs: unknown option '-%c'\n", opt);
- } else {
- fprintf(stderr, "qjs: unknown option '--%s'\n", longopt);
- }
- help();
- }
- }
- if (trace_memory) {
- js_trace_malloc_init(&trace_data);
- rt = JS_NewRuntime2(&trace_mf, &trace_data);
- } else {
- rt = JS_NewRuntime();
- }
- if (!rt) {
- fprintf(stderr, "qjs: cannot allocate JS runtime\n");
- exit(2);
- }
- if (memory_limit != 0)
- JS_SetMemoryLimit(rt, memory_limit);
- if (stack_size != 0)
- JS_SetMaxStackSize(rt, stack_size);
- JS_SetStripInfo(rt, strip_flags);
- js_std_set_worker_new_context_func(JS_NewCustomContext);
- js_std_init_handlers(rt);
- ctx = JS_NewCustomContext(rt);
- if (!ctx) {
- fprintf(stderr, "qjs: cannot allocate JS context\n");
- exit(2);
- }
- /* loader for ES6 modules */
- JS_SetModuleLoaderFunc2(rt, NULL, js_module_loader, js_module_check_attributes, NULL);
- if (dump_unhandled_promise_rejection) {
- JS_SetHostPromiseRejectionTracker(rt, js_std_promise_rejection_tracker,
- NULL);
- }
- if (!empty_run) {
- js_std_add_helpers(ctx, argc - optind, argv + optind);
- /* make 'std' and 'os' visible to non module code */
- if (load_std) {
- const char *str = "import * as std from 'std';\n"
- "import * as os from 'os';\n"
- "globalThis.std = std;\n"
- "globalThis.os = os;\n";
- eval_buf(ctx, str, strlen(str), "<input>", JS_EVAL_TYPE_MODULE);
- }
- for(i = 0; i < include_count; i++) {
- if (eval_file(ctx, include_list[i], 0, strict))
- goto fail;
- }
- if (expr) {
- int eval_flags;
- if (module > 0) {
- eval_flags = JS_EVAL_TYPE_MODULE;
- } else {
- eval_flags = JS_EVAL_TYPE_GLOBAL;
- if (strict)
- eval_flags |= JS_EVAL_FLAG_STRICT;
- }
- if (eval_buf(ctx, expr, strlen(expr), "<cmdline>", eval_flags))
- goto fail;
- } else
- if (optind >= argc) {
- /* interactive mode */
- interactive = 1;
- } else {
- const char *filename;
- filename = argv[optind];
- if (eval_file(ctx, filename, module, strict))
- goto fail;
- }
- if (interactive) {
- JS_SetHostPromiseRejectionTracker(rt, NULL, NULL);
- js_std_eval_binary(ctx, qjsc_repl, qjsc_repl_size, 0);
- }
- js_std_loop(ctx);
- }
- if (dump_memory) {
- JSMemoryUsage stats;
- JS_ComputeMemoryUsage(rt, &stats);
- JS_DumpMemoryUsage(stdout, &stats, rt);
- }
- js_std_free_handlers(rt);
- JS_FreeContext(ctx);
- JS_FreeRuntime(rt);
- if (empty_run && dump_memory) {
- clock_t t[5];
- double best[5];
- int i, j;
- for (i = 0; i < 100; i++) {
- t[0] = clock();
- rt = JS_NewRuntime();
- t[1] = clock();
- ctx = JS_NewContext(rt);
- t[2] = clock();
- JS_FreeContext(ctx);
- t[3] = clock();
- JS_FreeRuntime(rt);
- t[4] = clock();
- for (j = 4; j > 0; j--) {
- double ms = 1000.0 * (t[j] - t[j - 1]) / CLOCKS_PER_SEC;
- if (i == 0 || best[j] > ms)
- best[j] = ms;
- }
- }
- printf("\nInstantiation times (ms): %.3f = %.3f+%.3f+%.3f+%.3f\n",
- best[1] + best[2] + best[3] + best[4],
- best[1], best[2], best[3], best[4]);
- }
- return 0;
- fail:
- js_std_free_handlers(rt);
- JS_FreeContext(ctx);
- JS_FreeRuntime(rt);
- return 1;
- }
|