diff --git a/include/base64.h b/include/base64.h new file mode 100644 index 0000000..a3c32d4 --- /dev/null +++ b/include/base64.h @@ -0,0 +1,10 @@ +#ifndef BASE64_H +#define BASE64_H + +#include +#include + +char *ant_base64_encode(const uint8_t *data, size_t len, size_t *out_len); +uint8_t *ant_base64_decode(const char *data, size_t len, size_t *out_len); + +#endif diff --git a/include/modules/buffer.h b/include/modules/buffer.h index aa58b98..e990f19 100644 --- a/include/modules/buffer.h +++ b/include/modules/buffer.h @@ -3,10 +3,7 @@ #include #include - -void init_buffer_module(void); -void cleanup_buffer_module(void); -size_t buffer_get_external_memory(void); +#include "types.h" typedef struct { uint8_t *data; @@ -45,4 +42,23 @@ typedef struct { size_t byte_length; } DataViewData; +void init_buffer_module(void); +void cleanup_buffer_module(void); +void free_array_buffer_data(ArrayBufferData *data); +size_t buffer_get_external_memory(void); + +ArrayBufferData *create_array_buffer_data(size_t length); +jsval_t create_arraybuffer_obj(struct js *js, ArrayBufferData *buffer); + +jsval_t create_typed_array( + struct js *js, TypedArrayType type, ArrayBufferData *buffer, + size_t byte_offset, size_t length, const char *type_name +); + +jsval_t create_typed_array_with_buffer( + struct js *js, TypedArrayType type, ArrayBufferData *buffer, + size_t byte_offset, size_t length, + const char *type_name, jsval_t arraybuffer_obj +); + #endif diff --git a/include/modules/io.h b/include/modules/io.h index 093b492..03865d5 100644 --- a/include/modules/io.h +++ b/include/modules/io.h @@ -7,6 +7,12 @@ extern bool io_no_color; +typedef struct { + jsoff_t *visited; + int count; + int capacity; +} inspect_visited_t; + #define C(color) (io_no_color ? "" : (color)) #define C_RESET C("\x1b[0m") #define C_BOLD C("\x1b[1m") @@ -23,6 +29,23 @@ extern bool io_no_color; void init_console_module(void); void print_value_colored(const char *str, FILE *stream); -void console_print(struct js *js, jsval_t *args, int nargs, const char *color, FILE *stream); +void print_repl_value(ant_t *js, jsval_t val, FILE *stream); + +void inspect_object( + ant_t *js, jsval_t obj, + FILE *stream, int depth, + inspect_visited_t *visited +); + +void inspect_value( + ant_t *js, jsval_t val, + FILE *stream, int depth, + inspect_visited_t *visited +); + +jsval_t console_print( + ant_t *js, jsval_t *args, + int nargs, const char *color, FILE *stream +); #endif diff --git a/include/utils.h b/include/utils.h index 4debe5e..cf972e7 100644 --- a/include/utils.h +++ b/include/utils.h @@ -15,6 +15,7 @@ char *resolve_js_file(const char *filename); uint64_t hash_key(const char *key, size_t len); int hex_digit(char c); +char hex_char(int v); int is_typescript_file(const char *filename); void *try_oom(size_t size); diff --git a/meson/deps/meson.build b/meson/deps/meson.build index ebb34a7..b545682 100644 --- a/meson/deps/meson.build +++ b/meson/deps/meson.build @@ -109,6 +109,7 @@ tlsuv_dep = cmake.subproject('tlsuv', options: tlsuv_opts).dependency('tlsuv') uthash_dep = subproject('uthash').get_variable('uthash_dep') yyjson_dep = subproject('yyjson').get_variable('yyjson_dep') uuidv7_dep = subproject('uuidv7').get_variable('uuidv7_dep') +base64_dep = subproject('aklomp-base64').get_variable('base64_dep') argtable3_dep = subproject('argtable3').get_variable('argtable3_dep') minicoro_dep = subproject('minicoro').get_variable('minicoro_dep') crprintf_dep = subproject('crprintf').get_variable('crprintf_dep') @@ -207,7 +208,7 @@ pkg_build_dir = meson.current_build_dir() ant_deps = [ libffi_dep, uuid_dep, crprintf_dep, - llhttp, pcre2_dep, libuv_dep, + llhttp, pcre2_dep, libuv_dep, base64_dep, argtable3_dep, tlsuv_dep, libsodium_dep, yyjson_dep, minicoro_dep, uuidv7_dep, openssl_dep, zlib_dep, uthash_dep, diff --git a/src/ant.c b/src/ant.c index f8043b8..898fe65 100644 --- a/src/ant.c +++ b/src/ant.c @@ -10,6 +10,7 @@ #include "common.h" #include "arena.h" #include "utils.h" +#include "base64.h" #include "runtime.h" #include "internal.h" #include "sugar.h" @@ -20219,8 +20220,6 @@ static jsval_t builtin_parseFloat(struct js *js, jsval_t *args, int nargs) { return tov(result); } -static const char base64_chars[] = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/"; - static jsval_t builtin_btoa(struct js *js, jsval_t *args, int nargs) { if (nargs < 1) return js_mkerr(js, "btoa requires 1 argument"); @@ -20233,55 +20232,16 @@ static jsval_t builtin_btoa(struct js *js, jsval_t *args, int nargs) { jsoff_t str_len, str_off = vstr(js, str_val, &str_len); const char *str = (char *) &js->mem[str_off]; - for (jsoff_t i = 0; i < str_len; i++) { - if ((unsigned char)str[i] > 255) { - return js_mkerr(js, "btoa: character out of range"); - } - } - - size_t out_len = ((str_len + 2) / 3) * 4; - char *out = (char *)ant_calloc(out_len + 1); + size_t out_len; + char *out = ant_base64_encode((const uint8_t *)str, str_len, &out_len); if (!out) return js_mkerr(js, "out of memory"); - size_t i = 0, j = 0; - while (i < str_len) { - size_t remaining = str_len - i; - uint32_t a = (unsigned char)str[i++]; - uint32_t b = (remaining > 1) ? (unsigned char)str[i++] : 0; - uint32_t c = (remaining > 2) ? (unsigned char)str[i++] : 0; - uint32_t triple = (a << 16) | (b << 8) | c; - - out[j++] = base64_chars[(triple >> 18) & 0x3F]; - out[j++] = base64_chars[(triple >> 12) & 0x3F]; - out[j++] = (remaining > 1) ? base64_chars[(triple >> 6) & 0x3F] : '='; - out[j++] = (remaining > 2) ? base64_chars[triple & 0x3F] : '='; - } - out[j] = '\0'; - - jsval_t result = js_mkstr(js, out, j); + jsval_t result = js_mkstr(js, out, out_len); free(out); + return result; } -static const int8_t decode_table[256] = { - -1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1, - -1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1, - -1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,62,-1,-1,-1,63, - 52,53,54,55,56,57,58,59,60,61,-1,-1,-1,-1,-1,-1, - -1, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9,10,11,12,13,14, - 15,16,17,18,19,20,21,22,23,24,25,-1,-1,-1,-1,-1, - -1,26,27,28,29,30,31,32,33,34,35,36,37,38,39,40, - 41,42,43,44,45,46,47,48,49,50,51,-1,-1,-1,-1,-1, - -1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1, - -1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1, - -1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1, - -1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1, - -1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1, - -1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1, - -1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1, - -1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1 -}; - static jsval_t builtin_atob(struct js *js, jsval_t *args, int nargs) { if (nargs < 1) return js_mkerr(js, "atob requires 1 argument"); @@ -20293,37 +20253,15 @@ static jsval_t builtin_atob(struct js *js, jsval_t *args, int nargs) { jsoff_t str_len, str_off = vstr(js, str_val, &str_len); const char *str = (char *) &js->mem[str_off]; - if (str_len == 0) return js_mkstr(js, "", 0); - if (str_len % 4 != 0) return js_mkerr(js, "atob: invalid base64 string"); - - size_t out_len = (str_len / 4) * 3; - if (str_len > 0 && str[str_len - 1] == '=') out_len--; - if (str_len > 1 && str[str_len - 2] == '=') out_len--; - char *out = (char *)ant_calloc(out_len + 1); - if (!out) return js_mkerr(js, "out of memory"); - - size_t i = 0, j = 0; - while (i < str_len) { - int8_t a = decode_table[(unsigned char)str[i++]]; - int8_t b = decode_table[(unsigned char)str[i++]]; - int8_t c = (str[i] == '=') ? 0 : decode_table[(unsigned char)str[i]]; i++; - int8_t d = (str[i] == '=') ? 0 : decode_table[(unsigned char)str[i]]; i++; - - if (a < 0 || b < 0 || (str[i-2] != '=' && c < 0) || (str[i-1] != '=' && d < 0)) { - free(out); - return js_mkerr(js, "atob: invalid character in base64 string"); - } - - uint32_t triple = ((uint32_t)a << 18) | ((uint32_t)b << 12) | ((uint32_t)c << 6) | (uint32_t)d; - if (j < out_len) out[j++] = (triple >> 16) & 0xFF; - if (j < out_len) out[j++] = (triple >> 8) & 0xFF; - if (j < out_len) out[j++] = triple & 0xFF; - } + size_t out_len; + uint8_t *out = ant_base64_decode(str, str_len, &out_len); + if (!out) return js_mkerr(js, "atob: invalid base64 string"); - jsval_t result = js_mkstr(js, out, out_len); + jsval_t result = js_mkstr(js, (char *)out, out_len); free(out); + return result; } diff --git a/src/base64.c b/src/base64.c new file mode 100644 index 0000000..386ad71 --- /dev/null +++ b/src/base64.c @@ -0,0 +1,26 @@ +#include +#include +#include "base64.h" + +char *ant_base64_encode(const uint8_t *data, size_t len, size_t *out_len) { + size_t encoded_len = 4 * ((len + 2) / 3); + char *result = malloc(encoded_len + 1); + if (!result) return NULL; + + base64_encode((const char *)data, len, result, out_len, 0); + result[*out_len] = '\0'; + return result; +} + +uint8_t *ant_base64_decode(const char *data, size_t len, size_t *out_len) { + size_t decoded_len = (len / 4) * 3 + 3; + uint8_t *result = malloc(decoded_len); + if (!result) return NULL; + + if (!base64_decode(data, len, (char *)result, out_len, 0)) { + free(result); + return NULL; + } + + return result; +} diff --git a/src/modules/buffer.c b/src/modules/buffer.c index 4da3799..8709d29 100644 --- a/src/modules/buffer.c +++ b/src/modules/buffer.c @@ -12,6 +12,7 @@ #include "ant.h" #include "errors.h" #include "arena.h" +#include "base64.h" #include "internal.h" #include "runtime.h" @@ -116,70 +117,7 @@ static jsval_t js_buffer_toString(struct js *js, jsval_t *args, int nargs); static jsval_t js_buffer_toBase64(struct js *js, jsval_t *args, int nargs); static jsval_t js_buffer_write(struct js *js, jsval_t *args, int nargs); -static const char base64_chars[] = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/"; - -static const unsigned char base64_decode_table[256] = { - ['A'] = 0, ['B'] = 1, ['C'] = 2, ['D'] = 3, ['E'] = 4, ['F'] = 5, ['G'] = 6, ['H'] = 7, - ['I'] = 8, ['J'] = 9, ['K'] = 10, ['L'] = 11, ['M'] = 12, ['N'] = 13, ['O'] = 14, ['P'] = 15, - ['Q'] = 16, ['R'] = 17, ['S'] = 18, ['T'] = 19, ['U'] = 20, ['V'] = 21, ['W'] = 22, ['X'] = 23, - ['Y'] = 24, ['Z'] = 25, ['a'] = 26, ['b'] = 27, ['c'] = 28, ['d'] = 29, ['e'] = 30, ['f'] = 31, - ['g'] = 32, ['h'] = 33, ['i'] = 34, ['j'] = 35, ['k'] = 36, ['l'] = 37, ['m'] = 38, ['n'] = 39, - ['o'] = 40, ['p'] = 41, ['q'] = 42, ['r'] = 43, ['s'] = 44, ['t'] = 45, ['u'] = 46, ['v'] = 47, - ['w'] = 48, ['x'] = 49, ['y'] = 50, ['z'] = 51, ['0'] = 52, ['1'] = 53, ['2'] = 54, ['3'] = 55, - ['4'] = 56, ['5'] = 57, ['6'] = 58, ['7'] = 59, ['8'] = 60, ['9'] = 61, ['+'] = 62, ['/'] = 63, -}; - -static char *base64_encode(const uint8_t *data, size_t len, size_t *out_len) { - size_t encoded_len = 4 * ((len + 2) / 3); - char *result = malloc(encoded_len + 1); - if (!result) return NULL; - - size_t j = 0; - for (size_t i = 0; i < len; i += 3) { - uint32_t octet_a = i < len ? data[i] : 0; - uint32_t octet_b = i + 1 < len ? data[i + 1] : 0; - uint32_t octet_c = i + 2 < len ? data[i + 2] : 0; - uint32_t triple = (octet_a << 16) + (octet_b << 8) + octet_c; - - result[j++] = base64_chars[(triple >> 18) & 0x3F]; - result[j++] = base64_chars[(triple >> 12) & 0x3F]; - result[j++] = (i + 1 < len) ? base64_chars[(triple >> 6) & 0x3F] : '='; - result[j++] = (i + 2 < len) ? base64_chars[triple & 0x3F] : '='; - } - - result[j] = '\0'; - *out_len = j; - return result; -} - -static uint8_t *base64_decode(const char *data, size_t len, size_t *out_len) { - if (len % 4 != 0) return NULL; - - size_t decoded_len = len / 4 * 3; - if (len > 0 && data[len - 1] == '=') decoded_len--; - if (len > 1 && data[len - 2] == '=') decoded_len--; - - uint8_t *result = malloc(decoded_len); - if (!result) return NULL; - - size_t j = 0; - for (size_t i = 0; i < len; i += 4) { - uint32_t sextet_a = base64_decode_table[(unsigned char)data[i]]; - uint32_t sextet_b = base64_decode_table[(unsigned char)data[i + 1]]; - uint32_t sextet_c = data[i + 2] == '=' ? 0 : base64_decode_table[(unsigned char)data[i + 2]]; - uint32_t sextet_d = data[i + 3] == '=' ? 0 : base64_decode_table[(unsigned char)data[i + 3]]; - uint32_t triple = (sextet_a << 18) + (sextet_b << 12) + (sextet_c << 6) + sextet_d; - - if (j < decoded_len) result[j++] = (triple >> 16) & 0xFF; - if (j < decoded_len) result[j++] = (triple >> 8) & 0xFF; - if (j < decoded_len) result[j++] = triple & 0xFF; - } - - *out_len = decoded_len; - return result; -} - -static ArrayBufferData *create_array_buffer_data(size_t length) { +ArrayBufferData *create_array_buffer_data(size_t length) { ArrayBufferData *data = ant_calloc(sizeof(ArrayBufferData) + length); if (!data) return NULL; @@ -202,7 +140,7 @@ static ArrayBufferData *create_shared_array_buffer_data(size_t length) { return data; } -static void free_array_buffer_data(ArrayBufferData *data) { +void free_array_buffer_data(ArrayBufferData *data) { if (!data) return; data->ref_count--; if (data->ref_count <= 0) { @@ -424,7 +362,7 @@ static bool typedarray_index_setter(struct js *js, jsval_t obj, const char *key, S_DONE: return true; } -static jsval_t create_arraybuffer_obj(struct js *js, ArrayBufferData *buffer) { +jsval_t create_arraybuffer_obj(struct js *js, ArrayBufferData *buffer) { jsval_t ab_obj = js_mkobj(js); jsval_t ab_proto = js_get_ctor_proto(js, "ArrayBuffer", 11); if (is_special_object(ab_proto)) js_set_proto(js, ab_obj, ab_proto); @@ -436,7 +374,7 @@ static jsval_t create_arraybuffer_obj(struct js *js, ArrayBufferData *buffer) { return ab_obj; } -static jsval_t create_typed_array_with_buffer( +jsval_t create_typed_array_with_buffer( struct js *js, TypedArrayType type, ArrayBufferData *buffer, size_t byte_offset, size_t length, const char *type_name, jsval_t arraybuffer_obj ) { @@ -468,12 +406,13 @@ static jsval_t create_typed_array_with_buffer( return obj; } -static jsval_t create_typed_array( +jsval_t create_typed_array( struct js *js, TypedArrayType type, ArrayBufferData *buffer, size_t byte_offset, size_t length, const char *type_name ) { jsval_t ab_obj = create_arraybuffer_obj(js, buffer); - return create_typed_array_with_buffer(js, type, buffer, byte_offset, length, type_name, ab_obj); + jsval_t result = create_typed_array_with_buffer(js, type, buffer, byte_offset, length, type_name, ab_obj); + free_array_buffer_data(buffer); return result; } typedef struct { @@ -1329,7 +1268,7 @@ static jsval_t js_buffer_from(struct js *js, jsval_t *args, int nargs) { if (encoding == ENC_BASE64) { size_t decoded_len; - uint8_t *decoded = base64_decode(str, len, &decoded_len); + uint8_t *decoded = ant_base64_decode(str, len, &decoded_len); if (!decoded) return js_mkerr(js, "Failed to decode base64"); ArrayBufferData *buffer = create_array_buffer_data(decoded_len); @@ -1436,7 +1375,7 @@ static jsval_t js_buffer_toString(struct js *js, jsval_t *args, int nargs) { if (encoding == ENC_BASE64) { size_t out_len; - char *encoded = base64_encode(data, len, &out_len); + char *encoded = ant_base64_encode(data, len, &out_len); if (!encoded) return js_mkerr(js, "Failed to encode base64"); jsval_t result = js_mkstr(js, encoded, out_len); diff --git a/src/modules/fs.c b/src/modules/fs.c index 857157c..45fcd46 100644 --- a/src/modules/fs.c +++ b/src/modules/fs.c @@ -11,11 +11,15 @@ #include #include "ant.h" +#include "utf8.h" +#include "utils.h" +#include "base64.h" #include "errors.h" #include "internal.h" #include "runtime.h" #include "modules/fs.h" +#include "modules/buffer.h" #include "modules/symbol.h" #define fs_err_code(js, code, op, path) ({ \ @@ -24,6 +28,103 @@ js_mkerr_props(js, JS_ERR_TYPE, _props, "%s: %s, %s '%s'", code, strerror(errno), op, path); \ }) +typedef enum { + FS_ENC_NONE = 0, + FS_ENC_UTF8, + FS_ENC_UTF16LE, + FS_ENC_LATIN1, + FS_ENC_BASE64, + FS_ENC_BASE64URL, + FS_ENC_HEX, + FS_ENC_ASCII, +} fs_encoding_t; + +static fs_encoding_t parse_encoding(struct js *js, jsval_t arg) { + size_t len; + const char *str = NULL; + + if (vtype(arg) == T_STR) str = js_getstr(js, arg, &len); + else if (vtype(arg) == T_OBJ) { + jsval_t enc = js_get(js, arg, "encoding"); + if (vtype(enc) == T_STR) str = js_getstr(js, enc, &len); + } + + if (!str) return FS_ENC_NONE; + + if (len == 4 && memcmp(str, "utf8", 4) == 0) return FS_ENC_UTF8; + if (len == 5 && memcmp(str, "utf-8", 5) == 0) return FS_ENC_UTF8; + if (len == 7 && memcmp(str, "utf16le", 7) == 0) return FS_ENC_UTF16LE; + if (len == 4 && memcmp(str, "ucs2", 4) == 0) return FS_ENC_UTF16LE; + if (len == 5 && memcmp(str, "ucs-2", 5) == 0) return FS_ENC_UTF16LE; + if (len == 6 && memcmp(str, "latin1", 6) == 0) return FS_ENC_LATIN1; + if (len == 6 && memcmp(str, "binary", 6) == 0) return FS_ENC_LATIN1; + if (len == 6 && memcmp(str, "base64", 6) == 0) return FS_ENC_BASE64; + if (len == 9 && memcmp(str, "base64url", 9) == 0) return FS_ENC_BASE64URL; + if (len == 3 && memcmp(str, "hex", 3) == 0) return FS_ENC_HEX; + if (len == 5 && memcmp(str, "ascii", 5) == 0) return FS_ENC_ASCII; + + return FS_ENC_NONE; +} + +static jsval_t encode_data(struct js *js, const char *data, size_t len, fs_encoding_t enc) { + switch (enc) { + case FS_ENC_UTF8: + case FS_ENC_LATIN1: + case FS_ENC_ASCII: return js_mkstr(js, data, len); + + case FS_ENC_HEX: { + char *out = malloc(len * 2); + if (!out) return js_mkerr(js, "Out of memory"); + for (size_t i = 0; i < len; i++) { + out[i * 2] = hex_char((unsigned char)data[i] >> 4); + out[i * 2 + 1] = hex_char(data[i]); + } + jsval_t result = js_mkstr(js, out, len * 2); + free(out); return result; + } + + case FS_ENC_BASE64: { + size_t out_len; + char *out = ant_base64_encode((const uint8_t *)data, len, &out_len); + if (!out) return js_mkerr(js, "Out of memory"); + jsval_t result = js_mkstr(js, out, out_len); + free(out); return result; + } + + case FS_ENC_BASE64URL: { + size_t out_len; + char *out = ant_base64_encode((const uint8_t *)data, len, &out_len); + if (!out) return js_mkerr(js, "Out of memory"); + for (size_t i = 0; i < out_len; i++) { + if (out[i] == '+') out[i] = '-'; + else if (out[i] == '/') out[i] = '_'; + } + while (out_len > 0 && out[out_len - 1] == '=') out_len--; + jsval_t result = js_mkstr(js, out, out_len); + free(out); return result; + } + + case FS_ENC_UTF16LE: { + const unsigned char *s = (const unsigned char *)data; + char *out = malloc((len / 2) * 4); + if (!out) return js_mkerr(js, "Out of memory"); + size_t j = 0; + for (size_t i = 0; i + 1 < len; i += 2) { + uint32_t cp = s[i] | (s[i + 1] << 8); + bool is_hi = cp >= 0xD800 && cp <= 0xDBFF && i + 3 < len; + uint32_t lo = is_hi ? s[i + 2] | (s[i + 3] << 8) : 0; + bool is_pair = is_hi && lo >= 0xDC00 && lo <= 0xDFFF; + if (is_pair) { cp = 0x10000 + ((cp - 0xD800) << 10) + (lo - 0xDC00); i += 2; } + j += utf8_encode(cp, out + j); + } + jsval_t result = js_mkstr(js, out, j); + free(out); return result; + } + + default: return js_mkstr(js, data, len); + } +} + typedef enum { FS_OP_READ, FS_OP_WRITE, @@ -38,17 +139,18 @@ typedef enum { } fs_op_type_t; typedef struct fs_request_s { + uv_fs_t uv_req; struct js *js; jsval_t promise; - uv_fs_t uv_req; - fs_op_type_t op_type; char *path; char *data; + char *error_msg; size_t data_len; + fs_op_type_t op_type; + fs_encoding_t encoding; uv_file fd; int completed; int failed; - char *error_msg; } fs_request_t; static UT_array *pending_requests = NULL; @@ -66,30 +168,36 @@ static void free_fs_request(fs_request_t *req) { static void remove_pending_request(fs_request_t *req) { if (!req || !pending_requests) return; + unsigned int len = utarray_len(pending_requests); - fs_request_t **p = NULL; - unsigned int i = 0; - - while ((p = (fs_request_t**)utarray_next(pending_requests, p))) { - if (*p == req) { - utarray_erase(pending_requests, i, 1); - break; - } i++; + for (unsigned int i = 0; i < len; i++) { + if (*(fs_request_t**)utarray_eltptr(pending_requests, i) != req) continue; + utarray_erase(pending_requests, i, 1); break; } } +static jsval_t fs_read_to_uint8array(struct js *js, const char *data, size_t len) { + ArrayBufferData *ab = create_array_buffer_data(len); + if (!ab) return js_mkundef(); + memcpy(ab->data, data, len); + jsval_t result = create_typed_array(js, TYPED_ARRAY_UINT8, ab, 0, len, "Uint8Array"); + if (vtype(result) == T_ERR) free_array_buffer_data(ab); + return result; +} + static void complete_request(fs_request_t *req) { if (req->failed) { const char *err_msg = req->error_msg ? req->error_msg : "Unknown error"; jsval_t err = js_mkstr(req->js, err_msg, strlen(err_msg)); js_reject_promise(req->js, req->promise, err); } else { - jsval_t result; - if ((req->op_type == FS_OP_READ || req->op_type == FS_OP_READ_BYTES) && req->data) { + jsval_t result = js_mkundef(); + if (req->op_type == FS_OP_READ && req->data) { + if (req->encoding != FS_ENC_NONE) + result = encode_data(req->js, req->data, req->data_len, req->encoding); + else result = fs_read_to_uint8array(req->js, req->data, req->data_len); + } else if (req->op_type == FS_OP_READ_BYTES && req->data) result = js_mkstr(req->js, req->data, req->data_len); - } else if (req->op_type == FS_OP_STAT) { - result = js_mkundef(); - } else result = js_mkundef(); js_resolve_promise(req->js, req->promise, result); } @@ -386,10 +494,12 @@ static jsval_t builtin_fs_readFileSync(struct js *js, jsval_t *args, int nargs) return js_mkerr(js, "Failed to read entire file"); } - data[file_size] = '\0'; - jsval_t result = js_mkstr(js, data, file_size); + fs_encoding_t enc = (nargs > 1) ? parse_encoding(js, args[1]) : FS_ENC_NONE; + jsval_t result = (enc != FS_ENC_NONE) + ? encode_data(js, data, file_size, enc) + : fs_read_to_uint8array(js, data, file_size); + free(data); - return result; } @@ -458,6 +568,7 @@ static jsval_t builtin_fs_readFile(struct js *js, jsval_t *args, int nargs) { req->js = js; req->op_type = FS_OP_READ; + req->encoding = (nargs > 1) ? parse_encoding(js, args[1]) : FS_ENC_NONE; req->promise = js_mkpromise(js); req->path = strndup(path, path_len); req->uv_req.data = req; diff --git a/src/modules/io.c b/src/modules/io.c index 618c5bc..80b9fa7 100644 --- a/src/modules/io.c +++ b/src/modules/io.c @@ -17,32 +17,16 @@ bool io_no_color = false; -#define ANSI_RED "\x1b[31m" -#define ANSI_YELLOW "\x1b[33m" -#define ANSI_CYAN "\x1b[36m" -#define ANSI_MAGENTA "\x1b[35m" -#define ANSI_RESET "\x1b[0m" - -#define JSON_KEY "\x1b[0m" +#define JSON_KEY "\x1b[0m" #define JSON_STRING "\x1b[32m" #define JSON_NUMBER "\x1b[33m" -#define JSON_BOOL "\x1b[35m" -#define JSON_NULL "\x1b[90m" -#define JSON_BRACE "\x1b[37m" -#define JSON_FUNC "\x1b[36m" -#define JSON_TAG "\x1b[34m" -#define JSON_REF "\x1b[90m" -#define JSON_WHITE "\x1b[97m" - -typedef struct { - jsoff_t *visited; - int count; - int capacity; -} inspect_visited_t; - -static void io_putc(int c, FILE *stream) { fputc(c, stream); } -static void io_puts(const char *s, FILE *stream) { fputs(s, stream); } -static void inspect_object_full(struct js *js, jsval_t obj, FILE *stream, int depth, inspect_visited_t *visited); +#define JSON_BOOL "\x1b[35m" +#define JSON_NULL "\x1b[90m" +#define JSON_BRACE "\x1b[37m" +#define JSON_FUNC "\x1b[36m" +#define JSON_TAG "\x1b[34m" +#define JSON_REF "\x1b[90m" +#define JSON_WHITE "\x1b[97m" static void io_print(const char *str, FILE *stream) { if (!io_no_color) { @@ -51,35 +35,36 @@ static void io_print(const char *str, FILE *stream) { } static void *states[] = {&&normal, &&esc, &&csi, &&done}; - const char *p = str; - char c; - - goto *states[0]; + const char *p = str; char c; -normal: - c = *p++; - if (!c) goto *states[3]; - if (c == '\x1b') goto *states[1]; - fputc(c, stream); goto *states[0]; -esc: - c = *p++; - if (!c) goto *states[3]; - if (c == '[') goto *states[2]; - fputc('\x1b', stream); - fputc(c, stream); - goto *states[0]; + normal: { + c = *p++; + if (!c) goto *states[3]; + if (c == '\x1b') goto *states[1]; + fputc(c, stream); + goto *states[0]; + } -csi: - c = *p++; - if (!c) goto *states[3]; - if ((c >= '0' && c <= '9') || c == ';') goto *states[2]; - if (c != 'm') fputc(c, stream); - goto *states[0]; + esc: { + c = *p++; + if (!c) goto *states[3]; + if (c == '[') goto *states[2]; + fputc('\x1b', stream); + fputc(c, stream); + goto *states[0]; + } -done: - return; + csi: { + c = *p++; + if (!c) goto *states[3]; + if ((c >= '0' && c <= '9') || c == ';') goto *states[2]; + if (c != 'm') fputc(c, stream); + goto *states[0]; + } + + done: return; } enum char_class { @@ -115,19 +100,19 @@ static const uint8_t char_class_table[256] = { #define KEYWORD(kw, color) \ if (memcmp(p, kw, sizeof(kw) - 1) == 0 && !isalnum((unsigned char)p[sizeof(kw) - 1]) && p[sizeof(kw) - 1] != '_') { \ - io_puts(color, stream); io_puts(kw, stream); io_puts(ANSI_RESET, stream); \ + fputs(color, stream); fputs(kw, stream); fputs(C_RESET, stream); \ p += sizeof(kw) - 1; goto next; \ } #define EMIT_UNTIL(end_char, color) \ - io_puts(color, stream); \ - while (*p && *p != end_char) io_putc(*p++, stream); \ - if (*p == end_char) io_putc(*p++, stream); \ - io_puts(ANSI_RESET, stream); goto next; + fputs(color, stream); \ + while (*p && *p != end_char) fputc(*p++, stream); \ + if (*p == end_char) fputc(*p++, stream); \ + fputs(C_RESET, stream); goto next; #define EMIT_TYPE(tag, len, color) \ if (!(is_key && brace_depth > 0) && memcmp(p, tag, len) == 0) { \ - io_puts(color, stream); io_puts(tag, stream); io_puts(ANSI_RESET, stream); \ + fputs(color, stream); fputs(tag, stream); fputs(C_RESET, stream); \ p += len; goto next; \ } @@ -158,22 +143,22 @@ done: quote: string_char = *p; - io_puts((is_key && brace_depth > 0) ? JSON_KEY : JSON_STRING, stream); - io_putc(*p++, stream); + fputs((is_key && brace_depth > 0) ? JSON_KEY : JSON_STRING, stream); + fputc(*p++, stream); while (*p) { - if (*p == '\\' && p[1]) { io_putc(*p++, stream); io_putc(*p++, stream); continue; } - if (*p == string_char) { io_putc(*p++, stream); break; } - io_putc(*p++, stream); + if (*p == '\\' && p[1]) { fputc(*p++, stream); fputc(*p++, stream); continue; } + if (*p == string_char) { fputc(*p++, stream); break; } + fputc(*p++, stream); } - io_puts(ANSI_RESET, stream); + fputs(C_RESET, stream); goto next; lbrace: - io_puts(JSON_BRACE, stream); io_putc(*p++, stream); io_puts(ANSI_RESET, stream); + fputs(JSON_BRACE, stream); fputc(*p++, stream); fputs(C_RESET, stream); brace_depth++; is_key = true; goto next; rbrace: - io_puts(JSON_BRACE, stream); io_putc(*p++, stream); io_puts(ANSI_RESET, stream); + fputs(JSON_BRACE, stream); fputc(*p++, stream); fputs(C_RESET, stream); brace_depth--; is_key = false; goto next; lbrack: @@ -190,57 +175,57 @@ lbrack: case 'U': if (memcmp(p + 2, "int8Contents]", 13) == 0) { EMIT_UNTIL(']', JSON_FUNC) } break; case 'P': if (memcmp(p + 2, "romise]", 7) == 0) { EMIT_UNTIL(']', JSON_TAG) } break; } - io_puts(JSON_BRACE, stream); io_putc(*p++, stream); io_puts(ANSI_RESET, stream); + fputs(JSON_BRACE, stream); fputc(*p++, stream); fputs(C_RESET, stream); array_depth++; is_key = false; goto next; rbrack: - io_puts(JSON_BRACE, stream); io_putc(*p++, stream); io_puts(ANSI_RESET, stream); + fputs(JSON_BRACE, stream); fputc(*p++, stream); fputs(C_RESET, stream); array_depth--; is_key = false; goto next; colon: - io_putc(*p++, stream); is_key = false; goto next; + fputc(*p++, stream); is_key = false; goto next; separator: - io_putc(*p++, stream); + fputc(*p++, stream); is_key = (brace_depth > 0 && array_depth == 0); goto next; number: - io_puts(JSON_NUMBER, stream); + fputs(JSON_NUMBER, stream); while ((*p >= '0' && *p <= '9') || *p == '.' || *p == 'e' || *p == 'E' || *p == '+' || *p == '-') - io_putc(*p++, stream); - io_puts(ANSI_RESET, stream); + fputc(*p++, stream); + fputs(C_RESET, stream); goto next; minus: if (p[1] >= '0' && p[1] <= '9') { - io_puts(JSON_NUMBER, stream); io_putc(*p++, stream); + fputs(JSON_NUMBER, stream); fputc(*p++, stream); while ((*p >= '0' && *p <= '9') || *p == '.' || *p == 'e' || *p == 'E' || *p == '+' || *p == '-') - io_putc(*p++, stream); - io_puts(ANSI_RESET, stream); + fputc(*p++, stream); + fputs(C_RESET, stream); goto next; } - io_putc(*p++, stream); goto next; + fputc(*p++, stream); goto next; lt: if (memcmp(p, "', JSON_REF) } - if (memcmp(p, "', ANSI_CYAN) } - if (memcmp(p, "', ANSI_CYAN) } + if (memcmp(p, "', C_CYAN) } + if (memcmp(p, "', C_CYAN) } if (p[1] == '>' || (isxdigit((unsigned char)p[1]) && isxdigit((unsigned char)p[2]))) { - io_puts(JSON_BRACE, stream); io_putc(*p++, stream); - io_puts(JSON_WHITE, stream); - while (*p && *p != '>') io_putc(*p++, stream); - io_puts(ANSI_RESET, stream); - if (*p == '>') { io_puts(JSON_BRACE, stream); io_putc(*p++, stream); io_puts(ANSI_RESET, stream); } + fputs(JSON_BRACE, stream); fputc(*p++, stream); + fputs(JSON_WHITE, stream); + while (*p && *p != '>') fputc(*p++, stream); + fputs(C_RESET, stream); + if (*p == '>') { fputs(JSON_BRACE, stream); fputc(*p++, stream); fputs(C_RESET, stream); } goto next; } - io_puts(JSON_BRACE, stream); io_putc(*p++, stream); io_puts(ANSI_RESET, stream); + fputs(JSON_BRACE, stream); fputc(*p++, stream); fputs(C_RESET, stream); goto next; gt: - io_puts(JSON_BRACE, stream); io_putc(*p++, stream); io_puts(ANSI_RESET, stream); + fputs(JSON_BRACE, stream); fputc(*p++, stream); fputs(C_RESET, stream); goto next; alpha: @@ -259,57 +244,71 @@ alpha: ident: if (is_key && brace_depth > 0) { - io_puts(JSON_KEY, stream); - while (isalnum((unsigned char)*p) || *p == '_' || *p == '$') io_putc(*p++, stream); - io_puts(ANSI_RESET, stream); + fputs(JSON_KEY, stream); + while (isalnum((unsigned char)*p) || *p == '_' || *p == '$') fputc(*p++, stream); + fputs(C_RESET, stream); goto next; } - io_putc(*p++, stream); goto next; + fputc(*p++, stream); goto next; other: - io_putc(*p++, stream); goto next; + fputc(*p++, stream); goto next; } #undef KEYWORD #undef EMIT_UNTIL -void console_print(struct js *js, jsval_t *args, int nargs, const char *color, FILE *stream) { - if (color && !io_no_color) io_puts(color, stream); +void print_repl_value(struct js *js, jsval_t val, FILE *stream) { + if (vtype(val) == T_STR) { + char *str = js_getstr(js, val, NULL); + fprintf(stream, "%s'%s'%s\n", C(JSON_STRING), str ? str : "", C(C_RESET)); + return; + } + + char cbuf[512]; + js_cstr_t cstr = js_to_cstr(js, val, cbuf, sizeof(cbuf)); + + if (vtype(val) == T_ERR) fprintf(stderr, "%s\n", cstr.ptr); else { + print_value_colored(cstr.ptr, stream); + fputc('\n', stream); + } + + if (cstr.needs_free) free((void *)cstr.ptr); +} + +jsval_t console_print(struct js *js, jsval_t *args, int nargs, const char *color, FILE *stream) { + if (color && !io_no_color) fputs(color, stream); for (int i = 0; i < nargs; i++) { - const char *space = i == 0 ? "" : " "; - io_puts(space, stream); - if (vtype(args[i]) == T_STR) { - char *str = js_getstr(js, args[i], NULL); - io_print(str ? str : "", stream); - } else { - char cbuf_stack[512]; js_cstr_t cstr = js_to_cstr( - js, args[i], cbuf_stack, sizeof(cbuf_stack) - ); - if (color && !io_no_color) io_puts(ANSI_RESET, stream); + if (i) fputc(' ', stream); + char cbuf[512]; + js_cstr_t cstr = js_to_cstr(js, args[i], cbuf, sizeof(cbuf)); + + if (vtype(args[i]) == T_STR) io_print(cstr.ptr, stream); else { + if (color && !io_no_color) fputs(C_RESET, stream); print_value_colored(cstr.ptr, stream); - if (color && !io_no_color) io_puts(color, stream); - if (cstr.needs_free) free((void *)cstr.ptr); + if (color && !io_no_color) fputs(color, stream); } + + if (cstr.needs_free) free((void *)cstr.ptr); } - if (color && !io_no_color) io_puts(ANSI_RESET, stream); - io_putc('\n', stream); + if (color && !io_no_color) fputs(C_RESET, stream); + fputc('\n', stream); + + return js_mkundef(); } static jsval_t js_console_log(struct js *js, jsval_t *args, int nargs) { - console_print(js, args, nargs, NULL, stdout); - return js_mkundef(); + return console_print(js, args, nargs, NULL, stdout); } static jsval_t js_console_error(struct js *js, jsval_t *args, int nargs) { - console_print(js, args, nargs, ANSI_RED, stderr); - return js_mkundef(); + return console_print(js, args, nargs, C_RED, stderr); } static jsval_t js_console_warn(struct js *js, jsval_t *args, int nargs) { - console_print(js, args, nargs, ANSI_YELLOW, stderr); - return js_mkundef(); + return console_print(js, args, nargs, C_YELLOW, stderr); } static jsval_t js_console_assert(struct js *js, jsval_t *args, int nargs) { @@ -318,36 +317,34 @@ static jsval_t js_console_assert(struct js *js, jsval_t *args, int nargs) { bool is_truthy = js_truthy(js, args[0]); if (is_truthy) return js_mkundef(); - io_puts("Assertion failed", stderr); + fputs("Assertion failed", stderr); if (nargs > 1) { - io_puts(": ", stderr); + fputs(": ", stderr); console_print(js, args + 1, nargs - 1, NULL, stderr); return js_mkundef(); } - io_putc('\n', stderr); + fputc('\n', stderr); return js_mkundef(); } static jsval_t js_console_trace(struct js *js, jsval_t *args, int nargs) { - io_puts("Trace", stderr); + fputs("Trace", stderr); if (nargs > 0) { - io_puts(": ", stderr); + fputs(": ", stderr); console_print(js, args, nargs, NULL, stderr); - } else io_putc('\n', stderr); + } else fputc('\n', stderr); js_print_stack_trace(stderr); return js_mkundef(); } static jsval_t js_console_info(struct js *js, jsval_t *args, int nargs) { - console_print(js, args, nargs, ANSI_CYAN, stdout); - return js_mkundef(); + return console_print(js, args, nargs, C_CYAN, stdout); } static jsval_t js_console_debug(struct js *js, jsval_t *args, int nargs) { - console_print(js, args, nargs, ANSI_MAGENTA, stdout); - return js_mkundef(); + return console_print(js, args, nargs, C_MAGENTA, stdout); } static jsval_t js_console_clear(struct js *js, jsval_t *args, int nargs) { @@ -508,7 +505,7 @@ static void inspect_mark_visited(inspect_visited_t *v, jsoff_t off) { v->visited[v->count++] = off; } -static void inspect_value(struct js *js, jsval_t val, FILE *stream, int depth, inspect_visited_t *visited) { +void inspect_value(ant_t *js, jsval_t val, FILE *stream, int depth, inspect_visited_t *visited) { int t = vtype(val); if (t == T_UNDEF) { fprintf(stream, "undefined"); return; } @@ -532,7 +529,7 @@ static void inspect_value(struct js *js, jsval_t val, FILE *stream, int depth, i if (t == T_OBJ || t == T_FUNC || t == T_PROMISE || t == T_ARR) { if (depth > 10) fprintf(stream, "<%s @%" PRIu64 " ...>", get_type_name(t), (uint64_t)vdata(val)); - else inspect_object_full(js, val, stream, depth, visited); + else inspect_object(js, val, stream, depth, visited); return; } @@ -544,7 +541,7 @@ static void inspect_value(struct js *js, jsval_t val, FILE *stream, int depth, i fprintf(stream, "<%s rawtype=%d data=%" PRIu64 ">", get_type_name(t), vtype(val), (uint64_t)vdata(val)); } -static void inspect_object_full(struct js *js, jsval_t obj, FILE *stream, int depth, inspect_visited_t *visited) { +void inspect_object(ant_t *js, jsval_t obj, FILE *stream, int depth, inspect_visited_t *visited) { int type = vtype(obj); jsoff_t obj_off = (jsoff_t)vdata(obj); diff --git a/src/repl.c b/src/repl.c index 21c5bbe..c61d44d 100644 --- a/src/repl.c +++ b/src/repl.c @@ -588,20 +588,7 @@ void ant_repl_run() { history_add(&history, multiline_buf); jsval_t eval_result = js_eval(js, multiline_buf, multiline_len); - js_run_event_loop(js); - - char cbuf_stack[512]; js_cstr_t cstr = js_to_cstr( - js, eval_result, cbuf_stack, sizeof(cbuf_stack) - ); - - if (vtype(eval_result) == T_ERR) - fprintf(stderr, "%s\n", cstr.ptr); - else if (vtype(eval_result) == T_STR) - printf("%s\n", cstr.ptr ? cstr.ptr : ""); - else { - print_value_colored(cstr.ptr, stdout); - printf("\n"); - } if (cstr.needs_free) free((void *)cstr.ptr); + js_run_event_loop(js); print_repl_value(js, eval_result, stdout); free(multiline_buf); multiline_buf = NULL; diff --git a/src/utils.c b/src/utils.c index 8772c53..503cb84 100644 --- a/src/utils.c +++ b/src/utils.c @@ -21,6 +21,10 @@ int hex_digit(char c) { return val ? val : (c == '0' ? 0 : -1); } +char hex_char(int v) { + return "0123456789abcdef"[v & 0x0f]; +} + uint64_t hash_key(const char *key, size_t len) { uint64_t hash = 14695981039346656037ULL; size_t i = 0; diff --git a/vendor/aklomp-base64.wrap b/vendor/aklomp-base64.wrap new file mode 100644 index 0000000..def7082 --- /dev/null +++ b/vendor/aklomp-base64.wrap @@ -0,0 +1,14 @@ +[wrap-file] +directory = base64-0.5.2 +source_url = https://github.com/aklomp/base64/archive/refs/tags/v0.5.2.tar.gz +source_filename = base64-0.5.2.tar.gz +source_hash = 723a0f9f4cf44cf79e97bcc315ec8f85e52eb104c8882942c3f2fba95acc080d +source_fallback_url = https://wrapdb.mesonbuild.com/v2/aklomp-base64_0.5.2-1/get_source/base64-0.5.2.tar.gz +patch_filename = aklomp-base64_0.5.2-1_patch.zip +patch_url = https://wrapdb.mesonbuild.com/v2/aklomp-base64_0.5.2-1/get_patch +patch_fallback_url = https://github.com/mesonbuild/wrapdb/releases/download/aklomp-base64_0.5.2-1/aklomp-base64_0.5.2-1_patch.zip +patch_hash = 9805354b8c0333fe0123c10d8c62356ef1d0d67a2689a348d18f73bddc1e2b10 +wrapdb_version = 0.5.2-1 + +[provide] +dependency_names = base64