MIRROR: javascript for ๐Ÿœ's, a tiny runtime with big ambitions
1
fork

Configure Feed

Select the types of activity you want to include in your feed.

migrate process.stdout/stdin to readablestream

+462 -54
+2
include/modules/stream.h
··· 27 27 ant_value_t stream_writable_constructor(ant_t *js); 28 28 ant_value_t stream_readable_prototype(ant_t *js); 29 29 ant_value_t stream_writable_prototype(ant_t *js); 30 + ant_value_t stream_duplex_prototype(ant_t *js); 30 31 31 32 ant_value_t stream_construct_readable(ant_t *js, ant_value_t base_proto, ant_value_t options); 32 33 ant_value_t stream_construct_writable(ant_t *js, ant_value_t base_proto, ant_value_t options); ··· 40 41 41 42 void stream_init_readable_object(ant_t *js, ant_value_t obj, ant_value_t options); 42 43 void stream_init_writable_object(ant_t *js, ant_value_t obj, ant_value_t options); 44 + void stream_init_duplex_object(ant_t *js, ant_value_t obj, ant_value_t options); 43 45 44 46 void *stream_get_attached_state(ant_value_t stream_obj); 45 47 void stream_clear_attached_state(ant_value_t stream_obj);
+12
include/modules/string_decoder.h
··· 1 1 #ifndef STRING_DECODER_H 2 2 #define STRING_DECODER_H 3 3 4 + #include <stdbool.h> 4 5 #include "types.h" 5 6 6 7 ant_value_t string_decoder_library(ant_t *js); 8 + ant_value_t string_decoder_create(ant_t *js, ant_value_t encoding); 9 + 10 + ant_value_t string_decoder_decode_value( 11 + ant_t *js, ant_value_t decoder, 12 + ant_value_t chunk, bool flush 13 + ); 14 + 15 + ant_value_t string_decoder_decode_bytes( 16 + ant_t *js, ant_value_t decoder, 17 + const uint8_t *src, size_t len, bool flush 18 + ); 7 19 8 20 #endif
+28 -15
src/modules/process.c
··· 44 44 #include "modules/buffer.h" 45 45 #include "modules/napi.h" 46 46 #include "modules/timer.h" 47 + #include "modules/string_decoder.h" 47 48 48 49 #ifndef _WIN32 49 50 extern char **environ; ··· 88 89 bool tty_initialized; 89 90 bool reading; 90 91 bool keypress_enabled; 92 + ant_value_t decoder; 91 93 int escape_state; 92 94 int escape_len; 93 95 char escape_buf[16]; ··· 644 646 if (nread > 0 && rt->js) { 645 647 ArrayBufferData *ab = create_array_buffer_data((size_t)nread); 646 648 if (ab) memcpy(ab->data, buf->base, (size_t)nread); 647 - ant_value_t data_val = ab 649 + ant_value_t raw_val = ab 648 650 ? create_typed_array(rt->js, TYPED_ARRAY_UINT8, ab, 0, (size_t)nread, "Buffer") 649 651 : js_mkstr(rt->js, buf->base, (size_t)nread); 652 + ant_value_t data_val = is_object_type(stdin_state.decoder) 653 + ? string_decoder_decode_value(rt->js, stdin_state.decoder, raw_val, false) 654 + : raw_val; 655 + if (is_err(data_val)) data_val = raw_val; 650 656 emit_stdio_event(&stdin_events, "data", &data_val, 1); 651 657 if (stdin_state.keypress_enabled) process_keypress_data(rt->js, buf->base, (size_t)nread); 652 658 } ··· 717 723 return js_bool(stdin_set_raw_mode(enable)); 718 724 } 719 725 726 + static ant_value_t js_stdin_set_encoding(ant_t *js, ant_value_t *args, int nargs) { 727 + ant_value_t this_obj = js_getthis(js); 728 + 729 + ant_value_t encoding = nargs > 0 && !is_undefined(args[0]) ? args[0] : js_mkstr(js, "utf8", 4); 730 + ant_value_t decoder = string_decoder_create(js, encoding); 731 + ant_value_t encoding_str = 0; 732 + 733 + if (is_err(decoder)) return decoder; 734 + encoding_str = js_tostring_val(js, encoding); 735 + if (is_err(encoding_str)) return encoding_str; 736 + 737 + stdin_state.decoder = decoder; 738 + js_set(js, this_obj, "encoding", encoding_str); 739 + 740 + return this_obj; 741 + } 742 + 720 743 static ant_value_t js_stdin_resume(ant_t *js, ant_value_t *args, int nargs) { 721 744 (void)args; (void)nargs; 722 745 stdin_start_reading(); ··· 801 824 802 825 if (!ant_output_stream_append(out, data, len)) return js_false; 803 826 return ant_output_stream_flush(out) ? js_true : js_false; 804 - } 805 - 806 - static ant_value_t process_write_stream_set_encoding(ant_t *js, ant_value_t *args, int nargs) { 807 - ant_value_t this_obj = js_getthis(js); 808 - ant_value_t encoding = nargs > 0 && vtype(args[0]) != T_UNDEF 809 - ? js_tostring_val(js, args[0]) 810 - : js_mkstr(js, "utf8", 4); 811 - 812 - if (is_err(encoding)) return encoding; 813 - js_set(js, this_obj, "encoding", encoding); 814 - 815 - return this_obj; 816 827 } 817 828 818 829 static ant_value_t js_stdout_write(ant_t *js, ant_value_t *args, int nargs) { ··· 1837 1848 ant_t *js = rt->js; 1838 1849 ant_value_t global = js_glob(js); 1839 1850 1851 + stdin_state.decoder = js_mkundef(); 1840 1852 process_start_time = uv_hrtime(); 1841 1853 ant_value_t process_proto = js_mkobj(js); 1842 1854 ··· 1912 1924 1913 1925 ant_value_t stdin_proto = js_mkobj(js); 1914 1926 js_set(js, stdin_proto, "setRawMode", js_mkfun(js_stdin_set_raw_mode)); 1927 + js_set(js, stdin_proto, "setEncoding", js_mkfun(js_stdin_set_encoding)); 1915 1928 js_set(js, stdin_proto, "resume", js_mkfun(js_stdin_resume)); 1916 1929 js_set(js, stdin_proto, "pause", js_mkfun(js_stdin_pause)); 1917 1930 js_set(js, stdin_proto, "on", js_mkfun(js_stdin_on)); ··· 1923 1936 ant_value_t stdin_obj = js_mkobj(js); 1924 1937 js_set_proto_init(stdin_obj, stdin_proto); 1925 1938 js_set(js, stdin_obj, "isTTY", js_bool(stdin_is_tty())); 1939 + js_set(js, stdin_obj, "encoding", js_mkundef()); 1926 1940 js_set(js, process_obj, "stdin", stdin_obj); 1927 1941 1928 1942 ant_value_t stdout_proto = js_mkobj(js); 1929 1943 js_set(js, stdout_proto, "write", js_mkfun(js_stdout_write)); 1930 - js_set(js, stdout_proto, "setEncoding", js_mkfun(process_write_stream_set_encoding)); 1931 1944 js_set(js, stdout_proto, "on", js_mkfun(js_stdout_on)); 1932 1945 js_set(js, stdout_proto, "once", js_mkfun(js_stdout_once)); 1933 1946 js_set(js, stdout_proto, "removeListener", js_mkfun(js_stdout_remove_listener)); ··· 1945 1958 1946 1959 ant_value_t stderr_proto = js_mkobj(js); 1947 1960 js_set(js, stderr_proto, "write", js_mkfun(js_stderr_write)); 1948 - js_set(js, stderr_proto, "setEncoding", js_mkfun(process_write_stream_set_encoding)); 1949 1961 js_set(js, stderr_proto, "on", js_mkfun(js_stderr_on)); 1950 1962 js_set(js, stderr_proto, "once", js_mkfun(js_stderr_once)); 1951 1963 js_set(js, stderr_proto, "removeListener", js_mkfun(js_stderr_remove_listener)); ··· 1973 1985 HASH_ITER(hh, tables[t], evt, tmp) 1974 1986 for (int i = 0; i < evt->listener_count; i++) mark(js, evt->listeners[i].listener); 1975 1987 } 1988 + if (is_object_type(stdin_state.decoder)) mark(js, stdin_state.decoder); 1976 1989 } 1977 1990 1978 1991 void process_enable_keypress_events(void) {
+83 -13
src/modules/stream.c
··· 13 13 #include "modules/events.h" 14 14 #include "modules/stream.h" 15 15 #include "modules/symbol.h" 16 + #include "modules/string_decoder.h" 16 17 17 18 enum { STREAM_NATIVE_TAG = 0x5354524Du }; // STRM 18 19 ··· 62 63 63 64 static ant_value_t stream_truthy_or_object(ant_t *js, ant_value_t value) { 64 65 return js_truthy(js, value) ? value : js_mkobj(js); 66 + } 67 + 68 + static ant_value_t stream_readable_state(ant_t *js, ant_value_t stream_obj) { 69 + return js_get(js, stream_obj, "_readableState"); 70 + } 71 + 72 + static ant_value_t stream_writable_state(ant_t *js, ant_value_t stream_obj) { 73 + return js_get(js, stream_obj, "_writableState"); 74 + } 75 + 76 + static ant_value_t stream_pipes(ant_t *js, ant_value_t stream_obj) { 77 + return js_get(js, stream_obj, "_pipes"); 65 78 } 66 79 67 80 static bool stream_key_is_cstr(ant_t *js, ant_value_t value, const char *expected) { ··· 164 177 return js_get(js, ns, "Buffer"); 165 178 } 166 179 180 + static ant_value_t stream_readable_decoder(ant_t *js, ant_value_t stream_obj) { 181 + ant_value_t state = stream_readable_state(js, stream_obj); 182 + if (!is_object_type(state)) return js_mkundef(); 183 + return js_get(js, state, "decoder"); 184 + } 185 + 186 + static ant_value_t stream_readable_decode_chunk( 187 + ant_t *js, ant_value_t stream_obj, 188 + ant_value_t chunk, bool flush 189 + ) { 190 + ant_value_t decoder = stream_readable_decoder(js, stream_obj); 191 + if (!is_object_type(decoder)) return chunk; 192 + return string_decoder_decode_value(js, decoder, chunk, flush); 193 + } 194 + 195 + static bool stream_value_is_empty_string(ant_t *js, ant_value_t value) { 196 + size_t len = 0; 197 + if (vtype(value) != T_STR) return false; 198 + (void)js_getstr(js, value, &len); 199 + return len == 0; 200 + } 201 + 167 202 static ant_value_t stream_make_buffer(ant_t *js, ant_value_t value, ant_value_t encoding) { 168 203 ant_value_t buffer_ctor = stream_buffer_ctor(js); 169 204 ant_value_t from_fn = 0; ··· 173 208 from_fn = js_get(js, buffer_ctor, "from"); 174 209 if (is_err(from_fn) || !is_callable(from_fn)) 175 210 return js_mkerr(js, "Buffer.from is not available"); 176 - 211 + 177 212 args[0] = value; 178 213 args[1] = encoding; 179 214 return stream_call(js, from_fn, buffer_ctor, args, 2, false); ··· 200 235 return stream_make_buffer(js, str_val, encoding); 201 236 } 202 237 203 - static ant_value_t stream_readable_state(ant_t *js, ant_value_t stream_obj) { 204 - return js_get(js, stream_obj, "_readableState"); 205 - } 206 - 207 - static ant_value_t stream_writable_state(ant_t *js, ant_value_t stream_obj) { 208 - return js_get(js, stream_obj, "_writableState"); 209 - } 210 - 211 - static ant_value_t stream_pipes(ant_t *js, ant_value_t stream_obj) { 212 - return js_get(js, stream_obj, "_pipes"); 213 - } 214 - 215 238 static ant_value_t stream_readable_buffer(ant_t *js, ant_value_t stream_obj) { 216 239 ant_value_t state = stream_readable_state(js, stream_obj); 217 240 if (!is_object_type(state)) return js_mkundef(); ··· 715 738 716 739 while (js_truthy(js, js_get(js, state, "flowing")) && stream_readable_buffer_len(js, stream_obj) > 0) { 717 740 ant_value_t chunk = stream_buffer_shift(js, stream_obj); 741 + chunk = stream_readable_decode_chunk(js, stream_obj, chunk, false); 742 + if (is_err(chunk)) return chunk; 718 743 emitted_data = true; 719 744 eventemitter_emit_args(js, stream_obj, "data", &chunk, 1); 720 745 } ··· 724 749 stream_readable_buffer_len(js, stream_obj) == 0 && 725 750 !js_truthy(js, js_get(js, state, "endEmitted")) 726 751 ) { 752 + ant_value_t tail = stream_readable_decode_chunk(js, stream_obj, js_mkundef(), true); 753 + if (is_err(tail)) return tail; 754 + if (!is_undefined(tail) && !stream_value_is_empty_string(js, tail)) { 755 + emitted_data = true; 756 + eventemitter_emit_args(js, stream_obj, "data", &tail, 1); 757 + } 727 758 js_set(js, state, "endEmitted", js_true); 728 759 js_set(js, stream_obj, "readableEnded", js_true); 729 760 stream_emit_named(js, stream_obj, "end"); ··· 806 837 if (stream_readable_buffer_len(js, stream_obj) == 0) return js_mknull(); 807 838 808 839 chunk = stream_buffer_shift(js, stream_obj); 840 + chunk = stream_readable_decode_chunk(js, stream_obj, chunk, false); 841 + if (is_err(chunk)) return chunk; 809 842 if (js_truthy(js, js_get(js, state, "flowing"))) stream_readable_flush(js, stream_obj); 810 843 811 844 return chunk; 845 + } 846 + 847 + static ant_value_t js_readable_set_encoding(ant_t *js, ant_value_t *args, int nargs) { 848 + ant_value_t stream_obj = stream_require_this(js, js_getthis(js), "Readable"); 849 + ant_value_t state = 0; ant_value_t decoder = 0; 850 + 851 + ant_value_t encoding = nargs > 0 && !is_undefined(args[0]) ? args[0] : js_mkstr(js, "utf8", 4); 852 + ant_value_t encoding_str = 0; 853 + 854 + if (is_err(stream_obj)) return stream_obj; 855 + state = stream_readable_state(js, stream_obj); 856 + if (!is_object_type(state)) return stream_obj; 857 + 858 + decoder = string_decoder_create(js, encoding); 859 + if (is_err(decoder)) return decoder; 860 + encoding_str = js_tostring_val(js, encoding); 861 + if (is_err(encoding_str)) return encoding_str; 862 + 863 + js_set(js, state, "decoder", decoder); 864 + js_set(js, stream_obj, "encoding", encoding_str); 865 + js_set(js, stream_obj, "readableEncoding", encoding_str); 866 + 867 + return stream_obj; 812 868 } 813 869 814 870 static ant_value_t js_readable_on(ant_t *js, ant_value_t *args, int nargs) { ··· 1648 1704 js_set(js, g_readable_proto, "_read", js_mkfun(js_readable__read)); 1649 1705 js_set(js, g_readable_proto, "push", js_mkfun(js_readable_push)); 1650 1706 js_set(js, g_readable_proto, "read", js_mkfun(js_readable_read)); 1707 + js_set(js, g_readable_proto, "setEncoding", js_mkfun(js_readable_set_encoding)); 1651 1708 js_set(js, g_readable_proto, "on", js_mkfun(js_readable_on)); 1652 1709 js_set(js, g_readable_proto, "resume", js_mkfun(js_readable_resume)); 1653 1710 js_set(js, g_readable_proto, "pause", js_mkfun(js_readable_pause)); ··· 1726 1783 return g_writable_proto; 1727 1784 } 1728 1785 1786 + ant_value_t stream_duplex_prototype(ant_t *js) { 1787 + stream_init_constructors(js); 1788 + return g_duplex_proto; 1789 + } 1790 + 1729 1791 ant_value_t stream_construct_readable(ant_t *js, ant_value_t base_proto, ant_value_t options) { 1730 1792 stream_init_constructors(js); 1731 1793 return stream_construct(js, base_proto, options, stream_init_readable); ··· 1747 1809 stream_init_constructors(js); 1748 1810 if (!is_object_type(obj)) return; 1749 1811 js_set_native_tag(obj, STREAM_NATIVE_TAG); 1812 + stream_init_writable(js, obj, options); 1813 + } 1814 + 1815 + void stream_init_duplex_object(ant_t *js, ant_value_t obj, ant_value_t options) { 1816 + stream_init_constructors(js); 1817 + if (!is_object_type(obj)) return; 1818 + js_set_native_tag(obj, STREAM_NATIVE_TAG); 1819 + stream_init_readable(js, obj, options); 1750 1820 stream_init_writable(js, obj, options); 1751 1821 } 1752 1822
+41 -6
src/modules/string_decoder.c
··· 205 205 return sd_do_write(js, st, src, len, true); 206 206 } 207 207 208 - static ant_value_t js_sd_ctor(ant_t *js, ant_value_t *args, int nargs) { 209 - if (vtype(js->new_target) == T_UNDEF) 210 - return js_mkerr_typed(js, JS_ERR_TYPE, "StringDecoder constructor requires 'new'"); 211 - 208 + ant_value_t string_decoder_create(ant_t *js, ant_value_t encoding) { 212 209 int enc = SD_ENC_UTF8; 213 - if (nargs > 0 && !is_undefined(args[0])) { 214 - ant_value_t label_val = (vtype(args[0]) == T_STR) ? args[0] : coerce_to_str(js, args[0]); 210 + if (!is_undefined(encoding)) { 211 + ant_value_t label_val = (vtype(encoding) == T_STR) ? encoding : coerce_to_str(js, encoding); 215 212 if (!is_err(label_val) && vtype(label_val) == T_STR) { 216 213 size_t llen; 217 214 const char *label = js_getstr(js, label_val, &llen); ··· 236 233 js_set_finalizer(obj, sd_finalize); 237 234 238 235 return obj; 236 + } 237 + 238 + ant_value_t string_decoder_decode_bytes( 239 + ant_t *js, ant_value_t decoder, 240 + const uint8_t *src, size_t len, bool flush 241 + ) { 242 + sd_state_t *st = sd_get_state(decoder); 243 + if (!st) return js_mkerr_typed(js, JS_ERR_TYPE, "Invalid StringDecoder"); 244 + return sd_do_write(js, st, src, len, flush); 245 + } 246 + 247 + ant_value_t string_decoder_decode_value( 248 + ant_t *js, ant_value_t decoder, 249 + ant_value_t chunk, bool flush 250 + ) { 251 + sd_state_t *st = sd_get_state(decoder); 252 + if (!st) return js_mkerr_typed(js, JS_ERR_TYPE, "Invalid StringDecoder"); 253 + 254 + if (vtype(chunk) == T_STR) { 255 + size_t slen = 0; 256 + const char *s = js_getstr(js, chunk, &slen); 257 + return s ? js_mkstr(js, s, slen) : js_mkstr(js, "", 0); 258 + } 259 + 260 + const uint8_t *src = NULL; 261 + size_t len = 0; 262 + if (is_object_type(chunk)) 263 + buffer_source_get_bytes(js, chunk, &src, &len); 264 + 265 + return sd_do_write(js, st, src, len, flush); 266 + } 267 + 268 + static ant_value_t js_sd_ctor(ant_t *js, ant_value_t *args, int nargs) { 269 + if (vtype(js->new_target) == T_UNDEF) 270 + return js_mkerr_typed(js, JS_ERR_TYPE, "StringDecoder constructor requires 'new'"); 271 + 272 + ant_value_t encoding = nargs > 0 ? args[0] : js_mkundef(); 273 + return string_decoder_create(js, encoding); 239 274 } 240 275 241 276 ant_value_t string_decoder_library(ant_t *js) {
+6 -4
src/modules/tty.c
··· 886 886 js_set_getter_desc(js, stdout_obj, "columns", 7, js_mkfun(tty_write_stream_columns_getter), JS_DESC_E | JS_DESC_C); 887 887 888 888 ant_value_t stdout_proto = js_get_proto(js, stdout_obj); 889 - if (is_special_object(stdout_proto)) js_set_proto_init(stdout_proto, stream_writable_prototype(js)); 889 + if (is_special_object(stdout_proto)) js_set_proto_init(stdout_proto, stream_duplex_prototype(js)); 890 890 setup_writestream_proto(js, stdout_proto); 891 - stream_init_writable_object(js, stdout_obj, js_mkundef()); 891 + stream_init_duplex_object(js, stdout_obj, js_mkundef()); 892 + js_set(js, stdout_obj, "readable", js_false); 892 893 } 893 894 894 895 ant_value_t stderr_obj = js_get(js, process_obj, "stderr"); ··· 898 899 js_set_getter_desc(js, stderr_obj, "columns", 7, js_mkfun(tty_write_stream_columns_getter), JS_DESC_E | JS_DESC_C); 899 900 900 901 ant_value_t stderr_proto = js_get_proto(js, stderr_obj); 901 - if (is_special_object(stderr_proto)) js_set_proto_init(stderr_proto, stream_writable_prototype(js)); 902 + if (is_special_object(stderr_proto)) js_set_proto_init(stderr_proto, stream_duplex_prototype(js)); 902 903 setup_writestream_proto(js, stderr_proto); 903 - stream_init_writable_object(js, stderr_obj, js_mkundef()); 904 + stream_init_duplex_object(js, stderr_obj, js_mkundef()); 905 + js_set(js, stderr_obj, "readable", js_false); 904 906 } 905 907 } 906 908
+1
src/types/modules/stream.d.ts
··· 18 18 _read(size?: number): void; 19 19 read(size?: number): Uint8Array | string | null; 20 20 push(chunk: Uint8Array | string | null): boolean; 21 + setEncoding(encoding?: string): this; 21 22 on(event: 'data', listener: (chunk: Uint8Array | string) => void): this; 22 23 on(event: 'end' | 'close' | 'readable', listener: () => void): this; 23 24 on(event: 'error', listener: (error: Error) => void): this;
+3
src/types/process.d.ts
··· 51 51 interface ReadStream { 52 52 isTTY: boolean; 53 53 setRawMode(enable?: boolean): boolean; 54 + setEncoding(encoding?: string): this; 54 55 resume(): this; 55 56 pause(): this; 56 57 on(event: string, listener: EventListener): this; ··· 63 64 isTTY: boolean; 64 65 rows: number; 65 66 columns: number; 67 + readableEncoding?: string; 68 + setEncoding(encoding?: string): this; 66 69 write(data: string): boolean; 67 70 on(event: string, listener: EventListener): this; 68 71 once(event: string, listener: EventListener): this;
+33
tests/fixtures/process_stdin_setencoding_tty_child.cjs
··· 1 + function cleanup() { 2 + try { process.stdin.setRawMode(false); } catch {} 3 + try { process.stdin.pause(); } catch {} 4 + } 5 + 6 + if (!process.stdin.isTTY || typeof process.stdin.setRawMode !== 'function') { 7 + console.log('SKIP'); 8 + process.exit(0); 9 + } 10 + 11 + process.stdin.setRawMode(true); 12 + process.stdin.setEncoding('utf8'); 13 + process.stdin.resume(); 14 + process.stdout.write('READY\n'); 15 + 16 + const timeout = setTimeout(() => { 17 + cleanup(); 18 + console.log('TIMEOUT'); 19 + process.exit(3); 20 + }, 2000); 21 + 22 + process.stdin.on('data', chunk => { 23 + clearTimeout(timeout); 24 + cleanup(); 25 + 26 + if (typeof chunk !== 'string') { 27 + console.log('TYPE', typeof chunk); 28 + process.exit(4); 29 + } 30 + 31 + console.log('DATA', JSON.stringify(chunk)); 32 + process.exit(chunk === 'Aโ‚ฌ' ? 0 : 5); 33 + });
+33
tests/fixtures/tty_readstream_setencoding_child.cjs
··· 1 + const fs = require('fs'); 2 + const tty = require('tty'); 3 + 4 + const fd = fs.openSync('/dev/tty', 'r'); 5 + const stream = new tty.ReadStream(fd); 6 + 7 + function cleanup() { 8 + try { stream.destroy(); } catch {} 9 + } 10 + 11 + stream.setEncoding('utf8'); 12 + process.stdout.write('READY\n'); 13 + 14 + const timeout = setTimeout(() => { 15 + cleanup(); 16 + console.log('TIMEOUT'); 17 + process.exit(3); 18 + }, 2000); 19 + 20 + stream.on('data', chunk => { 21 + clearTimeout(timeout); 22 + cleanup(); 23 + 24 + if (typeof chunk !== 'string') { 25 + console.log('TYPE', typeof chunk); 26 + process.exit(4); 27 + } 28 + 29 + console.log('DATA', JSON.stringify(chunk)); 30 + process.exit(chunk === 'Aโ‚ฌ\n' ? 0 : 5); 31 + }); 32 + 33 + stream.resume();
+102
tests/test_process_stdin_setencoding_tty.cjs
··· 1 + const { spawnSync } = require('child_process'); 2 + const path = require('path'); 3 + 4 + function fail(message) { 5 + throw new Error(message); 6 + } 7 + 8 + function runInPty() { 9 + const helper = path.join(__dirname, 'fixtures', 'process_stdin_setencoding_tty_child.cjs'); 10 + const script = ` 11 + import os, select, signal, sys, time 12 + 13 + exec_path, helper = sys.argv[1], sys.argv[2] 14 + pid, master = os.forkpty() 15 + 16 + if pid == 0: 17 + os.execv(exec_path, [exec_path, helper]) 18 + 19 + buf = bytearray() 20 + sent = False 21 + exit_code = None 22 + deadline = time.time() + 7.0 23 + 24 + while time.time() < deadline: 25 + done, status = os.waitpid(pid, os.WNOHANG) 26 + if done == pid: 27 + exit_code = os.waitstatus_to_exitcode(status) 28 + break 29 + 30 + r, _, _ = select.select([master], [], [], 0.1) 31 + if master not in r: 32 + continue 33 + 34 + try: 35 + chunk = os.read(master, 4096) 36 + except OSError: 37 + break 38 + 39 + if not chunk: 40 + break 41 + 42 + buf.extend(chunk) 43 + if (not sent) and b'READY' in buf: 44 + os.write(master, b'A\\xe2\\x82\\xac') 45 + sent = True 46 + 47 + if exit_code is None: 48 + os.kill(pid, signal.SIGKILL) 49 + _, status = os.waitpid(pid, 0) 50 + exit_code = os.waitstatus_to_exitcode(status) 51 + 52 + while True: 53 + r, _, _ = select.select([master], [], [], 0.05) 54 + if master not in r: 55 + break 56 + try: 57 + chunk = os.read(master, 4096) 58 + except OSError: 59 + break 60 + if not chunk: 61 + break 62 + buf.extend(chunk) 63 + 64 + sys.stdout.buffer.write(bytes(buf)) 65 + sys.exit(exit_code) 66 + `; 67 + 68 + if (process.platform === 'win32') { 69 + console.log('skipping process.stdin setEncoding tty test on win32'); 70 + process.exit(0); 71 + } 72 + 73 + return spawnSync('python3', ['-c', script, process.execPath, helper], { 74 + encoding: 'utf8', 75 + timeout: 9000, 76 + }); 77 + } 78 + 79 + const result = runInPty(); 80 + 81 + if (result.error && result.error.code === 'ENOENT') { 82 + console.log('skipping process.stdin setEncoding tty test because `python3` is unavailable'); 83 + process.exit(0); 84 + } 85 + 86 + if (result.error) throw result.error; 87 + 88 + const output = `${result.stdout || ''}${result.stderr || ''}`; 89 + 90 + if (result.status !== 0) { 91 + fail(`child exited ${result.status}\n${output}`); 92 + } 93 + 94 + if (!output.includes('READY')) { 95 + fail(`expected READY banner\n${output}`); 96 + } 97 + 98 + if (!output.includes('DATA "Aโ‚ฌ"')) { 99 + fail(`expected decoded stdin string payload\n${output}`); 100 + } 101 + 102 + console.log('process.stdin setEncoding decodes tty data');
+16
tests/test_process_stdout_setencoding.cjs
··· 1 + function assert(condition, message) { 2 + if (!condition) throw new Error(message); 3 + } 4 + 5 + assert(typeof process.stdout.setEncoding === 'function', 'process.stdout.setEncoding should exist'); 6 + assert(typeof process.stderr.setEncoding === 'function', 'process.stderr.setEncoding should exist'); 7 + 8 + const stdoutReturn = process.stdout.setEncoding('utf8'); 9 + const stderrReturn = process.stderr.setEncoding('utf8'); 10 + 11 + assert(stdoutReturn === process.stdout, 'stdout.setEncoding should return process.stdout'); 12 + assert(stderrReturn === process.stderr, 'stderr.setEncoding should return process.stderr'); 13 + assert(process.stdout.readableEncoding === 'utf8', 'stdout readableEncoding should be utf8'); 14 + assert(process.stderr.readableEncoding === 'utf8', 'stderr readableEncoding should be utf8'); 15 + 16 + console.log('process stdout/stderr setEncoding ok');
-16
tests/test_process_write_stream_setencoding.cjs
··· 1 - function assert(condition, message) { 2 - if (!condition) throw new Error(message); 3 - } 4 - 5 - for (const [name, stream] of [ 6 - ['stdout', process.stdout], 7 - ['stderr', process.stderr], 8 - ]) { 9 - assert(typeof stream.setEncoding === 'function', `${name}.setEncoding should exist`); 10 - 11 - const returned = stream.setEncoding('utf8'); 12 - assert(returned === stream, `${name}.setEncoding should return the stream`); 13 - assert(stream.encoding === 'utf8', `${name}.encoding should be utf8`); 14 - } 15 - 16 - console.log('process stdout/stderr setEncoding ok');
+102
tests/test_tty_readstream_setencoding.cjs
··· 1 + const { spawnSync } = require('child_process'); 2 + const path = require('path'); 3 + 4 + function fail(message) { 5 + throw new Error(message); 6 + } 7 + 8 + function runInPty() { 9 + const helper = path.join(__dirname, 'fixtures', 'tty_readstream_setencoding_child.cjs'); 10 + const script = ` 11 + import os, select, signal, sys, time 12 + 13 + exec_path, helper = sys.argv[1], sys.argv[2] 14 + pid, master = os.forkpty() 15 + 16 + if pid == 0: 17 + os.execv(exec_path, [exec_path, helper]) 18 + 19 + buf = bytearray() 20 + sent = False 21 + exit_code = None 22 + deadline = time.time() + 7.0 23 + 24 + while time.time() < deadline: 25 + done, status = os.waitpid(pid, os.WNOHANG) 26 + if done == pid: 27 + exit_code = os.waitstatus_to_exitcode(status) 28 + break 29 + 30 + r, _, _ = select.select([master], [], [], 0.1) 31 + if master not in r: 32 + continue 33 + 34 + try: 35 + chunk = os.read(master, 4096) 36 + except OSError: 37 + break 38 + 39 + if not chunk: 40 + break 41 + 42 + buf.extend(chunk) 43 + if (not sent) and b'READY' in buf: 44 + os.write(master, b'A\\xe2\\x82\\xac\\n') 45 + sent = True 46 + 47 + if exit_code is None: 48 + os.kill(pid, signal.SIGKILL) 49 + _, status = os.waitpid(pid, 0) 50 + exit_code = os.waitstatus_to_exitcode(status) 51 + 52 + while True: 53 + r, _, _ = select.select([master], [], [], 0.05) 54 + if master not in r: 55 + break 56 + try: 57 + chunk = os.read(master, 4096) 58 + except OSError: 59 + break 60 + if not chunk: 61 + break 62 + buf.extend(chunk) 63 + 64 + sys.stdout.buffer.write(bytes(buf)) 65 + sys.exit(exit_code) 66 + `; 67 + 68 + if (process.platform === 'win32') { 69 + console.log('skipping tty.ReadStream setEncoding test on win32'); 70 + process.exit(0); 71 + } 72 + 73 + return spawnSync('python3', ['-c', script, process.execPath, helper], { 74 + encoding: 'utf8', 75 + timeout: 9000, 76 + }); 77 + } 78 + 79 + const result = runInPty(); 80 + 81 + if (result.error && result.error.code === 'ENOENT') { 82 + console.log('skipping tty.ReadStream setEncoding test because `python3` is unavailable'); 83 + process.exit(0); 84 + } 85 + 86 + if (result.error) throw result.error; 87 + 88 + const output = `${result.stdout || ''}${result.stderr || ''}`; 89 + 90 + if (result.status !== 0) { 91 + fail(`child exited ${result.status}\n${output}`); 92 + } 93 + 94 + if (!output.includes('READY')) { 95 + fail(`expected READY banner\n${output}`); 96 + } 97 + 98 + if (!output.includes('DATA "Aโ‚ฌ\\n"')) { 99 + fail(`expected decoded tty.ReadStream string payload\n${output}`); 100 + } 101 + 102 + console.log('tty.ReadStream setEncoding decodes tty data');