]> git.kaiwu.me - quickjs.git/commitdiff
added JS_PrintValue() and use it in console.log(), print() and the REPL (#256)
authorFabrice Bellard <fabrice@bellard.org>
Wed, 30 Apr 2025 11:40:15 +0000 (13:40 +0200)
committerFabrice Bellard <fabrice@bellard.org>
Wed, 30 Apr 2025 11:40:15 +0000 (13:40 +0200)
quickjs-libc.c
quickjs.c
quickjs.h
repl.js
run-test262.c

index 091fd4228e28eccc0c510f38cbda59fdbe2a2ab3..b59c6ac55b4191362ec021f4cafb608b31ba0147 100644 (file)
@@ -1083,6 +1083,13 @@ static JSValue js_std_file_printf(JSContext *ctx, JSValueConst this_val,
     return js_printf_internal(ctx, argc, argv, f);
 }
 
+static JSValue js_std_file_printObject(JSContext *ctx, JSValueConst this_val,
+                                       int argc, JSValueConst *argv)
+{
+    JS_PrintValue(ctx, stdout, argv[0], NULL);
+    return JS_UNDEFINED;
+}
+
 static JSValue js_std_file_flush(JSContext *ctx, JSValueConst this_val,
                                  int argc, JSValueConst *argv)
 {
@@ -1540,6 +1547,7 @@ static const JSCFunctionListEntry js_std_funcs[] = {
     JS_PROP_INT32_DEF("SEEK_CUR", SEEK_CUR, JS_PROP_CONFIGURABLE ),
     JS_PROP_INT32_DEF("SEEK_END", SEEK_END, JS_PROP_CONFIGURABLE ),
     JS_OBJECT_DEF("Error", js_std_error_props, countof(js_std_error_props), JS_PROP_CONFIGURABLE),
+    JS_CFUNC_DEF("__printObject", 1, js_std_file_printObject ),
 };
 
 static const JSCFunctionListEntry js_std_file_proto_funcs[] = {
@@ -3891,17 +3899,23 @@ static JSValue js_print(JSContext *ctx, JSValueConst this_val,
                         int argc, JSValueConst *argv)
 {
     int i;
-    const char *str;
-    size_t len;
-
+    JSValueConst v;
+    
     for(i = 0; i < argc; i++) {
         if (i != 0)
             putchar(' ');
-        str = JS_ToCStringLen(ctx, &len, argv[i]);
-        if (!str)
-            return JS_EXCEPTION;
-        fwrite(str, 1, len, stdout);
-        JS_FreeCString(ctx, str);
+        v = argv[i];
+        if (JS_IsString(v)) {
+            const char *str;
+            size_t len;
+            str = JS_ToCStringLen(ctx, &len, v);
+            if (!str)
+                return JS_EXCEPTION;
+            fwrite(str, 1, len, stdout);
+            JS_FreeCString(ctx, str);
+        } else {
+            JS_PrintValue(ctx, stdout, v, NULL);
+        }
     }
     putchar('\n');
     return JS_UNDEFINED;
@@ -4012,33 +4026,10 @@ void js_std_free_handlers(JSRuntime *rt)
     JS_SetRuntimeOpaque(rt, NULL); /* fail safe */
 }
 
-static void js_dump_obj(JSContext *ctx, FILE *f, JSValueConst val)
-{
-    const char *str;
-
-    str = JS_ToCString(ctx, val);
-    if (str) {
-        fprintf(f, "%s\n", str);
-        JS_FreeCString(ctx, str);
-    } else {
-        fprintf(f, "[exception]\n");
-    }
-}
-
 static void js_std_dump_error1(JSContext *ctx, JSValueConst exception_val)
 {
-    JSValue val;
-    BOOL is_error;
-
-    is_error = JS_IsError(ctx, exception_val);
-    js_dump_obj(ctx, stderr, exception_val);
-    if (is_error) {
-        val = JS_GetPropertyStr(ctx, exception_val, "stack");
-        if (!JS_IsUndefined(val)) {
-            js_dump_obj(ctx, stderr, val);
-        }
-        JS_FreeValue(ctx, val);
-    }
+    JS_PrintValue(ctx, stderr, exception_val, NULL);
+    fputc('\n', stderr);
 }
 
 void js_std_dump_error(JSContext *ctx)
index aa174e9189dda88a49f2ec13353b275580e4b2f3..428288b78abaed7c1c28d242f4b31515d675137a 100644 (file)
--- a/quickjs.c
+++ b/quickjs.c
@@ -984,6 +984,27 @@ struct JSObject {
     } u;
 };
 
+typedef struct JSMapRecord {
+    int ref_count; /* used during enumeration to avoid freeing the record */
+    BOOL empty : 8; /* TRUE if the record is deleted */
+    struct list_head link;
+    struct JSMapRecord *hash_next;
+    JSValue key;
+    JSValue value;
+} JSMapRecord;
+
+typedef struct JSMapState {
+    BOOL is_weak; /* TRUE if WeakSet/WeakMap */
+    struct list_head records; /* list of JSMapRecord.link */
+    uint32_t record_count;
+    JSMapRecord **hash_table;
+    int hash_bits;
+    uint32_t hash_size; /* = 2 ^ hash_bits */
+    uint32_t record_count_threshold; /* count at which a hash table
+                                        resize is needed */
+    JSWeakRefHeader weakref_header; /* only used if is_weak = TRUE */
+} JSMapState;
+
 enum {
     __JS_ATOM_NULL = JS_ATOM_NULL,
 #define DEF(name, str) JS_ATOM_ ## name,
@@ -1057,16 +1078,14 @@ static __exception int JS_ToArrayLengthFree(JSContext *ctx, uint32_t *plen,
 static JSValue JS_EvalObject(JSContext *ctx, JSValueConst this_obj,
                              JSValueConst val, int flags, int scope_idx);
 JSValue __attribute__((format(printf, 2, 3))) JS_ThrowInternalError(JSContext *ctx, const char *fmt, ...);
+static void js_print_atom(JSRuntime *rt, FILE *fo, JSAtom atom);
 static __maybe_unused void JS_DumpAtoms(JSRuntime *rt);
 static __maybe_unused void JS_DumpString(JSRuntime *rt, const JSString *p);
 static __maybe_unused void JS_DumpObjectHeader(JSRuntime *rt);
 static __maybe_unused void JS_DumpObject(JSRuntime *rt, JSObject *p);
 static __maybe_unused void JS_DumpGCObject(JSRuntime *rt, JSGCObjectHeader *p);
-static __maybe_unused void JS_DumpValueShort(JSRuntime *rt, JSValueConst val);
-static __maybe_unused void JS_DumpValue(JSContext *ctx, JSValueConst val);
-static __maybe_unused void JS_PrintValue(JSContext *ctx,
-                                                  const char *str,
-                                                  JSValueConst val);
+static __maybe_unused void JS_DumpValueRT(JSRuntime *rt, const char *str, JSValueConst val);
+static __maybe_unused void JS_DumpValue(JSContext *ctx, const char *str, JSValueConst val);
 static __maybe_unused void JS_DumpShapes(JSRuntime *rt);
 static JSValue js_function_apply(JSContext *ctx, JSValueConst this_val,
                                  int argc, JSValueConst *argv, int magic);
@@ -1261,6 +1280,12 @@ static void JS_RunGCInternal(JSRuntime *rt, BOOL remove_weak_objects);
 static JSValue js_array_from_iterator(JSContext *ctx, uint32_t *plen,
                                       JSValueConst obj, JSValueConst method);
 static int js_string_find_invalid_codepoint(JSString *p);
+static JSValue js_regexp_toString(JSContext *ctx, JSValueConst this_val,
+                                  int argc, JSValueConst *argv);
+static JSValue get_date_string(JSContext *ctx, JSValueConst this_val,
+                               int argc, JSValueConst *argv, int magic);
+static JSValue js_error_toString(JSContext *ctx, JSValueConst this_val,
+                                 int argc, JSValueConst *argv);
 
 static const JSClassExoticMethods js_arguments_exotic_methods;
 static const JSClassExoticMethods js_string_exotic_methods;
@@ -2405,18 +2430,18 @@ static uint32_t hash_string_rope(JSValueConst val, uint32_t h)
     }
 }
 
-static __maybe_unused void JS_DumpChar(JSRuntime *rt, int c, int sep)
+static __maybe_unused void JS_DumpChar(FILE *fo, int c, int sep)
 {
     if (c == sep || c == '\\') {
-        putchar('\\');
-        putchar(c);
+        fputc('\\', fo);
+        fputc(c, fo);
     } else if (c >= ' ' && c <= 126) {
-        putchar(c);
+        fputc(c, fo);
     } else if (c == '\n') {
-        putchar('\\');
-        putchar('n');
+        fputc('\\', fo);
+        fputc('n', fo);
     } else {
-        printf("\\u%04x", c);
+        fprintf(fo, "\\u%04x", c);
     }
 }
 
@@ -2432,7 +2457,7 @@ static __maybe_unused void JS_DumpString(JSRuntime *rt, const JSString *p)
     sep = (p->header.ref_count == 1) ? '\"' : '\'';
     putchar(sep);
     for(i = 0; i < p->len; i++) {
-        JS_DumpChar(rt, string_get(p, i), sep);
+        JS_DumpChar(stdout, string_get(p, i), sep);
     }
     putchar(sep);
 }
@@ -3178,40 +3203,7 @@ static BOOL JS_AtomSymbolHasDescription(JSContext *ctx, JSAtom v)
 
 static __maybe_unused void print_atom(JSContext *ctx, JSAtom atom)
 {
-    char buf[ATOM_GET_STR_BUF_SIZE];
-    const char *p;
-    int i;
-
-    /* XXX: should handle embedded null characters */
-    /* XXX: should move encoding code to JS_AtomGetStr */
-    p = JS_AtomGetStr(ctx, buf, sizeof(buf), atom);
-    for (i = 0; p[i]; i++) {
-        int c = (unsigned char)p[i];
-        if (!((c >= 'a' && c <= 'z') || (c >= 'A' && c <= 'Z') ||
-              (c == '_' || c == '$') || (c >= '0' && c <= '9' && i > 0)))
-            break;
-    }
-    if (i > 0 && p[i] == '\0') {
-        printf("%s", p);
-    } else {
-        putchar('"');
-        printf("%.*s", i, p);
-        for (; p[i]; i++) {
-            int c = (unsigned char)p[i];
-            if (c == '\"' || c == '\\') {
-                putchar('\\');
-                putchar(c);
-            } else if (c >= ' ' && c <= 126) {
-                putchar(c);
-            } else if (c == '\n') {
-                putchar('\\');
-                putchar('n');
-            } else {
-                printf("\\u%04x", c);
-            }
-        }
-        putchar('\"');
-    }
+    js_print_atom(ctx->rt, stdout, atom);
 }
 
 /* free with JS_FreeCString() */
@@ -12964,158 +12956,455 @@ static JSValue JS_ToQuotedString(JSContext *ctx, JSValueConst val1)
     return JS_EXCEPTION;
 }
 
