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.

add console.inspect for strings

+195 -3
+162 -3
src/modules/io.c
··· 830 830 v->visited[v->count++] = off; 831 831 } 832 832 833 + static const char *inspect_ascii_state(uint8_t state) { 834 + switch (state) { 835 + case STR_ASCII_YES: return "yes"; 836 + case STR_ASCII_NO: return "no"; 837 + default: return "unknown"; 838 + }} 839 + 840 + static void inspect_string_bytes(FILE *stream, const char *bytes, size_t len) { 841 + size_t limit = len > 96 ? 96 : len; 842 + fprintf(stream, "\""); 843 + for (size_t i = 0; i < limit; i++) { 844 + unsigned char c = (unsigned char)bytes[i]; 845 + switch (c) { 846 + case '\n': fprintf(stream, "\\n"); break; 847 + case '\r': fprintf(stream, "\\r"); break; 848 + case '\t': fprintf(stream, "\\t"); break; 849 + case '\\': fprintf(stream, "\\\\"); break; 850 + case '"': fprintf(stream, "\\\""); break; 851 + default: 852 + if (c < 0x20 || c == 0x7f) fprintf(stream, "\\x%02x", c); 853 + else fputc(c, stream); 854 + break; 855 + } 856 + } 857 + if (limit < len) fprintf(stream, "...<%zu bytes truncated>", len - limit); 858 + fprintf(stream, "\""); 859 + } 860 + 861 + static void inspect_string_value( 862 + ant_t *js, 863 + ant_value_t val, 864 + FILE *stream, 865 + int depth, 866 + inspect_visited_t *visited 867 + ) { 868 + uint64_t raw_value = (uint64_t)val; 869 + uint64_t raw_data = (uint64_t)vdata(val); 870 + uintptr_t tag = (uintptr_t)(vdata(val) & STR_HEAP_TAG_MASK); 871 + 872 + if (tag == STR_HEAP_TAG_FLAT) { 873 + ant_flat_string_t *flat = ant_str_flat_ptr(val); 874 + fprintf( 875 + stream, 876 + "<String flat value=0x%016" PRIx64 " data=0x%012" PRIx64 " ptr=%p", 877 + raw_value, 878 + raw_data, 879 + (void *)flat 880 + ); 881 + if (!flat) { 882 + fprintf(stream, ">"); 883 + return; 884 + } 885 + 886 + fprintf( 887 + stream, 888 + " len=%" PRIu64 " ascii=%s bytes=", 889 + (uint64_t)flat->len, 890 + inspect_ascii_state(flat->is_ascii) 891 + ); 892 + inspect_string_bytes(stream, flat->bytes, (size_t)flat->len); 893 + fprintf(stream, ">"); 894 + return; 895 + } 896 + 897 + if (tag == STR_HEAP_TAG_ROPE) { 898 + ant_rope_heap_t *rope = ant_str_rope_ptr(val); 899 + fprintf( 900 + stream, 901 + "<String rope value=0x%016" PRIx64 " data=0x%012" PRIx64 " ptr=%p", 902 + raw_value, 903 + raw_data, 904 + (void *)rope 905 + ); 906 + if (!rope) { 907 + fprintf(stream, ">"); 908 + return; 909 + } 910 + 911 + fprintf( 912 + stream, 913 + " len=%" PRIu64 " depth=%u cached=", 914 + (uint64_t)rope->len, 915 + (unsigned)rope->depth 916 + ); 917 + if (vtype(rope->cached) == T_UNDEF) fprintf(stream, "undefined"); 918 + else inspect_value(js, rope->cached, stream, depth + 1, visited); 919 + fprintf(stream, "> {\n"); 920 + 921 + inspect_print_indent(stream, depth + 1); 922 + fprintf(stream, "left: "); 923 + if (depth > 10) fprintf(stream, "<String ...>"); 924 + else inspect_value(js, rope->left, stream, depth + 1, visited); 925 + fprintf(stream, "\n"); 926 + 927 + inspect_print_indent(stream, depth + 1); 928 + fprintf(stream, "right: "); 929 + if (depth > 10) fprintf(stream, "<String ...>"); 930 + else inspect_value(js, rope->right, stream, depth + 1, visited); 931 + fprintf(stream, "\n"); 932 + 933 + inspect_print_indent(stream, depth); 934 + fprintf(stream, "}"); 935 + return; 936 + } 937 + 938 + if (tag == STR_HEAP_TAG_BUILDER) { 939 + ant_string_builder_t *builder = ant_str_builder_ptr(val); 940 + fprintf( 941 + stream, 942 + "<String builder value=0x%016" PRIx64 " data=0x%012" PRIx64 " ptr=%p", 943 + raw_value, 944 + raw_data, 945 + (void *)builder 946 + ); 947 + if (!builder) { 948 + fprintf(stream, ">"); 949 + return; 950 + } 951 + 952 + fprintf( 953 + stream, 954 + " len=%" PRIu64 " ascii=%s head=%p chunk_tail=%p tail_len=%u cached=", 955 + (uint64_t)builder->len, 956 + inspect_ascii_state(builder->ascii_state), 957 + (void *)builder->head, 958 + (void *)builder->chunk_tail, 959 + (unsigned)builder->tail_len 960 + ); 961 + if (vtype(builder->cached) == T_UNDEF) fprintf(stream, "undefined"); 962 + else inspect_value(js, builder->cached, stream, depth + 1, visited); 963 + fprintf(stream, "> {\n"); 964 + 965 + int chunk_index = 0; 966 + for (ant_builder_chunk_t *chunk = builder->head; chunk; chunk = chunk->next) { 967 + if (chunk_index >= 64) { 968 + inspect_print_indent(stream, depth + 1); 969 + fprintf(stream, "...<builder chunks truncated>\n"); 970 + break; 971 + } 972 + 973 + inspect_print_indent(stream, depth + 1); 974 + fprintf(stream, "chunk[%d] @%p next=%p value: ", chunk_index, (void *)chunk, (void *)chunk->next); 975 + if (depth > 10) fprintf(stream, "<String ...>"); 976 + else inspect_value(js, chunk->value, stream, depth + 1, visited); 977 + fprintf(stream, "\n"); 978 + chunk_index++; 979 + } 980 + 981 + inspect_print_indent(stream, depth + 1); 982 + fprintf(stream, "tail: "); 983 + inspect_string_bytes(stream, builder->tail, builder->tail_len); 984 + fprintf(stream, "\n"); 985 + 986 + inspect_print_indent(stream, depth); 987 + fprintf(stream, "}"); 988 + return; 989 + } 990 + 991 + fprintf(stream, "<String unknown-tag=%" PRIuPTR " value=0x%016" PRIx64 " data=0x%012" PRIx64 ">", tag, raw_value, raw_data); 992 + } 993 + 833 994 void inspect_value(ant_t *js, ant_value_t val, FILE *stream, int depth, inspect_visited_t *visited) { 834 995 int t = vtype(val); 835 996 ··· 840 1001 if (t == T_ERR) { fprintf(stream, "[Error]"); return; } 841 1002 842 1003 if (t == T_STR) { 843 - size_t len; 844 - char *str = js_getstr(js, val, &len); 845 - fprintf(stream, "\"%.*s\"", (int)len, str ? str : ""); 1004 + inspect_string_value(js, val, stream, depth, visited); 846 1005 return; 847 1006 } 848 1007
+33
tests/test_console_inspect_string_internals.cjs
··· 1 + const { spawnSync } = require('child_process'); 2 + 3 + function assert(condition, message) { 4 + if (!condition) throw new Error(message); 5 + } 6 + 7 + function run(source) { 8 + const result = spawnSync(process.execPath, ['-e', source], { encoding: 'utf8' }); 9 + if (result.error) throw result.error; 10 + assert( 11 + result.status === 0, 12 + `expected console.inspect probe to exit 0, got ${result.status}\nstdout:\n${result.stdout}\nstderr:\n${result.stderr}` 13 + ); 14 + return result.stdout; 15 + } 16 + 17 + const flat = run('console.inspect("hello world")'); 18 + assert(flat.includes('<String flat '), `expected flat string internals, got ${JSON.stringify(flat)}`); 19 + assert(/value=0x[0-9a-f]+/.test(flat), `expected raw value bits, got ${JSON.stringify(flat)}`); 20 + assert(/data=0x[0-9a-f]+/.test(flat), `expected raw data bits, got ${JSON.stringify(flat)}`); 21 + assert(/ptr=0x[0-9a-f]+/.test(flat), `expected flat string pointer, got ${JSON.stringify(flat)}`); 22 + assert(flat.includes('len=11'), `expected flat string length, got ${JSON.stringify(flat)}`); 23 + assert(flat.includes('ascii=yes'), `expected ASCII state, got ${JSON.stringify(flat)}`); 24 + assert(flat.includes('bytes="hello world"'), `expected byte preview, got ${JSON.stringify(flat)}`); 25 + 26 + const rope = run('console.inspect("a" + "b")'); 27 + assert(rope.includes('<String rope '), `expected rope string internals, got ${JSON.stringify(rope)}`); 28 + assert(rope.includes('depth=1'), `expected rope depth, got ${JSON.stringify(rope)}`); 29 + assert(rope.includes('cached=undefined'), `expected untouched rope cache, got ${JSON.stringify(rope)}`); 30 + assert(rope.includes('left: <String flat '), `expected left flat leaf, got ${JSON.stringify(rope)}`); 31 + assert(rope.includes('right: <String flat '), `expected right flat leaf, got ${JSON.stringify(rope)}`); 32 + 33 + console.log('console.inspect string internals ok');