/* * QuickJS C library * * Copyright (c) 2017-2019 Fabrice Bellard * Copyright (c) 2017-2019 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 #include #if defined(_WIN32) #include #include #else #include #include #include #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 exec() wrapper - 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; 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, &len, argv[0], FALSE); if (!fmt_str) goto fail; i = 1; fmt = (const uint8_t *)fmt_str; fmt_end = 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; f = fopen(filename, "rb"); if (!f) return NULL; fseek(f, 0, SEEK_END); buf_len = ftell(f); fseek(f, 0, SEEK_SET); if (ctx) buf = js_malloc(ctx, buf_len + 1); else buf = malloc(buf_len + 1); fread(buf, 1, buf_len, f); 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_EVAL_FLAG_SHEBANG); 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 */ 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; /* 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 JSValue js_evalScript(JSContext *ctx, JSValueConst this_val, int argc, JSValueConst *argv) { const char *str; int len; JSValue ret; str = JS_ToCStringLen(ctx, &len, argv[0], FALSE); if (!str) return JS_EXCEPTION; if (++eval_script_recurse == 1) { /* install the interrupt handler */ JS_SetInterruptHandler(JS_GetRuntime(ctx), interrupt_handler, NULL); } ret = JS_Eval(ctx, str, len, "", JS_EVAL_TYPE_GLOBAL); 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; } 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) 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) { 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->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); fail: JS_FreeCString(ctx, filename); 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); } 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); 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); } 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 ), /* FILE I/O */ JS_CFUNC_DEF("open", 2, js_std_open ), 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)); JS_SetModuleExport(ctx, m, "out", js_new_std_file(ctx, stdout, FALSE)); JS_SetModuleExport(ctx, m, "err", js_new_std_file(ctx, stderr, 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); /* global object */ JS_SetModuleExport(ctx, m, "global", JS_GetGlobalObject(ctx)); 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, "global"); 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; if (JS_ToInt64(ctx, &delay, argv[0])) return JS_EXCEPTION; func = argv[1]; if (!JS_IsFunction(ctx, func)) return JS_ThrowTypeError(ctx, "not a function"); obj = JS_NewObjectClass(ctx, js_os_timer_class_id); if (JS_IsException(obj)) return obj; th = js_mallocz(ctx, sizeof(*th)); th->has_object = TRUE; if (!th) { JS_FreeValue(ctx, obj); return JS_EXCEPTION; } 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; ret = JS_Call(ctx, func, JS_UNDEFINED, 0, NULL); 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 */ #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), JS_CFUNC_DEF("setTimeout", 2, js_os_setTimeout ), JS_CFUNC_DEF("clearTimeout", 1, js_os_clearTimeout ), JS_PROP_STRING_DEF("platform", OS_PLATFORM, 0 ), /* stat, readlink, opendir, closedir, ... */ }; 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); } } void js_std_dump_error(JSContext *ctx) { JSValue exception_val, val; const char *stack; BOOL is_error; exception_val = JS_GetException(ctx); is_error = JS_IsError(ctx, exception_val); if (!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); } JS_FreeValue(ctx, exception_val); } /* 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 flags) { JSValue val; val = JS_EvalBinary(ctx, buf, buf_len, flags); if (JS_IsException(val)) { js_std_dump_error(ctx); exit(1); } JS_FreeValue(ctx, val); }