-static __maybe_unused void JS_DumpObjectHeader(JSRuntime *rt)
+#define JS_PRINT_MAX_DEPTH 8
+
+typedef struct {
+    JSRuntime *rt;
+    JSContext *ctx; /* may be NULL */
+    JSPrintValueOptions options;
+    FILE *fo;
+    int level;
+    JSObject *print_stack[JS_PRINT_MAX_DEPTH]; /* level values */
+} JSPrintValueState;
+
+static void js_print_value(JSPrintValueState *s, JSValueConst val);
+
+static void js_print_float64(JSPrintValueState *s, double d)
 {
-    printf("%14s %4s %4s %14s %10s %s\n",
-           "ADDRESS", "REFS", "SHRF", "PROTO", "CLASS", "PROPS");
+    JSDTOATempMem dtoa_mem;
+    char buf[32];
+    int len;
+    len = js_dtoa(buf, d, 10, 0, JS_DTOA_FORMAT_FREE | JS_DTOA_MINUS_ZERO, &dtoa_mem);
+    fwrite(buf, 1, len, s->fo);
 }
 
-/* for debug only: dump an object without side effect */
-static __maybe_unused void JS_DumpObject(JSRuntime *rt, JSObject *p)
+static uint32_t js_string_get_length(JSValueConst val)
 {
-    uint32_t i;
-    char atom_buf[ATOM_GET_STR_BUF_SIZE];
+    if (JS_VALUE_GET_TAG(val) == JS_TAG_STRING) {
+        JSString *p = JS_VALUE_GET_STRING(val);
+        return p->len;
+    } else if (JS_VALUE_GET_TAG(val) == JS_TAG_STRING_ROPE) {
+        JSStringRope *r = JS_VALUE_GET_PTR(val);
+        return r->len;
+    } else {
+        return 0;
+    }
+}
+
+static void js_print_string_rec(JSPrintValueState *s, JSValueConst val,
+                                    int sep, uint32_t pos)
+{
+    if (JS_VALUE_GET_TAG(val) == JS_TAG_STRING) {
+        JSString *p = JS_VALUE_GET_STRING(val);
+        uint32_t i, len;
+        if (pos < s->options.max_string_length) {
+            len = min_uint32(p->len, s->options.max_string_length - pos);
+            for(i = 0; i < len; i++) {
+                JS_DumpChar(s->fo, string_get(p, i), sep);
+            }
+        }
+    } else if (JS_VALUE_GET_TAG(val) == JS_TAG_STRING_ROPE) {
+        JSStringRope *r = JS_VALUE_GET_PTR(val);
+        js_print_string_rec(s, r->left, sep, pos);
+        js_print_string_rec(s, r->right, sep, pos + js_string_get_length(r->left));
+    } else {
+        fprintf(s->fo, "<invalid string tag %d>", (int)JS_VALUE_GET_TAG(val));
+    }
+}
+
+static void js_print_string(JSPrintValueState *s, JSValueConst val)
+{
+    int sep;
+    if (s->options.raw_dump && JS_VALUE_GET_TAG(val) == JS_TAG_STRING) {
+        JSString *p = JS_VALUE_GET_STRING(val);
+        fprintf(s->fo, "%d", p->header.ref_count);
+        sep = (p->header.ref_count == 1) ? '\"' : '\'';
+    } else {
+        sep = '\"';
+    }
+    fputc(sep, s->fo);
+    js_print_string_rec(s, val, sep, 0);
+    fputc(sep, s->fo);
+    if (js_string_get_length(val) > s->options.max_string_length) {
+        uint32_t n = js_string_get_length(val) - s->options.max_string_length;
+        fprintf(s->fo, "... %u more character%s", n, n > 1 ? "s" : "");
+    }
+}
+
+static void js_print_raw_string2(JSPrintValueState *s, JSValueConst val, BOOL remove_last_lf)
+{
+    const char *cstr;
+    size_t len;
+    cstr = JS_ToCStringLen(s->ctx, &len, val);
+    if (cstr) {
+        if (remove_last_lf && len > 0 && cstr[len - 1] == '\n')
+            len--;
+        fwrite(cstr, 1, len, s->fo);
+        JS_FreeCString(s->ctx, cstr);
+    }
+}
+
+static void js_print_raw_string(JSPrintValueState *s, JSValueConst val)
+{
+    js_print_raw_string2(s, val, FALSE);
+}
+
+static BOOL is_ascii_ident(const JSString *p)
+{
+    int i, c;
+
+    if (p->len == 0)
+        return FALSE;
+    for(i = 0; i < p->len; i++) {
+        c = string_get(p, i);
+        if (!((c >= 'a' && c <= 'z') || (c >= 'A' && c <= 'Z') ||
+              (c == '_' || c == '$') || (c >= '0' && c <= '9' && i > 0)))
+            return FALSE;
+    }
+    return TRUE;
+}
+
+static void js_print_atom(JSRuntime *rt, FILE *fo, JSAtom atom)
+{
+    int i;
+    if (__JS_AtomIsTaggedInt(atom)) {
+        fprintf(fo, "%u", __JS_AtomToUInt32(atom));
+    } else if (atom == JS_ATOM_NULL) {
+        fprintf(fo, "<null>");
+    } else {
+        assert(atom < rt->atom_size);
+        JSString *p;
+        p = rt->atom_array[atom];
+        if (is_ascii_ident(p)) {
+            for(i = 0; i < p->len; i++) {
+                fputc(string_get(p, i), fo);
+            }
+        } else {
+            fputc('"', fo);
+            for(i = 0; i < p->len; i++) {
+                JS_DumpChar(fo, string_get(p, i), '\"');
+            }
+            fputc('"', fo);
+        }
+    }
+}
+
+/* return 0 if invalid length */
+static uint32_t js_print_array_get_length(JSObject *p)
+{
+    JSProperty *pr;
+    JSShapeProperty *prs;
+    JSValueConst val;
+
+    prs = find_own_property(&pr, p, JS_ATOM_length);
+    if (!prs)
+        return 0;
+    if ((prs->flags & JS_PROP_TMASK) != JS_PROP_NORMAL)
+        return 0;
+    val = pr->u.value;
+    switch(JS_VALUE_GET_NORM_TAG(val)) {
+    case JS_TAG_INT:
+        return JS_VALUE_GET_INT(val);
+    case JS_TAG_FLOAT64:
+        return (uint32_t)JS_VALUE_GET_FLOAT64(val);
+    default:
+        return 0;
+    }
+}
+
+static void js_print_comma(JSPrintValueState *s, int *pcomma_state)
+{
+    switch(*pcomma_state) {
+    case 0:
+        break;
+    case 1:
+        fprintf(s->fo, ", ");
+        break;
+    case 2:
+        fprintf(s->fo, " { ");
+        break;
+    }
+    *pcomma_state = 1;
+}
+
+static void js_print_more_items(JSPrintValueState *s, int *pcomma_state,
+                                uint32_t n)
+{
+    js_print_comma(s, pcomma_state);
+    fprintf(s->fo, "... %u more item%s", n, n > 1 ? "s" : "");
+}
+
+static void js_print_object(JSPrintValueState *s, JSObject *p)
+{
+    JSRuntime *rt = s->rt;
     JSShape *sh;
     JSShapeProperty *prs;
     JSProperty *pr;
-    BOOL is_first = TRUE;
+    int comma_state;
+    BOOL is_array;
+    uint32_t i;
+    
+    comma_state = 0;
+    is_array = FALSE;
+    if (p->class_id == JS_CLASS_ARRAY) {
+        is_array = TRUE;
+        fprintf(s->fo, "[ ");
+        /* XXX: print array like properties even if not fast array */
+        if (p->fast_array) {
+            uint32_t len, n, len1;
+            len = js_print_array_get_length(p);
 
-    /* XXX: should encode atoms with special characters */
-    sh = p->shape; /* the shape can be NULL while freeing an object */
-    printf("%14p %4d ",
-           (void *)p,
-           p->header.ref_count);
-    if (sh) {
-        printf("%3d%c %14p ",
-               sh->header.ref_count,
-               " *"[sh->is_hashed],
-               (void *)sh->proto);
-    } else {
-        printf("%3s  %14s ", "-", "-");
-    }
-    printf("%10s ",
-           JS_AtomGetStrRT(rt, atom_buf, sizeof(atom_buf), rt->class_array[p->class_id].class_name));
-    if (p->is_exotic && p->fast_array) {
-        printf("[ ");
-        for(i = 0; i < p->u.array.count; i++) {
-            if (i != 0)
-                printf(", ");
-            switch (p->class_id) {
-            case JS_CLASS_ARRAY:
-            case JS_CLASS_ARGUMENTS:
-                JS_DumpValueShort(rt, p->u.array.u.values[i]);
-                break;
+            len1 = min_uint32(p->u.array.count, s->options.max_item_count);
+            for(i = 0; i < len1; i++) {
+                js_print_comma(s, &comma_state);
+                js_print_value(s, p->u.array.u.values[i]);
+            }
+            if (len1 < p->u.array.count)
+                js_print_more_items(s, &comma_state, p->u.array.count - len1);
+            if (p->u.array.count < len) {
+                n = len - p->u.array.count;
+                js_print_comma(s, &comma_state);
+                fprintf(s->fo, "<%u empty item%s>", n, n > 1 ? "s" : "");
+            }
+        }
+    } else if (p->class_id >= JS_CLASS_UINT8C_ARRAY && p->class_id <= JS_CLASS_FLOAT64_ARRAY) {
+        uint32_t size = 1 << typed_array_size_log2(p->class_id);
+        uint32_t len1;
+        int64_t v;
+
+        js_print_atom(s->rt, s->fo, rt->class_array[p->class_id].class_name);
+        fprintf(s->fo, "(%u) [ ", p->u.array.count);
+        
+        is_array = TRUE;
+        len1 = min_uint32(p->u.array.count, s->options.max_item_count);
+        for(i = 0; i < len1; i++) {
+            const uint8_t *ptr = p->u.array.u.uint8_ptr + i * size;
+            js_print_comma(s, &comma_state);
+            switch(p->class_id) {
             case JS_CLASS_UINT8C_ARRAY:
-            case JS_CLASS_INT8_ARRAY:
             case JS_CLASS_UINT8_ARRAY:
+                v = *ptr;
+                goto ta_int64;
+            case JS_CLASS_INT8_ARRAY:
+                v = *(int8_t *)ptr;
+                goto ta_int64;
             case JS_CLASS_INT16_ARRAY:
+                v = *(int16_t *)ptr;
+                goto ta_int64;
             case JS_CLASS_UINT16_ARRAY:
+                v = *(uint16_t *)ptr;
+                goto ta_int64;
             case JS_CLASS_INT32_ARRAY:
+                v = *(int32_t *)ptr;
+                goto ta_int64;
             case JS_CLASS_UINT32_ARRAY:
+                v = *(uint32_t *)ptr;
+                goto ta_int64;
             case JS_CLASS_BIG_INT64_ARRAY:
+                v = *(int64_t *)ptr;
+            ta_int64:
+                fprintf(s->fo, "%" PRId64, v);
+                break;
             case JS_CLASS_BIG_UINT64_ARRAY:
+                fprintf(s->fo, "%" PRIu64, *(uint64_t *)ptr);
+                break;
             case JS_CLASS_FLOAT32_ARRAY:
+                js_print_float64(s, *(float *)ptr);
+                break;
             case JS_CLASS_FLOAT64_ARRAY:
-                {
-                    int size = 1 << typed_array_size_log2(p->class_id);
-                    const uint8_t *b = p->u.array.u.uint8_ptr + i * size;
-                    while (size-- > 0)
-                        printf("%02X", *b++);
-                }
+                js_print_float64(s, *(double *)ptr);
                 break;
             }
         }
-        printf(" ] ");
+        if (len1 < p->u.array.count)
+            js_print_more_items(s, &comma_state, p->u.array.count - len1);
+    } else if (p->class_id == JS_CLASS_BYTECODE_FUNCTION ||
+               (rt->class_array[p->class_id].call != NULL &&
+                p->class_id != JS_CLASS_PROXY)) {
+        fprintf(s->fo, "[Function");
+        /* XXX: allow dump without ctx */
+        if (!s->options.raw_dump && s->ctx) {
+            const char *func_name_str;
+            fputc(' ', s->fo);
+            func_name_str = get_func_name(s->ctx, JS_MKPTR(JS_TAG_OBJECT, p));
+            if (!func_name_str || func_name_str[0] == '\0')
+                fputs("(anonymous)", s->fo);
+            else
+                fputs(func_name_str, s->fo);
+            JS_FreeCString(s->ctx, func_name_str);
+        }
+        fprintf(s->fo, "]");
+        comma_state = 2;
+    } else if (p->class_id == JS_CLASS_MAP || p->class_id == JS_CLASS_SET) {
+        JSMapState *ms = p->u.opaque;
+        struct list_head *el;
+        
+        if (!ms)
+            goto default_obj;
+        js_print_atom(s->rt, s->fo, rt->class_array[p->class_id].class_name);
+        fprintf(s->fo, "(%u) { ", ms->record_count);
+        i = 0;
+        list_for_each(el, &ms->records) {
+            JSMapRecord *mr = list_entry(el, JSMapRecord, link);
+            js_print_comma(s, &comma_state);
+            if (mr->empty)
+                continue;
+            js_print_value(s, mr->key);
+            if (p->class_id == JS_CLASS_MAP) {
+                fprintf(s->fo, " => ");
+                js_print_value(s, mr->value);
+            }
+            i++;
+            if (i >= s->options.max_item_count)
+                break;
+        }
+        if (i < ms->record_count)
+            js_print_more_items(s, &comma_state, ms->record_count - i);
+    } else if (p->class_id == JS_CLASS_REGEXP && s->ctx && !s->options.raw_dump) {
+        JSValue str = js_regexp_toString(s->ctx, JS_MKPTR(JS_TAG_OBJECT, p), 0, NULL);
+        if (JS_IsException(str))
+            goto default_obj;
+        js_print_raw_string(s, str);
+        JS_FreeValueRT(s->rt, str);
+        comma_state = 2;
+    } else if (p->class_id == JS_CLASS_DATE && s->ctx && !s->options.raw_dump) {
+        JSValue str = get_date_string(s->ctx, JS_MKPTR(JS_TAG_OBJECT, p), 0, NULL, 0x23); /* toISOString() */
+        if (JS_IsException(str))
+            goto default_obj;
+        js_print_raw_string(s, str);
+        JS_FreeValueRT(s->rt, str);
+        comma_state = 2;
+    } else if (p->class_id == JS_CLASS_ERROR && s->ctx && !s->options.raw_dump) {
+        JSValue str = js_error_toString(s->ctx, JS_MKPTR(JS_TAG_OBJECT, p), 0, NULL);
+        if (JS_IsException(str))
+            goto default_obj;
+        js_print_raw_string(s, str);
+        JS_FreeValueRT(s->rt, str);
+        /* dump the stack if present */
+        str = JS_GetProperty(s->ctx, JS_MKPTR(JS_TAG_OBJECT, p), JS_ATOM_stack);
+        if (JS_IsString(str)) {
+            fputc('\n', s->fo);
+            /* XXX: should remove the last '\n' in stack as
+               v8. SpiderMonkey does not do it */
+            js_print_raw_string2(s, str, TRUE);
+        }
+        JS_FreeValueRT(s->rt, str);
+        comma_state = 2;
+    } else {
+        default_obj:
+        if (p->class_id != JS_CLASS_OBJECT) {
+            js_print_atom(s->rt, s->fo, rt->class_array[p->class_id].class_name);
+            fprintf(s->fo, " ");
+        }
+        fprintf(s->fo, "{ ");
     }
-
+    
+    sh = p->shape; /* the shape can be NULL while freeing an object */
     if (sh) {
-        printf("{ ");
+        uint32_t j;
+        
+        j = 0;
         for(i = 0, prs = get_shape_prop(sh); i < sh->prop_count; i++, prs++) {
             if (prs->atom != JS_ATOM_NULL) {
-                pr = &p->prop[i];
-                if (!is_first)
-                    printf(", ");
-                printf("%s: ",
-                       JS_AtomGetStrRT(rt, atom_buf, sizeof(atom_buf), prs->atom));
-                if ((prs->flags & JS_PROP_TMASK) == JS_PROP_GETSET) {
-                    printf("[getset %p %p]", (void *)pr->u.getset.getter,
-                           (void *)pr->u.getset.setter);
-                } else if ((prs->flags & JS_PROP_TMASK) == JS_PROP_VARREF) {
-                    printf("[varref %p]", (void *)pr->u.var_ref);
-                } else if ((prs->flags & JS_PROP_TMASK) == JS_PROP_AUTOINIT) {
-                    printf("[autoinit %p %d %p]",
-                           (void *)js_autoinit_get_realm(pr),
-                           js_autoinit_get_id(pr),
-                           (void *)pr->u.init.opaque);
-                } else {
-                    JS_DumpValueShort(rt, pr->u.value);
+                if (!(prs->flags & JS_PROP_ENUMERABLE) &&
+                    !s->options.show_hidden) {
+                    continue;
                 }
-                is_first = FALSE;
+                if (j < s->options.max_item_count) {
+                    pr = &p->prop[i];
+                    js_print_comma(s, &comma_state);
+                    js_print_atom(s->rt, s->fo, prs->atom);
+                    fprintf(s->fo, ": ");
+                    
+                    /* XXX: autoinit property */
+                    if ((prs->flags & JS_PROP_TMASK) == JS_PROP_GETSET) {
+                        if (s->options.raw_dump) {
+                            fprintf(s->fo, "[Getter %p Setter %p]",
+                                    pr->u.getset.getter, pr->u.getset.setter);
+                        } else {
+                            if (pr->u.getset.getter && pr->u.getset.setter) {
+                                fprintf(s->fo, "[Getter/Setter]");
+                            } else if (pr->u.getset.setter) {
+                                fprintf(s->fo, "[Setter]");
+                            } else {
+                                fprintf(s->fo, "[Getter]");
+                            }
+                        }
+                    } else if ((prs->flags & JS_PROP_TMASK) == JS_PROP_VARREF) {
+                        if (s->options.raw_dump) {
+                            fprintf(s->fo, "[varref %p]", (void *)pr->u.var_ref);
+                        } else {
+                            js_print_value(s, *pr->u.var_ref->pvalue);
+                        }
+                    } else if ((prs->flags & JS_PROP_TMASK) == JS_PROP_AUTOINIT) {
+                        if (s->options.raw_dump) {
+                            fprintf(s->fo, "[autoinit %p %d %p]",
+                                    (void *)js_autoinit_get_realm(pr),
+                                    js_autoinit_get_id(pr),
+                                    (void *)pr->u.init.opaque);
+                        } else {
+                            /* XXX: could autoinit but need to restart
+                               the iteration */
+                            fprintf(s->fo, "[autoinit]");
+                        }
+                    } else {
+                        js_print_value(s, pr->u.value);
+                    }
+                }
+                j++;
             }
         }
-        printf(" }");
+        if (j > s->options.max_item_count)
+            js_print_more_items(s, &comma_state, j - s->options.max_item_count);
     }
-
-    if (js_class_has_bytecode(p->class_id)) {
+    if (s->options.show_closure && js_class_has_bytecode(p->class_id)) {
         JSFunctionBytecode *b = p->u.func.function_bytecode;
-        JSVarRef **var_refs;
         if (b->closure_var_count) {
+            JSVarRef **var_refs;
             var_refs = p->u.func.var_refs;
-            printf(" Closure:");
+            
+            js_print_comma(s, &comma_state);
+            fprintf(s->fo, "[[Closure]]: [");
             for(i = 0; i < b->closure_var_count; i++) {
-                printf(" ");
-                JS_DumpValueShort(rt, var_refs[i]->value);
-            }
-            if (p->u.func.home_object) {
-                printf(" HomeObject: ");
-                JS_DumpValueShort(rt, JS_MKPTR(JS_TAG_OBJECT, p->u.func.home_object));
+                if (i != 0)
+                    fprintf(s->fo, ", ");
+                js_print_value(s, var_refs[i]->value);
             }
+            fprintf(s->fo, " ]");
+        }
+        if (p->u.func.home_object) {
+            js_print_comma(s, &comma_state);
+            fprintf(s->fo, "[[HomeObject]]: ");
+            js_print_value(s, JS_MKPTR(JS_TAG_OBJECT, p->u.func.home_object));
         }
     }
-    printf("\n");
-}
 
-static __maybe_unused void JS_DumpGCObject(JSRuntime *rt, JSGCObjectHeader *p)
-{
-    if (p->gc_obj_type == JS_GC_OBJ_TYPE_JS_OBJECT) {
-        JS_DumpObject(rt, (JSObject *)p);
-    } else {
-        printf("%14p %4d ",
-               (void *)p,
-               p->ref_count);
-        switch(p->gc_obj_type) {
-        case JS_GC_OBJ_TYPE_FUNCTION_BYTECODE:
-            printf("[function bytecode]");
-            break;
-        case JS_GC_OBJ_TYPE_SHAPE:
-            printf("[shape]");
-            break;
-        case JS_GC_OBJ_TYPE_VAR_REF:
-            printf("[var_ref]");
-            break;
-        case JS_GC_OBJ_TYPE_ASYNC_FUNCTION:
-            printf("[async_function]");
-            break;
-        case JS_GC_OBJ_TYPE_JS_CONTEXT:
-            printf("[js_context]");
-            break;
-        default:
-            printf("[unknown %d]", p->gc_obj_type);
-            break;
+    if (!is_array) {
+        if (comma_state != 2) {
+            fprintf(s->fo, " }");
         }
-        printf("\n");
+    } else {
+        fprintf(s->fo, " ]");
     }
 }
 
-static __maybe_unused void JS_DumpValueShort(JSRuntime *rt,
-                                                      JSValueConst val)
+static int js_print_stack_index(JSPrintValueState *s, JSObject *p)
+{
+    int i;
+    for(i = 0; i < s->level; i++)
+        if (s->print_stack[i] == p)
+            return i;
+    return -1;
+}
+
+static void js_print_value(JSPrintValueState *s, JSValueConst val)
 {
     uint32_t tag = JS_VALUE_GET_NORM_TAG(val);
     const char *str;
 
     switch(tag) {
     case JS_TAG_INT:
-        printf("%d", JS_VALUE_GET_INT(val));
+        fprintf(s->fo, "%d", JS_VALUE_GET_INT(val));
         break;
     case JS_TAG_BOOL:
         if (JS_VALUE_GET_BOOL(val))
@@ -13135,98 +13424,224 @@ static __maybe_unused void JS_DumpValueShort(JSRuntime *rt,
     case JS_TAG_UNDEFINED:
         str = "undefined";
     print_str:
-        printf("%s", str);
+        fprintf(s->fo, "%s", str);
         break;
     case JS_TAG_FLOAT64:
-        printf("%.14g", JS_VALUE_GET_FLOAT64(val));
+        js_print_float64(s, JS_VALUE_GET_FLOAT64(val));
         break;
     case JS_TAG_SHORT_BIG_INT:
-        printf("%" PRId64 "n", (int64_t)JS_VALUE_GET_SHORT_BIG_INT(val));
+        fprintf(s->fo, "%" PRId64 "n", (int64_t)JS_VALUE_GET_SHORT_BIG_INT(val));
         break;
     case JS_TAG_BIG_INT:
-        {
-            JSBigInt *p = JS_VALUE_GET_PTR(val);
+        if (!s->options.raw_dump && s->ctx) {
+            JSValue str = js_bigint_to_string(s->ctx, val);
+            if (JS_IsException(str))
+                goto raw_bigint;
+            js_print_raw_string(s, str);
+            fputc('n', s->fo);
+            JS_FreeValueRT(s->rt, str);
+        } else {
+            JSBigInt *p;
             int sgn, i;
+        raw_bigint:
+            p = JS_VALUE_GET_PTR(val);
             /* In order to avoid allocations we just dump the limbs */
             sgn = js_bigint_sign(p);
             if (sgn)
-                printf("BigInt.asIntN(%d,", p->len * JS_LIMB_BITS);
-            printf("0x");
+                fprintf(s->fo, "BigInt.asIntN(%d,", p->len * JS_LIMB_BITS);
+            fprintf(s->fo, "0x");
             for(i = p->len - 1; i >= 0; i--) {
                 if (i != p->len - 1)
-                    printf("_");
+                    fprintf(s->fo, "_");
 #if JS_LIMB_BITS == 32
-                printf("%08x", p->tab[i]);
+                fprintf(s->fo, "%08x", p->tab[i]);
 #else
-                printf("%016" PRIx64, p->tab[i]);
+                fprintf(s->fo, "%016" PRIx64, p->tab[i]);
 #endif
             }
-            printf("n");
+            fprintf(s->fo, "n");
             if (sgn)
-                printf(")");
+                fprintf(s->fo, ")");
         }
         break;
     case JS_TAG_STRING:
-        {
-            JSString *p;
-            p = JS_VALUE_GET_STRING(val);
-            JS_DumpString(rt, p);
-        }
-        break;
     case JS_TAG_STRING_ROPE:
-        {
+        if (s->options.raw_dump && tag == JS_TAG_STRING_ROPE) {
             JSStringRope *r = JS_VALUE_GET_STRING_ROPE(val);
-            printf("[rope len=%d depth=%d]", r->len, r->depth);
+            fprintf(s->fo, "[rope len=%d depth=%d]", r->len, r->depth);
+        } else {
+            js_print_string(s, val);
         }
         break;
     case JS_TAG_FUNCTION_BYTECODE:
         {
             JSFunctionBytecode *b = JS_VALUE_GET_PTR(val);
-            char buf[ATOM_GET_STR_BUF_SIZE];
-            printf("[bytecode %s]", JS_AtomGetStrRT(rt, buf, sizeof(buf), b->func_name));
+            fprintf(s->fo, "[bytecode ");
+            js_print_atom(s->rt, s->fo, b->func_name);
+            fprintf(s->fo, "]");
         }
         break;
     case JS_TAG_OBJECT:
         {
             JSObject *p = JS_VALUE_GET_OBJ(val);
-            JSAtom atom = rt->class_array[p->class_id].class_name;
-            char atom_buf[ATOM_GET_STR_BUF_SIZE];
-            printf("[%s %p]",
-                   JS_AtomGetStrRT(rt, atom_buf, sizeof(atom_buf), atom), (void *)p);
+            int idx;
+            idx = js_print_stack_index(s, p);
+            if (idx >= 0) {
+                fprintf(s->fo, "[circular %d]", idx);
+            } else if (s->level < s->options.max_depth) {
+                s->print_stack[s->level++] = p;
+                js_print_object(s, JS_VALUE_GET_OBJ(val));
+                s->level--;
+            } else {
+                JSAtom atom = s->rt->class_array[p->class_id].class_name;
+                fprintf(s->fo, "[");
+                js_print_atom(s->rt, s->fo, atom);
+                if (s->options.raw_dump) {
+                    fprintf(s->fo, " %p", (void *)p);
+                }
+                fprintf(s->fo, "]");
+            }
         }
         break;
     case JS_TAG_SYMBOL:
         {
             JSAtomStruct *p = JS_VALUE_GET_PTR(val);
-            char atom_buf[ATOM_GET_STR_BUF_SIZE];
-            printf("Symbol(%s)",
-                   JS_AtomGetStrRT(rt, atom_buf, sizeof(atom_buf), js_get_atom_index(rt, p)));
+            fprintf(s->fo, "Symbol(");
+            js_print_atom(s->rt, s->fo, js_get_atom_index(s->rt, p));
+            fprintf(s->fo, ")");
         }
         break;
     case JS_TAG_MODULE:
-        printf("[module]");
+        fprintf(s->fo, "[module]");
         break;
     default:
-        printf("[unknown tag %d]", tag);
+        fprintf(s->fo, "[unknown tag %d]", tag);
         break;
     }
 }
 
-static __maybe_unused void JS_DumpValue(JSContext *ctx,
-                                                 JSValueConst val)
+void JS_PrintValueSetDefaultOptions(JSPrintValueOptions *options)
 {
-    JS_DumpValueShort(ctx->rt, val);
+    memset(options, 0, sizeof(*options));
+    options->max_depth = 2;
+    options->max_string_length = 1000;
+    options->max_item_count = 100;
 }
 
-static __maybe_unused void JS_PrintValue(JSContext *ctx,
-                                                  const char *str,
-                                                  JSValueConst val)
+static void JS_PrintValueInternal(JSRuntime *rt, JSContext *ctx,
+                                  FILE *fo, JSValueConst val, const JSPrintValueOptions *options)
+{
+    JSPrintValueState ss, *s = &ss;
+    if (options)
+        s->options = *options;
+    else
+        JS_PrintValueSetDefaultOptions(&s->options);
+    if (s->options.max_depth <= 0)
+        s->options.max_depth = JS_PRINT_MAX_DEPTH;
+    else
+        s->options.max_depth = min_int(s->options.max_depth, JS_PRINT_MAX_DEPTH);
+    if (s->options.max_string_length == 0)
+        s->options.max_string_length = UINT32_MAX;
+    if (s->options.max_item_count == 0)
+        s->options.max_item_count = UINT32_MAX;
+    s->rt = rt;
+    s->ctx = ctx;
+    s->fo = fo;
+    s->level = 0;
+    js_print_value(s, val);
+}
+
+void JS_PrintValueRT(JSRuntime *rt, FILE *fo, JSValueConst val, const JSPrintValueOptions *options)
+{
+    JS_PrintValueInternal(rt, NULL, fo, val, options);
+}
+
+void JS_PrintValue(JSContext *ctx, FILE *fo, JSValueConst val, const JSPrintValueOptions *options)
+{
+    JS_PrintValueInternal(ctx->rt, ctx, fo, val, options);
+}
+
+static __maybe_unused void JS_DumpValue(JSContext *ctx, const char *str, JSValueConst val)
+{
+    printf("%s=", str);
+    JS_PrintValue(ctx, stdout, val, NULL);
+    printf("\n");
+}
+
+static __maybe_unused void JS_DumpValueRT(JSRuntime *rt, const char *str, JSValueConst val)
 {
     printf("%s=", str);
-    JS_DumpValueShort(ctx->rt, val);
+    JS_PrintValueRT(rt, stdout, val, NULL);
+    printf("\n");
+}
+
+static __maybe_unused void JS_DumpObjectHeader(JSRuntime *rt)
+{
+    printf("%14s %4s %4s %14s %s\n",
+           "ADDRESS", "REFS", "SHRF", "PROTO", "CONTENT");
+}
+
+/* for debug only: dump an object without side effect */
+static __maybe_unused void JS_DumpObject(JSRuntime *rt, JSObject *p)
+{
+    JSShape *sh;
+    JSPrintValueOptions options;
+    
+    /* XXX: should encode atoms with special characters */
+    sh = p->shape; /* the shape can be NULL while freeing an object */
+    printf("%14p %4d ",
+           (void *)p,
+           p->header.ref_count);
+    if (sh) {
+        printf("%3d%c %14p ",
+               sh->header.ref_count,
+               " *"[sh->is_hashed],
+               (void *)sh->proto);
+    } else {
+        printf("%3s  %14s ", "-", "-");
+    }
+
+    JS_PrintValueSetDefaultOptions(&options);
+    options.max_depth = 1;
+    options.show_hidden = TRUE;
+    options.raw_dump = TRUE;
+    JS_PrintValueRT(rt, stdout, JS_MKPTR(JS_TAG_OBJECT, p), &options);
+
     printf("\n");
 }
 
+static __maybe_unused void JS_DumpGCObject(JSRuntime *rt, JSGCObjectHeader *p)
+{
+    if (p->gc_obj_type == JS_GC_OBJ_TYPE_JS_OBJECT) {
+        JS_DumpObject(rt, (JSObject *)p);
+    } else {
+        printf("%14p %4d ",
+               (void *)p,
+               p->ref_count);
+        switch(p->gc_obj_type) {
+        case JS_GC_OBJ_TYPE_FUNCTION_BYTECODE:
+            printf("[function bytecode]");
+            break;
+        case JS_GC_OBJ_TYPE_SHAPE:
+            printf("[shape]");
+            break;
+        case JS_GC_OBJ_TYPE_VAR_REF:
+            printf("[var_ref]");
+            break;
+        case JS_GC_OBJ_TYPE_ASYNC_FUNCTION:
+            printf("[async_function]");
+            break;
+        case JS_GC_OBJ_TYPE_JS_CONTEXT:
+            printf("[js_context]");
+            break;
+        default:
+            printf("[unknown %d]", p->gc_obj_type);
+            break;
+        }
+        printf("\n");
+    }
+}
+
 /* return -1 if exception (proxy case) or TRUE/FALSE */
 // TODO: should take flags to make proxy resolution and exceptions optional
 int JS_IsArray(JSContext *ctx, JSValueConst val)
@@ -47150,27 +47565,6 @@ static const JSCFunctionListEntry js_symbol_funcs[] = {
 
 /* Set/Map/WeakSet/WeakMap */
 
-typedef struct JSMapRecord {
-    int ref_count; /* used during enumeration to avoid freeing the record */
-    BOOL empty : 8; /* TRUE if the record is deleted */
-    struct list_head link;
-    struct JSMapRecord *hash_next;
-    JSValue key;
-    JSValue value;
-} JSMapRecord;
-
-typedef struct JSMapState {
-    BOOL is_weak; /* TRUE if WeakSet/WeakMap */
-    struct list_head records; /* list of JSMapRecord.link */
-    uint32_t record_count;
-    JSMapRecord **hash_table;
-    int hash_bits;
-    uint32_t hash_size; /* = 2 ^ hash_bits */
-    uint32_t record_count_threshold; /* count at which a hash table
-                                        resize is needed */
-    JSWeakRefHeader weakref_header; /* only used if is_weak = TRUE */
-} JSMapState;
-
 static BOOL js_weakref_is_target(JSValueConst val)
 {
     switch (JS_VALUE_GET_TAG(val)) {
index adc86208f635ec20e8b11a7882c4a5c01e7de334..b851cd9ca73b5487c829cb0fb8ae95199e2320f1 100644 (file)
--- a/quickjs.h
+++ b/quickjs.h
@@ -1113,6 +1113,23 @@ int JS_SetModuleExport(JSContext *ctx, JSModuleDef *m, const char *export_name,
 int JS_SetModuleExportList(JSContext *ctx, JSModuleDef *m,
                            const JSCFunctionListEntry *tab, int len);
 
+/* debug value output */
+
+typedef struct {
+    JS_BOOL show_hidden : 8; /* only show enumerable properties */
+    JS_BOOL show_closure : 8; /* show closure variables */
+    JS_BOOL raw_dump : 8; /* avoid doing autoinit and avoid any malloc() call (for internal use) */
+    uint32_t max_depth; /* recurse up to this depth, 0 = no limit */
+    uint32_t max_string_length; /* print no more than this length for
+                                   strings, 0 = no limit */
+    uint32_t max_item_count; /*  print no more than this count for
+                                 arrays or objects, 0 = no limit */
+} JSPrintValueOptions;
+
+void JS_PrintValueSetDefaultOptions(JSPrintValueOptions *options);
+void JS_PrintValueRT(JSRuntime *rt, FILE *fo, JSValueConst val, const JSPrintValueOptions *options);
+void JS_PrintValue(JSContext *ctx, FILE *fo, JSValueConst val, const JSPrintValueOptions *options);
+
 #undef js_unlikely
 #undef js_force_inline
 
diff --git a/repl.js b/repl.js
index c65100d77592bdedf044e7e00141f3d6b4e36753..06d4f2629df233aa23a0b26e1a87c6b42b1bc900 100644 (file)
--- a/repl.js
+++ b/repl.js
@@ -875,126 +875,19 @@ import * as os from "os";
     }
 
     var hex_mode = false;
-    var eval_mode = "std";
 
-    function number_to_string(a, radix) {
+    function number_to_string_hex(a) {
         var s;
-        if (!isFinite(a)) {
-            /* NaN, Infinite */
-            return a.toString();
+        if (a < 0) {
+            a = -a;
+            s = "-";
         } else {
-            if (a == 0) {
-                if (1 / a < 0)
-                    s = "-0";
-                else
-                    s = "0";
-            } else {
-                if (radix == 16 && a === Math.floor(a)) {
-                    var s;
-                    if (a < 0) {
-                        a = -a;
-                        s = "-";
-                    } else {
-                        s = "";
-                    }
-                    s += "0x" + a.toString(16);
-                } else {
-                    s = a.toString();
-                }
-            }
-            return s;
-        }
-    }
-
-    function bigint_to_string(a, radix) {
-        var s;
-        if (radix == 16) {
-            var s;
-            if (a < 0) {
-                a = -a;
-                s = "-";
-            } else {
-                s = "";
-            }
-            s += "0x" + a.toString(16);
-        } else {
-            s = a.toString();
+            s = "";
         }
-        if (eval_mode === "std")
-            s += "n";
+        s += "0x" + a.toString(16);
         return s;
     }
-
-    function print(a) {
-        var stack = [];
-
-        function print_rec(a) {
-            var n, i, keys, key, type, s;
-
-            type = typeof(a);
-            if (type === "object") {
-                if (a === null) {
-                    std.puts(a);
-                } else if (stack.indexOf(a) >= 0) {
-                    std.puts("[circular]");
-                } else if (a instanceof Date) {
-                    std.puts("Date " + a.toGMTString().__quote());
-                } else {
-                    stack.push(a);
-                    if (Array.isArray(a)) {
-                        n = a.length;
-                        std.puts("[ ");
-                        for(i = 0; i < n; i++) {
-                            if (i !== 0)
-                                std.puts(", ");
-                            if (i in a) {
-                                print_rec(a[i]);
-                            } else {
-                                std.puts("<empty>");
-                            }
-                            if (i > 20) {
-                                std.puts("...");
-                                break;
-                            }
-                        }
-                        std.puts(" ]");
-                    } else if (Object.__getClass(a) === "RegExp") {
-                        std.puts(a.toString());
-                    } else {
-                        keys = Object.keys(a);
-                        n = keys.length;
-                        std.puts("{ ");
-                        for(i = 0; i < n; i++) {
-                            if (i !== 0)
-                                std.puts(", ");
-                            key = keys[i];
-                            std.puts(key, ": ");
-                            print_rec(a[key]);
-                        }
-                        std.puts(" }");
-                    }
-                    stack.pop(a);
-                }
-            } else if (type === "string") {
-                s = a.__quote();
-                if (s.length > 79)
-                    s = s.substring(0, 75) + "...\"";
-                std.puts(s);
-            } else if (type === "number") {
-                std.puts(number_to_string(a, hex_mode ? 16 : 10));
-            } else if (type === "bigint") {
-                std.puts(bigint_to_string(a, hex_mode ? 16 : 10));
-            } else if (type === "symbol") {
-                std.puts(String(a));
-            } else if (type === "function") {
-                std.puts("function " + a.name + "()");
-            } else {
-                std.puts(a);
-            }
-        }
-        print_rec(a);
-    }
-
+    
     function extract_directive(a) {
         var pos;
         if (a[0] !== '\\')
@@ -1116,10 +1009,25 @@ import * as os from "os";
     }
 
     function print_eval_result(result) {
+        var default_print = true;
+
         result = result.value;
         eval_time = os.now() - eval_start_time;
         std.puts(colors[styles.result]);
-        print(result);
+        if (hex_mode) {
+            if (typeof result == "number" &&
+                result === Math.floor(result)) {
+                std.puts(number_to_string_hex(result));
+                default_print = false;
+            } else if (typeof result == "bigint") {
+                std.puts(number_to_string_hex(result));
+                std.puts("n");
+                default_print = false;
+            }
+        }
+        if (default_print) {
+            std.__printObject(result);
+        }
         std.puts("\n");
         std.puts(colors.none);
         /* set the last result */
@@ -1130,15 +1038,10 @@ import * as os from "os";
 
     function print_eval_error(error) {
         std.puts(colors[styles.error_msg]);
-        if (error instanceof Error) {
-            console.log(error);
-            if (error.stack) {
-                std.puts(error.stack);
-            }
-        } else {
+        if (!(error instanceof Error))
             std.puts("Throw: ");
-            console.log(error);
-        }
+        std.__printObject(error);
+        std.puts("\n");
         std.puts(colors.none);
 
         handle_cmd_end();
index 4397a1d841a56a24ce20fb72372563cd704010e0..d1fa4f30a9973346d8cdd1d74e083f3e401dbb01 100644 (file)
@@ -376,22 +376,29 @@ static JSValue js_print(JSContext *ctx, JSValueConst this_val,
                         int argc, JSValueConst *argv)
 {
     int i;
-    const char *str;
-
+    JSValueConst v;
+    
     if (outfile) {
         for (i = 0; i < argc; i++) {
             if (i != 0)
                 fputc(' ', outfile);
-            str = JS_ToCString(ctx, argv[i]);
-            if (!str)
-                return JS_EXCEPTION;
-            if (!strcmp(str, "Test262:AsyncTestComplete")) {
-                async_done++;
-            } else if (strstart(str, "Test262:AsyncTestFailure", NULL)) {
-                async_done = 2; /* force an error */
+            v = argv[i];
+            if (JS_IsString(v)) {
+                const char *str;
+                size_t len;
+                str = JS_ToCStringLen(ctx, &len, v);
+                if (!str)
+                    return JS_EXCEPTION;
+                if (!strcmp(str, "Test262:AsyncTestComplete")) {
+                    async_done++;
+                } else if (strstart(str, "Test262:AsyncTestFailure", NULL)) {
+                    async_done = 2; /* force an error */
+                }
+                fwrite(str, 1, len, outfile);
+                JS_FreeCString(ctx, str);
+            } else {
+                JS_PrintValue(ctx, outfile, v, NULL);
             }
-            fputs(str, outfile);
-            JS_FreeCString(ctx, str);
         }
         fputc('\n', outfile);
     }