duk_ret_t duk_bi_duktape_object_act(duk_context *ctx) { duk_hthread *thr = (duk_hthread *) ctx; duk_activation *act; duk_hobject *h_func; duk_hbuffer_fixed *pc2line; duk_uint_fast32_t pc; duk_uint_fast32_t line; duk_int_t level; /* -1 = top callstack entry, callstack[callstack_top - 1] * -callstack_top = bottom callstack entry, callstack[0] */ level = duk_to_int(ctx, 0); if (level >= 0 || -level > (duk_int_t) thr->callstack_top) { return 0; } DUK_ASSERT(level >= -((duk_int_t) thr->callstack_top) && level <= -1); act = thr->callstack + thr->callstack_top + level; duk_push_object(ctx); h_func = act->func; DUK_ASSERT(h_func != NULL); duk_push_hobject(ctx, h_func); pc = (duk_uint_fast32_t) act->pc; duk_push_int(ctx, (int) pc); /* FIXME: typing */ duk_get_prop_stridx(ctx, -2, DUK_STRIDX_INT_PC2LINE); if (duk_is_buffer(ctx, -1)) { pc2line = (duk_hbuffer_fixed *) duk_get_hbuffer(ctx, -1); DUK_ASSERT(!DUK_HBUFFER_HAS_DYNAMIC((duk_hbuffer *) pc2line)); pc = (duk_uint_fast32_t) act->pc; line = duk_hobject_pc2line_query(pc2line, (duk_uint_fast32_t) pc); } else { line = 0; } duk_pop(ctx); duk_push_int(ctx, (int) line); /* FIXME: typing */ /* Providing access to e.g. act->lex_env would be dangerous: these * internal structures must never be accessible to the application. * Duktape relies on them having consistent data, and this consistency * is only asserted for, not checked for. */ /* [ level obj func pc line ] */ /* FIXME: version specific array format instead? */ duk_def_prop_stridx(ctx, -4, DUK_STRIDX_LINE_NUMBER, DUK_PROPDESC_FLAGS_WEC); duk_def_prop_stridx(ctx, -3, DUK_STRIDX_PC, DUK_PROPDESC_FLAGS_WEC); duk_def_prop_stridx(ctx, -2, DUK_STRIDX_LC_FUNCTION, DUK_PROPDESC_FLAGS_WEC); return 1; }
int duk_bi_pointer_constructor(duk_context *ctx) { /* FIXME: this behavior is quite useless now; it would be nice to be able * to create pointer values from e.g. numbers or strings. Numbers are * problematic on 64-bit platforms though. Hex encoded strings? */ if (duk_get_top(ctx) == 0) { duk_push_pointer(ctx, NULL); } else { duk_to_pointer(ctx, 0); } DUK_ASSERT(duk_is_pointer(ctx, 0)); duk_set_top(ctx, 1); if (duk_is_constructor_call(ctx)) { duk_push_object_helper(ctx, DUK_HOBJECT_FLAG_EXTENSIBLE | DUK_HOBJECT_CLASS_AS_FLAGS(DUK_HOBJECT_CLASS_POINTER), DUK_BIDX_POINTER_PROTOTYPE); /* Pointer object internal value is immutable */ duk_dup(ctx, 0); duk_def_prop_stridx(ctx, -2, DUK_STRIDX_INT_VALUE, DUK_PROPDESC_FLAGS_NONE); } /* Note: unbalanced stack on purpose */ return 1; }
duk_ret_t duk_bi_string_constructor(duk_context *ctx) { /* String constructor needs to distinguish between an argument not given at all * vs. given as 'undefined'. We're a vararg function to handle this properly. */ if (duk_get_top(ctx) == 0) { duk_push_hstring_stridx(ctx, DUK_STRIDX_EMPTY_STRING); } else { duk_to_string(ctx, 0); } DUK_ASSERT(duk_is_string(ctx, 0)); duk_set_top(ctx, 1); if (duk_is_constructor_call(ctx)) { duk_push_object_helper(ctx, DUK_HOBJECT_FLAG_EXTENSIBLE | DUK_HOBJECT_FLAG_EXOTIC_STRINGOBJ | DUK_HOBJECT_CLASS_AS_FLAGS(DUK_HOBJECT_CLASS_STRING), DUK_BIDX_STRING_PROTOTYPE); /* String object internal value is immutable */ duk_dup(ctx, 0); duk_def_prop_stridx(ctx, -2, DUK_STRIDX_INT_VALUE, DUK_PROPDESC_FLAGS_NONE); } /* Note: unbalanced stack on purpose */ return 1; }
int duk_builtin_date_constructor(duk_context *ctx) { int nargs = duk_get_top(ctx); int is_cons = duk_is_constructor_call(ctx); double dparts[NUM_PARTS]; double d; DUK_DDDPRINT("Date constructor, nargs=%d, is_cons=%d", nargs, is_cons); duk_push_object_helper(ctx, DUK_HOBJECT_FLAG_EXTENSIBLE | DUK_HOBJECT_CLASS_AS_FLAGS(DUK_HOBJECT_CLASS_DATE), DUK_BIDX_DATE_PROTOTYPE); /* Unlike most built-ins, the internal [[PrimitiveValue]] of a Date * is mutable. */ if (nargs == 0 || !is_cons) { d = timeclip(GET_NOW_TIMEVAL(ctx)); duk_push_number(ctx, d); duk_def_prop_stridx(ctx, -2, DUK_STRIDX_INT_VALUE, DUK_PROPDESC_FLAGS_W); if (!is_cons) { /* called as a normal function: return new Date().toString() */ duk_to_string(ctx, -1); } return 1; } else if (nargs == 1) { duk_to_primitive(ctx, 0, DUK_HINT_NONE); if (duk_is_string(ctx, 0)) { parse_string(ctx, duk_to_string(ctx, 0)); duk_replace(ctx, 0); /* may be NaN */ } d = timeclip(duk_to_number(ctx, 0)); duk_push_number(ctx, d); duk_def_prop_stridx(ctx, -2, DUK_STRIDX_INT_VALUE, DUK_PROPDESC_FLAGS_W); return 1; } set_parts_from_args(ctx, dparts, nargs); /* Parts are in local time, convert when setting. */ set_this_timeval_from_dparts(ctx, dparts, FLAG_LOCALTIME /*flags*/); /* -> [ ... this timeval ] */ duk_pop(ctx); /* -> [ ... this ] */ return 1; }
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; }
duk_ret_t duk_bi_number_constructor(duk_context *ctx) { duk_idx_t nargs; duk_hobject *h_this; /* * The Number constructor uses ToNumber(arg) for number coercion * (coercing an undefined argument to NaN). However, if the * argument is not given at all, +0 must be used instead. To do * this, a vararg function is used. */ nargs = duk_get_top(ctx); if (nargs == 0) { duk_push_int(ctx, 0); } duk_to_number(ctx, 0); duk_set_top(ctx, 1); DUK_ASSERT_TOP(ctx, 1); if (!duk_is_constructor_call(ctx)) { return 1; } /* * E5 Section 15.7.2.1 requires that the constructed object * must have the original Number.prototype as its internal * prototype. However, since Number.prototype is non-writable * and non-configurable, this doesn't have to be enforced here: * The default object (bound to 'this') is OK, though we have * to change its class. * * Internal value set to ToNumber(arg) or +0; if no arg given, * ToNumber(undefined) = NaN, so special treatment is needed * (above). String internal value is immutable. */ /* XXX: helper */ duk_push_this(ctx); h_this = duk_get_hobject(ctx, -1); DUK_ASSERT(h_this != NULL); DUK_HOBJECT_SET_CLASS_NUMBER(h_this, DUK_HOBJECT_CLASS_NUMBER); DUK_ASSERT(h_this->prototype == ((duk_hthread *) ctx)->builtins[DUK_BIDX_NUMBER_PROTOTYPE]); DUK_ASSERT(DUK_HOBJECT_GET_CLASS_NUMBER(h_this) == DUK_HOBJECT_CLASS_NUMBER); DUK_ASSERT(DUK_HOBJECT_HAS_EXTENSIBLE(h_this)); duk_dup(ctx, 0); /* -> [ val obj val ] */ duk_def_prop_stridx(ctx, -2, DUK_STRIDX_INT_VALUE, DUK_PROPDESC_FLAGS_NONE); return 0; /* no return value -> don't replace created value */ }
void duk_regexp_create_instance(duk_hthread *thr) { duk_context *ctx = (duk_context *) thr; duk_hobject *h; duk_hstring *h_bc; duk_small_int_t re_flags; /* [ ... escape_source bytecode ] */ h_bc = duk_get_hstring(ctx, -1); DUK_ASSERT(h_bc != NULL); DUK_ASSERT(DUK_HSTRING_GET_BYTELEN(h_bc) >= 1); /* always at least the header */ DUK_ASSERT(DUK_HSTRING_GET_CHARLEN(h_bc) >= 1); DUK_ASSERT((duk_small_int_t) DUK_HSTRING_GET_DATA(h_bc)[0] < 0x80); /* flags always encodes to 1 byte */ re_flags = (duk_small_int_t) DUK_HSTRING_GET_DATA(h_bc)[0]; /* [ ... escaped_source bytecode ] */ duk_push_object(ctx); h = duk_get_hobject(ctx, -1); DUK_ASSERT(h != NULL); duk_insert(ctx, -3); /* [ ... regexp_object escaped_source bytecode ] */ DUK_HOBJECT_SET_CLASS_NUMBER(h, DUK_HOBJECT_CLASS_REGEXP); DUK_HOBJECT_SET_PROTOTYPE(thr, h, thr->builtins[DUK_BIDX_REGEXP_PROTOTYPE]); duk_def_prop_stridx(ctx, -3, DUK_STRIDX_INT_BYTECODE, DUK_PROPDESC_FLAGS_NONE); /* [ ... regexp_object escaped_source ] */ duk_def_prop_stridx(ctx, -2, DUK_STRIDX_SOURCE, DUK_PROPDESC_FLAGS_NONE); /* [ ... regexp_object ] */ duk_push_boolean(ctx, (re_flags & DUK_RE_FLAG_GLOBAL)); duk_def_prop_stridx(ctx, -2, DUK_STRIDX_GLOBAL, DUK_PROPDESC_FLAGS_NONE); duk_push_boolean(ctx, (re_flags & DUK_RE_FLAG_IGNORE_CASE)); duk_def_prop_stridx(ctx, -2, DUK_STRIDX_IGNORE_CASE, DUK_PROPDESC_FLAGS_NONE); duk_push_boolean(ctx, (re_flags & DUK_RE_FLAG_MULTILINE)); duk_def_prop_stridx(ctx, -2, DUK_STRIDX_MULTILINE, DUK_PROPDESC_FLAGS_NONE); duk_push_int(ctx, 0); duk_def_prop_stridx(ctx, -2, DUK_STRIDX_LAST_INDEX, DUK_PROPDESC_FLAGS_W); /* [ ... regexp_object ] */ }
duk_ret_t duk_bi_boolean_constructor(duk_context *ctx) { duk_hobject *h_this; duk_to_boolean(ctx, 0); if (duk_is_constructor_call(ctx)) { /* FIXME: helper; rely on Boolean.prototype as being non-writable, non-configurable */ duk_push_this(ctx); h_this = duk_get_hobject(ctx, -1); DUK_ASSERT(h_this != NULL); DUK_ASSERT(h_this->prototype == ((duk_hthread *) ctx)->builtins[DUK_BIDX_BOOLEAN_PROTOTYPE]); DUK_HOBJECT_SET_CLASS_NUMBER(h_this, DUK_HOBJECT_CLASS_BOOLEAN); duk_dup(ctx, 0); /* -> [ val obj val ] */ duk_def_prop_stridx(ctx, -2, DUK_STRIDX_INT_VALUE, DUK_PROPDESC_FLAGS_NONE); /* FIXME: proper flags? */ } /* unbalanced stack */ return 1; }
int duk_bi_error_constructor_shared(duk_context *ctx) { /* Behavior for constructor and non-constructor call is * the same except for augmenting the created error. When * called as a constructor, the caller (duk_new()) will handle * augmentation; when called as normal function, we need to do * it here. */ duk_hthread *thr = (duk_hthread *) ctx; int bidx_prototype = duk_get_magic(ctx); /* same for both error and each subclass like TypeError */ int flags_and_class = DUK_HOBJECT_FLAG_EXTENSIBLE | DUK_HOBJECT_CLASS_AS_FLAGS(DUK_HOBJECT_CLASS_ERROR); DUK_UNREF(thr); duk_push_object_helper(ctx, flags_and_class, bidx_prototype); /* If message is undefined, the own property 'message' is not set at * all to save property space. An empty message is inherited anyway. */ if (!duk_is_undefined(ctx, 0)) { duk_to_string(ctx, 0); duk_dup(ctx, 0); /* [ message error message ] */ duk_def_prop_stridx(ctx, -2, DUK_STRIDX_MESSAGE, DUK_PROPDESC_FLAGS_WC); } /* Augment the error if called as a normal function. __FILE__ and __LINE__ * are not desirable in this case. */ #ifdef DUK_USE_AUGMENT_ERROR_CREATE if (!duk_is_constructor_call(ctx)) { duk_err_augment_error_create(thr, thr, NULL, 0, 1 /*noblame_fileline*/); } #endif return 1; }
int duk_bi_buffer_constructor(duk_context *ctx) { if (duk_get_top(ctx) == 0) { (void) duk_push_fixed_buffer(ctx, 0); } else { duk_to_buffer(ctx, 0, NULL); } DUK_ASSERT(duk_is_buffer(ctx, 0)); duk_set_top(ctx, 1); if (duk_is_constructor_call(ctx)) { duk_push_object_helper(ctx, DUK_HOBJECT_FLAG_EXTENSIBLE | DUK_HOBJECT_FLAG_SPECIAL_BUFFEROBJ | DUK_HOBJECT_CLASS_AS_FLAGS(DUK_HOBJECT_CLASS_BUFFER), DUK_BIDX_BUFFER_PROTOTYPE); /* Buffer object internal value is immutable */ duk_dup(ctx, 0); duk_def_prop_stridx(ctx, -2, DUK_STRIDX_INT_VALUE, DUK_PROPDESC_FLAGS_NONE); } /* Note: unbalanced stack on purpose */ 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; }
int duk_bi_buffer_constructor(duk_context *ctx) { duk_size_t buf_size; duk_small_int_t buf_dynamic; duk_uint8_t *buf_data; const duk_uint8_t *src_data; duk_hobject *h_obj; /* * Constructor arguments are currently somewhat compatible with * (keep it that way if possible): * * http://nodejs.org/api/buffer.html * */ buf_dynamic = duk_get_boolean(ctx, 1); /* default to false */ switch (duk_get_type(ctx, 0)) { case DUK_TYPE_NUMBER: /* new buffer of specified size */ buf_size = (duk_size_t) duk_to_int(ctx, 0); (void) duk_push_buffer(ctx, buf_size, buf_dynamic); break; case DUK_TYPE_BUFFER: /* return input buffer, converted to a Buffer object if called as a * constructor (no change if called as a function). */ duk_set_top(ctx, 1); break; case DUK_TYPE_STRING: /* new buffer with string contents */ src_data = (const duk_uint8_t *) duk_get_lstring(ctx, 0, &buf_size); DUK_ASSERT(src_data != NULL); /* even for zero-length string */ buf_data = (duk_uint8_t *) duk_push_buffer(ctx, buf_size, buf_dynamic); DUK_MEMCPY((void *) buf_data, (const void *) src_data, (size_t) buf_size); break; case DUK_TYPE_OBJECT: /* Buffer object: get the plain buffer inside. If called as as * constructor, a new Buffer object pointing to the same plain * buffer is created below. */ h_obj = duk_get_hobject(ctx, 0); DUK_ASSERT(h_obj != NULL); if (DUK_HOBJECT_GET_CLASS_NUMBER(h_obj) != DUK_HOBJECT_CLASS_BUFFER) { return DUK_RET_TYPE_ERROR; } duk_get_prop_stridx(ctx, 0, DUK_STRIDX_INT_VALUE); DUK_ASSERT(duk_is_buffer(ctx, -1)); break; case DUK_TYPE_NONE: default: return DUK_RET_TYPE_ERROR; } /* stack is unbalanced, but: [ <something> buf ] */ if (duk_is_constructor_call(ctx)) { duk_push_object_helper(ctx, DUK_HOBJECT_FLAG_EXTENSIBLE | DUK_HOBJECT_FLAG_EXOTIC_BUFFEROBJ | DUK_HOBJECT_CLASS_AS_FLAGS(DUK_HOBJECT_CLASS_BUFFER), DUK_BIDX_BUFFER_PROTOTYPE); /* Buffer object internal value is immutable */ duk_dup(ctx, -2); duk_def_prop_stridx(ctx, -2, DUK_STRIDX_INT_VALUE, DUK_PROPDESC_FLAGS_NONE); } /* Note: unbalanced stack on purpose */ return 1; }
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; }
void duk_err_augment_error(duk_hthread *thr, duk_hthread *thr_callstack, int err_index, const char *filename, int line, int noblame_fileline) { duk_context *ctx = (duk_context *) thr; duk_hobject *obj; DUK_ASSERT(thr != NULL); DUK_ASSERT(thr_callstack != NULL); DUK_ASSERT(ctx != NULL); err_index = duk_require_normalize_index(ctx, err_index); /* * Criteria for augmenting: * * - augmentation enabled in build (naturally) * - error value is an extensible object * - error value internal prototype chain contains the built-in * Error prototype object (i.e. 'val instanceof Error') */ obj = duk_require_hobject(ctx, err_index); if (!obj) { DUK_DDDPRINT("error value not an object, not augmented"); return; } if (!DUK_HOBJECT_HAS_EXTENSIBLE(obj)) { DUK_DDDPRINT("error value not extensible, not augmented"); return; } if (!duk_hobject_prototype_chain_contains(thr, obj, thr->builtins[DUK_BIDX_ERROR_PROTOTYPE])) { DUK_DDDPRINT("error value not inherited from Error, not augmented"); return; } /* Yes, augment error. */ #ifdef DUK_USE_TRACEBACKS /* * If tracebacks are enabled, the 'tracedata' property is the only * thing we need: 'fileName' and 'lineNumber' are virtual properties * which use 'tracedata'. */ if (duk_hobject_hasprop_raw(thr, obj, DUK_HTHREAD_STRING_TRACEDATA(thr))) { DUK_DDDPRINT("error value already has a 'traceback' property, not modifying it"); } else { add_traceback(thr, thr_callstack, obj, err_index, filename, line, noblame_fileline); } #else /* * If tracebacks are disabled, 'fileName' and 'lineNumber' are added * as plain own properties. Since Error.prototype has accessors of * the same name, we need to define own properties directly (cannot * just use e.g. duk_put_prop_stridx). Existing properties are not * overwritten in case they already exist. */ if (filename && !noblame_fileline) { /* FIXME: file/line is disabled in minimal builds, so disable this too * when appropriate. */ duk_push_string(ctx, filename); duk_def_prop_stridx(ctx, err_index, DUK_STRIDX_FILE_NAME, DUK_PROPDESC_FLAGS_WC | DUK_PROPDESC_FLAG_NO_OVERWRITE); duk_push_int(ctx, line); duk_def_prop_stridx(ctx, err_index, DUK_STRIDX_LINE_NUMBER, DUK_PROPDESC_FLAGS_WC | DUK_PROPDESC_FLAG_NO_OVERWRITE); } else if (thr_callstack->callstack_top > 0) { duk_activation *act; duk_hobject *func; duk_hbuffer *pc2line; act = thr_callstack->callstack + thr_callstack->callstack_top - 1; DUK_ASSERT(act >= thr_callstack->callstack && act < thr_callstack->callstack + thr_callstack->callstack_size); func = act->func; if (func) { int pc; duk_uint32_t line; /* PC points to next instruction, find offending PC. Note that * PC == 0 for native code. */ pc = act->pc; if (pc > 0) { pc--; } DUK_ASSERT(pc >= 0 && (double) pc < DUK_DOUBLE_2TO32); /* assume PC is at most 32 bits and non-negative */ act = NULL; /* invalidated by pushes, so get out of the way */ duk_push_hobject(ctx, func); duk_get_prop_stridx(ctx, -1, DUK_STRIDX_FILE_NAME); duk_def_prop_stridx(ctx, err_index, DUK_STRIDX_FILE_NAME, DUK_PROPDESC_FLAGS_WC | DUK_PROPDESC_FLAG_NO_OVERWRITE); if (DUK_HOBJECT_IS_COMPILEDFUNCTION(func)) { #if 0 duk_push_number(ctx, pc); duk_def_prop_stridx(ctx, err_index, DUK_STRIDX_PC, DUK_PROPDESC_FLAGS_WC | DUK_PROPDESC_FLAGS_NO_OVERWRITE); #endif duk_get_prop_stridx(ctx, -1, DUK_STRIDX_INT_PC2LINE); if (duk_is_buffer(ctx, -1)) { pc2line = duk_get_hbuffer(ctx, -1); DUK_ASSERT(pc2line != NULL); DUK_ASSERT(!DUK_HBUFFER_HAS_DYNAMIC(pc2line)); line = duk_hobject_pc2line_query((duk_hbuffer_fixed *) pc2line, (duk_uint_fast32_t) pc); duk_push_number(ctx, (double) line); /* FIXME: u32 */ duk_def_prop_stridx(ctx, err_index, DUK_STRIDX_LINE_NUMBER, DUK_PROPDESC_FLAGS_WC | DUK_PROPDESC_FLAG_NO_OVERWRITE); } duk_pop(ctx); } else { /* Native function, no relevant lineNumber. */ } duk_pop(ctx); } } #endif /* DUK_USE_TRACEBACKS */ }
/* FIXME: the implementation now assumes "chained" bound functions, * whereas "collapsed" bound functions (where there is ever only * one bound function which directly points to a non-bound, final * function) would require a "collapsing" implementation which * merges argument lists etc here. */ int duk_bi_function_prototype_bind(duk_context *ctx) { duk_hobject *h_target; int nargs; int i; /* FIXME: stack checks */ /* vararg function, careful arg handling (e.g. thisArg may not be present) */ nargs = duk_get_top(ctx); /* = 1 + arg count */ if (nargs == 0) { duk_push_undefined(ctx); nargs++; } DUK_ASSERT(nargs >= 1); duk_push_this(ctx); if (!duk_is_callable(ctx, -1)) { DUK_DDD(DUK_DDDPRINT("func is not callable")); goto type_error; } /* [ thisArg arg1 ... argN func ] (thisArg+args == nargs total) */ DUK_ASSERT_TOP(ctx, nargs + 1); /* create bound function object */ duk_push_object_helper(ctx, DUK_HOBJECT_FLAG_EXTENSIBLE | DUK_HOBJECT_FLAG_BOUND | DUK_HOBJECT_FLAG_CONSTRUCTABLE | DUK_HOBJECT_CLASS_AS_FLAGS(DUK_HOBJECT_CLASS_FUNCTION), DUK_BIDX_FUNCTION_PROTOTYPE); /* FIXME: check hobject flags (e.g. strict) */ /* [ thisArg arg1 ... argN func boundFunc ] */ duk_dup(ctx, -2); /* func */ duk_def_prop_stridx(ctx, -2, DUK_STRIDX_INT_TARGET, DUK_PROPDESC_FLAGS_NONE); duk_dup(ctx, 0); /* thisArg */ duk_def_prop_stridx(ctx, -2, DUK_STRIDX_INT_THIS, DUK_PROPDESC_FLAGS_NONE); duk_push_array(ctx); /* [ thisArg arg1 ... argN func boundFunc argArray ] */ for (i = 0; i < nargs - 1; i++) { duk_dup(ctx, 1 + i); duk_put_prop_index(ctx, -2, i); } duk_def_prop_stridx(ctx, -2, DUK_STRIDX_INT_ARGS, DUK_PROPDESC_FLAGS_NONE); /* [ thisArg arg1 ... argN func boundFunc ] */ /* bound function 'length' property is interesting */ h_target = duk_get_hobject(ctx, -2); DUK_ASSERT(h_target != NULL); if (DUK_HOBJECT_GET_CLASS_NUMBER(h_target) == DUK_HOBJECT_CLASS_FUNCTION) { int tmp; duk_get_prop_stridx(ctx, -2, DUK_STRIDX_LENGTH); tmp = duk_to_int(ctx, -1) - (nargs - 1); /* step 15.a */ duk_pop(ctx); duk_push_int(ctx, (tmp < 0 ? 0 : tmp)); } else { duk_push_int(ctx, 0); } duk_def_prop_stridx(ctx, -2, DUK_STRIDX_LENGTH, DUK_PROPDESC_FLAGS_NONE); /* attrs in E5 Section 15.3.5.1 */ /* caller and arguments must use the same thrower, [[ThrowTypeError]] */ duk_def_prop_stridx_thrower(ctx, -1, DUK_STRIDX_CALLER, DUK_PROPDESC_FLAGS_NONE); duk_def_prop_stridx_thrower(ctx, -1, DUK_STRIDX_LC_ARGUMENTS, DUK_PROPDESC_FLAGS_NONE); /* these non-standard properties are copied for convenience */ /* FIXME: 'copy properties' API call? */ duk_get_prop_stridx(ctx, -2, DUK_STRIDX_NAME); duk_def_prop_stridx(ctx, -2, DUK_STRIDX_NAME, DUK_PROPDESC_FLAGS_WC); duk_get_prop_stridx(ctx, -2, DUK_STRIDX_FILE_NAME); duk_def_prop_stridx(ctx, -2, DUK_STRIDX_FILE_NAME, DUK_PROPDESC_FLAGS_WC); DUK_DDD(DUK_DDDPRINT("created bound function: %!iT", duk_get_tval(ctx, -1))); return 1; type_error: return DUK_RET_TYPE_ERROR; }
static void duk__add_traceback(duk_hthread *thr, duk_hthread *thr_callstack, const char *filename, duk_int_t line, duk_bool_t noblame_fileline) { duk_context *ctx = (duk_context *) thr; duk_small_uint_t depth; duk_int_t i, i_min; duk_uarridx_t arr_idx; 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.txt. */ DUK_DDD(DUK_DDDPRINT("adding traceback to object: %!T", (duk_tval *) duk_get_tval(ctx, -1))); 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. */ /* XXX: optimize: allocate an array part to the necessary size (upwards * estimate) and fill in the values directly into the array part; finally * update 'length'. */ /* XXX: 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 heavier. * The best fix is to fill in the tracedata directly into the array part. */ /* [ ... error arr ] */ if (filename) { duk_push_string(ctx, filename); duk_def_prop_index_wec(ctx, -2, arr_idx); arr_idx++; d = (noblame_fileline ? ((duk_double_t) DUK_TB_FLAG_NOBLAME_FILELINE) * DUK_DOUBLE_2TO32 : 0.0) + (duk_double_t) line; duk_push_number(ctx, d); duk_def_prop_index_wec(ctx, -2, arr_idx); 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 > (duk_size_t) depth ? (duk_int_t) (thr_callstack->callstack_top - depth) : 0); DUK_ASSERT(i_min >= 0); /* [ ... error 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; /* * 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_DISABLE(thr_callstack->callstack[i].pc >= 0); /* unsigned */ /* add function */ duk_push_hobject(ctx, thr_callstack->callstack[i].func); /* -> [... arr func] */ duk_def_prop_index_wec(ctx, -2, arr_idx); 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_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_push_number(ctx, d); /* -> [... arr num] */ duk_def_prop_index_wec(ctx, -2, arr_idx); arr_idx++; } /* XXX: set with duk_hobject_set_length() when tracedata is filled directly */ duk_push_uint(ctx, (duk_uint_t) arr_idx); duk_def_prop_stridx(ctx, -2, DUK_STRIDX_LENGTH, DUK_PROPDESC_FLAGS_WC); /* [ ... error arr ] */ duk_def_prop_stridx_wec(ctx, -2, DUK_STRIDX_TRACEDATA); /* -> [ ... error ] */ }
static void duk__err_augment_builtin_throw(duk_hthread *thr, duk_hthread *thr_callstack, const char *filename, duk_int_t line, duk_small_int_t noblame_fileline, duk_hobject *obj) { duk_context *ctx = (duk_context *) thr; #ifdef DUK_USE_ASSERTIONS duk_int_t entry_top; #endif #ifdef DUK_USE_ASSERTIONS entry_top = duk_get_top(ctx); #endif DUK_ASSERT(obj != NULL); DUK_UNREF(obj); /* unreferenced w/o tracebacks */ DUK_UNREF(ctx); /* unreferenced w/ tracebacks */ #ifdef DUK_USE_TRACEBACKS /* * If tracebacks are enabled, the 'tracedata' property is the only * thing we need: 'fileName' and 'lineNumber' are virtual properties * which use 'tracedata'. */ if (duk_hobject_hasprop_raw(thr, obj, DUK_HTHREAD_STRING_TRACEDATA(thr))) { DUK_DDD(DUK_DDDPRINT("error value already has a 'tracedata' property, not modifying it")); } else { duk__add_traceback(thr, thr_callstack, filename, line, noblame_fileline); } #else /* * If tracebacks are disabled, 'fileName' and 'lineNumber' are added * as plain own properties. Since Error.prototype has accessors of * the same name, we need to define own properties directly (cannot * just use e.g. duk_put_prop_stridx). Existing properties are not * overwritten in case they already exist. */ if (filename && !noblame_fileline) { /* XXX: file/line is disabled in minimal builds, so disable this too * when appropriate. */ duk_push_string(ctx, filename); duk_def_prop_stridx(ctx, -2, DUK_STRIDX_FILE_NAME, DUK_PROPDESC_FLAGS_WC | DUK_PROPDESC_FLAG_NO_OVERWRITE); duk_push_int(ctx, line); duk_def_prop_stridx(ctx, -2, DUK_STRIDX_LINE_NUMBER, DUK_PROPDESC_FLAGS_WC | DUK_PROPDESC_FLAG_NO_OVERWRITE); } else if (thr_callstack->callstack_top > 0) { duk_activation *act; duk_hobject *func; act = thr_callstack->callstack + thr_callstack->callstack_top - 1; DUK_ASSERT(act >= thr_callstack->callstack && act < thr_callstack->callstack + thr_callstack->callstack_size); func = act->func; if (func) { duk_uint32_t pc; duk_uint32_t line; /* PC points to next instruction, find offending PC. Note that * PC == 0 for native code. */ pc = act->pc; if (pc > 0) { pc--; } 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 */ act = NULL; /* invalidated by pushes, so get out of the way */ duk_push_hobject(ctx, func); /* [ ... error func ] */ duk_get_prop_stridx(ctx, -1, DUK_STRIDX_FILE_NAME); duk_def_prop_stridx(ctx, -3, DUK_STRIDX_FILE_NAME, DUK_PROPDESC_FLAGS_WC | DUK_PROPDESC_FLAG_NO_OVERWRITE); if (DUK_HOBJECT_IS_COMPILEDFUNCTION(func)) { #if 0 duk_push_number(ctx, pc); duk_def_prop_stridx(ctx, -3, DUK_STRIDX_PC, DUK_PROPDESC_FLAGS_WC | DUK_PROPDESC_FLAGS_NO_OVERWRITE); #endif line = duk_hobject_pc2line_query(ctx, -1, (duk_uint_fast32_t) pc); if (line > 0) { duk_push_u32(ctx, (duk_uint32_t) line); /* -> [ ... error func line ] */ duk_def_prop_stridx(ctx, -3, DUK_STRIDX_LINE_NUMBER, DUK_PROPDESC_FLAGS_WC | DUK_PROPDESC_FLAG_NO_OVERWRITE); } } else { /* Native function, no relevant lineNumber. */ } duk_pop(ctx); } } #endif /* DUK_USE_TRACEBACKS */ #ifdef DUK_USE_ASSERTIONS DUK_ASSERT(duk_get_top(ctx) == entry_top); #endif }
void duk_hthread_create_builtin_objects(duk_hthread *thr) { duk_context *ctx = (duk_context *) thr; duk_bitdecoder_ctx bd_ctx; duk_bitdecoder_ctx *bd = &bd_ctx; /* convenience */ duk_hobject *h; int i, j; DUK_DPRINT("INITBUILTINS BEGIN"); DUK_MEMZERO(&bd_ctx, sizeof(bd_ctx)); bd->data = (const duk_uint8_t *) duk_builtins_data; bd->length = (duk_size_t) DUK_BUILTINS_DATA_LENGTH; /* * First create all built-in bare objects on the empty valstack. * During init, their indices will correspond to built-in indices. * * Built-ins will be reachable from both valstack and thr->builtins. */ /* XXX: there is no need to resize valstack because builtin count * is much less than the default space; assert for it. */ DUK_DDPRINT("create empty built-ins"); DUK_ASSERT_TOP(ctx, 0); for (i = 0; i < DUK_NUM_BUILTINS; i++) { int class_num; int len = -1; class_num = duk_bd_decode(bd, DUK__CLASS_BITS); len = duk_bd_decode_flagged(bd, DUK__LENGTH_PROP_BITS, (duk_int32_t) -1 /*def_value*/); if (class_num == DUK_HOBJECT_CLASS_FUNCTION) { int natidx; int stridx; int c_nargs; duk_c_function c_func; duk_int16_t magic; DUK_DDDPRINT("len=%d", len); DUK_ASSERT(len >= 0); natidx = duk_bd_decode(bd, DUK__NATIDX_BITS); stridx = duk_bd_decode(bd, DUK__STRIDX_BITS); c_func = duk_bi_native_functions[natidx]; c_nargs = duk_bd_decode_flagged(bd, DUK__NARGS_BITS, len /*def_value*/); if (c_nargs == DUK__NARGS_VARARGS_MARKER) { c_nargs = DUK_VARARGS; } /* FIXME: set magic directly here? (it could share the c_nargs arg) */ duk_push_c_function_nospecial(ctx, c_func, c_nargs); h = duk_require_hobject(ctx, -1); DUK_ASSERT(h != NULL); /* Currently all built-in native functions are strict. * duk_push_c_function() now sets strict flag, so * assert for it. */ DUK_ASSERT(DUK_HOBJECT_HAS_STRICT(h)); /* FIXME: function properties */ duk_push_hstring_stridx(ctx, stridx); duk_def_prop_stridx(ctx, -2, DUK_STRIDX_NAME, DUK_PROPDESC_FLAGS_NONE); /* Almost all global level Function objects are constructable * but not all: Function.prototype is a non-constructable, * callable Function. */ if (duk_bd_decode_flag(bd)) { DUK_ASSERT(DUK_HOBJECT_HAS_CONSTRUCTABLE(h)); } else { DUK_HOBJECT_CLEAR_CONSTRUCTABLE(h); } /* Cast converts magic to 16-bit signed value */ magic = (duk_int16_t) duk_bd_decode_flagged(bd, DUK__MAGIC_BITS, 0 /*def_value*/); ((duk_hnativefunction *) h)->magic = magic; } else { /* FIXME: ARRAY_PART for Array prototype? */ duk_push_object_helper(ctx, DUK_HOBJECT_FLAG_EXTENSIBLE, -1); /* no prototype or class yet */ h = duk_require_hobject(ctx, -1); DUK_ASSERT(h != NULL); } DUK_HOBJECT_SET_CLASS_NUMBER(h, class_num); thr->builtins[i] = h; DUK_HOBJECT_INCREF(thr, &h->hdr); if (len >= 0) { /* * For top-level objects, 'length' property has the following * default attributes: non-writable, non-enumerable, non-configurable * (E5 Section 15). * * However, 'length' property for Array.prototype has attributes * expected of an Array instance which are different: writable, * non-enumerable, non-configurable (E5 Section 15.4.5.2). * * This is currently determined implicitly based on class; there are * no attribute flags in the init data. */ duk_push_int(ctx, len); duk_def_prop_stridx(ctx, -2, DUK_STRIDX_LENGTH, (class_num == DUK_HOBJECT_CLASS_ARRAY ? /* only Array.prototype matches */ DUK_PROPDESC_FLAGS_W : DUK_PROPDESC_FLAGS_NONE)); } /* enable special behaviors last */ if (class_num == DUK_HOBJECT_CLASS_ARRAY) { DUK_HOBJECT_SET_SPECIAL_ARRAY(h); } if (class_num == DUK_HOBJECT_CLASS_STRING) { DUK_HOBJECT_SET_SPECIAL_STRINGOBJ(h); } /* some assertions */ DUK_ASSERT(DUK_HOBJECT_HAS_EXTENSIBLE(h)); /* DUK_HOBJECT_FLAG_CONSTRUCTABLE varies */ DUK_ASSERT(!DUK_HOBJECT_HAS_BOUND(h)); DUK_ASSERT(!DUK_HOBJECT_HAS_COMPILEDFUNCTION(h)); /* DUK_HOBJECT_FLAG_NATIVEFUNCTION varies */ DUK_ASSERT(!DUK_HOBJECT_HAS_THREAD(h)); DUK_ASSERT(!DUK_HOBJECT_HAS_ARRAY_PART(h)); /* currently, even for Array.prototype */ /* DUK_HOBJECT_FLAG_STRICT varies */ DUK_ASSERT(!DUK_HOBJECT_HAS_NATIVEFUNCTION(h) || /* all native functions have NEWENV */ DUK_HOBJECT_HAS_NEWENV(h)); DUK_ASSERT(!DUK_HOBJECT_HAS_NAMEBINDING(h)); DUK_ASSERT(!DUK_HOBJECT_HAS_CREATEARGS(h)); DUK_ASSERT(!DUK_HOBJECT_HAS_ENVRECCLOSED(h)); /* DUK_HOBJECT_FLAG_SPECIAL_ARRAY varies */ /* DUK_HOBJECT_FLAG_SPECIAL_STRINGOBJ varies */ DUK_ASSERT(!DUK_HOBJECT_HAS_SPECIAL_ARGUMENTS(h)); DUK_DDDPRINT("created built-in %d, class=%d, length=%d", i, class_num, len); } /* * Then decode the builtins init data (see genbuiltins.py) to * init objects */ DUK_DDPRINT("initialize built-in object properties"); for (i = 0; i < DUK_NUM_BUILTINS; i++) { unsigned char t; int num; DUK_DDDPRINT("initializing built-in object at index %d", i); h = thr->builtins[i]; t = duk_bd_decode(bd, DUK__BIDX_BITS); if (t != DUK__NO_BIDX_MARKER) { DUK_DDDPRINT("set internal prototype: built-in %d", (int) t); DUK_HOBJECT_SET_PROTOTYPE_UPDREF(thr, h, thr->builtins[t]); } t = duk_bd_decode(bd, DUK__BIDX_BITS); if (t != DUK__NO_BIDX_MARKER) { /* 'prototype' property for all built-in objects (which have it) has attributes: * [[Writable]] = false, * [[Enumerable]] = false, * [[Configurable]] = false */ DUK_DDDPRINT("set external prototype: built-in %d", (int) t); duk_def_prop_stridx_builtin(ctx, i, DUK_STRIDX_PROTOTYPE, t, DUK_PROPDESC_FLAGS_NONE); } t = duk_bd_decode(bd, DUK__BIDX_BITS); if (t != DUK__NO_BIDX_MARKER) { /* 'constructor' property for all built-in objects (which have it) has attributes: * [[Writable]] = true, * [[Enumerable]] = false, * [[Configurable]] = true */ DUK_DDDPRINT("set external constructor: built-in %d", (int) t); duk_def_prop_stridx_builtin(ctx, i, DUK_STRIDX_CONSTRUCTOR, t, DUK_PROPDESC_FLAGS_WC); } /* normal valued properties */ num = duk_bd_decode(bd, DUK__NUM_NORMAL_PROPS_BITS); DUK_DDDPRINT("built-in object %d, %d normal valued properties", i, num); for (j = 0; j < num; j++) { int stridx; int prop_flags; stridx = duk_bd_decode(bd, DUK__STRIDX_BITS); /* * Property attribute defaults are defined in E5 Section 15 (first * few pages); there is a default for all properties and a special * default for 'length' properties. Variation from the defaults is * signaled using a single flag bit in the bitstream. */ if (duk_bd_decode_flag(bd)) { prop_flags = duk_bd_decode(bd, DUK__PROP_FLAGS_BITS); } else { if (stridx == DUK_STRIDX_LENGTH) { prop_flags = DUK_PROPDESC_FLAGS_NONE; } else { prop_flags = DUK_PROPDESC_FLAGS_WC; } } t = duk_bd_decode(bd, DUK__PROP_TYPE_BITS); DUK_DDDPRINT("built-in %d, normal-valued property %d, stridx %d, flags 0x%02x, type %d", i, j, stridx, prop_flags, (int) t); switch (t) { case DUK__PROP_TYPE_DOUBLE: { duk_double_union du; int k; for (k = 0; k < 8; k++) { /* Encoding endianness must match target memory layout, * build scripts and genbuiltins.py must ensure this. */ du.uc[k] = (duk_uint8_t) duk_bd_decode(bd, 8); } duk_push_number(ctx, du.d); /* push operation normalizes NaNs */ break; } case DUK__PROP_TYPE_STRING: { int n; int k; char *p; n = duk_bd_decode(bd, DUK__STRING_LENGTH_BITS); p = (char *) duk_push_fixed_buffer(ctx, n); for (k = 0; k < n; k++) { *p++ = duk_bd_decode(bd, DUK__STRING_CHAR_BITS); } duk_to_string(ctx, -1); break; } case DUK__PROP_TYPE_STRIDX: { int n; n = duk_bd_decode(bd, DUK__STRIDX_BITS); DUK_ASSERT(n >= 0 && n < DUK_HEAP_NUM_STRINGS); duk_push_hstring_stridx(ctx, n); break; } case DUK__PROP_TYPE_BUILTIN: { int bidx; bidx = duk_bd_decode(bd, DUK__BIDX_BITS); DUK_ASSERT(bidx != DUK__NO_BIDX_MARKER); duk_dup(ctx, bidx); break; } case DUK__PROP_TYPE_UNDEFINED: { duk_push_undefined(ctx); break; } case DUK__PROP_TYPE_BOOLEAN_TRUE: { duk_push_true(ctx); break; } case DUK__PROP_TYPE_BOOLEAN_FALSE: { duk_push_false(ctx); break; } case DUK__PROP_TYPE_ACCESSOR: { int natidx_getter = duk_bd_decode(bd, DUK__NATIDX_BITS); int natidx_setter = duk_bd_decode(bd, DUK__NATIDX_BITS); duk_c_function c_func_getter; duk_c_function c_func_setter; /* XXX: this is a bit awkward because there is no exposed helper * in the API style, only this internal helper. */ DUK_DDDPRINT("built-in accessor property: objidx=%d, stridx=%d, getteridx=%d, setteridx=%d, flags=0x%04x", i, stridx, natidx_getter, natidx_setter, prop_flags); c_func_getter = duk_bi_native_functions[natidx_getter]; c_func_setter = duk_bi_native_functions[natidx_setter]; duk_push_c_function_noconstruct_nospecial(ctx, c_func_getter, 0); /* always 0 args */ duk_push_c_function_noconstruct_nospecial(ctx, c_func_setter, 1); /* always 1 arg */ /* FIXME: magic for getter/setter? */ prop_flags |= DUK_PROPDESC_FLAG_ACCESSOR; /* accessor flag not encoded explicitly */ duk_hobject_define_accessor_internal(thr, duk_require_hobject(ctx, i), DUK_HTHREAD_GET_STRING(thr, stridx), duk_require_hobject(ctx, -2), duk_require_hobject(ctx, -1), prop_flags); duk_pop_2(ctx); /* getter and setter, now reachable through object */ goto skip_value; } default: { /* exhaustive */ DUK_UNREACHABLE(); } } DUK_ASSERT((prop_flags & DUK_PROPDESC_FLAG_ACCESSOR) == 0); duk_def_prop_stridx(ctx, i, stridx, prop_flags); skip_value: continue; /* avoid empty label at the end of a compound statement */ } /* native function properties */ num = duk_bd_decode(bd, DUK__NUM_FUNC_PROPS_BITS); DUK_DDDPRINT("built-in object %d, %d function valued properties", i, num); for (j = 0; j < num; j++) { int stridx; int natidx; int c_nargs; int c_length; duk_int16_t magic; duk_c_function c_func; duk_hnativefunction *h_func; stridx = duk_bd_decode(bd, DUK__STRIDX_BITS); natidx = duk_bd_decode(bd, DUK__NATIDX_BITS); c_length = duk_bd_decode(bd, DUK__LENGTH_PROP_BITS); c_nargs = duk_bd_decode_flagged(bd, DUK__NARGS_BITS, (duk_int32_t) c_length /*def_value*/); if (c_nargs == DUK__NARGS_VARARGS_MARKER) { c_nargs = DUK_VARARGS; } c_func = duk_bi_native_functions[natidx]; DUK_DDDPRINT("built-in %d, function-valued property %d, stridx %d, natidx %d, length %d, nargs %d", i, j, stridx, natidx, c_length, (c_nargs == DUK_VARARGS ? -1 : c_nargs)); /* [ (builtin objects) ] */ duk_push_c_function_noconstruct_nospecial(ctx, c_func, c_nargs); h_func = duk_require_hnativefunction(ctx, -1); DUK_UNREF(h_func); /* Currently all built-in native functions are strict. * This doesn't matter for many functions, but e.g. * String.prototype.charAt (and other string functions) * rely on being strict so that their 'this' binding is * not automatically coerced. */ DUK_HOBJECT_SET_STRICT((duk_hobject *) h_func); /* No built-in functions are constructable except the top * level ones (Number, etc). */ DUK_ASSERT(!DUK_HOBJECT_HAS_CONSTRUCTABLE((duk_hobject *) h_func)); /* FIXME: any way to avoid decoding magic bit; there are quite * many function properties and relatively few with magic values. */ /* Cast converts magic to 16-bit signed value */ magic = (duk_int16_t) duk_bd_decode_flagged(bd, DUK__MAGIC_BITS, 0); h_func->magic = magic; /* [ (builtin objects) func ] */ duk_push_int(ctx, c_length); duk_def_prop_stridx(ctx, -2, DUK_STRIDX_LENGTH, DUK_PROPDESC_FLAGS_NONE); duk_push_hstring_stridx(ctx, stridx); duk_def_prop_stridx(ctx, -2, DUK_STRIDX_NAME, DUK_PROPDESC_FLAGS_NONE); /* FIXME: other properties of function instances; 'arguments', 'caller'. */ DUK_DDPRINT("built-in object %d, function property %d -> %!T", i, j, duk_get_tval(ctx, -1)); /* [ (builtin objects) func ] */ /* * The default property attributes are correct for all * function valued properties of built-in objects now. */ duk_def_prop_stridx(ctx, i, stridx, DUK_PROPDESC_FLAGS_WC); /* [ (builtin objects) ] */ } } /* * Special post-tweaks, for cases not covered by the init data format. * * - Set Date.prototype.toGMTString to Date.prototype.toUTCString. * toGMTString is required to have the same Function object as * toUTCString in E5 Section B.2.6. Note that while Smjs respects * this, V8 does not (the Function objects are distinct). * * - Make DoubleError non-extensible. * * - Add info about most important effective compile options to Duktape. */ duk_get_prop_stridx(ctx, DUK_BIDX_DATE_PROTOTYPE, DUK_STRIDX_TO_UTC_STRING); duk_def_prop_stridx(ctx, DUK_BIDX_DATE_PROTOTYPE, DUK_STRIDX_TO_GMT_STRING, DUK_PROPDESC_FLAGS_WC); h = duk_require_hobject(ctx, DUK_BIDX_DOUBLE_ERROR); DUK_ASSERT(h != NULL); DUK_HOBJECT_CLEAR_EXTENSIBLE(h); duk_push_string(ctx, #if defined(DUK_USE_INTEGER_LE) "l" #elif defined(DUK_USE_INTEGER_BE) "b" #elif defined(DUK_USE_INTEGER_ME) /* integer mixed endian not really used now */ "m" #else "?" #endif #if defined(DUK_USE_DOUBLE_LE) "l" #elif defined(DUK_USE_DOUBLE_BE) "b" #elif defined(DUK_USE_DOUBLE_ME) "m" #else "?" #endif #if defined(DUK_USE_BYTEORDER_FORCED) "f" #endif " " #if defined(DUK_USE_PACKED_TVAL) "p" #else "u" #endif " " #if defined(DUK_USE_HOBJECT_LAYOUT_1) "p1" #elif defined(DUK_USE_HOBJECT_LAYOUT_2) "p2" #elif defined(DUK_USE_HOBJECT_LAYOUT_3) "p3" #else "p?" #endif " " #if defined(DUK_USE_ALIGN_4) "a4" #elif defined(DUK_USE_ALIGN_8) "a8" #else "a1" #endif " " DUK_USE_ARCH_STRING); duk_def_prop_stridx(ctx, DUK_BIDX_DUKTAPE, DUK_STRIDX_ENV, DUK_PROPDESC_FLAGS_WC); /* * InitJS code - Ecmascript code evaluated from a built-in source * which provides e.g. backward compatibility. User can also provide * JS code to be evaluated at startup. */ #ifdef DUK_USE_INITJS /* FIXME: compression */ duk_eval_string(ctx, (const char *) duk_initjs_data); /* initjs data is NUL terminated */ duk_pop(ctx); #endif /* DUK_USE_INITJS */ #ifdef DUK_USE_USER_INITJS /* FIXME: compression, at least as an option? */ /* FIXME: unused now */ duk_eval_string(ctx, (const char *) DUK_USE_USER_INITJS); duk_pop(ctx); #endif /* DUK_USE_USER_INITJS */ /* * Since built-ins are not often extended, compact them. */ DUK_DDPRINT("compact built-ins"); for (i = 0; i < DUK_NUM_BUILTINS; i++) { duk_hobject_compact_props(thr, thr->builtins[i]); } DUK_DPRINT("INITBUILTINS END"); #ifdef DUK_USE_DDEBUG for (i = 0; i < DUK_NUM_BUILTINS; i++) { DUK_DDPRINT("built-in object %d after initialization and compacting: %!@iO", i, thr->builtins[i]); } #endif #ifdef DUK_USE_DDDEBUG for (i = 0; i < DUK_NUM_BUILTINS; i++) { DUK_DDDPRINT("built-in object %d after initialization and compacting", i); DUK_DEBUG_DUMP_HOBJECT(thr->builtins[i]); } #endif /* * Pop built-ins from stack: they are now INCREF'd and * reachable from the builtins[] array. */ duk_pop_n(ctx, DUK_NUM_BUILTINS); DUK_ASSERT_TOP(ctx, 0); }