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_LOCAL duk_uint8_t *duk__dump_string_prop(duk_hthread *thr, duk_uint8_t *p, duk_bufwriter_ctx *bw_ctx, duk_hobject *func, duk_small_uint_t stridx) { duk_hstring *h_str; duk_tval *tv; tv = duk_hobject_find_existing_entry_tval_ptr(thr->heap, (duk_hobject *) func, DUK_HTHREAD_GET_STRING(thr, stridx)); if (tv != NULL && DUK_TVAL_IS_STRING(tv)) { h_str = DUK_TVAL_GET_STRING(tv); DUK_ASSERT(h_str != NULL); } else { h_str = DUK_HTHREAD_STRING_EMPTY_STRING(thr); DUK_ASSERT(h_str != NULL); } DUK_ASSERT(DUK_HSTRING_MAX_BYTELEN <= 0x7fffffffUL); /* ensures no overflow */ p = DUK_BW_ENSURE_RAW(thr, bw_ctx, 4 + DUK_HSTRING_GET_BYTELEN(h_str), p); p = duk__dump_hstring_raw(p, h_str); return p; }
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_LOCAL duk_uint8_t *duk__dump_formals(duk_hthread *thr, duk_uint8_t *p, duk_bufwriter_ctx *bw_ctx, duk_hobject *func) { duk_tval *tv; tv = duk_hobject_find_existing_entry_tval_ptr(thr->heap, (duk_hobject *) func, DUK_HTHREAD_STRING_INT_FORMALS(thr)); if (tv != NULL && DUK_TVAL_IS_OBJECT(tv)) { duk_hobject *h; duk_uint_fast32_t i; h = DUK_TVAL_GET_OBJECT(tv); DUK_ASSERT(h != NULL); /* We know _Formals is dense and all entries will be in the * array part. GC and finalizers shouldn't affect _Formals * so side effects should be fine. */ for (i = 0; i < (duk_uint_fast32_t) DUK_HOBJECT_GET_ASIZE(h); i++) { duk_tval *tv_val; duk_hstring *varname; tv_val = DUK_HOBJECT_A_GET_VALUE_PTR(thr->heap, h, i); DUK_ASSERT(tv_val != NULL); if (DUK_TVAL_IS_STRING(tv_val)) { /* Array is dense and contains only strings, but ASIZE may * be larger than used part and there are UNUSED entries. */ varname = DUK_TVAL_GET_STRING(tv_val); DUK_ASSERT(varname != NULL); DUK_ASSERT(DUK_HSTRING_MAX_BYTELEN <= 0x7fffffffUL); /* ensures no overflow */ p = DUK_BW_ENSURE_RAW(thr, bw_ctx, 4 + DUK_HSTRING_GET_BYTELEN(varname), p); p = duk__dump_hstring_raw(p, varname); } } } p = DUK_BW_ENSURE_RAW(thr, bw_ctx, 4, p); DUK_RAW_WRITE_U32_BE(p, 0); /* end of _Formals */ return p; }
DUK_INTERNAL duk_bool_t duk_js_compare_helper(duk_hthread *thr, duk_tval *tv_x, duk_tval *tv_y, duk_small_int_t flags) { duk_context *ctx = (duk_context *) thr; duk_double_t d1, d2; duk_small_int_t c1, c2; duk_small_int_t s1, s2; duk_small_int_t rc; duk_bool_t retval; /* Fast path for fastints */ #if defined(DUK_USE_FASTINT) if (DUK_TVAL_IS_FASTINT(tv_x) && DUK_TVAL_IS_FASTINT(tv_y)) { duk_int64_t v1 = DUK_TVAL_GET_FASTINT(tv_x); duk_int64_t v2 = DUK_TVAL_GET_FASTINT(tv_y); if (v1 < v2) { /* 'lt is true' */ retval = 1; } else { retval = 0; } if (flags & DUK_COMPARE_FLAG_NEGATE) { retval ^= 1; } return retval; } #endif /* DUK_USE_FASTINT */ /* Fast path for numbers (one of which may be a fastint) */ #if 1 /* XXX: make fast paths optional for size minimization? */ if (DUK_TVAL_IS_NUMBER(tv_x) && DUK_TVAL_IS_NUMBER(tv_y)) { d1 = DUK_TVAL_GET_NUMBER(tv_x); d2 = DUK_TVAL_GET_NUMBER(tv_y); c1 = DUK_FPCLASSIFY(d1); c2 = DUK_FPCLASSIFY(d2); if (c1 == DUK_FP_NORMAL && c2 == DUK_FP_NORMAL) { /* XXX: this is a very narrow check, and doesn't cover * zeroes, subnormals, infinities, which compare normally. */ if (d1 < d2) { /* 'lt is true' */ retval = 1; } else { retval = 0; } if (flags & DUK_COMPARE_FLAG_NEGATE) { retval ^= 1; } return retval; } } #endif /* Slow path */ duk_push_tval(ctx, tv_x); duk_push_tval(ctx, tv_y); if (flags & DUK_COMPARE_FLAG_EVAL_LEFT_FIRST) { duk_to_primitive(ctx, -2, DUK_HINT_NUMBER); duk_to_primitive(ctx, -1, DUK_HINT_NUMBER); } else { duk_to_primitive(ctx, -1, DUK_HINT_NUMBER); duk_to_primitive(ctx, -2, DUK_HINT_NUMBER); } /* Note: reuse variables */ tv_x = duk_get_tval(ctx, -2); tv_y = duk_get_tval(ctx, -1); if (DUK_TVAL_IS_STRING(tv_x) && DUK_TVAL_IS_STRING(tv_y)) { duk_hstring *h1 = DUK_TVAL_GET_STRING(tv_x); duk_hstring *h2 = DUK_TVAL_GET_STRING(tv_y); DUK_ASSERT(h1 != NULL); DUK_ASSERT(h2 != NULL); rc = duk_js_string_compare(h1, h2); if (rc < 0) { goto lt_true; } else { goto lt_false; } } else { /* Ordering should not matter (E5 Section 11.8.5, step 3.a) but * preserve it just in case. */ if (flags & DUK_COMPARE_FLAG_EVAL_LEFT_FIRST) { d1 = duk_to_number(ctx, -2); d2 = duk_to_number(ctx, -1); } else { d2 = duk_to_number(ctx, -1); d1 = duk_to_number(ctx, -2); } c1 = (duk_small_int_t) DUK_FPCLASSIFY(d1); s1 = (duk_small_int_t) DUK_SIGNBIT(d1); c2 = (duk_small_int_t) DUK_FPCLASSIFY(d2); s2 = (duk_small_int_t) DUK_SIGNBIT(d2); if (c1 == DUK_FP_NAN || c2 == DUK_FP_NAN) { goto lt_undefined; } if (c1 == DUK_FP_ZERO && c2 == DUK_FP_ZERO) { /* For all combinations: +0 < +0, +0 < -0, -0 < +0, -0 < -0, * steps e, f, and g. */ goto lt_false; } if (d1 == d2) { goto lt_false; } if (c1 == DUK_FP_INFINITE && s1 == 0) { /* x == +Infinity */ goto lt_false; } if (c2 == DUK_FP_INFINITE && s2 == 0) { /* y == +Infinity */ goto lt_true; } if (c2 == DUK_FP_INFINITE && s2 != 0) { /* y == -Infinity */ goto lt_false; } if (c1 == DUK_FP_INFINITE && s1 != 0) { /* x == -Infinity */ goto lt_true; } if (d1 < d2) { goto lt_true; } goto lt_false; } lt_undefined: /* Note: undefined from Section 11.8.5 always results in false * return (see e.g. Section 11.8.3) - hence special treatment here. */ retval = 0; goto cleanup; lt_true: if (flags & DUK_COMPARE_FLAG_NEGATE) { retval = 0; goto cleanup; } else { retval = 1; goto cleanup; } /* never here */ lt_false: if (flags & DUK_COMPARE_FLAG_NEGATE) { retval = 1; goto cleanup; } else { retval = 0; goto cleanup; } /* never here */ cleanup: duk_pop_2(ctx); return retval; }
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); } }
duk_bool_t duk_js_compare_helper(duk_hthread *thr, duk_tval *tv_x, duk_tval *tv_y, duk_small_int_t flags) { duk_context *ctx = (duk_context *) thr; duk_double_t d1, d2; duk_small_int_t c1, c2; duk_small_int_t s1, s2; duk_small_int_t rc; duk_bool_t retval; duk_push_tval(ctx, tv_x); duk_push_tval(ctx, tv_y); if (flags & DUK_COMPARE_FLAG_EVAL_LEFT_FIRST) { duk_to_primitive(ctx, -2, DUK_HINT_NUMBER); duk_to_primitive(ctx, -1, DUK_HINT_NUMBER); } else { duk_to_primitive(ctx, -1, DUK_HINT_NUMBER); duk_to_primitive(ctx, -2, DUK_HINT_NUMBER); } /* Note: reuse variables */ tv_x = duk_get_tval(ctx, -2); tv_y = duk_get_tval(ctx, -1); if (DUK_TVAL_IS_STRING(tv_x) && DUK_TVAL_IS_STRING(tv_y)) { duk_hstring *h1 = DUK_TVAL_GET_STRING(tv_x); duk_hstring *h2 = DUK_TVAL_GET_STRING(tv_y); DUK_ASSERT(h1 != NULL); DUK_ASSERT(h2 != NULL); rc = duk_js_string_compare(h1, h2); if (rc < 0) { goto lt_true; } else { goto lt_false; } } else { /* Ordering should not matter (E5 Section 11.8.5, step 3.a) but * preserve it just in case. */ if (flags & DUK_COMPARE_FLAG_EVAL_LEFT_FIRST) { d1 = duk_to_number(ctx, -2); d2 = duk_to_number(ctx, -1); } else { d2 = duk_to_number(ctx, -1); d1 = duk_to_number(ctx, -2); } c1 = (duk_small_int_t) DUK_FPCLASSIFY(d1); s1 = (duk_small_int_t) DUK_SIGNBIT(d1); c2 = (duk_small_int_t) DUK_FPCLASSIFY(d2); s2 = (duk_small_int_t) DUK_SIGNBIT(d2); if (c1 == DUK_FP_NAN || c2 == DUK_FP_NAN) { goto lt_undefined; } if (c1 == DUK_FP_ZERO && c2 == DUK_FP_ZERO) { /* For all combinations: +0 < +0, +0 < -0, -0 < +0, -0 < -0, * steps e, f, and g. */ goto lt_false; } if (d1 == d2) { goto lt_false; } if (c1 == DUK_FP_INFINITE && s1 == 0) { /* x == +Infinity */ goto lt_false; } if (c2 == DUK_FP_INFINITE && s2 == 0) { /* y == +Infinity */ goto lt_true; } if (c2 == DUK_FP_INFINITE && s2 != 0) { /* y == -Infinity */ goto lt_false; } if (c1 == DUK_FP_INFINITE && s1 != 0) { /* x == -Infinity */ goto lt_true; } if (d1 < d2) { goto lt_true; } goto lt_false; } lt_undefined: /* Note: undefined from Section 11.8.5 always results in false * return (see e.g. Section 11.8.3) - hence special treatment here. */ retval = 0; goto cleanup; lt_true: if (flags & DUK_COMPARE_FLAG_NEGATE) { retval = 0; goto cleanup; } else { retval = 1; goto cleanup; } /* never here */ lt_false: if (flags & DUK_COMPARE_FLAG_NEGATE) { retval = 1; goto cleanup; } else { retval = 0; goto cleanup; } /* never here */ cleanup: duk_pop_2(ctx); return retval; }
static duk_uint8_t *duk__dump_func(duk_context *ctx, duk_hcompiledfunction *func, duk_bufwriter_ctx *bw_ctx, duk_uint8_t *p) { duk_hthread *thr; duk_tval *tv, *tv_end; duk_instr_t *ins, *ins_end; duk_hobject **fn, **fn_end; duk_hstring *h_str; duk_uint32_t count_instr; duk_uint32_t tmp32; duk_uint16_t tmp16; duk_double_t d; thr = (duk_hthread *) ctx; DUK_UNREF(ctx); DUK_UNREF(thr); DUK_DD(DUK_DDPRINT("dumping function %p to %p: " "consts=[%p,%p[ (%ld bytes, %ld items), " "funcs=[%p,%p[ (%ld bytes, %ld items), " "code=[%p,%p[ (%ld bytes, %ld items)", (void *) func, (void *) p, (void *) DUK_HCOMPILEDFUNCTION_GET_CONSTS_BASE(thr->heap, func), (void *) DUK_HCOMPILEDFUNCTION_GET_CONSTS_END(thr->heap, func), (long) DUK_HCOMPILEDFUNCTION_GET_CONSTS_SIZE(thr->heap, func), (long) DUK_HCOMPILEDFUNCTION_GET_CONSTS_COUNT(thr->heap, func), (void *) DUK_HCOMPILEDFUNCTION_GET_FUNCS_BASE(thr->heap, func), (void *) DUK_HCOMPILEDFUNCTION_GET_FUNCS_END(thr->heap, func), (long) DUK_HCOMPILEDFUNCTION_GET_FUNCS_SIZE(thr->heap, func), (long) DUK_HCOMPILEDFUNCTION_GET_FUNCS_COUNT(thr->heap, func), (void *) DUK_HCOMPILEDFUNCTION_GET_CODE_BASE(thr->heap, func), (void *) DUK_HCOMPILEDFUNCTION_GET_CODE_END(thr->heap, func), (long) DUK_HCOMPILEDFUNCTION_GET_CODE_SIZE(thr->heap, func), (long) DUK_HCOMPILEDFUNCTION_GET_CODE_COUNT(thr->heap, func))); DUK_ASSERT(DUK_USE_ESBC_MAX_BYTES <= 0x7fffffffUL); /* ensures no overflow */ count_instr = (duk_uint32_t) DUK_HCOMPILEDFUNCTION_GET_CODE_COUNT(thr->heap, func); p = DUK_BW_ENSURE_RAW(thr, bw_ctx, 3 * 4 + 2 * 2 + 3 * 4 + count_instr * 4, p); /* Fixed header info. */ tmp32 = count_instr; DUK_RAW_WRITE_U32_BE(p, tmp32); tmp32 = (duk_uint32_t) DUK_HCOMPILEDFUNCTION_GET_CONSTS_COUNT(thr->heap, func); DUK_RAW_WRITE_U32_BE(p, tmp32); tmp32 = (duk_uint32_t) DUK_HCOMPILEDFUNCTION_GET_FUNCS_COUNT(thr->heap, func); DUK_RAW_WRITE_U32_BE(p, tmp32); tmp16 = func->nregs; DUK_RAW_WRITE_U16_BE(p, tmp16); tmp16 = func->nargs; DUK_RAW_WRITE_U16_BE(p, tmp16); #if defined(DUK_USE_DEBUGGER_SUPPORT) tmp32 = func->start_line; DUK_RAW_WRITE_U32_BE(p, tmp32); tmp32 = func->end_line; DUK_RAW_WRITE_U32_BE(p, tmp32); #else DUK_RAW_WRITE_U32_BE(p, 0); DUK_RAW_WRITE_U32_BE(p, 0); #endif tmp32 = ((duk_heaphdr *) func)->h_flags & DUK_HEAPHDR_FLAGS_FLAG_MASK; DUK_RAW_WRITE_U32_BE(p, tmp32); /* Bytecode instructions: endian conversion needed unless * platform is big endian. */ ins = DUK_HCOMPILEDFUNCTION_GET_CODE_BASE(thr->heap, func); ins_end = DUK_HCOMPILEDFUNCTION_GET_CODE_END(thr->heap, func); DUK_ASSERT((duk_size_t) (ins_end - ins) == (duk_size_t) count_instr); #if defined(DUK_USE_INTEGER_BE) DUK_MEMCPY((void *) p, (const void *) ins, (size_t) (ins_end - ins)); p += (size_t) (ins_end - ins); #else while (ins != ins_end) { tmp32 = (duk_uint32_t) (*ins); DUK_RAW_WRITE_U32_BE(p, tmp32); ins++; } #endif /* Constants: variable size encoding. */ tv = DUK_HCOMPILEDFUNCTION_GET_CONSTS_BASE(thr->heap, func); tv_end = DUK_HCOMPILEDFUNCTION_GET_CONSTS_END(thr->heap, func); while (tv != tv_end) { /* constants are strings or numbers now */ DUK_ASSERT(DUK_TVAL_IS_STRING(tv) || DUK_TVAL_IS_NUMBER(tv)); if (DUK_TVAL_IS_STRING(tv)) { h_str = DUK_TVAL_GET_STRING(tv); DUK_ASSERT(h_str != NULL); DUK_ASSERT(DUK_HSTRING_MAX_BYTELEN <= 0x7fffffffUL); /* ensures no overflow */ p = DUK_BW_ENSURE_RAW(thr, bw_ctx, 1 + 4 + DUK_HSTRING_GET_BYTELEN(h_str), p), *p++ = DUK__SER_STRING; p = duk__dump_hstring_raw(p, h_str); } else { DUK_ASSERT(DUK_TVAL_IS_NUMBER(tv)); p = DUK_BW_ENSURE_RAW(thr, bw_ctx, 1 + 8, p); *p++ = DUK__SER_NUMBER; d = DUK_TVAL_GET_NUMBER(tv); DUK_RAW_WRITE_DOUBLE_BE(p, d); } tv++; } /* Inner functions recursively. */ fn = (duk_hobject **) DUK_HCOMPILEDFUNCTION_GET_FUNCS_BASE(thr->heap, func); fn_end = (duk_hobject **) DUK_HCOMPILEDFUNCTION_GET_FUNCS_END(thr->heap, func); while (fn != fn_end) { /* XXX: This causes recursion up to inner function depth * which is normally not an issue, e.g. mark-and-sweep uses * a recursion limiter to avoid C stack issues. Avoiding * this would mean some sort of a work list or just refusing * to serialize deep functions. */ DUK_ASSERT(DUK_HOBJECT_IS_COMPILEDFUNCTION(*fn)); p = duk__dump_func(ctx, (duk_hcompiledfunction *) *fn, bw_ctx, p); fn++; } /* Object extra properties. * * There are some difference between function templates and functions. * For example, function templates don't have .length and nargs is * normally used to instantiate the functions. */ p = duk__dump_uint32_prop(thr, p, bw_ctx, (duk_hobject *) func, DUK_STRIDX_LENGTH, (duk_uint32_t) func->nargs); p = duk__dump_string_prop(thr, p, bw_ctx, (duk_hobject *) func, DUK_STRIDX_NAME); p = duk__dump_string_prop(thr, p, bw_ctx, (duk_hobject *) func, DUK_STRIDX_FILE_NAME); p = duk__dump_buffer_prop(thr, p, bw_ctx, (duk_hobject *) func, DUK_STRIDX_INT_PC2LINE); p = duk__dump_varmap(thr, p, bw_ctx, (duk_hobject *) func); p = duk__dump_formals(thr, p, bw_ctx, (duk_hobject *) func); DUK_DD(DUK_DDPRINT("serialized function %p -> final pointer %p", (void *) func, (void *) p)); return p; }
DUK_LOCAL void duk__add_traceback(duk_hthread *thr, duk_hthread *thr_callstack, const char *c_filename, duk_int_t c_line, duk_bool_t noblame_fileline) { duk_context *ctx = (duk_context *) thr; duk_small_uint_t depth; duk_int_t i, i_min; duk_int_t arr_size; duk_harray *a; duk_tval *tv; duk_hstring *s; duk_uint32_t u32; duk_double_t d; DUK_ASSERT(thr != NULL); DUK_ASSERT(thr_callstack != NULL); DUK_ASSERT(ctx != NULL); /* [ ... error ] */ /* * The traceback format is pretty arcane in an attempt to keep it compact * and cheap to create. It may change arbitrarily from version to version. * It should be decoded/accessed through version specific accessors only. * * See doc/error-objects.rst. */ DUK_DDD(DUK_DDDPRINT("adding traceback to object: %!T", (duk_tval *) duk_get_tval(ctx, -1))); /* Preallocate array to correct size, so that we can just write out * the _Tracedata values into the array part. */ depth = DUK_USE_TRACEBACK_DEPTH; arr_size = (duk_int_t) (thr_callstack->callstack_top <= depth ? thr_callstack->callstack_top : depth) * 2; if (thr->compile_ctx != NULL && thr->compile_ctx->h_filename != NULL) { arr_size += 2; } if (c_filename) { /* We need the C filename to be interned before getting the * array part pointer to avoid any GC interference while the * array part is populated. */ duk_push_string(ctx, c_filename); arr_size += 2; } DUK_D(DUK_DPRINT("preallocated _Tracedata to %ld items", (long) arr_size)); a = duk_push_harray_with_size(ctx, (duk_uint32_t) arr_size); /* XXX: call which returns array part pointer directly */ DUK_ASSERT(a != NULL); tv = DUK_HOBJECT_A_GET_BASE(thr->heap, (duk_hobject *) a); DUK_ASSERT(tv != NULL || arr_size == 0); /* Compiler SyntaxErrors (and other errors) come first, and are * blamed by default (not flagged "noblame"). */ if (thr->compile_ctx != NULL && thr->compile_ctx->h_filename != NULL) { s = thr->compile_ctx->h_filename; DUK_TVAL_SET_STRING(tv, s); DUK_HSTRING_INCREF(thr, s); tv++; u32 = (duk_uint32_t) thr->compile_ctx->curr_token.start_line; /* (flags<<32) + (line), flags = 0 */ DUK_TVAL_SET_U32(tv, u32); tv++; } /* Filename/line from C macros (__FILE__, __LINE__) are added as an * entry with a special format: (string, number). The number contains * the line and flags. */ /* [ ... error c_filename? arr ] */ if (c_filename) { DUK_ASSERT(DUK_TVAL_IS_STRING(thr->valstack_top - 2)); s = DUK_TVAL_GET_STRING(thr->valstack_top - 2); /* interned c_filename */ DUK_ASSERT(s != NULL); DUK_TVAL_SET_STRING(tv, s); DUK_HSTRING_INCREF(thr, s); tv++; d = (noblame_fileline ? ((duk_double_t) DUK_TB_FLAG_NOBLAME_FILELINE) * DUK_DOUBLE_2TO32 : 0.0) + (duk_double_t) c_line; DUK_TVAL_SET_DOUBLE(tv, d); tv++; } /* traceback depth doesn't take into account the filename/line * special handling above (intentional) */ depth = DUK_USE_TRACEBACK_DEPTH; i_min = (thr_callstack->callstack_top > (duk_size_t) depth ? (duk_int_t) (thr_callstack->callstack_top - depth) : 0); DUK_ASSERT(i_min >= 0); /* [ ... error c_filename? arr ] */ DUK_ASSERT(thr_callstack->callstack_top <= DUK_INT_MAX); /* callstack limits */ for (i = (duk_int_t) (thr_callstack->callstack_top - 1); i >= i_min; i--) { duk_uint32_t pc; duk_tval *tv_src; /* * Note: each API operation potentially resizes the callstack, * so be careful to re-lookup after every operation. Currently * these is no issue because we don't store a temporary 'act' * pointer at all. (This would be a non-issue if we operated * directly on the array part.) */ /* [... arr] */ DUK_ASSERT_DISABLE(thr_callstack->callstack[i].pc >= 0); /* unsigned */ /* Add function object. */ tv_src = &(thr_callstack->callstack + i)->tv_func; /* object (function) or lightfunc */ DUK_ASSERT(DUK_TVAL_IS_OBJECT(tv_src) || DUK_TVAL_IS_LIGHTFUNC(tv_src)); DUK_TVAL_SET_TVAL(tv, tv_src); DUK_TVAL_INCREF(thr, tv); tv++; /* Add a number containing: pc, activation flags. * * PC points to next instruction, find offending PC. Note that * PC == 0 for native code. */ pc = duk_hthread_get_act_prev_pc(thr_callstack, thr_callstack->callstack + i); DUK_ASSERT_DISABLE(pc >= 0); /* unsigned */ DUK_ASSERT((duk_double_t) pc < DUK_DOUBLE_2TO32); /* assume PC is at most 32 bits and non-negative */ d = ((duk_double_t) thr_callstack->callstack[i].flags) * DUK_DOUBLE_2TO32 + (duk_double_t) pc; DUK_TVAL_SET_DOUBLE(tv, d); tv++; } DUK_ASSERT((duk_uint32_t) (tv - DUK_HOBJECT_A_GET_BASE(thr->heap, (duk_hobject *) a)) == a->length); DUK_ASSERT(a->length == (duk_uint32_t) arr_size); /* [ ... error c_filename? arr ] */ if (c_filename) { duk_remove(ctx, -2); } /* [ ... error arr ] */ duk_xdef_prop_stridx_wec(ctx, -2, DUK_STRIDX_INT_TRACEDATA); /* -> [ ... error ] */ }