summaryrefslogtreecommitdiff
path: root/quickjs-libc.c
diff options
context:
space:
mode:
authorbellard <6490144+bellard@users.noreply.github.com>2020-09-06 18:53:08 +0200
committerbellard <6490144+bellard@users.noreply.github.com>2020-09-06 18:53:08 +0200
commit91459fb6723e29e923380cec0023af93819ae69d (patch)
tree6a1aff8d9b290ed184d1481da50d0e6b4a9a324c /quickjs-libc.c
parent9096e544ba2357eeadc6f09fc6e5cf58db7751bc (diff)
downloadquickjs-91459fb6723e29e923380cec0023af93819ae69d.tar.gz
quickjs-91459fb6723e29e923380cec0023af93819ae69d.zip
2020-01-05 release
Diffstat (limited to 'quickjs-libc.c')
-rw-r--r--quickjs-libc.c2804
1 files changed, 2804 insertions, 0 deletions
diff --git a/quickjs-libc.c b/quickjs-libc.c
new file mode 100644
index 0000000..da53fef
--- /dev/null
+++ b/quickjs-libc.c
@@ -0,0 +1,2804 @@
+/*
+ * QuickJS C library
+ *
+ * Copyright (c) 2017-2020 Fabrice Bellard
+ * Copyright (c) 2017-2020 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 <sys/time.h>
+#include <time.h>
+#include <signal.h>
+#include <limits.h>
+#include <sys/stat.h>
+#include <dirent.h>
+#if defined(_WIN32)
+#include <windows.h>
+#include <conio.h>
+#else
+#include <dlfcn.h>
+#include <termios.h>
+#include <sys/ioctl.h>
+#include <sys/wait.h>
+#if defined(__APPLE__)
+typedef sig_t sighandler_t;
+#endif
+#endif
+
+#include "cutils.h"
+#include "list.h"
+#include "quickjs-libc.h"
+
+static void js_std_dbuf_init(JSContext *ctx, DynBuf *s)
+{
+ dbuf_init2(s, JS_GetRuntime(ctx), (DynBufReallocFunc *)js_realloc_rt);
+}
+
+/* TODO:
+ - add worker
+ - add minimal VT100 emulation for win32
+ - add socket calls
+*/
+
+typedef struct {
+ struct list_head link;
+ int fd;
+ JSValue rw_func[2];
+} JSOSRWHandler;
+
+typedef struct {
+ struct list_head link;
+ int sig_num;
+ JSValue func;
+} JSOSSignalHandler;
+
+typedef struct {
+ struct list_head link;
+ BOOL has_object;
+ int64_t timeout;
+ JSValue func;
+} JSOSTimer;
+
+/* initialize the lists so js_std_free_handlers() can always be called */
+static struct list_head os_rw_handlers = LIST_HEAD_INIT(os_rw_handlers);
+static struct list_head os_signal_handlers = LIST_HEAD_INIT(os_signal_handlers);
+static struct list_head os_timers = LIST_HEAD_INIT(os_timers);
+static uint64_t os_pending_signals;
+static int eval_script_recurse;
+static int (*os_poll_func)(JSContext *ctx);
+
+static JSValue js_printf_internal(JSContext *ctx,
+ int argc, JSValueConst *argv, FILE *fp)
+{
+ char fmtbuf[32];
+ uint8_t cbuf[UTF8_CHAR_LEN_MAX+1];
+ JSValue res;
+ DynBuf dbuf;
+ const char *fmt_str;
+ const uint8_t *fmt, *fmt_end;
+ const uint8_t *p;
+ char *q;
+ int i, c, len;
+ size_t fmt_len;
+ int32_t int32_arg;
+ int64_t int64_arg;
+ double double_arg;
+ const char *string_arg;
+ enum { PART_FLAGS, PART_WIDTH, PART_DOT, PART_PREC, PART_MODIFIER } part;
+ int modsize;
+ /* Use indirect call to dbuf_printf to prevent gcc warning */
+ int (*dbuf_printf_fun)(DynBuf *s, const char *fmt, ...) = (void*)dbuf_printf;
+
+ js_std_dbuf_init(ctx, &dbuf);
+
+ if (argc > 0) {
+ fmt_str = JS_ToCStringLen(ctx, &fmt_len, argv[0]);
+ if (!fmt_str)
+ goto fail;
+
+ i = 1;
+ fmt = (const uint8_t *)fmt_str;
+ fmt_end = fmt + fmt_len;
+ while (fmt < fmt_end) {
+ for (p = fmt; fmt < fmt_end && *fmt != '%'; fmt++)
+ continue;
+ dbuf_put(&dbuf, p, fmt - p);
+ if (fmt >= fmt_end)
+ break;
+ q = fmtbuf;
+ *q++ = *fmt++; /* copy '%' */
+ part = PART_FLAGS;
+ modsize = 0;
+ for (;;) {
+ if (q >= fmtbuf + sizeof(fmtbuf) - 1)
+ goto invalid;
+
+ c = *fmt++;
+ *q++ = c;
+ *q = '\0';
+
+ switch (c) {
+ case '1': case '2': case '3':
+ case '4': case '5': case '6':
+ case '7': case '8': case '9':
+ if (part != PART_PREC) {
+ if (part <= PART_WIDTH)
+ part = PART_WIDTH;
+ else
+ goto invalid;
+ }
+ continue;
+
+ case '0': case '#': case '+': case '-': case ' ': case '\'':
+ if (part > PART_FLAGS)
+ goto invalid;
+ continue;
+
+ case '.':
+ if (part > PART_DOT)
+ goto invalid;
+ part = PART_DOT;
+ continue;
+
+ case '*':
+ if (part < PART_WIDTH)
+ part = PART_DOT;
+ else if (part == PART_DOT)
+ part = PART_MODIFIER;
+ else
+ goto invalid;
+
+ if (i >= argc)
+ goto missing;
+
+ if (JS_ToInt32(ctx, &int32_arg, argv[i++]))
+ goto fail;
+ q += snprintf(q, fmtbuf + sizeof(fmtbuf) - q, "%d", int32_arg);
+ continue;
+
+ case 'h':
+ if (modsize != 0 && modsize != -1)
+ goto invalid;
+ modsize--;
+ part = PART_MODIFIER;
+ continue;
+ case 'l':
+ q--;
+ if (modsize != 0 && modsize != 1)
+ goto invalid;
+ modsize++;
+ part = PART_MODIFIER;
+ continue;
+
+ case 'c':
+ if (i >= argc)
+ goto missing;
+ if (JS_IsString(argv[i])) {
+ string_arg = JS_ToCString(ctx, argv[i++]);
+ if (!string_arg)
+ goto fail;
+ int32_arg = unicode_from_utf8((uint8_t *)string_arg, UTF8_CHAR_LEN_MAX, &p);
+ JS_FreeCString(ctx, string_arg);
+ } else {
+ if (JS_ToInt32(ctx, &int32_arg, argv[i++]))
+ goto fail;
+ }
+ /* handle utf-8 encoding explicitly */
+ if ((unsigned)int32_arg > 0x10FFFF)
+ int32_arg = 0xFFFD;
+ /* ignore conversion flags, width and precision */
+ len = unicode_to_utf8(cbuf, int32_arg);
+ dbuf_put(&dbuf, cbuf, len);
+ break;
+
+ case 'd':
+ case 'i':
+ case 'o':
+ case 'u':
+ case 'x':
+ case 'X':
+ if (i >= argc)
+ goto missing;
+ if (modsize > 0) {
+ if (JS_ToInt64(ctx, &int64_arg, argv[i++]))
+ goto fail;
+ q[1] = q[-1];
+ q[-1] = q[0] = 'l';
+ q[2] = '\0';
+ dbuf_printf_fun(&dbuf, fmtbuf, (long long)int64_arg);
+ } else {
+ if (JS_ToInt32(ctx, &int32_arg, argv[i++]))
+ goto fail;
+ dbuf_printf_fun(&dbuf, fmtbuf, int32_arg);
+ }
+ break;
+
+ case 's':
+ if (i >= argc)
+ goto missing;
+ string_arg = JS_ToCString(ctx, argv[i++]);
+ if (!string_arg)
+ goto fail;
+ dbuf_printf_fun(&dbuf, fmtbuf, string_arg);
+ JS_FreeCString(ctx, string_arg);
+ break;
+
+ case 'e':
+ case 'f':
+ case 'g':
+ case 'a':
+ case 'E':
+ case 'F':
+ case 'G':
+ case 'A':
+ if (i >= argc)
+ goto missing;
+ if (JS_ToFloat64(ctx, &double_arg, argv[i++]))
+ goto fail;
+ dbuf_printf_fun(&dbuf, fmtbuf, double_arg);
+ break;
+
+ case '%':
+ dbuf_putc(&dbuf, '%');
+ break;
+
+ default:
+ /* XXX: should support an extension mechanism */
+ invalid:
+ JS_ThrowTypeError(ctx, "invalid conversion specifier in format string");
+ goto fail;
+ missing:
+ JS_ThrowReferenceError(ctx, "missing argument for conversion specifier");
+ goto fail;
+ }
+ break;
+ }
+ }
+ JS_FreeCString(ctx, fmt_str);
+ }
+ if (dbuf.error) {
+ res = JS_ThrowOutOfMemory(ctx);
+ } else {
+ if (fp) {
+ len = fwrite(dbuf.buf, 1, dbuf.size, fp);
+ res = JS_NewInt32(ctx, len);
+ } else {
+ res = JS_NewStringLen(ctx, (char *)dbuf.buf, dbuf.size);
+ }
+ }
+ dbuf_free(&dbuf);
+ return res;
+
+fail:
+ dbuf_free(&dbuf);
+ return JS_EXCEPTION;
+}
+
+uint8_t *js_load_file(JSContext *ctx, size_t *pbuf_len, const char *filename)
+{
+ FILE *f;
+ uint8_t *buf;
+ size_t buf_len;
+ long lret;
+
+ f = fopen(filename, "rb");
+ if (!f)
+ return NULL;
+ if (fseek(f, 0, SEEK_END) < 0)
+ goto fail;
+ lret = ftell(f);
+ if (lret < 0)
+ goto fail;
+ /* XXX: on Linux, ftell() return LONG_MAX for directories */
+ if (lret == LONG_MAX) {
+ errno = EISDIR;
+ goto fail;
+ }
+ buf_len = lret;
+ if (fseek(f, 0, SEEK_SET) < 0)
+ goto fail;
+ if (ctx)
+ buf = js_malloc(ctx, buf_len + 1);
+ else
+ buf = malloc(buf_len + 1);
+ if (!buf)
+ goto fail;
+ if (fread(buf, 1, buf_len, f) != buf_len) {
+ errno = EIO;
+ if (ctx)
+ js_free(ctx, buf);
+ else
+ free(buf);
+ fail:
+ fclose(f);
+ return NULL;
+ }
+ buf[buf_len] = '\0';
+ fclose(f);
+ *pbuf_len = buf_len;
+ return buf;
+}
+
+/* load and evaluate a file */
+static JSValue js_loadScript(JSContext *ctx, JSValueConst this_val,
+ int argc, JSValueConst *argv)
+{
+ uint8_t *buf;
+ const char *filename;
+ JSValue ret;
+ size_t buf_len;
+
+ filename = JS_ToCString(ctx, argv[0]);
+ if (!filename)
+ return JS_EXCEPTION;
+ buf = js_load_file(ctx, &buf_len, filename);
+ if (!buf) {
+ JS_ThrowReferenceError(ctx, "could not load '%s'", filename);
+ JS_FreeCString(ctx, filename);
+ return JS_EXCEPTION;
+ }
+ ret = JS_Eval(ctx, (char *)buf, buf_len, filename,
+ JS_EVAL_TYPE_GLOBAL);
+ js_free(ctx, buf);
+ JS_FreeCString(ctx, filename);
+ return ret;
+}
+
+typedef JSModuleDef *(JSInitModuleFunc)(JSContext *ctx,
+ const char *module_name);
+
+
+#if defined(_WIN32)
+static JSModuleDef *js_module_loader_so(JSContext *ctx,
+ const char *module_name)
+{
+ JS_ThrowReferenceError(ctx, "shared library modules are not supported yet");
+ return NULL;
+}
+#else
+static JSModuleDef *js_module_loader_so(JSContext *ctx,
+ const char *module_name)
+{
+ JSModuleDef *m;
+ void *hd;
+ JSInitModuleFunc *init;
+ char *filename;
+
+ if (!strchr(module_name, '/')) {
+ /* must add a '/' so that the DLL is not searched in the
+ system library paths */
+ filename = js_malloc(ctx, strlen(module_name) + 2 + 1);
+ if (!filename)
+ return NULL;
+ strcpy(filename, "./");
+ strcpy(filename + 2, module_name);
+ } else {
+ filename = (char *)module_name;
+ }
+
+ /* C module */
+ hd = dlopen(filename, RTLD_NOW | RTLD_LOCAL);
+ if (filename != module_name)
+ js_free(ctx, filename);
+ if (!hd) {
+ JS_ThrowReferenceError(ctx, "could not load module filename '%s' as shared library",
+ module_name);
+ goto fail;
+ }
+
+ init = dlsym(hd, "js_init_module");
+ if (!init) {
+ JS_ThrowReferenceError(ctx, "could not load module filename '%s': js_init_module not found",
+ module_name);
+ goto fail;
+ }
+
+ m = init(ctx, module_name);
+ if (!m) {
+ JS_ThrowReferenceError(ctx, "could not load module filename '%s': initialization error",
+ module_name);
+ fail:
+ if (hd)
+ dlclose(hd);
+ return NULL;
+ }
+ return m;
+}
+#endif /* !_WIN32 */
+
+int js_module_set_import_meta(JSContext *ctx, JSValueConst func_val,
+ JS_BOOL use_realpath, JS_BOOL is_main)
+{
+ JSModuleDef *m;
+ char buf[PATH_MAX + 16];
+ JSValue meta_obj;
+ JSAtom module_name_atom;
+ const char *module_name;
+
+ assert(JS_VALUE_GET_TAG(func_val) == JS_TAG_MODULE);
+ m = JS_VALUE_GET_PTR(func_val);
+
+ module_name_atom = JS_GetModuleName(ctx, m);
+ module_name = JS_AtomToCString(ctx, module_name_atom);
+ JS_FreeAtom(ctx, module_name_atom);
+ if (!module_name)
+ return -1;
+ if (!strchr(module_name, ':')) {
+ strcpy(buf, "file://");
+#if !defined(_WIN32)
+ /* realpath() cannot be used with modules compiled with qjsc
+ because the corresponding module source code is not
+ necessarily present */
+ if (use_realpath) {
+ char *res = realpath(module_name, buf + strlen(buf));
+ if (!res) {
+ JS_ThrowTypeError(ctx, "realpath failure");
+ JS_FreeCString(ctx, module_name);
+ return -1;
+ }
+ } else
+#endif
+ {
+ pstrcat(buf, sizeof(buf), module_name);
+ }
+ } else {
+ pstrcpy(buf, sizeof(buf), module_name);
+ }
+ JS_FreeCString(ctx, module_name);
+
+ meta_obj = JS_GetImportMeta(ctx, m);
+ if (JS_IsException(meta_obj))
+ return -1;
+ JS_DefinePropertyValueStr(ctx, meta_obj, "url",
+ JS_NewString(ctx, buf),
+ JS_PROP_C_W_E);
+ JS_DefinePropertyValueStr(ctx, meta_obj, "main",
+ JS_NewBool(ctx, is_main),
+ JS_PROP_C_W_E);
+ JS_FreeValue(ctx, meta_obj);
+ return 0;
+}
+
+JSModuleDef *js_module_loader(JSContext *ctx,
+ const char *module_name, void *opaque)
+{
+ JSModuleDef *m;
+
+ if (has_suffix(module_name, ".so")) {
+ m = js_module_loader_so(ctx, module_name);
+ } else {
+ size_t buf_len;
+ uint8_t *buf;
+ JSValue func_val;
+
+ buf = js_load_file(ctx, &buf_len, module_name);
+ if (!buf) {
+ JS_ThrowReferenceError(ctx, "could not load module filename '%s'",
+ module_name);
+ return NULL;
+ }
+
+ /* compile the module */
+ func_val = JS_Eval(ctx, (char *)buf, buf_len, module_name,
+ JS_EVAL_TYPE_MODULE | JS_EVAL_FLAG_COMPILE_ONLY);
+ js_free(ctx, buf);
+ if (JS_IsException(func_val))
+ return NULL;
+ /* XXX: could propagate the exception */
+ js_module_set_import_meta(ctx, func_val, TRUE, FALSE);
+ /* the module is already referenced, so we must free it */
+ m = JS_VALUE_GET_PTR(func_val);
+ JS_FreeValue(ctx, func_val);
+ }
+ return m;
+}
+
+static JSValue js_std_exit(JSContext *ctx, JSValueConst this_val,
+ int argc, JSValueConst *argv)
+{
+ int status;
+ if (JS_ToInt32(ctx, &status, argv[0]))
+ status = -1;
+ exit(status);
+ return JS_UNDEFINED;
+}
+
+static JSValue js_std_getenv(JSContext *ctx, JSValueConst this_val,
+ int argc, JSValueConst *argv)
+{
+ const char *name, *str;
+ name = JS_ToCString(ctx, argv[0]);
+ if (!name)
+ return JS_EXCEPTION;
+ str = getenv(name);
+ JS_FreeCString(ctx, name);
+ if (!str)
+ return JS_UNDEFINED;
+ else
+ return JS_NewString(ctx, str);
+}
+
+static JSValue js_std_gc(JSContext *ctx, JSValueConst this_val,
+ int argc, JSValueConst *argv)
+{
+ JS_RunGC(JS_GetRuntime(ctx));
+ return JS_UNDEFINED;
+}
+
+static int interrupt_handler(JSRuntime *rt, void *opaque)
+{
+ return (os_pending_signals >> SIGINT) & 1;
+}
+
+static int get_bool_option(JSContext *ctx, BOOL *pbool,
+ JSValueConst obj,
+ const char *option)
+{
+ JSValue val;
+ val = JS_GetPropertyStr(ctx, obj, option);
+ if (JS_IsException(val))
+ return -1;
+ if (!JS_IsUndefined(val)) {
+ *pbool = JS_ToBool(ctx, val);
+ }
+ JS_FreeValue(ctx, val);
+ return 0;
+}
+
+static JSValue js_evalScript(JSContext *ctx, JSValueConst this_val,
+ int argc, JSValueConst *argv)
+{
+ const char *str;
+ size_t len;
+ JSValue ret;
+ JSValueConst options_obj;
+ BOOL backtrace_barrier = FALSE;
+ int flags;
+
+ if (argc >= 2) {
+ options_obj = argv[1];
+ if (get_bool_option(ctx, &backtrace_barrier, options_obj,
+ "backtrace_barrier"))
+ return JS_EXCEPTION;
+ }
+
+ str = JS_ToCStringLen(ctx, &len, argv[0]);
+ if (!str)
+ return JS_EXCEPTION;
+ if (++eval_script_recurse == 1) {
+ /* install the interrupt handler */
+ JS_SetInterruptHandler(JS_GetRuntime(ctx), interrupt_handler, NULL);
+ }
+ flags = JS_EVAL_TYPE_GLOBAL;
+ if (backtrace_barrier)
+ flags |= JS_EVAL_FLAG_BACKTRACE_BARRIER;
+ ret = JS_Eval(ctx, str, len, "<evalScript>", flags);
+ JS_FreeCString(ctx, str);
+ if (--eval_script_recurse == 0) {
+ /* remove the interrupt handler */
+ JS_SetInterruptHandler(JS_GetRuntime(ctx), NULL, NULL);
+ os_pending_signals &= ~((uint64_t)1 << SIGINT);
+ /* convert the uncatchable "interrupted" error into a normal error
+ so that it can be caught by the REPL */
+ if (JS_IsException(ret))
+ JS_ResetUncatchableError(ctx);
+ }
+ return ret;
+}
+
+static JSClassID js_std_file_class_id;
+
+typedef struct {
+ FILE *f;
+ BOOL close_in_finalizer;
+ BOOL is_popen;
+} JSSTDFile;
+
+static void js_std_file_finalizer(JSRuntime *rt, JSValue val)
+{
+ JSSTDFile *s = JS_GetOpaque(val, js_std_file_class_id);
+ if (s) {
+ if (s->f && s->close_in_finalizer) {
+ if (s->is_popen)
+ pclose(s->f);
+ else
+ fclose(s->f);
+ }
+ js_free_rt(rt, s);
+ }
+}
+
+static JSValue js_new_std_error(JSContext *ctx, int err)
+{
+ JSValue obj;
+ /* XXX: could add a specific Error prototype */
+ obj = JS_NewError(ctx);
+ JS_DefinePropertyValueStr(ctx, obj, "message",
+ JS_NewString(ctx, strerror(err)),
+ JS_PROP_WRITABLE | JS_PROP_CONFIGURABLE);
+ JS_DefinePropertyValueStr(ctx, obj, "errno",
+ JS_NewInt32(ctx, err),
+ JS_PROP_WRITABLE | JS_PROP_CONFIGURABLE);
+ return obj;
+}
+
+static JSValue js_std_error_constructor(JSContext *ctx, JSValueConst new_target,
+ int argc, JSValueConst *argv)
+{
+ int err;
+ if (JS_ToInt32(ctx, &err, argv[0]))
+ return JS_EXCEPTION;
+ return js_new_std_error(ctx, err);
+}
+
+static JSValue js_std_error_strerror(JSContext *ctx, JSValueConst this_val,
+ int argc, JSValueConst *argv)
+{
+ int err;
+ if (JS_ToInt32(ctx, &err, argv[0]))
+ return JS_EXCEPTION;
+ return JS_NewString(ctx, strerror(err));
+}
+
+static JSValue js_std_throw_errno(JSContext *ctx, int err)
+{
+ JSValue obj;
+ obj = js_new_std_error(ctx, err);
+ if (JS_IsException(obj))
+ obj = JS_NULL;
+ return JS_Throw(ctx, obj);
+}
+
+static JSValue js_new_std_file(JSContext *ctx, FILE *f,
+ BOOL close_in_finalizer,
+ BOOL is_popen)
+{
+ JSSTDFile *s;
+ JSValue obj;
+ obj = JS_NewObjectClass(ctx, js_std_file_class_id);
+ if (JS_IsException(obj))
+ return obj;
+ s = js_mallocz(ctx, sizeof(*s));
+ if (!s) {
+ JS_FreeValue(ctx, obj);
+ return JS_EXCEPTION;
+ }
+ s->close_in_finalizer = close_in_finalizer;
+ s->is_popen = is_popen;
+ s->f = f;
+ JS_SetOpaque(obj, s);
+ return obj;
+}
+
+static JSValue js_std_open(JSContext *ctx, JSValueConst this_val,
+ int argc, JSValueConst *argv)
+{
+ const char *filename, *mode = NULL;
+ FILE *f;
+
+ filename = JS_ToCString(ctx, argv[0]);
+ if (!filename)
+ goto fail;
+ mode = JS_ToCString(ctx, argv[1]);
+ if (!mode)
+ goto fail;
+ if (mode[strspn(mode, "rwa+b")] != '\0') {
+ js_std_throw_errno(ctx, EINVAL);
+ goto fail;
+ }
+
+ f = fopen(filename, mode);
+ JS_FreeCString(ctx, filename);
+ JS_FreeCString(ctx, mode);
+ if (!f)
+ return js_std_throw_errno(ctx, errno);
+ return js_new_std_file(ctx, f, TRUE, FALSE);
+ fail:
+ JS_FreeCString(ctx, filename);
+ JS_FreeCString(ctx, mode);
+ return JS_EXCEPTION;
+}
+
+static JSValue js_std_popen(JSContext *ctx, JSValueConst this_val,
+ int argc, JSValueConst *argv)
+{
+ const char *filename, *mode = NULL;
+ FILE *f;
+
+ filename = JS_ToCString(ctx, argv[0]);
+ if (!filename)
+ goto fail;
+ mode = JS_ToCString(ctx, argv[1]);
+ if (!mode)
+ goto fail;
+ if (mode[strspn(mode, "rw")] != '\0') {
+ js_std_throw_errno(ctx, EINVAL);
+ goto fail;
+ }
+
+ f = popen(filename, mode);
+ JS_FreeCString(ctx, filename);
+ JS_FreeCString(ctx, mode);
+ if (!f)
+ return js_std_throw_errno(ctx, errno);
+ return js_new_std_file(ctx, f, TRUE, TRUE);
+ fail:
+ JS_FreeCString(ctx, filename);
+ JS_FreeCString(ctx, mode);
+ return JS_EXCEPTION;
+}
+
+static JSValue js_std_fdopen(JSContext *ctx, JSValueConst this_val,
+ int argc, JSValueConst *argv)
+{
+ const char *mode;
+ FILE *f;
+ int fd;
+
+ if (JS_ToInt32(ctx, &fd, argv[0]))
+ return JS_EXCEPTION;
+ mode = JS_ToCString(ctx, argv[1]);
+ if (!mode)
+ goto fail;
+ if (mode[strspn(mode, "rwa+")] != '\0') {
+ js_std_throw_errno(ctx, EINVAL);
+ goto fail;
+ }
+
+ f = fdopen(fd, mode);
+ JS_FreeCString(ctx, mode);
+ if (!f)
+ return js_std_throw_errno(ctx, errno);
+ return js_new_std_file(ctx, f, TRUE, FALSE);
+ fail:
+ JS_FreeCString(ctx, mode);
+ return JS_EXCEPTION;
+}
+
+static JSValue js_std_tmpfile(JSContext *ctx, JSValueConst this_val,
+ int argc, JSValueConst *argv)
+{
+ FILE *f;
+ f = tmpfile();
+ if (!f)
+ return js_std_throw_errno(ctx, errno);
+ return js_new_std_file(ctx, f, TRUE, FALSE);
+}
+
+static JSValue js_std_sprintf(JSContext *ctx, JSValueConst this_val,
+ int argc, JSValueConst *argv)
+{
+ return js_printf_internal(ctx, argc, argv, NULL);
+}
+
+static JSValue js_std_printf(JSContext *ctx, JSValueConst this_val,
+ int argc, JSValueConst *argv)
+{
+ return js_printf_internal(ctx, argc, argv, stdout);
+}
+
+static FILE *js_std_file_get(JSContext *ctx, JSValueConst obj)
+{
+ JSSTDFile *s = JS_GetOpaque2(ctx, obj, js_std_file_class_id);
+ if (!s)
+ return NULL;
+ if (!s->f) {
+ js_std_throw_errno(ctx, EBADF);
+ return NULL;
+ }
+ return s->f;
+}
+
+static JSValue js_std_file_puts(JSContext *ctx, JSValueConst this_val,
+ int argc, JSValueConst *argv, int magic)
+{
+ FILE *f;
+ int i;
+ const char *str;
+
+ if (magic == 0) {
+ f = stdout;
+ } else {
+ f = js_std_file_get(ctx, this_val);
+ if (!f)
+ return JS_EXCEPTION;
+ }
+
+ for(i = 0; i < argc; i++) {
+ str = JS_ToCString(ctx, argv[i]);
+ if (!str)
+ return JS_EXCEPTION;
+ fputs(str, f);
+ JS_FreeCString(ctx, str);
+ }
+ return JS_UNDEFINED;
+}
+
+static JSValue js_std_file_close(JSContext *ctx, JSValueConst this_val,
+ int argc, JSValueConst *argv)
+{
+ JSSTDFile *s = JS_GetOpaque2(ctx, this_val, js_std_file_class_id);
+ if (!s)
+ return JS_EXCEPTION;
+ if (!s->f)
+ return js_std_throw_errno(ctx, EBADF);
+ /* XXX: could return exit code */
+ if (s->is_popen)
+ pclose(s->f);
+ else
+ fclose(s->f);
+ s->f = NULL;
+ return JS_UNDEFINED;
+}
+
+static JSValue js_std_file_printf(JSContext *ctx, JSValueConst this_val,
+ int argc, JSValueConst *argv)
+{
+ FILE *f = js_std_file_get(ctx, this_val);
+ if (!f)
+ return JS_EXCEPTION;
+ return js_printf_internal(ctx, argc, argv, f);
+}
+
+static JSValue js_std_file_flush(JSContext *ctx, JSValueConst this_val,
+ int argc, JSValueConst *argv)
+{
+ FILE *f = js_std_file_get(ctx, this_val);
+ if (!f)
+ return JS_EXCEPTION;
+ fflush(f);
+ return JS_UNDEFINED;
+}
+
+static JSValue js_std_file_tell(JSContext *ctx, JSValueConst this_val,
+ int argc, JSValueConst *argv)
+{
+ FILE *f = js_std_file_get(ctx, this_val);
+ int64_t pos;
+ if (!f)
+ return JS_EXCEPTION;
+#if defined(__linux__)
+ pos = ftello(f);
+#else
+ pos = ftell(f);
+#endif
+ return JS_NewInt64(ctx, pos);
+}
+
+static JSValue js_std_file_seek(JSContext *ctx, JSValueConst this_val,
+ int argc, JSValueConst *argv)
+{
+ FILE *f = js_std_file_get(ctx, this_val);
+ int64_t pos;
+ int whence, ret;
+ if (!f)
+ return JS_EXCEPTION;
+ if (JS_ToInt64(ctx, &pos, argv[0]))
+ return JS_EXCEPTION;
+ if (JS_ToInt32(ctx, &whence, argv[1]))
+ return JS_EXCEPTION;
+#if defined(__linux__)
+ ret = fseeko(f, pos, whence);
+#else
+ ret = fseek(f, pos, whence);
+#endif
+ if (ret < 0)
+ return js_std_throw_errno(ctx, EBADF);
+ return JS_UNDEFINED;
+}
+
+static JSValue js_std_file_eof(JSContext *ctx, JSValueConst this_val,
+ int argc, JSValueConst *argv)
+{
+ FILE *f = js_std_file_get(ctx, this_val);
+ if (!f)
+ return JS_EXCEPTION;
+ return JS_NewBool(ctx, feof(f));
+}
+
+static JSValue js_std_file_fileno(JSContext *ctx, JSValueConst this_val,
+ int argc, JSValueConst *argv)
+{
+ FILE *f = js_std_file_get(ctx, this_val);
+ if (!f)
+ return JS_EXCEPTION;
+ return JS_NewInt32(ctx, fileno(f));
+}
+
+static JSValue js_std_file_read_write(JSContext *ctx, JSValueConst this_val,
+ int argc, JSValueConst *argv, int magic)
+{
+ FILE *f = js_std_file_get(ctx, this_val);
+ uint64_t pos, len;
+ size_t size, ret;
+ uint8_t *buf;
+
+ if (!f)
+ return JS_EXCEPTION;
+ if (JS_ToIndex(ctx, &pos, argv[1]))
+ return JS_EXCEPTION;
+ if (JS_ToIndex(ctx, &len, argv[2]))
+ return JS_EXCEPTION;
+ buf = JS_GetArrayBuffer(ctx, &size, argv[0]);
+ if (!buf)
+ return JS_EXCEPTION;
+ if (pos + len > size)
+ return JS_ThrowRangeError(ctx, "read/write array buffer overflow");
+ if (magic)
+ ret = fwrite(buf + pos, 1, len, f);
+ else
+ ret = fread(buf + pos, 1, len, f);
+ return JS_NewInt64(ctx, ret);
+}
+
+/* XXX: could use less memory and go faster */
+static JSValue js_std_file_getline(JSContext *ctx, JSValueConst this_val,
+ int argc, JSValueConst *argv)
+{
+ FILE *f = js_std_file_get(ctx, this_val);
+ int c;
+ DynBuf dbuf;
+ JSValue obj;
+
+ if (!f)
+ return JS_EXCEPTION;
+
+ js_std_dbuf_init(ctx, &dbuf);
+ for(;;) {
+ c = fgetc(f);
+ if (c == EOF) {
+ if (dbuf.size == 0) {
+ /* EOF */
+ dbuf_free(&dbuf);
+ return JS_NULL;
+ } else {
+ break;
+ }
+ }
+ if (c == '\n')
+ break;
+ if (dbuf_putc(&dbuf, c)) {
+ dbuf_free(&dbuf);
+ return JS_ThrowOutOfMemory(ctx);
+ }
+ }
+ obj = JS_NewStringLen(ctx, (const char *)dbuf.buf, dbuf.size);
+ dbuf_free(&dbuf);
+ return obj;
+}
+
+/* XXX: could use less memory and go faster */
+static JSValue js_std_file_readAsString(JSContext *ctx, JSValueConst this_val,
+ int argc, JSValueConst *argv)
+{
+ FILE *f = js_std_file_get(ctx, this_val);
+ int c;
+ DynBuf dbuf;
+ JSValue obj;
+ uint64_t max_size64;
+ size_t max_size;
+ JSValueConst max_size_val;
+
+ if (!f)
+ return JS_EXCEPTION;
+
+ if (argc >= 1)
+ max_size_val = argv[0];
+ else
+ max_size_val = JS_UNDEFINED;
+ max_size = (size_t)-1;
+ if (!JS_IsUndefined(max_size_val)) {
+ if (JS_ToIndex(ctx, &max_size64, max_size_val))
+ return JS_EXCEPTION;
+ if (max_size64 < max_size)
+ max_size = max_size64;
+ }
+
+ js_std_dbuf_init(ctx, &dbuf);
+ while (max_size != 0) {
+ c = fgetc(f);
+ if (c == EOF)
+ break;
+ if (dbuf_putc(&dbuf, c)) {
+ dbuf_free(&dbuf);
+ return JS_EXCEPTION;
+ }
+ max_size--;
+ }
+ obj = JS_NewStringLen(ctx, (const char *)dbuf.buf, dbuf.size);
+ dbuf_free(&dbuf);
+ return obj;
+}
+
+static JSValue js_std_file_getByte(JSContext *ctx, JSValueConst this_val,
+ int argc, JSValueConst *argv)
+{
+ FILE *f = js_std_file_get(ctx, this_val);
+ if (!f)
+ return JS_EXCEPTION;
+ return JS_NewInt32(ctx, fgetc(f));
+}
+
+static JSValue js_std_file_putByte(JSContext *ctx, JSValueConst this_val,
+ int argc, JSValueConst *argv)
+{
+ FILE *f = js_std_file_get(ctx, this_val);
+ int c;
+ if (!f)
+ return JS_EXCEPTION;
+ if (JS_ToInt32(ctx, &c, argv[0]))
+ return JS_EXCEPTION;
+ c = fputc(c, f);
+ return JS_NewInt32(ctx, c);
+}
+
+/* urlGet */
+
+#define URL_GET_PROGRAM "curl -s -i"
+#define URL_GET_BUF_SIZE 4096
+
+static int http_get_header_line(FILE *f, char *buf, size_t buf_size,
+ DynBuf *dbuf)
+{
+ int c;
+ char *p;
+
+ p = buf;
+ for(;;) {
+ c = fgetc(f);
+ if (c < 0)
+ return -1;
+ if ((p - buf) < buf_size - 1)
+ *p++ = c;
+ if (dbuf)
+ dbuf_putc(dbuf, c);
+ if (c == '\n')
+ break;
+ }
+ *p = '\0';
+ return 0;
+}
+
+static int http_get_status(const char *buf)
+{
+ const char *p = buf;
+ while (*p != ' ' && *p != '\0')
+ p++;
+ if (*p != ' ')
+ return 0;
+ while (*p == ' ')
+ p++;
+ return atoi(p);
+}
+
+static JSValue js_std_urlGet(JSContext *ctx, JSValueConst this_val,
+ int argc, JSValueConst *argv)
+{
+ const char *url;
+ DynBuf cmd_buf;
+ DynBuf data_buf_s, *data_buf = &data_buf_s;
+ DynBuf header_buf_s, *header_buf = &header_buf_s;
+ char *buf;
+ size_t i, len;
+ int c, status;
+ JSValue response = JS_UNDEFINED, ret_obj;
+ JSValueConst options_obj;
+ FILE *f;
+ BOOL binary_flag, full_flag;
+
+ url = JS_ToCString(ctx, argv[0]);
+ if (!url)
+ return JS_EXCEPTION;
+
+ binary_flag = FALSE;
+ full_flag = FALSE;
+
+ if (argc >= 2) {
+ options_obj = argv[1];
+
+ if (get_bool_option(ctx, &binary_flag, options_obj, "binary"))
+ goto fail_obj;
+
+ if (get_bool_option(ctx, &full_flag, options_obj, "full")) {
+ fail_obj:
+ JS_FreeCString(ctx, url);
+ return JS_EXCEPTION;
+ }
+ }
+
+ js_std_dbuf_init(ctx, &cmd_buf);
+ dbuf_printf(&cmd_buf, "%s ''", URL_GET_PROGRAM);
+ len = strlen(url);
+ for(i = 0; i < len; i++) {
+ c = url[i];
+ if (c == '\'' || c == '\\')
+ dbuf_putc(&cmd_buf, '\\');
+ dbuf_putc(&cmd_buf, c);
+ }
+ JS_FreeCString(ctx, url);
+ dbuf_putstr(&cmd_buf, "''");
+ dbuf_putc(&cmd_buf, '\0');
+ if (dbuf_error(&cmd_buf)) {
+ dbuf_free(&cmd_buf);
+ return JS_EXCEPTION;
+ }
+ // printf("%s\n", (char *)cmd_buf.buf);
+ f = popen((char *)cmd_buf.buf, "r");
+ dbuf_free(&cmd_buf);
+ if (!f) {
+ return js_std_throw_errno(ctx, errno);
+ }
+
+ js_std_dbuf_init(ctx, data_buf);
+ js_std_dbuf_init(ctx, header_buf);
+
+ buf = js_malloc(ctx, URL_GET_BUF_SIZE);
+ if (!buf)
+ goto fail;
+
+ /* get the HTTP status */
+ if (http_get_header_line(f, buf, URL_GET_BUF_SIZE, NULL) < 0)
+ goto bad_header;
+ status = http_get_status(buf);
+ if (!full_flag && !(status >= 200 && status <= 299)) {
+ js_std_throw_errno(ctx, ENOENT);
+ goto fail;
+ }
+
+ /* wait until there is an empty line */
+ for(;;) {
+ if (http_get_header_line(f, buf, URL_GET_BUF_SIZE, header_buf) < 0) {
+ bad_header:
+ js_std_throw_errno(ctx, EINVAL);
+ goto fail;
+ }
+ if (!strcmp(buf, "\r\n"))
+ break;
+ }
+ if (dbuf_error(header_buf))
+ goto fail;
+ header_buf->size -= 2; /* remove the trailing CRLF */
+
+ /* download the data */
+ for(;;) {
+ len = fread(buf, 1, URL_GET_BUF_SIZE, f);
+ if (len == 0)
+ break;
+ dbuf_put(data_buf, (uint8_t *)buf, len);
+ }
+ js_free(ctx, buf);
+ buf = NULL;
+ pclose(f);
+ f = NULL;
+
+ if (dbuf_error(data_buf))
+ goto fail;
+ if (binary_flag) {
+ response = JS_NewArrayBufferCopy(ctx,
+ data_buf->buf, data_buf->size);
+ } else {
+ response = JS_NewStringLen(ctx, (char *)data_buf->buf, data_buf->size);
+ }
+ dbuf_free(data_buf);
+ data_buf = NULL;
+ if (JS_IsException(response))
+ goto fail;
+
+ if (full_flag) {
+ ret_obj = JS_NewObject(ctx);
+ if (JS_IsException(ret_obj))
+ goto fail;
+ JS_DefinePropertyValueStr(ctx, ret_obj, "response",
+ response,
+ JS_PROP_C_W_E);
+ JS_DefinePropertyValueStr(ctx, ret_obj, "responseHeaders",
+ JS_NewStringLen(ctx, (char *)header_buf->buf,
+ header_buf->size),
+ JS_PROP_C_W_E);
+ JS_DefinePropertyValueStr(ctx, ret_obj, "status",
+ JS_NewInt32(ctx, status),
+ JS_PROP_C_W_E);
+ } else {
+ ret_obj = response;
+ }
+ dbuf_free(header_buf);
+ return ret_obj;
+ fail:
+ if (f)
+ pclose(f);
+ js_free(ctx, buf);
+ if (data_buf)
+ dbuf_free(data_buf);
+ if (header_buf)
+ dbuf_free(header_buf);
+ JS_FreeValue(ctx, response);
+ return JS_EXCEPTION;
+}
+
+static JSClassDef js_std_file_class = {
+ "FILE",
+ .finalizer = js_std_file_finalizer,
+};
+
+static const JSCFunctionListEntry js_std_funcs[] = {
+ JS_CFUNC_DEF("exit", 1, js_std_exit ),
+ JS_CFUNC_DEF("gc", 0, js_std_gc ),
+ JS_CFUNC_DEF("evalScript", 1, js_evalScript ),
+ JS_CFUNC_DEF("loadScript", 1, js_loadScript ),
+ JS_CFUNC_DEF("getenv", 1, js_std_getenv ),
+ JS_CFUNC_DEF("urlGet", 1, js_std_urlGet ),
+
+ /* FILE I/O */
+ JS_CFUNC_DEF("open", 2, js_std_open ),
+ JS_CFUNC_DEF("popen", 2, js_std_popen ),
+ JS_CFUNC_DEF("fdopen", 2, js_std_fdopen ),
+ JS_CFUNC_DEF("tmpfile", 0, js_std_tmpfile ),
+ JS_CFUNC_MAGIC_DEF("puts", 1, js_std_file_puts, 0 ),
+ JS_CFUNC_DEF("printf", 1, js_std_printf ),
+ JS_CFUNC_DEF("sprintf", 1, js_std_sprintf ),
+ JS_PROP_INT32_DEF("SEEK_SET", SEEK_SET, JS_PROP_CONFIGURABLE ),
+ JS_PROP_INT32_DEF("SEEK_CUR", SEEK_CUR, JS_PROP_CONFIGURABLE ),
+ JS_PROP_INT32_DEF("SEEK_END", SEEK_END, JS_PROP_CONFIGURABLE ),
+
+ /* setenv, ... */
+};
+
+static const JSCFunctionListEntry js_std_error_funcs[] = {
+ JS_CFUNC_DEF("strerror", 1, js_std_error_strerror ),
+ /* various errno values */
+#define DEF(x) JS_PROP_INT32_DEF(#x, x, JS_PROP_CONFIGURABLE )
+ DEF(EINVAL),
+ DEF(EIO),
+ DEF(EACCES),
+ DEF(EEXIST),
+ DEF(ENOSPC),
+ DEF(ENOSYS),
+ DEF(EBUSY),
+ DEF(ENOENT),
+ DEF(EPERM),
+ DEF(EPIPE),
+ DEF(EBADF),
+#undef DEF
+};
+
+static const JSCFunctionListEntry js_std_file_proto_funcs[] = {
+ JS_CFUNC_DEF("close", 0, js_std_file_close ),
+ JS_CFUNC_MAGIC_DEF("puts", 1, js_std_file_puts, 1 ),
+ JS_CFUNC_DEF("printf", 1, js_std_file_printf ),
+ JS_CFUNC_DEF("flush", 0, js_std_file_flush ),
+ JS_CFUNC_DEF("tell", 0, js_std_file_tell ),
+ JS_CFUNC_DEF("seek", 2, js_std_file_seek ),
+ JS_CFUNC_DEF("eof", 0, js_std_file_eof ),
+ JS_CFUNC_DEF("fileno", 0, js_std_file_fileno ),
+ JS_CFUNC_MAGIC_DEF("read", 3, js_std_file_read_write, 0 ),
+ JS_CFUNC_MAGIC_DEF("write", 3, js_std_file_read_write, 1 ),
+ JS_CFUNC_DEF("getline", 0, js_std_file_getline ),
+ JS_CFUNC_DEF("readAsString", 0, js_std_file_readAsString ),
+ JS_CFUNC_DEF("getByte", 0, js_std_file_getByte ),
+ JS_CFUNC_DEF("putByte", 1, js_std_file_putByte ),
+ /* setvbuf, ferror, clearerr, ... */
+};
+
+static int js_std_init(JSContext *ctx, JSModuleDef *m)
+{
+ JSValue proto, obj;
+
+ /* FILE class */
+ /* the class ID is created once */
+ JS_NewClassID(&js_std_file_class_id);
+ /* the class is created once per runtime */
+ JS_NewClass(JS_GetRuntime(ctx), js_std_file_class_id, &js_std_file_class);
+ proto = JS_NewObject(ctx);
+ JS_SetPropertyFunctionList(ctx, proto, js_std_file_proto_funcs,
+ countof(js_std_file_proto_funcs));
+ JS_SetClassProto(ctx, js_std_file_class_id, proto);
+
+ JS_SetModuleExportList(ctx, m, js_std_funcs,
+ countof(js_std_funcs));
+ JS_SetModuleExport(ctx, m, "in", js_new_std_file(ctx, stdin, FALSE, FALSE));
+ JS_SetModuleExport(ctx, m, "out", js_new_std_file(ctx, stdout, FALSE, FALSE));
+ JS_SetModuleExport(ctx, m, "err", js_new_std_file(ctx, stderr, FALSE, FALSE));
+
+ obj = JS_NewCFunction2(ctx, js_std_error_constructor,
+ "Error", 1, JS_CFUNC_constructor, 0);
+ JS_SetPropertyFunctionList(ctx, obj, js_std_error_funcs,
+ countof(js_std_error_funcs));
+ JS_SetModuleExport(ctx, m, "Error", obj);
+
+ return 0;
+}
+
+JSModuleDef *js_init_module_std(JSContext *ctx, const char *module_name)
+{
+ JSModuleDef *m;
+ m = JS_NewCModule(ctx, module_name, js_std_init);
+ if (!m)
+ return NULL;
+ JS_AddModuleExportList(ctx, m, js_std_funcs, countof(js_std_funcs));
+ JS_AddModuleExport(ctx, m, "in");
+ JS_AddModuleExport(ctx, m, "out");
+ JS_AddModuleExport(ctx, m, "err");
+ JS_AddModuleExport(ctx, m, "Error");
+ return m;
+}
+
+/**********************************************************/
+/* 'os' object */
+
+static JSValue js_os_return(JSContext *ctx, ssize_t ret)
+{
+ if (ret < 0)
+ ret = -errno;
+ return JS_NewInt64(ctx, ret);
+}
+
+static JSValue js_os_open(JSContext *ctx, JSValueConst this_val,
+ int argc, JSValueConst *argv)
+{
+ const char *filename;
+ int flags, mode, ret;
+
+ filename = JS_ToCString(ctx, argv[0]);
+ if (!filename)
+ return JS_EXCEPTION;
+ if (JS_ToInt32(ctx, &flags, argv[1]))
+ goto fail;
+ if (argc >= 3 && !JS_IsUndefined(argv[2])) {
+ if (JS_ToInt32(ctx, &mode, argv[2])) {
+ fail:
+ JS_FreeCString(ctx, filename);
+ return JS_EXCEPTION;
+ }
+ } else {
+ mode = 0666;
+ }
+#if defined(_WIN32)
+ /* force binary mode by default */
+ if (!(flags & O_TEXT))
+ flags |= O_BINARY;
+#endif
+ ret = open(filename, flags, mode);
+ JS_FreeCString(ctx, filename);
+ return js_os_return(ctx, ret);
+}
+
+static JSValue js_os_close(JSContext *ctx, JSValueConst this_val,
+ int argc, JSValueConst *argv)
+{
+ int fd, ret;
+ if (JS_ToInt32(ctx, &fd, argv[0]))
+ return JS_EXCEPTION;
+ ret = close(fd);
+ return js_os_return(ctx, ret);
+}
+
+static JSValue js_os_seek(JSContext *ctx, JSValueConst this_val,
+ int argc, JSValueConst *argv)
+{
+ int fd, whence, ret;
+ int64_t pos;
+
+ if (JS_ToInt32(ctx, &fd, argv[0]))
+ return JS_EXCEPTION;
+ if (JS_ToInt64(ctx, &pos, argv[1]))
+ return JS_EXCEPTION;
+ if (JS_ToInt32(ctx, &whence, argv[2]))
+ return JS_EXCEPTION;
+ ret = lseek(fd, pos, whence);
+ return js_os_return(ctx, ret);
+}
+
+static JSValue js_os_read_write(JSContext *ctx, JSValueConst this_val,
+ int argc, JSValueConst *argv, int magic)
+{
+ int fd;
+ uint64_t pos, len;
+ size_t size;
+ ssize_t ret;
+ uint8_t *buf;
+
+ if (JS_ToInt32(ctx, &fd, argv[0]))
+ return JS_EXCEPTION;
+ if (JS_ToIndex(ctx, &pos, argv[2]))
+ return JS_EXCEPTION;
+ if (JS_ToIndex(ctx, &len, argv[3]))
+ return JS_EXCEPTION;
+ buf = JS_GetArrayBuffer(ctx, &size, argv[1]);
+ if (!buf)
+ return JS_EXCEPTION;
+ if (pos + len > size)
+ return JS_ThrowRangeError(ctx, "read/write array buffer overflow");
+ if (magic)
+ ret = write(fd, buf + pos, len);
+ else
+ ret = read(fd, buf + pos, len);
+ return js_os_return(ctx, ret);
+}
+
+static JSValue js_os_isatty(JSContext *ctx, JSValueConst this_val,
+ int argc, JSValueConst *argv)
+{
+ int fd;
+ if (JS_ToInt32(ctx, &fd, argv[0]))
+ return JS_EXCEPTION;
+ return JS_NewBool(ctx, isatty(fd) == 1);
+}
+
+#if defined(_WIN32)
+static JSValue js_os_ttyGetWinSize(JSContext *ctx, JSValueConst this_val,
+ int argc, JSValueConst *argv)
+{
+ int fd;
+ HANDLE handle;
+ CONSOLE_SCREEN_BUFFER_INFO info;
+ JSValue obj;
+
+ if (JS_ToInt32(ctx, &fd, argv[0]))
+ return JS_EXCEPTION;
+ handle = (HANDLE)_get_osfhandle(fd);
+
+ if (!GetConsoleScreenBufferInfo(handle, &info))
+ return JS_NULL;
+ obj = JS_NewArray(ctx);
+ if (JS_IsException(obj))
+ return obj;
+ JS_DefinePropertyValueUint32(ctx, obj, 0, JS_NewInt32(ctx, info.dwSize.X), JS_PROP_C_W_E);
+ JS_DefinePropertyValueUint32(ctx, obj, 1, JS_NewInt32(ctx, info.dwSize.Y), JS_PROP_C_W_E);
+ return obj;
+}
+
+static JSValue js_os_ttySetRaw(JSContext *ctx, JSValueConst this_val,
+ int argc, JSValueConst *argv)
+{
+ int fd;
+ HANDLE handle;
+
+ if (JS_ToInt32(ctx, &fd, argv[0]))
+ return JS_EXCEPTION;
+ handle = (HANDLE)_get_osfhandle(fd);
+
+ SetConsoleMode(handle, ENABLE_WINDOW_INPUT);
+ return JS_UNDEFINED;
+}
+#else
+static JSValue js_os_ttyGetWinSize(JSContext *ctx, JSValueConst this_val,
+ int argc, JSValueConst *argv)
+{
+ int fd;
+ struct winsize ws;
+ JSValue obj;
+
+ if (JS_ToInt32(ctx, &fd, argv[0]))
+ return JS_EXCEPTION;
+ if (ioctl(fd, TIOCGWINSZ, &ws) == 0 &&
+ ws.ws_col >= 4 && ws.ws_row >= 4) {
+ obj = JS_NewArray(ctx);
+ if (JS_IsException(obj))
+ return obj;
+ JS_DefinePropertyValueUint32(ctx, obj, 0, JS_NewInt32(ctx, ws.ws_col), JS_PROP_C_W_E);
+ JS_DefinePropertyValueUint32(ctx, obj, 1, JS_NewInt32(ctx, ws.ws_row), JS_PROP_C_W_E);
+ return obj;
+ } else {
+ return JS_NULL;
+ }
+}
+
+static struct termios oldtty;
+
+static void term_exit(void)
+{
+ tcsetattr(0, TCSANOW, &oldtty);
+}
+
+/* XXX: should add a way to go back to normal mode */
+static JSValue js_os_ttySetRaw(JSContext *ctx, JSValueConst this_val,
+ int argc, JSValueConst *argv)
+{
+ struct termios tty;
+ int fd;
+
+ if (JS_ToInt32(ctx, &fd, argv[0]))
+ return JS_EXCEPTION;
+
+ memset(&tty, 0, sizeof(tty));
+ tcgetattr(fd, &tty);
+ oldtty = tty;
+
+ tty.c_iflag &= ~(IGNBRK|BRKINT|PARMRK|ISTRIP
+ |INLCR|IGNCR|ICRNL|IXON);
+ tty.c_oflag |= OPOST;
+ tty.c_lflag &= ~(ECHO|ECHONL|ICANON|IEXTEN);
+ tty.c_cflag &= ~(CSIZE|PARENB);
+ tty.c_cflag |= CS8;
+ tty.c_cc[VMIN] = 1;
+ tty.c_cc[VTIME] = 0;
+
+ tcsetattr(fd, TCSANOW, &tty);
+
+ atexit(term_exit);
+ return JS_UNDEFINED;
+}
+
+#endif /* !_WIN32 */
+
+static JSValue js_os_remove(JSContext *ctx, JSValueConst this_val,
+ int argc, JSValueConst *argv)
+{
+ const char *filename;
+ int ret;
+
+ filename = JS_ToCString(ctx, argv[0]);
+ if (!filename)
+ return JS_EXCEPTION;
+ ret = remove(filename);
+ JS_FreeCString(ctx, filename);
+ return js_os_return(ctx, ret);
+}
+
+static JSValue js_os_rename(JSContext *ctx, JSValueConst this_val,
+ int argc, JSValueConst *argv)
+{
+ const char *oldpath, *newpath;
+ int ret;
+
+ oldpath = JS_ToCString(ctx, argv[0]);
+ if (!oldpath)
+ return JS_EXCEPTION;
+ newpath = JS_ToCString(ctx, argv[1]);
+ if (!newpath) {
+ JS_FreeCString(ctx, oldpath);
+ return JS_EXCEPTION;
+ }
+ ret = rename(oldpath, newpath);
+ JS_FreeCString(ctx, oldpath);
+ JS_FreeCString(ctx, newpath);
+ return js_os_return(ctx, ret);
+}
+
+static JSOSRWHandler *find_rh(int fd)
+{
+ JSOSRWHandler *rh;
+ struct list_head *el;
+ list_for_each(el, &os_rw_handlers) {
+ rh = list_entry(el, JSOSRWHandler, link);
+ if (rh->fd == fd)
+ return rh;
+ }
+ return NULL;
+}
+
+static void free_rw_handler(JSRuntime *rt, JSOSRWHandler *rh)
+{
+ int i;
+ list_del(&rh->link);
+ for(i = 0; i < 2; i++) {
+ JS_FreeValueRT(rt, rh->rw_func[i]);
+ }
+ js_free_rt(rt, rh);
+}
+
+static JSValue js_os_setReadHandler(JSContext *ctx, JSValueConst this_val,
+ int argc, JSValueConst *argv, int magic)
+{
+ JSOSRWHandler *rh;
+ int fd;
+ JSValueConst func;
+
+ if (JS_ToInt32(ctx, &fd, argv[0]))
+ return JS_EXCEPTION;
+ func = argv[1];
+ if (JS_IsNull(func)) {
+ rh = find_rh(fd);
+ if (rh) {
+ JS_FreeValue(ctx, rh->rw_func[magic]);
+ rh->rw_func[magic] = JS_NULL;
+ if (JS_IsNull(rh->rw_func[0]) &&
+ JS_IsNull(rh->rw_func[1])) {
+ /* remove the entry */
+ free_rw_handler(JS_GetRuntime(ctx), rh);
+ }
+ }
+ } else {
+ if (!JS_IsFunction(ctx, func))
+ return JS_ThrowTypeError(ctx, "not a function");
+ rh = find_rh(fd);
+ if (!rh) {
+ rh = js_mallocz(ctx, sizeof(*rh));
+ if (!rh)
+ return JS_EXCEPTION;
+ rh->fd = fd;
+ rh->rw_func[0] = JS_NULL;
+ rh->rw_func[1] = JS_NULL;
+ list_add_tail(&rh->link, &os_rw_handlers);
+ }
+ JS_FreeValue(ctx, rh->rw_func[magic]);
+ rh->rw_func[magic] = JS_DupValue(ctx, func);
+ }
+ return JS_UNDEFINED;
+}
+
+static JSOSSignalHandler *find_sh(int sig_num)
+{
+ JSOSSignalHandler *sh;
+ struct list_head *el;
+ list_for_each(el, &os_signal_handlers) {
+ sh = list_entry(el, JSOSSignalHandler, link);
+ if (sh->sig_num == sig_num)
+ return sh;
+ }
+ return NULL;
+}
+
+static void free_sh(JSRuntime *rt, JSOSSignalHandler *sh)
+{
+ list_del(&sh->link);
+ JS_FreeValueRT(rt, sh->func);
+ js_free_rt(rt, sh);
+}
+
+static void os_signal_handler(int sig_num)
+{
+ os_pending_signals |= ((uint64_t)1 << sig_num);
+}
+
+#if defined(_WIN32)
+typedef void (*sighandler_t)(int sig_num);
+#endif
+
+static JSValue js_os_signal(JSContext *ctx, JSValueConst this_val,
+ int argc, JSValueConst *argv)
+{
+ JSOSSignalHandler *sh;
+ uint32_t sig_num;
+ JSValueConst func;
+ sighandler_t handler;
+
+ if (JS_ToUint32(ctx, &sig_num, argv[0]))
+ return JS_EXCEPTION;
+ if (sig_num >= 64)
+ return JS_ThrowRangeError(ctx, "invalid signal number");
+ func = argv[1];
+ /* func = null: SIG_DFL, func = undefined, SIG_IGN */
+ if (JS_IsNull(func) || JS_IsUndefined(func)) {
+ sh = find_sh(sig_num);
+ if (sh) {
+ free_sh(JS_GetRuntime(ctx), sh);
+ }
+ if (JS_IsNull(func))
+ handler = SIG_DFL;
+ else
+ handler = SIG_IGN;
+ signal(sig_num, handler);
+ } else {
+ if (!JS_IsFunction(ctx, func))
+ return JS_ThrowTypeError(ctx, "not a function");
+ sh = find_sh(sig_num);
+ if (!sh) {
+ sh = js_mallocz(ctx, sizeof(*sh));
+ if (!sh)
+ return JS_EXCEPTION;
+ sh->sig_num = sig_num;
+ list_add_tail(&sh->link, &os_signal_handlers);
+ }
+ JS_FreeValue(ctx, sh->func);
+ sh->func = JS_DupValue(ctx, func);
+ signal(sig_num, os_signal_handler);
+ }
+ 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
+/* more portable, but does not work if the date is updated */
+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 void unlink_timer(JSRuntime *rt, JSOSTimer *th)
+{
+ if (th->link.prev) {
+ list_del(&th->link);
+ th->link.prev = th->link.next = NULL;
+ }
+}
+
+static void free_timer(JSRuntime *rt, JSOSTimer *th)
+{
+ JS_FreeValueRT(rt, th->func);
+ js_free_rt(rt, th);
+}
+
+static JSClassID js_os_timer_class_id;
+
+static void js_os_timer_finalizer(JSRuntime *rt, JSValue val)
+{
+ JSOSTimer *th = JS_GetOpaque(val, js_os_timer_class_id);
+ if (th) {
+ th->has_object = FALSE;
+ if (!th->link.prev)
+ free_timer(rt, th);
+ }
+}
+
+static void js_os_timer_mark(JSRuntime *rt, JSValueConst val,
+ JS_MarkFunc *mark_func)
+{
+ JSOSTimer *th = JS_GetOpaque(val, js_os_timer_class_id);
+ if (th) {
+ JS_MarkValue(rt, th->func, mark_func);
+ }
+}
+
+static JSValue js_os_setTimeout(JSContext *ctx, JSValueConst this_val,
+ int argc, JSValueConst *argv)
+{
+ int64_t delay;
+ JSValueConst func;
+ JSOSTimer *th;
+ JSValue obj;
+
+ func = argv[0];
+ if (!JS_IsFunction(ctx, func))
+ return JS_ThrowTypeError(ctx, "not a function");
+ if (JS_ToInt64(ctx, &delay, argv[1]))
+ return JS_EXCEPTION;
+ obj = JS_NewObjectClass(ctx, js_os_timer_class_id);
+ if (JS_IsException(obj))
+ return obj;
+ th = js_mallocz(ctx, sizeof(*th));
+ if (!th) {
+ JS_FreeValue(ctx, obj);
+ return JS_EXCEPTION;
+ }
+ th->has_object = TRUE;
+ th->timeout = get_time_ms() + delay;
+ th->func = JS_DupValue(ctx, func);
+ list_add_tail(&th->link, &os_timers);
+ JS_SetOpaque(obj, th);
+ return obj;
+}
+
+static JSValue js_os_clearTimeout(JSContext *ctx, JSValueConst this_val,
+ int argc, JSValueConst *argv)
+{
+ JSOSTimer *th = JS_GetOpaque2(ctx, argv[0], js_os_timer_class_id);
+ if (!th)
+ return JS_EXCEPTION;
+ unlink_timer(JS_GetRuntime(ctx), th);
+ return JS_UNDEFINED;
+}
+
+static JSClassDef js_os_timer_class = {
+ "OSTimer",
+ .finalizer = js_os_timer_finalizer,
+ .gc_mark = js_os_timer_mark,
+};
+
+static void call_handler(JSContext *ctx, JSValueConst func)
+{
+ JSValue ret, func1;
+ /* 'func' might be destroyed when calling itself (if it frees the
+ handler), so must take extra care */
+ func1 = JS_DupValue(ctx, func);
+ ret = JS_Call(ctx, func1, JS_UNDEFINED, 0, NULL);
+ JS_FreeValue(ctx, func1);
+ if (JS_IsException(ret))
+ js_std_dump_error(ctx);
+ JS_FreeValue(ctx, ret);
+}
+
+#if defined(_WIN32)
+
+static int js_os_poll(JSContext *ctx)
+{
+ int min_delay, console_fd;
+ int64_t cur_time, delay;
+ JSOSRWHandler *rh;
+ struct list_head *el;
+
+ /* XXX: handle signals if useful */
+
+ if (list_empty(&os_rw_handlers) && list_empty(&os_timers))
+ return -1; /* no more events */
+
+ /* XXX: only timers and basic console input are supported */
+ if (!list_empty(&os_timers)) {
+ cur_time = get_time_ms();
+ min_delay = 10000;
+ list_for_each(el, &os_timers) {
+ JSOSTimer *th = list_entry(el, JSOSTimer, link);
+ delay = th->timeout - cur_time;
+ if (delay <= 0) {
+ JSValue func;
+ /* the timer expired */
+ func = th->func;
+ th->func = JS_UNDEFINED;
+ unlink_timer(JS_GetRuntime(ctx), th);
+ if (!th->has_object)
+ free_timer(JS_GetRuntime(ctx), th);
+ call_handler(ctx, func);
+ JS_FreeValue(ctx, func);
+ return 0;
+ } else if (delay < min_delay) {
+ min_delay = delay;
+ }
+ }
+ } else {
+ min_delay = -1;
+ }
+
+ console_fd = -1;
+ list_for_each(el, &os_rw_handlers) {
+ rh = list_entry(el, JSOSRWHandler, link);
+ if (rh->fd == 0 && !JS_IsNull(rh->rw_func[0])) {
+ console_fd = rh->fd;
+ break;
+ }
+ }
+
+ if (console_fd >= 0) {
+ DWORD ti, ret;
+ HANDLE handle;
+ if (min_delay == -1)
+ ti = INFINITE;
+ else
+ ti = min_delay;
+ handle = (HANDLE)_get_osfhandle(console_fd);
+ ret = WaitForSingleObject(handle, ti);
+ if (ret == WAIT_OBJECT_0) {
+ list_for_each(el, &os_rw_handlers) {
+ rh = list_entry(el, JSOSRWHandler, link);
+ if (rh->fd == console_fd && !JS_IsNull(rh->rw_func[0])) {
+ call_handler(ctx, rh->rw_func[0]);
+ /* must stop because the list may have been modified */
+ break;
+ }
+ }
+ }
+ } else {
+ Sleep(min_delay);
+ }
+ return 0;
+}
+#else
+static int js_os_poll(JSContext *ctx)
+{
+ int ret, fd_max, min_delay;
+ int64_t cur_time, delay;
+ fd_set rfds, wfds;
+ JSOSRWHandler *rh;
+ struct list_head *el;
+ struct timeval tv, *tvp;
+
+ if (unlikely(os_pending_signals != 0)) {
+ JSOSSignalHandler *sh;
+ uint64_t mask;
+
+ list_for_each(el, &os_signal_handlers) {
+ sh = list_entry(el, JSOSSignalHandler, link);
+ mask = (uint64_t)1 << sh->sig_num;
+ if (os_pending_signals & mask) {
+ os_pending_signals &= ~mask;
+ call_handler(ctx, sh->func);
+ return 0;
+ }
+ }
+ }
+
+ if (list_empty(&os_rw_handlers) && list_empty(&os_timers))
+ return -1; /* no more events */
+
+ if (!list_empty(&os_timers)) {
+ cur_time = get_time_ms();
+ min_delay = 10000;
+ list_for_each(el, &os_timers) {
+ JSOSTimer *th = list_entry(el, JSOSTimer, link);
+ delay = th->timeout - cur_time;
+ if (delay <= 0) {
+ JSValue func;
+ /* the timer expired */
+ func = th->func;
+ th->func = JS_UNDEFINED;
+ unlink_timer(JS_GetRuntime(ctx), th);
+ if (!th->has_object)
+ free_timer(JS_GetRuntime(ctx), th);
+ call_handler(ctx, func);
+ JS_FreeValue(ctx, func);
+ return 0;
+ } else if (delay < min_delay) {
+ min_delay = delay;
+ }
+ }
+ tv.tv_sec = min_delay / 1000;
+ tv.tv_usec = (min_delay % 1000) * 1000;
+ tvp = &tv;
+ } else {
+ tvp = NULL;
+ }
+
+ FD_ZERO(&rfds);
+ FD_ZERO(&wfds);
+ fd_max = -1;
+ list_for_each(el, &os_rw_handlers) {
+ rh = list_entry(el, JSOSRWHandler, link);
+ fd_max = max_int(fd_max, rh->fd);
+ if (!JS_IsNull(rh->rw_func[0]))
+ FD_SET(rh->fd, &rfds);
+ if (!JS_IsNull(rh->rw_func[1]))
+ FD_SET(rh->fd, &wfds);
+ }
+
+ ret = select(fd_max + 1, &rfds, &wfds, NULL, tvp);
+ if (ret > 0) {
+ list_for_each(el, &os_rw_handlers) {
+ rh = list_entry(el, JSOSRWHandler, link);
+ if (!JS_IsNull(rh->rw_func[0]) &&
+ FD_ISSET(rh->fd, &rfds)) {
+ call_handler(ctx, rh->rw_func[0]);
+ /* must stop because the list may have been modified */
+ break;
+ }
+ if (!JS_IsNull(rh->rw_func[1])) {
+ FD_SET(rh->fd, &wfds);
+ call_handler(ctx, rh->rw_func[1]);
+ /* must stop because the list may have been modified */
+ break;
+ }
+ }
+ }
+ return 0;
+}
+#endif /* !_WIN32 */
+
+static JSValue make_obj_error(JSContext *ctx,
+ JSValue obj,
+ int err)
+{
+ JSValue arr;
+ if (JS_IsException(obj))
+ return obj;
+ arr = JS_NewArray(ctx);
+ if (JS_IsException(arr))
+ return JS_EXCEPTION;
+ JS_DefinePropertyValueUint32(ctx, arr, 0, obj,
+ JS_PROP_C_W_E);
+ JS_DefinePropertyValueUint32(ctx, arr, 1, JS_NewInt32(ctx, err),
+ JS_PROP_C_W_E);
+ return arr;
+}
+
+static JSValue make_string_error(JSContext *ctx,
+ const char *buf,
+ int err)
+{
+ return make_obj_error(ctx, JS_NewString(ctx, buf), err);
+}
+
+/* return [cwd, errorcode] */
+static JSValue js_os_getcwd(JSContext *ctx, JSValueConst this_val,
+ int argc, JSValueConst *argv)
+{
+ char buf[PATH_MAX];
+ int err;
+
+ if (!getcwd(buf, sizeof(buf))) {
+ buf[0] = '\0';
+ err = -errno;
+ } else {
+ err = 0;
+ }
+ return make_string_error(ctx, buf, err);
+}
+
+#if !defined(_WIN32)
+
+/* return [path, errorcode] */
+static JSValue js_os_realpath(JSContext *ctx, JSValueConst this_val,
+ int argc, JSValueConst *argv)
+{
+ const char *path;
+ char buf[PATH_MAX], *res;
+ int err;
+
+ path = JS_ToCString(ctx, argv[0]);
+ if (!path)
+ return JS_EXCEPTION;
+ res = realpath(path, buf);
+ JS_FreeCString(ctx, path);
+ if (!res) {
+ buf[0] = '\0';
+ err = -errno;
+ } else {
+ err = 0;
+ }
+ return make_string_error(ctx, buf, err);
+}
+
+static JSValue js_os_mkdir(JSContext *ctx, JSValueConst this_val,
+ int argc, JSValueConst *argv)
+{
+ int mode, ret;
+ const char *path;
+
+ if (argc >= 2) {
+ if (JS_ToInt32(ctx, &mode, argv[1]))
+ return JS_EXCEPTION;
+ } else {
+ mode = 0777;
+ }
+ path = JS_ToCString(ctx, argv[0]);
+ if (!path)
+ return JS_EXCEPTION;
+ ret = mkdir(path, mode);
+ JS_FreeCString(ctx, path);
+ return js_os_return(ctx, ret);
+}
+
+static int64_t timespec_to_ms(const struct timespec *tv)
+{
+ return (int64_t)tv->tv_sec * 1000 + (tv->tv_nsec / 1000000);
+}
+
+/* return [obj, errcode] */
+static JSValue js_os_stat(JSContext *ctx, JSValueConst this_val,
+ int argc, JSValueConst *argv, int is_lstat)
+{
+ const char *path;
+ int err, res;
+ struct stat st;
+ JSValue obj;
+
+ path = JS_ToCString(ctx, argv[0]);
+ if (!path)
+ return JS_EXCEPTION;
+ if (is_lstat)
+ res = lstat(path, &st);
+ else
+ res = stat(path, &st);
+ JS_FreeCString(ctx, path);
+ if (res < 0) {
+ err = -errno;
+ obj = JS_NULL;
+ } else {
+ err = 0;
+ obj = JS_NewObject(ctx);
+ if (JS_IsException(obj))
+ return JS_EXCEPTION;
+ JS_DefinePropertyValueStr(ctx, obj, "dev",
+ JS_NewInt64(ctx, st.st_dev),
+ JS_PROP_C_W_E);
+ JS_DefinePropertyValueStr(ctx, obj, "ino",
+ JS_NewInt64(ctx, st.st_ino),
+ JS_PROP_C_W_E);
+ JS_DefinePropertyValueStr(ctx, obj, "mode",
+ JS_NewInt32(ctx, st.st_mode),
+ JS_PROP_C_W_E);
+ JS_DefinePropertyValueStr(ctx, obj, "nlink",
+ JS_NewInt64(ctx, st.st_nlink),
+ JS_PROP_C_W_E);
+ JS_DefinePropertyValueStr(ctx, obj, "uid",
+ JS_NewInt64(ctx, st.st_uid),
+ JS_PROP_C_W_E);
+ JS_DefinePropertyValueStr(ctx, obj, "gid",
+ JS_NewInt64(ctx, st.st_gid),
+ JS_PROP_C_W_E);
+ JS_DefinePropertyValueStr(ctx, obj, "rdev",
+ JS_NewInt64(ctx, st.st_rdev),
+ JS_PROP_C_W_E);
+ JS_DefinePropertyValueStr(ctx, obj, "size",
+ JS_NewInt64(ctx, st.st_size),
+ JS_PROP_C_W_E);
+ JS_DefinePropertyValueStr(ctx, obj, "blocks",
+ JS_NewInt64(ctx, st.st_blocks),
+ JS_PROP_C_W_E);
+#if defined(__APPLE__)
+ JS_DefinePropertyValueStr(ctx, obj, "atime",
+ JS_NewInt64(ctx, timespec_to_ms(&st.st_atimespec)),
+ JS_PROP_C_W_E);
+ JS_DefinePropertyValueStr(ctx, obj, "mtime",
+ JS_NewInt64(ctx, timespec_to_ms(&st.st_mtimespec)),
+ JS_PROP_C_W_E);
+ JS_DefinePropertyValueStr(ctx, obj, "ctime",
+ JS_NewInt64(ctx, timespec_to_ms(&st.st_ctimespec)),
+ JS_PROP_C_W_E);
+#else
+ JS_DefinePropertyValueStr(ctx, obj, "atime",
+ JS_NewInt64(ctx, timespec_to_ms(&st.st_atim)),
+ JS_PROP_C_W_E);
+ JS_DefinePropertyValueStr(ctx, obj, "mtime",
+ JS_NewInt64(ctx, timespec_to_ms(&st.st_mtim)),
+ JS_PROP_C_W_E);
+ JS_DefinePropertyValueStr(ctx, obj, "ctime",
+ JS_NewInt64(ctx, timespec_to_ms(&st.st_ctim)),
+ JS_PROP_C_W_E);
+#endif
+ }
+ return make_obj_error(ctx, obj, err);
+}
+
+static JSValue js_os_symlink(JSContext *ctx, JSValueConst this_val,
+ int argc, JSValueConst *argv)
+{
+ const char *target, *linkpath;
+ int err;
+
+ target = JS_ToCString(ctx, argv[0]);
+ if (!target)
+ return JS_EXCEPTION;
+ linkpath = JS_ToCString(ctx, argv[1]);
+ if (!linkpath) {
+ JS_FreeCString(ctx, target);
+ return JS_EXCEPTION;
+ }
+ err = symlink(target, linkpath);
+ JS_FreeCString(ctx, target);
+ JS_FreeCString(ctx, linkpath);
+ return js_os_return(ctx, err);
+}
+
+/* return [path, errorcode] */
+static JSValue js_os_readlink(JSContext *ctx, JSValueConst this_val,
+ int argc, JSValueConst *argv)
+{
+ const char *path;
+ char buf[PATH_MAX];
+ int err;
+ ssize_t res;
+
+ path = JS_ToCString(ctx, argv[0]);
+ if (!path)
+ return JS_EXCEPTION;
+ res = readlink(path, buf, sizeof(buf) - 1);
+ JS_FreeCString(ctx, path);
+ if (res < 0) {
+ buf[0] = '\0';
+ err = -errno;
+ } else {
+ buf[res] = '\0';
+ err = 0;
+ }
+ return make_string_error(ctx, buf, err);
+}
+
+/* return [array, errorcode] */
+static JSValue js_os_readdir(JSContext *ctx, JSValueConst this_val,
+ int argc, JSValueConst *argv)
+{
+ const char *path;
+ DIR *f;
+ struct dirent *d;
+ JSValue obj;
+ int err;
+ uint32_t len;
+
+ path = JS_ToCString(ctx, argv[0]);
+ if (!path)
+ return JS_EXCEPTION;
+ obj = JS_NewArray(ctx);
+ if (JS_IsException(obj)) {
+ JS_FreeCString(ctx, path);
+ return JS_EXCEPTION;
+ }
+ f = opendir(path);
+ JS_FreeCString(ctx, path);
+ err = 0;
+ if (!f) {
+ err = -errno;
+ goto done;
+ }
+ len = 0;
+ for(;;) {
+ errno = 0;
+ d = readdir(f);
+ if (!d) {
+ err = -errno;
+ break;
+ }
+ JS_DefinePropertyValueUint32(ctx, obj, len++,
+ JS_NewString(ctx, d->d_name),
+ JS_PROP_C_W_E);
+ }
+ closedir(f);
+ done:
+ return make_obj_error(ctx, obj, err);
+}
+
+static void ms_to_timeval(struct timeval *tv, uint64_t v)
+{
+ tv->tv_sec = v / 1000;
+ tv->tv_usec = (v % 1000) * 1000;
+}
+
+static JSValue js_os_utimes(JSContext *ctx, JSValueConst this_val,
+ int argc, JSValueConst *argv)
+{
+ const char *path;
+ int64_t atime, mtime;
+ int ret;
+ struct timeval times[2];
+
+ if (JS_ToInt64(ctx, &atime, argv[1]))
+ return JS_EXCEPTION;
+ if (JS_ToInt64(ctx, &mtime, argv[2]))
+ return JS_EXCEPTION;
+ path = JS_ToCString(ctx, argv[0]);
+ if (!path)
+ return JS_EXCEPTION;
+ ms_to_timeval(&times[0], atime);
+ ms_to_timeval(&times[1], mtime);
+ ret = utimes(path, times);
+ JS_FreeCString(ctx, path);
+ return js_os_return(ctx, ret);
+}
+
+/* exec(args[, options]) -> exitcode */
+static JSValue js_os_exec(JSContext *ctx, JSValueConst this_val,
+ int argc, JSValueConst *argv)
+{
+ JSValueConst options, args = argv[0];
+ JSValue val, ret_val;
+ const char **exec_argv, *file = NULL, *str, *cwd = NULL;
+ uint32_t exec_argc, i;
+ int ret, pid, status;
+ BOOL block_flag = TRUE, use_path = TRUE;
+ static const char *std_name[3] = { "stdin", "stdout", "stderr" };
+ int std_fds[3];
+
+ val = JS_GetPropertyStr(ctx, args, "length");
+ if (JS_IsException(val))
+ return JS_EXCEPTION;
+ ret = JS_ToUint32(ctx, &exec_argc, val);
+ JS_FreeValue(ctx, val);
+ if (ret)
+ return JS_EXCEPTION;
+ /* arbitrary limit to avoid overflow */
+ if (exec_argc < 1 || exec_argc > 65535) {
+ return JS_ThrowTypeError(ctx, "invalid number of arguments");
+ }
+ exec_argv = js_mallocz(ctx, sizeof(exec_argv[0]) * (exec_argc + 1));
+ if (!exec_argv)
+ return JS_EXCEPTION;
+ for(i = 0; i < exec_argc; i++) {
+ val = JS_GetPropertyUint32(ctx, args, i);
+ if (JS_IsException(val))
+ goto exception;
+ str = JS_ToCString(ctx, val);
+ JS_FreeValue(ctx, val);
+ if (!str)
+ goto exception;
+ exec_argv[i] = str;
+ }
+ exec_argv[exec_argc] = NULL;
+
+ for(i = 0; i < 3; i++)
+ std_fds[i] = i;
+
+ /* get the options, if any */
+ if (argc >= 2) {
+ options = argv[1];
+
+ if (get_bool_option(ctx, &block_flag, options, "block"))
+ goto exception;
+ if (get_bool_option(ctx, &use_path, options, "usePath"))
+ goto exception;
+
+ val = JS_GetPropertyStr(ctx, options, "file");
+ if (JS_IsException(val))
+ goto exception;
+ if (!JS_IsUndefined(val)) {
+ file = JS_ToCString(ctx, val);
+ JS_FreeValue(ctx, val);
+ if (!file)
+ goto exception;
+ }
+
+ val = JS_GetPropertyStr(ctx, options, "cwd");
+ if (JS_IsException(val))
+ goto exception;
+ if (!JS_IsUndefined(val)) {
+ cwd = JS_ToCString(ctx, val);
+ JS_FreeValue(ctx, val);
+ if (!cwd)
+ goto exception;
+ }
+
+ /* stdin/stdout/stderr handles */
+ for(i = 0; i < 3; i++) {
+ val = JS_GetPropertyStr(ctx, options, std_name[i]);
+ if (JS_IsException(val))
+ goto exception;
+ if (!JS_IsUndefined(val)) {
+ int fd;
+ ret = JS_ToInt32(ctx, &fd, val);
+ JS_FreeValue(ctx, val);
+ if (ret)
+ goto exception;
+ std_fds[i] = fd;
+ }
+ }
+ }
+
+ pid = fork();
+ if (pid < 0) {
+ JS_ThrowTypeError(ctx, "fork error");
+ goto exception;
+ }
+ if (pid == 0) {
+ /* child */
+ int fd_max = sysconf(_SC_OPEN_MAX);
+
+ /* remap the stdin/stdout/stderr handles if necessary */
+ for(i = 0; i < 3; i++) {
+ if (std_fds[i] != i) {
+ if (dup2(std_fds[i], i) < 0)
+ _exit(127);
+ }
+ }
+
+ for(i = 3; i < fd_max; i++)
+ close(i);
+ if (cwd) {
+ if (chdir(cwd) < 0)
+ _exit(127);
+ }
+ if (!file)
+ file = exec_argv[0];
+ if (use_path)
+ ret = execvp(file, (char **)exec_argv);
+ else
+ ret = execv(file, (char **)exec_argv);
+ _exit(127);
+ }
+ /* parent */
+ if (block_flag) {
+ for(;;) {
+ ret = waitpid(pid, &status, 0);
+ if (ret == pid) {
+ if (WIFEXITED(status)) {
+ ret = WEXITSTATUS(status);
+ break;
+ } else if (WIFSIGNALED(status)) {
+ ret = -WTERMSIG(status);
+ break;
+ }
+ }
+ }
+ } else {
+ ret = pid;
+ }
+ ret_val = JS_NewInt32(ctx, ret);
+ done:
+ JS_FreeCString(ctx, file);
+ JS_FreeCString(ctx, cwd);
+ for(i = 0; i < exec_argc; i++)
+ JS_FreeCString(ctx, exec_argv[i]);
+ js_free(ctx, exec_argv);
+ return ret_val;
+ exception:
+ ret_val = JS_EXCEPTION;
+ goto done;
+}
+
+/* waitpid(pid, block) -> [pid, status] */
+static JSValue js_os_waitpid(JSContext *ctx, JSValueConst this_val,
+ int argc, JSValueConst *argv)
+{
+ int pid, status, options, ret;
+ JSValue obj;
+
+ if (JS_ToInt32(ctx, &pid, argv[0]))
+ return JS_EXCEPTION;
+ if (JS_ToInt32(ctx, &options, argv[1]))
+ return JS_EXCEPTION;
+
+ ret = waitpid(pid, &status, options);
+ if (ret < 0) {
+ ret = -errno;
+ status = 0;
+ }
+
+ obj = JS_NewArray(ctx);
+ if (JS_IsException(obj))
+ return obj;
+ JS_DefinePropertyValueUint32(ctx, obj, 0, JS_NewInt32(ctx, ret),
+ JS_PROP_C_W_E);
+ JS_DefinePropertyValueUint32(ctx, obj, 1, JS_NewInt32(ctx, status),
+ JS_PROP_C_W_E);
+ return obj;
+}
+
+/* pipe() -> [read_fd, write_fd] or null if error */
+static JSValue js_os_pipe(JSContext *ctx, JSValueConst this_val,
+ int argc, JSValueConst *argv)
+{
+ int pipe_fds[2], ret;
+ JSValue obj;
+
+ ret = pipe(pipe_fds);
+ if (ret < 0)
+ return JS_NULL;
+ obj = JS_NewArray(ctx);
+ if (JS_IsException(obj))
+ return obj;
+ JS_DefinePropertyValueUint32(ctx, obj, 0, JS_NewInt32(ctx, pipe_fds[0]),
+ JS_PROP_C_W_E);
+ JS_DefinePropertyValueUint32(ctx, obj, 1, JS_NewInt32(ctx, pipe_fds[1]),
+ JS_PROP_C_W_E);
+ return obj;
+}
+
+/* kill(pid, sig) */
+static JSValue js_os_kill(JSContext *ctx, JSValueConst this_val,
+ int argc, JSValueConst *argv)
+{
+ int pid, sig, ret;
+
+ if (JS_ToInt32(ctx, &pid, argv[0]))
+ return JS_EXCEPTION;
+ if (JS_ToInt32(ctx, &sig, argv[1]))
+ return JS_EXCEPTION;
+ ret = kill(pid, sig);
+ return js_os_return(ctx, ret);
+}
+
+/* sleep(delay_ms) */
+static JSValue js_os_sleep(JSContext *ctx, JSValueConst this_val,
+ int argc, JSValueConst *argv)
+{
+ int64_t delay;
+ struct timespec ts;
+ int ret;
+
+ if (JS_ToInt64(ctx, &delay, argv[0]))
+ return JS_EXCEPTION;
+ ts.tv_sec = delay / 1000;
+ ts.tv_nsec = (delay % 1000) * 1000000;
+ ret = nanosleep(&ts, NULL);
+ return js_os_return(ctx, ret);
+}
+
+/* dup(fd) */
+static JSValue js_os_dup(JSContext *ctx, JSValueConst this_val,
+ int argc, JSValueConst *argv)
+{
+ int fd, ret;
+
+ if (JS_ToInt32(ctx, &fd, argv[0]))
+ return JS_EXCEPTION;
+ ret = dup(fd);
+ return js_os_return(ctx, ret);
+}
+
+/* dup2(fd) */
+static JSValue js_os_dup2(JSContext *ctx, JSValueConst this_val,
+ int argc, JSValueConst *argv)
+{
+ int fd, fd2, ret;
+
+ if (JS_ToInt32(ctx, &fd, argv[0]))
+ return JS_EXCEPTION;
+ if (JS_ToInt32(ctx, &fd2, argv[1]))
+ return JS_EXCEPTION;
+ ret = dup2(fd, fd2);
+ return js_os_return(ctx, ret);
+}
+
+#endif /* !_WIN32 */
+
+#if defined(_WIN32)
+#define OS_PLATFORM "win32"
+#elif defined(__APPLE__)
+#define OS_PLATFORM "darwin"
+#elif defined(EMSCRIPTEN)
+#define OS_PLATFORM "js"
+#else
+#define OS_PLATFORM "linux"
+#endif
+
+#define OS_FLAG(x) JS_PROP_INT32_DEF(#x, x, JS_PROP_CONFIGURABLE )
+
+static const JSCFunctionListEntry js_os_funcs[] = {
+ JS_CFUNC_DEF("open", 2, js_os_open ),
+ OS_FLAG(O_RDONLY),
+ OS_FLAG(O_WRONLY),
+ OS_FLAG(O_RDWR),
+ OS_FLAG(O_APPEND),
+ OS_FLAG(O_CREAT),
+ OS_FLAG(O_EXCL),
+ OS_FLAG(O_TRUNC),
+#if defined(_WIN32)
+ OS_FLAG(O_BINARY),
+ OS_FLAG(O_TEXT),
+#endif
+ JS_CFUNC_DEF("close", 1, js_os_close ),
+ JS_CFUNC_DEF("seek", 3, js_os_seek ),
+ JS_CFUNC_MAGIC_DEF("read", 4, js_os_read_write, 0 ),
+ JS_CFUNC_MAGIC_DEF("write", 4, js_os_read_write, 1 ),
+ JS_CFUNC_DEF("isatty", 1, js_os_isatty ),
+ JS_CFUNC_DEF("ttyGetWinSize", 1, js_os_ttyGetWinSize ),
+ JS_CFUNC_DEF("ttySetRaw", 1, js_os_ttySetRaw ),
+ JS_CFUNC_DEF("remove", 1, js_os_remove ),
+ JS_CFUNC_DEF("rename", 2, js_os_rename ),
+ JS_CFUNC_MAGIC_DEF("setReadHandler", 2, js_os_setReadHandler, 0 ),
+ JS_CFUNC_MAGIC_DEF("setWriteHandler", 2, js_os_setReadHandler, 1 ),
+ JS_CFUNC_DEF("signal", 2, js_os_signal ),
+ OS_FLAG(SIGINT),
+ OS_FLAG(SIGABRT),
+ OS_FLAG(SIGFPE),
+ OS_FLAG(SIGILL),
+ OS_FLAG(SIGSEGV),
+ OS_FLAG(SIGTERM),
+#if !defined(_WIN32)
+ OS_FLAG(SIGQUIT),
+ OS_FLAG(SIGPIPE),
+ OS_FLAG(SIGALRM),
+ OS_FLAG(SIGUSR1),
+ OS_FLAG(SIGUSR2),
+ OS_FLAG(SIGCHLD),
+ OS_FLAG(SIGCONT),
+ OS_FLAG(SIGSTOP),
+ OS_FLAG(SIGTSTP),
+ OS_FLAG(SIGTTIN),
+ OS_FLAG(SIGTTOU),
+#endif
+ JS_CFUNC_DEF("setTimeout", 2, js_os_setTimeout ),
+ JS_CFUNC_DEF("clearTimeout", 1, js_os_clearTimeout ),
+ JS_PROP_STRING_DEF("platform", OS_PLATFORM, 0 ),
+ JS_CFUNC_DEF("getcwd", 0, js_os_getcwd ),
+#if !defined(_WIN32)
+ JS_CFUNC_DEF("realpath", 1, js_os_realpath ),
+ JS_CFUNC_DEF("mkdir", 1, js_os_mkdir ),
+ JS_CFUNC_MAGIC_DEF("stat", 1, js_os_stat, 0 ),
+ JS_CFUNC_MAGIC_DEF("lstat", 1, js_os_stat, 1 ),
+ /* st_mode constants */
+ OS_FLAG(S_IFMT),
+ OS_FLAG(S_IFIFO),
+ OS_FLAG(S_IFCHR),
+ OS_FLAG(S_IFDIR),
+ OS_FLAG(S_IFBLK),
+ OS_FLAG(S_IFREG),
+ OS_FLAG(S_IFSOCK),
+ OS_FLAG(S_IFLNK),
+ OS_FLAG(S_ISGID),
+ OS_FLAG(S_ISUID),
+ JS_CFUNC_DEF("symlink", 2, js_os_symlink ),
+ JS_CFUNC_DEF("readlink", 1, js_os_readlink ),
+ JS_CFUNC_DEF("readdir", 1, js_os_readdir ),
+ JS_CFUNC_DEF("utimes", 3, js_os_utimes ),
+ JS_CFUNC_DEF("exec", 1, js_os_exec ),
+ JS_CFUNC_DEF("waitpid", 2, js_os_waitpid ),
+ OS_FLAG(WNOHANG),
+ JS_CFUNC_DEF("pipe", 0, js_os_pipe ),
+ JS_CFUNC_DEF("kill", 2, js_os_kill ),
+ JS_CFUNC_DEF("sleep", 1, js_os_sleep ),
+ JS_CFUNC_DEF("dup", 1, js_os_dup ),
+ JS_CFUNC_DEF("dup2", 2, js_os_dup2 ),
+#endif
+};
+
+static int js_os_init(JSContext *ctx, JSModuleDef *m)
+{
+ os_poll_func = js_os_poll;
+
+ /* OSTimer class */
+ JS_NewClassID(&js_os_timer_class_id);
+ JS_NewClass(JS_GetRuntime(ctx), js_os_timer_class_id, &js_os_timer_class);
+
+ return JS_SetModuleExportList(ctx, m, js_os_funcs,
+ countof(js_os_funcs));
+}
+
+JSModuleDef *js_init_module_os(JSContext *ctx, const char *module_name)
+{
+ JSModuleDef *m;
+ m = JS_NewCModule(ctx, module_name, js_os_init);
+ if (!m)
+ return NULL;
+ JS_AddModuleExportList(ctx, m, js_os_funcs, countof(js_os_funcs));
+ return m;
+}
+
+/**********************************************************/
+
+static JSValue js_print(JSContext *ctx, JSValueConst this_val,
+ int argc, JSValueConst *argv)
+{
+ int i;
+ const char *str;
+
+ for(i = 0; i < argc; i++) {
+ if (i != 0)
+ putchar(' ');
+ str = JS_ToCString(ctx, argv[i]);
+ if (!str)
+ return JS_EXCEPTION;
+ fputs(str, stdout);
+ JS_FreeCString(ctx, str);
+ }
+ putchar('\n');
+ return JS_UNDEFINED;
+}
+
+void js_std_add_helpers(JSContext *ctx, int argc, char **argv)
+{
+ JSValue global_obj, console, args;
+ int i;
+
+ /* XXX: should these global definitions be enumerable? */
+ global_obj = JS_GetGlobalObject(ctx);
+
+ console = JS_NewObject(ctx);
+ JS_SetPropertyStr(ctx, console, "log",
+ JS_NewCFunction(ctx, js_print, "log", 1));
+ JS_SetPropertyStr(ctx, global_obj, "console", console);
+
+ /* same methods as the mozilla JS shell */
+ args = JS_NewArray(ctx);
+ for(i = 0; i < argc; i++) {
+ JS_SetPropertyUint32(ctx, args, i, JS_NewString(ctx, argv[i]));
+ }
+ JS_SetPropertyStr(ctx, global_obj, "scriptArgs", args);
+
+ JS_SetPropertyStr(ctx, global_obj, "print",
+ JS_NewCFunction(ctx, js_print, "print", 1));
+ JS_SetPropertyStr(ctx, global_obj, "__loadScript",
+ JS_NewCFunction(ctx, js_loadScript, "__loadScript", 1));
+
+ JS_FreeValue(ctx, global_obj);
+
+ /* XXX: not multi-context */
+ init_list_head(&os_rw_handlers);
+ init_list_head(&os_signal_handlers);
+ init_list_head(&os_timers);
+ os_pending_signals = 0;
+}
+
+void js_std_free_handlers(JSRuntime *rt)
+{
+ struct list_head *el, *el1;
+
+ list_for_each_safe(el, el1, &os_rw_handlers) {
+ JSOSRWHandler *rh = list_entry(el, JSOSRWHandler, link);
+ free_rw_handler(rt, rh);
+ }
+
+ list_for_each_safe(el, el1, &os_signal_handlers) {
+ JSOSSignalHandler *sh = list_entry(el, JSOSSignalHandler, link);
+ free_sh(rt, sh);
+ }
+
+ list_for_each_safe(el, el1, &os_timers) {
+ JSOSTimer *th = list_entry(el, JSOSTimer, link);
+ unlink_timer(rt, th);
+ if (!th->has_object)
+ free_timer(rt, th);
+ }
+}
+
+static void js_std_dump_error1(JSContext *ctx, JSValueConst exception_val,
+ BOOL is_throw)
+{
+ JSValue val;
+ const char *stack;
+ BOOL is_error;
+
+ is_error = JS_IsError(ctx, exception_val);
+ if (is_throw && !is_error)
+ printf("Throw: ");
+ js_print(ctx, JS_NULL, 1, (JSValueConst *)&exception_val);
+ if (is_error) {
+ val = JS_GetPropertyStr(ctx, exception_val, "stack");
+ if (!JS_IsUndefined(val)) {
+ stack = JS_ToCString(ctx, val);
+ printf("%s\n", stack);
+ JS_FreeCString(ctx, stack);
+ }
+ JS_FreeValue(ctx, val);
+ }
+}
+
+void js_std_dump_error(JSContext *ctx)
+{
+ JSValue exception_val;
+
+ exception_val = JS_GetException(ctx);
+ js_std_dump_error1(ctx, exception_val, TRUE);
+ JS_FreeValue(ctx, exception_val);
+}
+
+void js_std_promise_rejection_tracker(JSContext *ctx, JSValueConst promise,
+ JSValueConst reason,
+ BOOL is_handled, void *opaque)
+{
+ if (!is_handled) {
+ printf("Possibly unhandled promise rejection: ");
+ js_std_dump_error1(ctx, reason, FALSE);
+ }
+}
+
+/* main loop which calls the user JS callbacks */
+void js_std_loop(JSContext *ctx)
+{
+ JSContext *ctx1;
+ int err;
+
+ for(;;) {
+ /* execute the pending jobs */
+ for(;;) {
+ err = JS_ExecutePendingJob(JS_GetRuntime(ctx), &ctx1);
+ if (err <= 0) {
+ if (err < 0) {
+ js_std_dump_error(ctx1);
+ }
+ break;
+ }
+ }
+
+ if (!os_poll_func || os_poll_func(ctx))
+ break;
+ }
+}
+
+void js_std_eval_binary(JSContext *ctx, const uint8_t *buf, size_t buf_len,
+ int load_only)
+{
+ JSValue obj, val;
+ obj = JS_ReadObject(ctx, buf, buf_len, JS_READ_OBJ_BYTECODE);
+ if (JS_IsException(obj))
+ goto exception;
+ if (load_only) {
+ if (JS_VALUE_GET_TAG(obj) == JS_TAG_MODULE) {
+ js_module_set_import_meta(ctx, obj, FALSE, FALSE);
+ }
+ } else {
+ if (JS_VALUE_GET_TAG(obj) == JS_TAG_MODULE) {
+ if (JS_ResolveModule(ctx, obj) < 0) {
+ JS_FreeValue(ctx, obj);
+ goto exception;
+ }
+ js_module_set_import_meta(ctx, obj, FALSE, TRUE);
+ }
+ val = JS_EvalFunction(ctx, obj);
+ if (JS_IsException(val)) {
+ exception:
+ js_std_dump_error(ctx);
+ exit(1);
+ }
+ JS_FreeValue(ctx, val);
+ }
+}