DUK_INTERNAL duk_hstring *duk_js_typeof(duk_hthread *thr, duk_tval *tv_x) { duk_small_int_t stridx = 0; switch (DUK_TVAL_GET_TAG(tv_x)) { case DUK_TAG_UNDEFINED: { stridx = DUK_STRIDX_LC_UNDEFINED; break; } case DUK_TAG_NULL: { /* Note: not a typo, "object" is returned for a null value */ stridx = DUK_STRIDX_LC_OBJECT; break; } case DUK_TAG_BOOLEAN: { stridx = DUK_STRIDX_LC_BOOLEAN; break; } case DUK_TAG_POINTER: { /* implementation specific */ stridx = DUK_STRIDX_LC_POINTER; break; } case DUK_TAG_STRING: { stridx = DUK_STRIDX_LC_STRING; break; } case DUK_TAG_OBJECT: { duk_hobject *obj = DUK_TVAL_GET_OBJECT(tv_x); DUK_ASSERT(obj != NULL); if (DUK_HOBJECT_IS_CALLABLE(obj)) { stridx = DUK_STRIDX_LC_FUNCTION; } else { stridx = DUK_STRIDX_LC_OBJECT; } break; } case DUK_TAG_BUFFER: { /* implementation specific */ stridx = DUK_STRIDX_LC_BUFFER; break; } case DUK_TAG_LIGHTFUNC: { stridx = DUK_STRIDX_LC_FUNCTION; break; } #if defined(DUK_USE_FASTINT) case DUK_TAG_FASTINT: #endif default: { /* number */ DUK_ASSERT(DUK_TVAL_IS_NUMBER(tv_x)); stridx = DUK_STRIDX_LC_NUMBER; break; } } DUK_ASSERT(stridx >= 0 && stridx < DUK_HEAP_NUM_STRINGS); return DUK_HTHREAD_GET_STRING(thr, stridx); }
/* * CheckObjectCoercible() (E5 Section 9.10) * * Note: no API equivalent now. */ #if 0 /* unused */ DUK_INTERNAL void duk_js_checkobjectcoercible(duk_hthread *thr, duk_tval *tv_x) { duk_small_uint_t tag = DUK_TVAL_GET_TAG(tv_x); /* Note: this must match ToObject() behavior */ if (tag == DUK_TAG_UNDEFINED || tag == DUK_TAG_NULL || tag == DUK_TAG_POINTER || tag == DUK_TAG_BUFFER) { DUK_ERROR(thr, DUK_ERR_TYPE_ERROR, "not object coercible"); } }
DUK_INTERNAL duk_bool_t duk_js_toboolean(duk_tval *tv) { switch (DUK_TVAL_GET_TAG(tv)) { case DUK_TAG_UNDEFINED: case DUK_TAG_NULL: return 0; case DUK_TAG_BOOLEAN: return DUK_TVAL_GET_BOOLEAN(tv); case DUK_TAG_STRING: { duk_hstring *h = DUK_TVAL_GET_STRING(tv); DUK_ASSERT(h != NULL); return (DUK_HSTRING_GET_BYTELEN(h) > 0 ? 1 : 0); } case DUK_TAG_OBJECT: { return 1; } case DUK_TAG_BUFFER: { /* mimic semantics for strings */ duk_hbuffer *h = DUK_TVAL_GET_BUFFER(tv); DUK_ASSERT(h != NULL); return (DUK_HBUFFER_GET_SIZE(h) > 0 ? 1 : 0); } case DUK_TAG_POINTER: { void *p = DUK_TVAL_GET_POINTER(tv); return (p != NULL ? 1 : 0); } case DUK_TAG_LIGHTFUNC: { return 1; } #if defined(DUK_USE_FASTINT) case DUK_TAG_FASTINT: if (DUK_TVAL_GET_FASTINT(tv) != 0) { return 1; } else { return 0; } #endif default: { /* number */ duk_double_t d; int c; DUK_ASSERT(!DUK_TVAL_IS_UNUSED(tv)); DUK_ASSERT(DUK_TVAL_IS_DOUBLE(tv)); d = DUK_TVAL_GET_DOUBLE(tv); c = DUK_FPCLASSIFY((double) d); if (c == DUK_FP_ZERO || c == DUK_FP_NAN) { return 0; } else { return 1; } } } DUK_UNREACHABLE(); }
duk_hstring *duk_js_typeof(duk_hthread *thr, duk_tval *tv_x) { int idx = 0; switch (DUK_TVAL_GET_TAG(tv_x)) { case DUK_TAG_UNDEFINED: { idx = DUK_STRIDX_LC_UNDEFINED; break; } case DUK_TAG_NULL: { /* Note: not a typo, "object" is returned for a null value */ idx = DUK_STRIDX_LC_OBJECT; break; } case DUK_TAG_BOOLEAN: { idx = DUK_STRIDX_LC_BOOLEAN; break; } case DUK_TAG_POINTER: { /* implementation specific */ idx = DUK_STRIDX_LC_POINTER; break; } case DUK_TAG_STRING: { idx = DUK_STRIDX_LC_STRING; break; } case DUK_TAG_OBJECT: { duk_hobject *obj = DUK_TVAL_GET_OBJECT(tv_x); DUK_ASSERT(obj != NULL); if (DUK_HOBJECT_IS_CALLABLE(obj)) { idx = DUK_STRIDX_LC_FUNCTION; } else { idx = DUK_STRIDX_LC_OBJECT; } break; } case DUK_TAG_BUFFER: { /* implementation specific */ idx = DUK_STRIDX_LC_BUFFER; break; } default: { /* number */ DUK_ASSERT(DUK_TVAL_IS_NUMBER(tv_x)); idx = DUK_STRIDX_LC_NUMBER; break; } } DUK_ASSERT(idx >= 0 && idx < DUK_HEAP_NUM_STRINGS); return thr->strs[idx]; }
/* Shared helper to implement Object.getPrototypeOf, * Object.prototype.__proto__ getter, and Reflect.getPrototypeOf. * * http://www.ecma-international.org/ecma-262/6.0/index.html#sec-get-object.prototype.__proto__ */ DUK_INTERNAL duk_ret_t duk_bi_object_getprototype_shared(duk_context *ctx) { /* * magic = 0: __proto__ getter * magic = 1: Object.getPrototypeOf() * magic = 2: Reflect.getPrototypeOf() */ duk_hthread *thr = (duk_hthread *) ctx; duk_hobject *h; duk_hobject *proto; duk_tval *tv; duk_int_t magic; magic = duk_get_current_magic(ctx); if (magic == 0) { DUK_ASSERT_TOP(ctx, 0); duk_push_this_coercible_to_object(ctx); } DUK_ASSERT(duk_get_top(ctx) >= 1); if (magic < 2) { /* ES2015 Section 19.1.2.9, step 1 */ duk_to_object(ctx, 0); } tv = DUK_GET_TVAL_POSIDX(ctx, 0); switch (DUK_TVAL_GET_TAG(tv)) { case DUK_TAG_BUFFER: proto = thr->builtins[DUK_BIDX_UINT8ARRAY_PROTOTYPE]; break; case DUK_TAG_LIGHTFUNC: proto = thr->builtins[DUK_BIDX_FUNCTION_PROTOTYPE]; break; case DUK_TAG_OBJECT: h = DUK_TVAL_GET_OBJECT(tv); proto = DUK_HOBJECT_GET_PROTOTYPE(thr->heap, h); break; default: /* This implicitly handles CheckObjectCoercible() caused * TypeError. */ DUK_DCERROR_TYPE_INVALID_ARGS(thr); } if (proto != NULL) { duk_push_hobject(ctx, proto); } else { duk_push_null(ctx); } return 1; }
/* for thread dumping */ static char duk__get_tval_summary_char(duk_tval *tv) { switch (DUK_TVAL_GET_TAG(tv)) { case DUK_TAG_UNDEFINED: if (DUK_TVAL_IS_UNDEFINED_UNUSED(tv)) { return '.'; } return 'u'; case DUK_TAG_NULL: return 'n'; case DUK_TAG_BOOLEAN: return 'b'; case DUK_TAG_STRING: return 's'; case DUK_TAG_OBJECT: { duk_hobject *h = DUK_TVAL_GET_OBJECT(tv); if (DUK_HOBJECT_IS_ARRAY(h)) { return 'A'; } else if (DUK_HOBJECT_IS_COMPILEDFUNCTION(h)) { return 'C'; } else if (DUK_HOBJECT_IS_NATIVEFUNCTION(h)) { return 'N'; } else if (DUK_HOBJECT_IS_THREAD(h)) { return 'T'; } return 'O'; } case DUK_TAG_BUFFER: { return 'B'; } case DUK_TAG_POINTER: { return 'P'; } default: DUK_ASSERT(DUK_TVAL_IS_NUMBER(tv)); return 'd'; } DUK_UNREACHABLE(); }
duk_bool_t duk_js_toboolean(duk_tval *tv) { switch (DUK_TVAL_GET_TAG(tv)) { case DUK_TAG_UNDEFINED: case DUK_TAG_NULL: return 0; case DUK_TAG_BOOLEAN: return DUK_TVAL_GET_BOOLEAN(tv); case DUK_TAG_STRING: { duk_hstring *h = DUK_TVAL_GET_STRING(tv); DUK_ASSERT(h != NULL); return (h->blen > 0 ? 1 : 0); } case DUK_TAG_OBJECT: { return 1; } case DUK_TAG_BUFFER: { /* mimic semantics for strings */ duk_hbuffer *h = DUK_TVAL_GET_BUFFER(tv); DUK_ASSERT(h != NULL); return (DUK_HBUFFER_GET_SIZE(h) > 0 ? 1 : 0); } case DUK_TAG_POINTER: { void *p = DUK_TVAL_GET_POINTER(tv); return (p != NULL ? 1 : 0); } default: { /* number */ int c; DUK_ASSERT(DUK_TVAL_IS_NUMBER(tv)); c = DUK_FPCLASSIFY(DUK_TVAL_GET_NUMBER(tv)); if (c == DUK_FP_ZERO || c == DUK_FP_NAN) { return 0; } else { return 1; } } } DUK_UNREACHABLE(); }
DUK_INTERNAL duk_bool_t duk_js_equals_helper(duk_hthread *thr, duk_tval *tv_x, duk_tval *tv_y, duk_small_int_t flags) { duk_context *ctx = (duk_context *) thr; duk_tval *tv_tmp; /* If flags != 0 (strict or SameValue), thr can be NULL. For loose * equals comparison it must be != NULL. */ DUK_ASSERT(flags != 0 || thr != NULL); /* * Same type? * * Note: since number values have no explicit tag in the 8-byte * representation, need the awkward if + switch. */ #if defined(DUK_USE_FASTINT) if (DUK_TVAL_IS_FASTINT(tv_x) && DUK_TVAL_IS_FASTINT(tv_y)) { if (DUK_TVAL_GET_FASTINT(tv_x) == DUK_TVAL_GET_FASTINT(tv_y)) { return 1; } else { return 0; } } else #endif if (DUK_TVAL_IS_NUMBER(tv_x) && DUK_TVAL_IS_NUMBER(tv_y)) { /* Catches both doubles and cases where only one argument is a fastint */ if (DUK_UNLIKELY((flags & DUK_EQUALS_FLAG_SAMEVALUE) != 0)) { /* SameValue */ return duk__js_samevalue_number(DUK_TVAL_GET_NUMBER(tv_x), DUK_TVAL_GET_NUMBER(tv_y)); } else { /* equals and strict equals */ return duk__js_equals_number(DUK_TVAL_GET_NUMBER(tv_x), DUK_TVAL_GET_NUMBER(tv_y)); } } else if (DUK_TVAL_GET_TAG(tv_x) == DUK_TVAL_GET_TAG(tv_y)) { switch (DUK_TVAL_GET_TAG(tv_x)) { case DUK_TAG_UNDEFINED: case DUK_TAG_NULL: { return 1; } case DUK_TAG_BOOLEAN: { return DUK_TVAL_GET_BOOLEAN(tv_x) == DUK_TVAL_GET_BOOLEAN(tv_y); } case DUK_TAG_POINTER: { return DUK_TVAL_GET_POINTER(tv_x) == DUK_TVAL_GET_POINTER(tv_y); } case DUK_TAG_STRING: case DUK_TAG_OBJECT: { /* heap pointer comparison suffices */ return DUK_TVAL_GET_HEAPHDR(tv_x) == DUK_TVAL_GET_HEAPHDR(tv_y); } case DUK_TAG_BUFFER: { if ((flags & (DUK_EQUALS_FLAG_STRICT | DUK_EQUALS_FLAG_SAMEVALUE)) != 0) { /* heap pointer comparison suffices */ return DUK_TVAL_GET_HEAPHDR(tv_x) == DUK_TVAL_GET_HEAPHDR(tv_y); } else { /* non-strict equality for buffers compares contents */ duk_hbuffer *h_x = DUK_TVAL_GET_BUFFER(tv_x); duk_hbuffer *h_y = DUK_TVAL_GET_BUFFER(tv_y); duk_size_t len_x = DUK_HBUFFER_GET_SIZE(h_x); duk_size_t len_y = DUK_HBUFFER_GET_SIZE(h_y); void *buf_x; void *buf_y; if (len_x != len_y) { return 0; } buf_x = (void *) DUK_HBUFFER_GET_DATA_PTR(thr->heap, h_x); buf_y = (void *) DUK_HBUFFER_GET_DATA_PTR(thr->heap, h_y); /* if len_x == len_y == 0, buf_x and/or buf_y may * be NULL, but that's OK. */ DUK_ASSERT(len_x == len_y); DUK_ASSERT(len_x == 0 || buf_x != NULL); DUK_ASSERT(len_y == 0 || buf_y != NULL); return (DUK_MEMCMP(buf_x, buf_y, len_x) == 0) ? 1 : 0; } } case DUK_TAG_LIGHTFUNC: { /* At least 'magic' has a significant impact on function * identity. */ duk_small_uint_t lf_flags_x; duk_small_uint_t lf_flags_y; duk_c_function func_x; duk_c_function func_y; DUK_TVAL_GET_LIGHTFUNC(tv_x, func_x, lf_flags_x); DUK_TVAL_GET_LIGHTFUNC(tv_y, func_y, lf_flags_y); return ((func_x == func_y) && (lf_flags_x == lf_flags_y)) ? 1 : 0; } #if defined(DUK_USE_FASTINT) case DUK_TAG_FASTINT: #endif default: { DUK_ASSERT(DUK_TVAL_IS_NUMBER(tv_x)); DUK_ASSERT(DUK_TVAL_IS_NUMBER(tv_y)); DUK_UNREACHABLE(); return 0; } } } if ((flags & (DUK_EQUALS_FLAG_STRICT | DUK_EQUALS_FLAG_SAMEVALUE)) != 0) { return 0; } DUK_ASSERT(flags == 0); /* non-strict equality from here on */ /* * Types are different; various cases for non-strict comparison * * Since comparison is symmetric, we use a "swap trick" to reduce * code size. */ /* Undefined/null are considered equal (e.g. "null == undefined" -> true). */ if ((DUK_TVAL_IS_UNDEFINED(tv_x) && DUK_TVAL_IS_NULL(tv_y)) || (DUK_TVAL_IS_NULL(tv_x) && DUK_TVAL_IS_UNDEFINED(tv_y))) { return 1; } /* Number/string-or-buffer -> coerce string to number (e.g. "'1.5' == 1.5" -> true). */ if (DUK_TVAL_IS_NUMBER(tv_x) && (DUK_TVAL_IS_STRING(tv_y) || DUK_TVAL_IS_BUFFER(tv_y))) { /* the next 'if' is guaranteed to match after swap */ tv_tmp = tv_x; tv_x = tv_y; tv_y = tv_tmp; } if ((DUK_TVAL_IS_STRING(tv_x) || DUK_TVAL_IS_BUFFER(tv_x)) && DUK_TVAL_IS_NUMBER(tv_y)) { /* XXX: this is possible without resorting to the value stack */ duk_double_t d1, d2; d2 = DUK_TVAL_GET_NUMBER(tv_y); duk_push_tval(ctx, tv_x); duk_to_string(ctx, -1); /* buffer values are coerced first to string here */ duk_to_number(ctx, -1); d1 = duk_require_number(ctx, -1); duk_pop(ctx); return duk__js_equals_number(d1, d2); } /* Buffer/string -> compare contents. */ if (DUK_TVAL_IS_BUFFER(tv_x) && DUK_TVAL_IS_STRING(tv_y)) { tv_tmp = tv_x; tv_x = tv_y; tv_y = tv_tmp; } if (DUK_TVAL_IS_STRING(tv_x) && DUK_TVAL_IS_BUFFER(tv_y)) { duk_hstring *h_x = DUK_TVAL_GET_STRING(tv_x); duk_hbuffer *h_y = DUK_TVAL_GET_BUFFER(tv_y); duk_size_t len_x = DUK_HSTRING_GET_BYTELEN(h_x); duk_size_t len_y = DUK_HBUFFER_GET_SIZE(h_y); void *buf_x; void *buf_y; if (len_x != len_y) { return 0; } buf_x = (void *) DUK_HSTRING_GET_DATA(h_x); buf_y = (void *) DUK_HBUFFER_GET_DATA_PTR(thr->heap, h_y); /* if len_x == len_y == 0, buf_x and/or buf_y may * be NULL, but that's OK. */ DUK_ASSERT(len_x == len_y); DUK_ASSERT(len_x == 0 || buf_x != NULL); DUK_ASSERT(len_y == 0 || buf_y != NULL); return (DUK_MEMCMP(buf_x, buf_y, len_x) == 0) ? 1 : 0; } /* Boolean/any -> coerce boolean to number and try again. If boolean is * compared to a pointer, the final comparison after coercion now always * yields false (as pointer vs. number compares to false), but this is * not special cased. */ if (DUK_TVAL_IS_BOOLEAN(tv_x)) { tv_tmp = tv_x; tv_x = tv_y; tv_y = tv_tmp; } if (DUK_TVAL_IS_BOOLEAN(tv_y)) { /* ToNumber(bool) is +1.0 or 0.0. Tagged boolean value is always 0 or 1. */ duk_bool_t rc; DUK_ASSERT(DUK_TVAL_GET_BOOLEAN(tv_y) == 0 || DUK_TVAL_GET_BOOLEAN(tv_y) == 1); duk_push_tval(ctx, tv_x); duk_push_int(ctx, DUK_TVAL_GET_BOOLEAN(tv_y)); rc = duk_js_equals_helper(thr, duk_get_tval(ctx, -2), duk_get_tval(ctx, -1), 0 /*flags:nonstrict*/); duk_pop_2(ctx); return rc; } /* String-number-buffer/object -> coerce object to primitive (apparently without hint), then try again. */ if ((DUK_TVAL_IS_STRING(tv_x) || DUK_TVAL_IS_NUMBER(tv_x) || DUK_TVAL_IS_BUFFER(tv_x)) && DUK_TVAL_IS_OBJECT(tv_y)) { tv_tmp = tv_x; tv_x = tv_y; tv_y = tv_tmp; } if (DUK_TVAL_IS_OBJECT(tv_x) && (DUK_TVAL_IS_STRING(tv_y) || DUK_TVAL_IS_NUMBER(tv_y) || DUK_TVAL_IS_BUFFER(tv_y))) { duk_bool_t rc; duk_push_tval(ctx, tv_x); duk_push_tval(ctx, tv_y); duk_to_primitive(ctx, -2, DUK_HINT_NONE); /* apparently no hint? */ rc = duk_js_equals_helper(thr, duk_get_tval(ctx, -2), duk_get_tval(ctx, -1), 0 /*flags:nonstrict*/); duk_pop_2(ctx); return rc; } /* Nothing worked -> not equal. */ return 0; }
DUK_INTERNAL duk_double_t duk_js_tonumber(duk_hthread *thr, duk_tval *tv) { duk_context *ctx = (duk_hthread *) thr; DUK_ASSERT(thr != NULL); DUK_ASSERT(tv != NULL); switch (DUK_TVAL_GET_TAG(tv)) { case DUK_TAG_UNDEFINED: { /* return a specific NaN (although not strictly necessary) */ duk_double_union du; DUK_DBLUNION_SET_NAN(&du); DUK_ASSERT(DUK_DBLUNION_IS_NORMALIZED(&du)); return du.d; } case DUK_TAG_NULL: { /* +0.0 */ return 0.0; } case DUK_TAG_BOOLEAN: { if (DUK_TVAL_IS_BOOLEAN_TRUE(tv)) { return 1.0; } return 0.0; } case DUK_TAG_STRING: { duk_hstring *h = DUK_TVAL_GET_STRING(tv); duk_push_hstring(ctx, h); return duk__tonumber_string_raw(thr); } case DUK_TAG_OBJECT: { /* Note: ToPrimitive(object,hint) == [[DefaultValue]](object,hint), * so use [[DefaultValue]] directly. */ duk_double_t d; duk_push_tval(ctx, tv); duk_to_defaultvalue(ctx, -1, DUK_HINT_NUMBER); /* 'tv' becomes invalid */ /* recursive call for a primitive value (guaranteed not to cause second * recursion). */ d = duk_js_tonumber(thr, duk_require_tval(ctx, -1)); duk_pop(ctx); return d; } case DUK_TAG_BUFFER: { /* Coerce like a string. This makes sense because addition also treats * buffers like strings. */ duk_hbuffer *h = DUK_TVAL_GET_BUFFER(tv); duk_push_hbuffer(ctx, h); duk_to_string(ctx, -1); /* XXX: expensive, but numconv now expects to see a string */ return duk__tonumber_string_raw(thr); } case DUK_TAG_POINTER: { /* Coerce like boolean */ void *p = DUK_TVAL_GET_POINTER(tv); return (p != NULL ? 1.0 : 0.0); } case DUK_TAG_LIGHTFUNC: { /* +(function(){}) -> NaN */ return DUK_DOUBLE_NAN; } #if defined(DUK_USE_FASTINT) case DUK_TAG_FASTINT: return (duk_double_t) DUK_TVAL_GET_FASTINT(tv); #endif default: { /* number */ DUK_ASSERT(DUK_TVAL_IS_DOUBLE(tv)); return DUK_TVAL_GET_DOUBLE(tv); } } DUK_UNREACHABLE(); }
DUK_LOCAL void duk__print_tval(duk__dprint_state *st, duk_tval *tv) { duk_fixedbuffer *fb = st->fb; if (duk_fb_is_full(fb)) { return; } /* depth check is done when printing an actual type */ if (st->heavy) { duk_fb_sprintf(fb, "(%p)", (void *) tv); } if (!tv) { duk_fb_put_cstring(fb, "NULL"); return; } if (st->binary) { duk_size_t i; duk_fb_put_byte(fb, (duk_uint8_t) DUK_ASC_LBRACKET); for (i = 0; i < (duk_size_t) sizeof(*tv); i++) { duk_fb_sprintf(fb, "%02lx", (unsigned long) ((duk_uint8_t *)tv)[i]); } duk_fb_put_byte(fb, (duk_uint8_t) DUK_ASC_RBRACKET); } if (st->heavy) { duk_fb_put_byte(fb, (duk_uint8_t) DUK_ASC_LANGLE); } switch (DUK_TVAL_GET_TAG(tv)) { case DUK_TAG_UNDEFINED: { duk_fb_put_cstring(fb, "undefined"); break; } case DUK_TAG_UNUSED: { duk_fb_put_cstring(fb, "unused"); break; } case DUK_TAG_NULL: { duk_fb_put_cstring(fb, "null"); break; } case DUK_TAG_BOOLEAN: { duk_fb_put_cstring(fb, DUK_TVAL_GET_BOOLEAN(tv) ? "true" : "false"); break; } case DUK_TAG_STRING: { /* Note: string is a terminal heap object, so no depth check here */ duk__print_hstring(st, DUK_TVAL_GET_STRING(tv), 1); break; } case DUK_TAG_OBJECT: { duk__print_hobject(st, DUK_TVAL_GET_OBJECT(tv)); break; } case DUK_TAG_BUFFER: { duk__print_hbuffer(st, DUK_TVAL_GET_BUFFER(tv)); break; } case DUK_TAG_POINTER: { duk_fb_sprintf(fb, "pointer:%p", (void *) DUK_TVAL_GET_POINTER(tv)); break; } case DUK_TAG_LIGHTFUNC: { duk_c_function func; duk_small_uint_t lf_flags; DUK_TVAL_GET_LIGHTFUNC(tv, func, lf_flags); duk_fb_sprintf(fb, "lightfunc:"); duk_fb_put_funcptr(fb, (duk_uint8_t *) &func, sizeof(func)); duk_fb_sprintf(fb, ":%04lx", (long) lf_flags); break; } #if defined(DUK_USE_FASTINT) case DUK_TAG_FASTINT: #endif default: { /* IEEE double is approximately 16 decimal digits; print a couple extra */ DUK_ASSERT(!DUK_TVAL_IS_UNUSED(tv)); DUK_ASSERT(DUK_TVAL_IS_NUMBER(tv)); duk_fb_sprintf(fb, "%.18g", (double) DUK_TVAL_GET_NUMBER(tv)); break; } } if (st->heavy) { duk_fb_put_byte(fb, (duk_uint8_t) DUK_ASC_RANGLE); } }
/* Raw helper to extract internal information / statistics about a value. * The return values are version specific and must not expose anything * that would lead to security issues (e.g. exposing compiled function * 'data' buffer might be an issue). Currently only counts and sizes and * such are given so there should not be a security impact. */ DUK_INTERNAL duk_ret_t duk_bi_duktape_object_info(duk_context *ctx) { duk_hthread *thr = (duk_hthread *) ctx; duk_tval *tv; duk_heaphdr *h; duk_int_t i, n; DUK_UNREF(thr); /* result array */ duk_push_array(ctx); /* -> [ val arr ] */ /* type tag (public) */ duk_push_int(ctx, duk_get_type(ctx, 0)); /* address */ tv = duk_get_tval(ctx, 0); DUK_ASSERT(tv != NULL); /* because arg count is 1 */ if (DUK_TVAL_IS_HEAP_ALLOCATED(tv)) { h = DUK_TVAL_GET_HEAPHDR(tv); duk_push_pointer(ctx, (void *) h); } else { /* internal type tag */ duk_push_int(ctx, (duk_int_t) DUK_TVAL_GET_TAG(tv)); goto done; } DUK_ASSERT(h != NULL); /* refcount */ #ifdef DUK_USE_REFERENCE_COUNTING duk_push_size_t(ctx, DUK_HEAPHDR_GET_REFCOUNT(h)); #else duk_push_undefined(ctx); #endif /* heaphdr size and additional allocation size, followed by * type specific stuff (with varying value count) */ switch ((duk_small_int_t) DUK_HEAPHDR_GET_TYPE(h)) { case DUK_HTYPE_STRING: { duk_hstring *h_str = (duk_hstring *) h; duk_push_uint(ctx, (duk_uint_t) (sizeof(duk_hstring) + DUK_HSTRING_GET_BYTELEN(h_str) + 1)); break; } case DUK_HTYPE_OBJECT: { duk_hobject *h_obj = (duk_hobject *) h; duk_small_uint_t hdr_size; if (DUK_HOBJECT_IS_COMPILEDFUNCTION(h_obj)) { hdr_size = (duk_small_uint_t) sizeof(duk_hcompiledfunction); } else if (DUK_HOBJECT_IS_NATIVEFUNCTION(h_obj)) { hdr_size = (duk_small_uint_t) sizeof(duk_hnativefunction); } else if (DUK_HOBJECT_IS_THREAD(h_obj)) { hdr_size = (duk_small_uint_t) sizeof(duk_hthread); #if defined(DUK_USE_BUFFEROBJECT_SUPPORT) } else if (DUK_HOBJECT_IS_BUFFEROBJECT(h_obj)) { hdr_size = (duk_small_uint_t) sizeof(duk_hbufferobject); #endif } else { hdr_size = (duk_small_uint_t) sizeof(duk_hobject); } duk_push_uint(ctx, (duk_uint_t) hdr_size); duk_push_uint(ctx, (duk_uint_t) DUK_HOBJECT_P_ALLOC_SIZE(h_obj)); duk_push_uint(ctx, (duk_uint_t) DUK_HOBJECT_GET_ESIZE(h_obj)); /* Note: e_next indicates the number of gc-reachable entries * in the entry part, and also indicates the index where the * next new property would be inserted. It does *not* indicate * the number of non-NULL keys present in the object. That * value could be counted separately but requires a pass through * the key list. */ duk_push_uint(ctx, (duk_uint_t) DUK_HOBJECT_GET_ENEXT(h_obj)); duk_push_uint(ctx, (duk_uint_t) DUK_HOBJECT_GET_ASIZE(h_obj)); duk_push_uint(ctx, (duk_uint_t) DUK_HOBJECT_GET_HSIZE(h_obj)); if (DUK_HOBJECT_IS_COMPILEDFUNCTION(h_obj)) { duk_hbuffer *h_data = (duk_hbuffer *) DUK_HCOMPILEDFUNCTION_GET_DATA(thr->heap, (duk_hcompiledfunction *) h_obj); if (h_data) { duk_push_uint(ctx, (duk_uint_t) DUK_HBUFFER_GET_SIZE(h_data)); } else { duk_push_uint(ctx, 0); } } break; } case DUK_HTYPE_BUFFER: { duk_hbuffer *h_buf = (duk_hbuffer *) h; if (DUK_HBUFFER_HAS_DYNAMIC(h_buf)) { if (DUK_HBUFFER_HAS_EXTERNAL(h_buf)) { duk_push_uint(ctx, (duk_uint_t) (sizeof(duk_hbuffer_external))); } else { /* When alloc_size == 0 the second allocation may not * actually exist. */ duk_push_uint(ctx, (duk_uint_t) (sizeof(duk_hbuffer_dynamic))); } duk_push_uint(ctx, (duk_uint_t) (DUK_HBUFFER_GET_SIZE(h_buf))); } else { duk_push_uint(ctx, (duk_uint_t) (sizeof(duk_hbuffer_fixed) + DUK_HBUFFER_GET_SIZE(h_buf) + 1)); } break; } } done: /* set values into ret array */ /* XXX: primitive to make array from valstack slice */ n = duk_get_top(ctx); for (i = 2; i < n; i++) { duk_dup(ctx, i); duk_put_prop_index(ctx, 1, i - 2); } duk_dup(ctx, 1); return 1; }