int duk_bi_array_prototype_slice(duk_context *ctx) { unsigned int len; int start, end; int idx; int i; duk_uint32_t res_length = 0; len = duk__push_this_obj_len_u32(ctx); duk_push_array(ctx); /* stack[0] = start * stack[1] = end * stack[2] = ToObject(this) * stack[3] = ToUint32(length) * stack[4] = result array */ start = duk_to_int_clamped(ctx, 0, -len, len); /* FIXME: does not support full 32-bit range */ if (start < 0) { start = len + start; } /* FIXME: could duk_is_undefined() provide defaulting undefined to 'len' * (the upper limit)? */ if (duk_is_undefined(ctx, 1)) { end = len; } else { end = duk_to_int_clamped(ctx, 1, -len, len); if (end < 0) { end = len + end; } } DUK_ASSERT(start >= 0 && (duk_uint32_t) start <= len); DUK_ASSERT(end >= 0 && (duk_uint32_t) end <= len); idx = 0; for (i = start; i < end; i++) { DUK_ASSERT_TOP(ctx, 5); if (duk_get_prop_index(ctx, 2, i)) { duk_def_prop_index(ctx, 4, idx, DUK_PROPDESC_FLAGS_WEC); res_length = idx + 1; } else { duk_pop(ctx); } idx++; DUK_ASSERT_TOP(ctx, 5); } duk_push_int(ctx, res_length); /* FIXME */ duk_def_prop_stridx(ctx, 4, DUK_STRIDX_LENGTH, DUK_PROPDESC_FLAGS_W); DUK_ASSERT_TOP(ctx, 5); return 1; }
int duk_bi_array_constructor(duk_context *ctx) { int nargs; double d; duk_uint32_t len; int i; nargs = duk_get_top(ctx); duk_push_array(ctx); if (nargs == 1 && duk_is_number(ctx, 0)) { /* FIXME: expensive check (also shared elsewhere - so add a shared internal API call?) */ d = duk_get_number(ctx, 0); len = duk_to_uint32(ctx, 0); if (((double) len) != d) { return DUK_RET_RANGE_ERROR; } /* FIXME: if 'len' is low, may want to ensure array part is kept: * the caller is likely to want a dense array. */ duk_dup(ctx, 0); duk_def_prop_stridx(ctx, -2, DUK_STRIDX_LENGTH, DUK_PROPDESC_FLAGS_W); /* [ ToUint32(len) array ToUint32(len) ] -> [ ToUint32(len) array ] */ return 1; } /* FIXME: optimize by creating array into correct size directly, and * operating on the array part directly; values can be memcpy()'d from * value stack directly as long as refcounts are increased. */ for (i = 0; i < nargs; i++) { duk_dup(ctx, i); duk_def_prop_index(ctx, -2, i, DUK_PROPDESC_FLAGS_WEC); } duk_push_number(ctx, (double) nargs); /* FIXME: push_u32 */ duk_def_prop_stridx(ctx, -2, DUK_STRIDX_LENGTH, DUK_PROPDESC_FLAGS_W); return 1; }
static void add_traceback(duk_hthread *thr, duk_hthread *thr_callstack, duk_hobject *obj, int err_index, const char *filename, int line, int noblame_fileline) { duk_context *ctx = (duk_context *) thr; int depth; int i, i_min; int arr_idx; double d; DUK_UNREF(obj); /* FIXME: remove entire argument (it's accessed through the stack)? */ DUK_ASSERT(thr != NULL); DUK_ASSERT(thr_callstack != NULL); DUK_ASSERT(obj != NULL); DUK_ASSERT(err_index >= 0); DUK_ASSERT(ctx != NULL); /* * 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.txt. */ DUK_DDDPRINT("adding traceback to object: %!O", (duk_heaphdr *) obj); duk_push_array(ctx); /* XXX: specify array size, as we know it */ arr_idx = 0; /* 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. */ /* FIXME: optimize: allocate an array part to the necessary size (upwards * estimate) and fill in the values directly into the array part; finally * update 'length'. */ /* FIXME: using duk_put_prop_index() would cause obscure error cases when Array.prototype * has write-protected array index named properties. This was seen as DoubleErrors * in e.g. some test262 test cases. Using duk_def_prop_index() is better but currently * there is no fast path variant for that; the current implementation interns the array * index as a string. This can be fixed directly, or perhaps the traceback can be fixed * altogether to fill in the tracedata directly into the array part. */ if (filename) { duk_push_string(ctx, filename); duk_def_prop_index(ctx, -2, arr_idx, DUK_PROPDESC_FLAGS_WEC); arr_idx++; d = (noblame_fileline ? ((double) DUK_TB_FLAG_NOBLAME_FILELINE) * DUK_DOUBLE_2TO32 : 0.0) + (double) line; duk_push_number(ctx, d); duk_def_prop_index(ctx, -2, arr_idx, DUK_PROPDESC_FLAGS_WEC); arr_idx++; } /* 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 > (size_t) depth ? (int) (thr_callstack->callstack_top - depth) : 0); DUK_ASSERT(i_min >= 0); for (i = thr_callstack->callstack_top - 1; i >= i_min; i--) { int pc; /* * 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(thr_callstack->callstack[i].func != NULL); DUK_ASSERT(thr_callstack->callstack[i].pc >= 0); /* add function */ duk_push_hobject(ctx, thr_callstack->callstack[i].func); /* -> [... arr func] */ duk_def_prop_index(ctx, -2, arr_idx, DUK_PROPDESC_FLAGS_WEC); arr_idx++; /* add a number containing: pc, activation flags */ /* Add a number containing: pc, activation flag * * PC points to next instruction, find offending PC. Note that * PC == 0 for native code. */ pc = thr_callstack->callstack[i].pc; if (pc > 0) { pc--; } DUK_ASSERT(pc >= 0 && (double) pc < DUK_DOUBLE_2TO32); /* assume PC is at most 32 bits and non-negative */ d = ((double) thr_callstack->callstack[i].flags) * DUK_DOUBLE_2TO32 + (double) pc; duk_push_number(ctx, d); /* -> [... arr num] */ duk_def_prop_index(ctx, -2, arr_idx, DUK_PROPDESC_FLAGS_WEC); arr_idx++; } /* FIXME: set with duk_hobject_set_length() when tracedata is filled directly */ duk_push_int(ctx, (int) arr_idx); duk_def_prop_stridx(ctx, -2, DUK_STRIDX_LENGTH, DUK_PROPDESC_FLAGS_WC); /* [... arr] */ duk_def_prop_stridx(ctx, err_index, DUK_STRIDX_TRACEDATA, DUK_PROPDESC_FLAGS_WEC); /* -> [...] */ }
int duk_bi_array_prototype_splice(duk_context *ctx) { int nargs; int have_delcount; int item_count; int len; int act_start; int del_count; int i; DUK_UNREF(have_delcount); nargs = duk_get_top(ctx); if (nargs < 2) { duk_set_top(ctx, 2); nargs = 2; have_delcount = 0; } else { have_delcount = 1; } len = duk__push_this_obj_len_u32(ctx); act_start = duk_to_int_clamped(ctx, 0, -len, len); if (act_start < 0) { act_start = len + act_start; } DUK_ASSERT(act_start >= 0 && act_start <= len); #ifdef DUK_USE_NONSTD_ARRAY_SPLICE_DELCOUNT if (have_delcount) { #endif del_count = duk_to_int_clamped(ctx, 1, 0, len - act_start); #ifdef DUK_USE_NONSTD_ARRAY_SPLICE_DELCOUNT } else { /* E5.1 standard behavior when deleteCount is not given would be * to treat it just like if 'undefined' was given, which coerces * ultimately to 0. Real world behavior is to splice to the end * of array, see test-bi-array-proto-splice-no-delcount.js. */ del_count = len - act_start; } #endif DUK_ASSERT(del_count >= 0 && del_count <= len - act_start); DUK_ASSERT(del_count + act_start <= len); duk_push_array(ctx); /* stack[0] = start * stack[1] = deleteCount * stack[2...nargs-1] = items * stack[nargs] = ToObject(this) -3 * stack[nargs+1] = ToUint32(length) -2 * stack[nargs+2] = result array -1 */ DUK_ASSERT_TOP(ctx, nargs + 3); /* Step 9: copy elements-to-be-deleted into the result array */ for (i = 0; i < del_count; i++) { if (duk_get_prop_index(ctx, -3, act_start + i)) { duk_def_prop_index(ctx, -2, i, DUK_PROPDESC_FLAGS_WEC); /* throw flag irrelevant (false in std alg) */ } else { duk_pop(ctx); } } duk_push_int(ctx, del_count); /* FIXME: typing */ duk_def_prop_stridx(ctx, -2, DUK_STRIDX_LENGTH, DUK_PROPDESC_FLAGS_W); /* Steps 12 and 13: reorganize elements to make room for itemCount elements */ DUK_ASSERT(nargs >= 2); item_count = nargs - 2; if (item_count < del_count) { /* [ A B C D E F G H ] rel_index = 2, del_count 3, item count 1 * -> [ A B F G H ] (conceptual intermediate step) * -> [ A B . F G H ] (placeholder marked) * [ A B C F G H ] (actual result at this point, C will be replaced) */ DUK_ASSERT_TOP(ctx, nargs + 3); for (i = act_start; i < len - del_count; i++) { if (duk_get_prop_index(ctx, -3, i + del_count)) { duk_put_prop_index(ctx, -4, i + item_count); /* FIXME: Throw */ } else { duk_pop(ctx); duk_del_prop_index(ctx, -3, i + item_count); /* FIXME: Throw */ } } DUK_ASSERT_TOP(ctx, nargs + 3); /* loop iterator init and limit changed from standard algorithm */ for (i = len - 1; i >= len - del_count + item_count; i--) { duk_del_prop_index(ctx, -3, i); /* FIXME: Throw */ } DUK_ASSERT_TOP(ctx, nargs + 3); } else if (item_count > del_count) { /* [ A B C D E F G H ] rel_index = 2, del_count 3, item count 4 * -> [ A B F G H ] (conceptual intermediate step) * -> [ A B . . . . F G H ] (placeholder marked) * [ A B C D E F F G H ] (actual result at this point) */ DUK_ASSERT_TOP(ctx, nargs + 3); /* loop iterator init and limit changed from standard algorithm */ for (i = len - del_count - 1; i >= act_start; i--) { if (duk_get_prop_index(ctx, -3, i + del_count)) { duk_put_prop_index(ctx, -4, i + item_count); /* FIXME: Throw */ } else { duk_pop(ctx); duk_del_prop_index(ctx, -3, i + item_count); /* FIXME: Throw */ } } DUK_ASSERT_TOP(ctx, nargs + 3); } else { /* [ A B C D E F G H ] rel_index = 2, del_count 3, item count 3 * -> [ A B F G H ] (conceptual intermediate step) * -> [ A B . . . F G H ] (placeholder marked) * [ A B C D E F G H ] (actual result at this point) */ } DUK_ASSERT_TOP(ctx, nargs + 3); /* Step 15: insert itemCount elements into the hole made above */ for (i = 0; i < item_count; i++) { duk_dup(ctx, i + 2); /* args start at index 2 */ duk_put_prop_index(ctx, -4, act_start + i); /* FIXME: Throw */ } /* Step 16: update length; note that the final length may be above 32 bit range */ duk_push_number(ctx, ((double) len) - ((double) del_count) + ((double) item_count)); duk_put_prop_stridx(ctx, -4, DUK_STRIDX_LENGTH); /* result array is already at the top of stack */ DUK_ASSERT_TOP(ctx, nargs + 3); return 1; }
int duk_bi_array_prototype_concat(duk_context *ctx) { int i, n; int j, len; int idx, idx_last; duk_hobject *h; /* XXX: the insert here is a bit expensive if there are a lot of items. * It could also be special cased in the outermost for loop quite easily * (as the element is dup()'d anyway). */ (void) duk_push_this_coercible_to_object(ctx); duk_insert(ctx, 0); n = duk_get_top(ctx); duk_push_array(ctx); /* -> [ ToObject(this) item1 ... itemN arr ] */ /* NOTE: The Array special behaviors are NOT invoked by duk_def_prop_index() * (which differs from the official algorithm). If no error is thrown, this * doesn't matter as the length is updated at the end. However, if an error * is thrown, the length will be unset. That shouldn't matter because the * caller won't get a reference to the intermediate value. */ idx = 0; idx_last = 0; for (i = 0; i < n; i++) { DUK_ASSERT_TOP(ctx, n + 1); /* [ ToObject(this) item1 ... itemN arr ] */ duk_dup(ctx, i); h = duk_get_hobject_with_class(ctx, -1, DUK_HOBJECT_CLASS_ARRAY); if (!h) { duk_def_prop_index(ctx, -2, idx++, DUK_PROPDESC_FLAGS_WEC); idx_last = idx; continue; } /* [ ToObject(this) item1 ... itemN arr item(i) ] */ /* FIXME: an array can have length higher than 32 bits; this is not handled * correctly now (also len is signed so length above 2**31-1 will have trouble. */ len = duk_get_length(ctx, -1); for (j = 0; j < len; j++) { if (duk_get_prop_index(ctx, -1, j)) { /* [ ToObject(this) item1 ... itemN arr item(i) item(i)[j] ] */ duk_def_prop_index(ctx, -3, idx++, DUK_PROPDESC_FLAGS_WEC); idx_last = idx; } else { /* XXX: according to E5.1 Section 15.4.4.4 nonexistent trailing * elements do not affect 'length' but test262 disagrees. Work * as E5.1 mandates for now and don't touch idx_last. */ idx++; duk_pop(ctx); } } duk_pop(ctx); } duk_push_number(ctx, (double) idx_last); duk_def_prop_stridx(ctx, -2, DUK_STRIDX_LENGTH, DUK_PROPDESC_FLAGS_W); DUK_ASSERT_TOP(ctx, n + 1); return 1; }
int duk_bi_array_prototype_iter_shared(duk_context *ctx) { int len; int i; int k; int bval; int iter_type = duk_get_magic(ctx); duk_uint32_t res_length = 0; /* each call this helper serves has nargs==2 */ DUK_ASSERT_TOP(ctx, 2); len = duk__push_this_obj_len_u32(ctx); if (!duk_is_callable(ctx, 0)) { goto type_error; } /* if thisArg not supplied, behave as if undefined was supplied */ if (iter_type == DUK__ITER_MAP || iter_type == DUK__ITER_FILTER) { duk_push_array(ctx); } else { duk_push_undefined(ctx); } /* stack[0] = callback * stack[1] = thisArg * stack[2] = object * stack[3] = ToUint32(length) (unused, but avoid unnecessary pop) * stack[4] = result array (or undefined) */ k = 0; /* result index for filter() */ for (i = 0; i < len; i++) { DUK_ASSERT_TOP(ctx, 5); if (!duk_get_prop_index(ctx, 2, i)) { duk_pop(ctx); continue; } /* The original value needs to be preserved for filter(), hence * this funny order. We can't re-get the value because of side * effects. */ duk_dup(ctx, 0); duk_dup(ctx, 1); duk_dup(ctx, -3); duk_push_int(ctx, i); duk_dup(ctx, 2); /* [ ... val callback thisArg val i obj ] */ duk_call_method(ctx, 3); /* -> [ ... val retval ] */ switch (iter_type) { case DUK__ITER_EVERY: bval = duk_to_boolean(ctx, -1); if (!bval) { /* stack top contains 'false' */ return 1; } break; case DUK__ITER_SOME: bval = duk_to_boolean(ctx, -1); if (bval) { /* stack top contains 'true' */ return 1; } break; case DUK__ITER_FOREACH: /* nop */ break; case DUK__ITER_MAP: duk_dup(ctx, -1); duk_def_prop_index(ctx, 4, i, DUK_PROPDESC_FLAGS_WEC); /* retval to result[i] */ res_length = i + 1; break; case DUK__ITER_FILTER: bval = duk_to_boolean(ctx, -1); if (bval) { duk_dup(ctx, -2); /* orig value */ duk_def_prop_index(ctx, 4, k, DUK_PROPDESC_FLAGS_WEC); k++; res_length = k; } break; default: DUK_UNREACHABLE(); break; } duk_pop_2(ctx); DUK_ASSERT_TOP(ctx, 5); } switch (iter_type) { case DUK__ITER_EVERY: duk_push_true(ctx); break; case DUK__ITER_SOME: duk_push_false(ctx); break; case DUK__ITER_FOREACH: duk_push_undefined(ctx); break; case DUK__ITER_MAP: case DUK__ITER_FILTER: DUK_ASSERT_TOP(ctx, 5); DUK_ASSERT(duk_is_array(ctx, -1)); /* topmost element is the result array already */ duk_push_number(ctx, (double) res_length); /* FIXME */ duk_def_prop_stridx(ctx, -2, DUK_STRIDX_LENGTH, DUK_PROPDESC_FLAGS_W); break; default: DUK_UNREACHABLE(); break; } return 1; type_error: return DUK_RET_TYPE_ERROR; }