diff options
Diffstat (limited to 'quickjs.c')
-rw-r--r-- | quickjs.c | 816 |
1 files changed, 605 insertions, 211 deletions
@@ -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)) { |