summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorFabrice Bellard <fabrice@bellard.org>2025-04-30 13:40:15 +0200
committerFabrice Bellard <fabrice@bellard.org>2025-04-30 13:40:15 +0200
commitbe06b3e92b77a343adefa49a4b29e1ad523014ea (patch)
tree4bb72313c0e0dffa830e8504db969a21efa1f5e8
parent30fe3de91dd49bc6f725bbae24095c99fe7d001f (diff)
downloadquickjs-be06b3e92b77a343adefa49a4b29e1ad523014ea.tar.gz
quickjs-be06b3e92b77a343adefa49a4b29e1ad523014ea.zip
added JS_PrintValue() and use it in console.log(), print() and the REPL (#256)
-rw-r--r--quickjs-libc.c57
-rw-r--r--quickjs.c816
-rw-r--r--quickjs.h17
-rw-r--r--repl.js149
-rw-r--r--run-test262.c29
5 files changed, 690 insertions, 378 deletions
diff --git a/quickjs-libc.c b/quickjs-libc.c
index 091fd42..b59c6ac 100644
--- a/quickjs-libc.c
+++ b/quickjs-libc.c
@@ -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)
diff --git a/quickjs.c b/quickjs.c
index aa174e9..428288b 100644
--- 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)) {
diff --git a/quickjs.h b/quickjs.h
index adc8620..b851cd9 100644
--- 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 c65100d..06d4f26 100644
--- 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();
diff --git a/run-test262.c b/run-test262.c
index 4397a1d..d1fa4f3 100644
--- a/run-test262.c
+++ b/run-test262.c
@@ -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);
}