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; }
/* Shared helper for Object.getOwnPropertyNames() and Object.keys(). * Magic: 0=getOwnPropertyNames, 1=Object.keys. */ DUK_INTERNAL duk_ret_t duk_bi_object_constructor_keys_shared(duk_context *ctx) { duk_hthread *thr = (duk_hthread *) ctx; duk_hobject *obj; #if defined(DUK_USE_ES6_PROXY) duk_hobject *h_proxy_target; duk_hobject *h_proxy_handler; duk_hobject *h_trap_result; duk_uarridx_t i, len, idx; #endif duk_small_uint_t enum_flags; DUK_ASSERT_TOP(ctx, 1); DUK_UNREF(thr); obj = duk_require_hobject_or_lfunc_coerce(ctx, 0); DUK_ASSERT(obj != NULL); DUK_UNREF(obj); #if defined(DUK_USE_ES6_PROXY) if (DUK_LIKELY(!duk_hobject_proxy_check(thr, obj, &h_proxy_target, &h_proxy_handler))) { goto skip_proxy; } duk_push_hobject(ctx, h_proxy_handler); if (!duk_get_prop_stridx(ctx, -1, DUK_STRIDX_OWN_KEYS)) { /* Careful with reachability here: don't pop 'obj' before pushing * proxy target. */ DUK_DDD(DUK_DDDPRINT("no ownKeys trap, get keys of target instead")); duk_pop_2(ctx); duk_push_hobject(ctx, h_proxy_target); duk_replace(ctx, 0); DUK_ASSERT_TOP(ctx, 1); goto skip_proxy; } /* [ obj handler trap ] */ duk_insert(ctx, -2); duk_push_hobject(ctx, h_proxy_target); /* -> [ obj trap handler target ] */ duk_call_method(ctx, 1 /*nargs*/); /* -> [ obj trap_result ] */ h_trap_result = duk_require_hobject(ctx, -1); DUK_UNREF(h_trap_result); len = (duk_uarridx_t) duk_get_length(ctx, -1); idx = 0; duk_push_array(ctx); for (i = 0; i < len; i++) { /* [ obj trap_result res_arr ] */ if (duk_get_prop_index(ctx, -2, i) && duk_is_string(ctx, -1)) { /* XXX: for Object.keys() we should check enumerability of key */ /* [ obj trap_result res_arr propname ] */ duk_put_prop_index(ctx, -2, idx); idx++; } else { duk_pop(ctx); } } /* XXX: missing trap result validation for non-configurable target keys * (must be present), for non-extensible target all target keys must be * present and no extra keys can be present. * http://www.ecma-international.org/ecma-262/6.0/#sec-proxy-object-internal-methods-and-internal-slots-ownpropertykeys */ /* XXX: for Object.keys() the [[OwnPropertyKeys]] result (trap result) * should be filtered so that only enumerable keys remain. Enumerability * should be checked with [[GetOwnProperty]] on the original object * (i.e., the proxy in this case). If the proxy has a getOwnPropertyDescriptor * trap, it should be triggered for every property. If the proxy doesn't have * the trap, enumerability should be checked against the target object instead. * We don't do any of this now, so Object.keys() and Object.getOwnPropertyNames() * return the same result now for proxy traps. We still do clean up the trap * result, so that Object.keys() and Object.getOwnPropertyNames() will return a * clean array of strings without gaps. */ return 1; skip_proxy: #endif /* DUK_USE_ES6_PROXY */ DUK_ASSERT_TOP(ctx, 1); if (duk_get_current_magic(ctx)) { /* Object.keys */ enum_flags = DUK_ENUM_OWN_PROPERTIES_ONLY | DUK_ENUM_NO_PROXY_BEHAVIOR; } else { /* Object.getOwnPropertyNames */ enum_flags = DUK_ENUM_INCLUDE_NONENUMERABLE | DUK_ENUM_OWN_PROPERTIES_ONLY | DUK_ENUM_NO_PROXY_BEHAVIOR; } return duk_hobject_get_enumerated_keys(ctx, enum_flags); }
static void duk__err_augment_builtin_throw(duk_hthread *thr, duk_hthread *thr_callstack, const char *filename, int line, int 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) { /* FIXME: 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; 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); /* [ ... 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 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); /* -> [ ... error func pc2line line ] */ /* FIXME: u32 */ duk_def_prop_stridx(ctx, -4, 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 */ #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); }
int handle_interactive(duk_context *ctx) { const char *prompt = "duk> "; char *buffer = NULL; int retval = 0; int rc; int got_eof = 0; duk_eval_string(ctx, GREET_CODE(" [no readline]")); duk_pop(ctx); buffer = (char *) malloc(LINEBUF_SIZE); if (!buffer) { fprintf(stderr, "failed to allocated a line buffer\n"); fflush(stderr); retval = -1; goto done; } while (!got_eof) { size_t idx = 0; fwrite(prompt, 1, strlen(prompt), stdout); fflush(stdout); for (;;) { int c = fgetc(stdin); if (c == EOF) { got_eof = 1; break; } else if (c == '\n') { break; } else if (idx >= LINEBUF_SIZE) { fprintf(stderr, "line too long\n"); fflush(stderr); retval = -1; goto done; } else { buffer[idx++] = (char) c; } } duk_push_lstring(ctx, buffer, idx); duk_push_string(ctx, "input"); interactive_mode = 1; /* global */ rc = duk_safe_call(ctx, wrapped_compile_execute, 2 /*nargs*/, 1 /*nret*/); if (rc != DUK_EXEC_SUCCESS) { /* in interactive mode, write to stdout */ print_error(ctx, stdout); retval = -1; /* an error 'taints' the execution */ } else { duk_pop(ctx); } } done: if (buffer) { free(buffer); buffer = NULL; } return retval; }
DUK_INTERNAL duk_ret_t duk_bi_array_prototype_splice(duk_context *ctx) { duk_idx_t nargs; duk_uint32_t len; duk_bool_t have_delcount; duk_int_t item_count; duk_int_t act_start; duk_int_t del_count; duk_int_t i, n; 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; } /* XXX: len >= 0x80000000 won't work below because we need to be * able to represent -len. */ len = duk__push_this_obj_len_u32_limited(ctx); act_start = duk_to_int_clamped(ctx, 0, -((duk_int_t) len), (duk_int_t) len); if (act_start < 0) { act_start = len + act_start; } DUK_ASSERT(act_start >= 0 && act_start <= (duk_int_t) 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(nargs >= 2); item_count = (duk_int_t) (nargs - 2); DUK_ASSERT(del_count >= 0 && del_count <= (duk_int_t) len - act_start); DUK_ASSERT(del_count + act_start <= (duk_int_t) len); /* For now, restrict result array into 32-bit length range. */ if (((duk_double_t) len) - ((duk_double_t) del_count) + ((duk_double_t) item_count) > (duk_double_t) DUK_UINT32_MAX) { DUK_D(DUK_DPRINT("Array.prototype.splice() would go beyond 32-bit length, throw")); return DUK_RET_RANGE_ERROR; } 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, (duk_uarridx_t) (act_start + i))) { duk_xdef_prop_index_wec(ctx, -2, i); /* throw flag irrelevant (false in std alg) */ } else { duk_pop(ctx); } } duk_push_u32(ctx, (duk_uint32_t) del_count); duk_xdef_prop_stridx(ctx, -2, DUK_STRIDX_LENGTH, DUK_PROPDESC_FLAGS_W); /* Steps 12 and 13: reorganize elements to make room for itemCount elements */ 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); n = len - del_count; for (i = act_start; i < n; i++) { if (duk_get_prop_index(ctx, -3, (duk_uarridx_t) (i + del_count))) { duk_put_prop_index(ctx, -4, (duk_uarridx_t) (i + item_count)); } else { duk_pop(ctx); duk_del_prop_index(ctx, -3, (duk_uarridx_t) (i + item_count)); } } DUK_ASSERT_TOP(ctx, nargs + 3); /* loop iterator init and limit changed from standard algorithm */ n = len - del_count + item_count; for (i = len - 1; i >= n; i--) { duk_del_prop_index(ctx, -3, (duk_uarridx_t) i); } 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, (duk_uarridx_t) (i + del_count))) { duk_put_prop_index(ctx, -4, (duk_uarridx_t) (i + item_count)); } else { duk_pop(ctx); duk_del_prop_index(ctx, -3, (duk_uarridx_t) (i + item_count)); } } 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, (duk_uarridx_t) (act_start + i)); } /* Step 16: update length; note that the final length may be above 32 bit range * (but we checked above that this isn't the case here) */ duk_push_u32(ctx, len - del_count + 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; }
static duk_ret_t test_1(duk_context *ctx, void *udata) { duk_size_t i, n; char *buf; (void) udata; duk_set_top(ctx, 0); duk_push_undefined(ctx); duk_push_null(ctx); duk_push_true(ctx); duk_push_false(ctx); duk_push_nan(ctx); duk_push_number(ctx, -INFINITY); duk_push_number(ctx, +INFINITY); duk_push_number(ctx, -0.0); duk_push_number(ctx, +0.0); duk_push_int(ctx, 123); duk_push_string(ctx, "foo"); duk_push_lstring(ctx, "foo\0bar", 7); /* internal NULs are kept */ duk_push_object(ctx); buf = (char *) duk_push_fixed_buffer(ctx, 0); buf = (char *) duk_push_fixed_buffer(ctx, 16); for (i = 0; i < 16; i++) { buf[i] = i; } buf = (char *) duk_push_dynamic_buffer(ctx, 0); buf = (char *) duk_push_dynamic_buffer(ctx, 16); for (i = 0; i < 16; i++) { buf[i] = i; } duk_push_pointer(ctx, (void *) NULL); duk_push_pointer(ctx, (void *) 0xdeadbeef); n = duk_get_top(ctx); printf("top: %ld\n", (long) n); for (i = 0; i < n; i++) { duk_int_t t1, t2; void *ptr; duk_size_t sz; duk_dup(ctx, i); t1 = duk_get_type(ctx, -1); sz = (duk_size_t) 0xdeadbeef; ptr = duk_to_buffer(ctx, -1, &sz); t2 = duk_get_type(ctx, -1); printf("index %ld, type %ld -> %ld, ptr-is-NULL %d, size %lu\n", (long) i, (long) t1, (long) t2, (sz == 0 ? -1 : (ptr == NULL ? 1 : 0)), (unsigned long) sz); dump_buffer(ctx); duk_pop(ctx); /* just check that this doesn't break */ duk_dup(ctx, i); ptr = duk_to_buffer(ctx, -1, NULL); duk_pop(ctx); } return 0; }
static duk_ret_t dukzip_zip_add(duk_context *ctx) { zip_fileinfo zi = {0}; int res = ZIP_OK; zipFile archive = dukzip_zip_from_this(ctx); const char *filename = ""; duk_int_t level = Z_DEFAULT_COMPRESSION; duk_int_t method = Z_DEFLATED; const char *comment = ""; int datalen = 0; void *data = NULL; if (duk_is_object(ctx, 0)) { dukzip_zip_checkoptions(ctx, 0, &filename, &level, &method, &comment); duk_get_prop_string(ctx, 0, "data"); if (duk_is_string(ctx, -1)) { data = (void *)duk_get_lstring(ctx, -1, &datalen); } else if (duk_is_buffer(ctx, -1) || duk_is_object(ctx, -1)) { data = duk_require_buffer_data(ctx, -1, &datalen); } else { duk_error(ctx, DUK_ERR_TYPE_ERROR, "unable to write data to zip file (supported types: string, buffer)"); return -1; } } else { filename = duk_require_string(ctx, 0); if (duk_is_string(ctx, 1)) { data = (void *)duk_get_lstring(ctx, 1, &datalen); } else if (duk_is_buffer(ctx, 1) || duk_is_object(ctx, 1)) { data = duk_require_buffer_data(ctx, 1, &datalen); } else { duk_error(ctx, DUK_ERR_TYPE_ERROR, "unable to write argument to zip file (supported types: string, buffer)"); return -1; } if (duk_is_number(ctx, 2)) { level = duk_get_int(ctx, 2); } /* push dummy to normalize stack */ duk_push_string(ctx, "dummy"); } res = zipOpenNewFileInZip64(archive, filename, &zi, NULL, 0, NULL, 0, comment, method, level, 1); if (res != ZIP_OK) { goto error; } res = zipWriteInFileInZip(archive, data, datalen); if (res != ZIP_OK) { goto error; } res = zipCloseFileInZip(archive); duk_pop(ctx); /* pop buffer (or dummy) from stack */ if (res == ZIP_OK) { duk_push_true(ctx); } else { duk_push_false(ctx); } return 1; error: zipCloseFileInZip(archive); duk_error(ctx, DUK_ERR_INTERNAL_ERROR, "could not write file '%s'", filename); return -1; }
/* in interactive mode, write to stdout */ print_pop_error(ctx, stdout); retval = -1; /* an error 'taints' the execution */ } else { duk_pop(ctx); } } done: if (buffer) { free(buffer); buffer = NULL; } return retval; } #else /* NO_READLINE */ static int handle_interactive(duk_context *ctx) { const char *prompt = "duk> "; char *buffer = NULL; int retval = 0; int rc; duk_eval_string(ctx, GREET_CODE("")); duk_pop(ctx); /* * Note: using readline leads to valgrind-reported leaks inside * readline itself. Execute code from an input file (and not * through stdin) for clean valgrind runs. */ rl_initialize(); for (;;) { if (buffer) { free(buffer); buffer = NULL; } buffer = readline(prompt); if (!buffer) { break; } if (buffer && buffer[0] != (char) 0) { add_history(buffer); } duk_push_pointer(ctx, (void *) buffer); duk_push_uint(ctx, (duk_uint_t) strlen(buffer)); duk_push_string(ctx, "input"); interactive_mode = 1; /* global */ rc = duk_safe_call(ctx, wrapped_compile_execute, 3 /*nargs*/, 1 /*nret*/); #if defined(DUK_CMDLINE_AJSHEAP) ajsheap_clear_exec_timeout(); #endif if (buffer) { free(buffer); buffer = NULL; } if (rc != DUK_EXEC_SUCCESS) { /* in interactive mode, write to stdout */ print_pop_error(ctx, stdout); retval = -1; /* an error 'taints' the execution */ } else { duk_pop(ctx); } } if (buffer) { free(buffer); buffer = NULL; } return retval; }
static int r2plugin(duk_context *ctx) { RLibStruct *lib_struct; int ret = R_TRUE; // args: type, function const char *type = duk_require_string (ctx, 0); if (strcmp (type, "asm")) { eprintf ("TODO: duk.r2plugin only supports 'asm' plugins atm\n"); return R_FALSE; } // call function of 2nd parameter, or get object if (duk_is_function (ctx, 1)) { duk_push_string (ctx, "TODO"); // TODO: this must be the RAsm object to get bits, offset, .. duk_call (ctx, 1); duk_to_object (ctx, 1); } if (!duk_is_object (ctx, 1)) { eprintf ("Expected object or function\n"); return R_FALSE; } duk_to_object (ctx, 1); #define ap asm_plugin ap = R_NEW0 (RAsmPlugin); #define GETSTR(x,y,or) \ duk_dup_top (ctx); \ duk_get_prop_string (ctx, 1, y); \ if (or) { \ const char *str = duk_to_string (ctx, -1); \ x = mystrdup (str? str: or); \ } else { \ x = mystrdup (duk_require_string (ctx, -1)); \ } \ duk_pop (ctx); #define GETINT(x,y,or) \ duk_dup_top (ctx); \ duk_get_prop_string (ctx, 1, y); \ if (or) { \ x = duk_is_number (ctx, -1)? \ duk_to_int (ctx, -1): or; \ } else { \ x = duk_require_int (ctx, -1); \ } \ duk_pop (ctx); #define GETFUN(x,y) \ duk_dup_top (ctx); \ duk_get_prop_string (ctx, 1, y); \ x = duk_require_tval (ctx, 1); \ duk_pop (ctx); // mandatory GETSTR (ap->name, "name", NULL); GETSTR (ap->arch, "arch", NULL); // optional GETSTR (ap->license, "license", "unlicensed"); GETSTR (ap->desc, "description", "JS Disasm Plugin"); GETINT (ap->bits, "bits", 32); // mandatory unless we handle asm+disasm ap->user = duk_require_tval (ctx, -1); //ap->user = duk_dup_top (ctx); // clone object inside user //GETFUN (ap->user, "disassemble"); duk_push_global_stash(ctx); duk_get_prop_string (ctx, 1, "disassemble"); duk_put_prop_string(ctx, -2, "disfun"); // TODO: prefix plugin name somehow ap->disassemble = duk_disasm; duk_push_global_stash(ctx); duk_get_prop_string (ctx, 1, "assemble"); duk_put_prop_string(ctx, -2, "asmfun"); // TODO: prefix plugin name somehow ap->assemble = duk_assemble; #if 0 duk_get_prop_string (ctx, 1, "disassemble"); duk_push_string (ctx, "WINRAR"); duk_call (ctx, 1); #endif #if 0 duk_get_prop_string (ctx, 1, "disassemble"); void *a = duk_require_tval (ctx, -1); if (duk_is_callable (ctx, -1)) { ut8 *b = a; eprintf ("IS FUNCTION %02x %02x \n", b[0], b[1]); } else eprintf ("NOT CALLABLE\n"); ap->user = a; eprintf ("---- %p\n", a); duk_push_string (ctx, "F**K YOU"); //duk_dup_top(ctx); //duk_call_method (ctx, 0); duk_call (ctx, 1); duk_push_tval (ctx, ap->user); // push fun duk_push_string (ctx, "WINRAR"); duk_call (ctx, 1); duk_pop (ctx); #endif // TODO: add support to assemble from js too //ap->assemble = duk_disasm; #define lp lib_struct lp = R_NEW0 (RLibStruct); lp->type = R_LIB_TYPE_ASM; // TODO resolve from handler lp->data = ap; r_lib_open_ptr (Gcore->lib, "duktape.js", NULL, lp); duk_push_boolean (ctx, ret); return 1; }
static int gum_duk_kernel_read (GumMemoryValueType type, const GumDukArgs * args) { duk_context * ctx = args->ctx; GumDukCore * core = args->core; GumAddress address; gssize length = 0; gsize n_bytes_read; gum_duk_kernel_check_api_available (ctx); switch (type) { case GUM_MEMORY_VALUE_BYTE_ARRAY: case GUM_MEMORY_VALUE_C_STRING: case GUM_MEMORY_VALUE_UTF8_STRING: case GUM_MEMORY_VALUE_UTF16_STRING: _gum_duk_args_parse (args, "QZ", &address, &length); break; default: _gum_duk_args_parse (args, "Q", &address); break; } if (address == 0) { duk_push_null (ctx); return 1; } if (length == 0) { switch (type) { case GUM_MEMORY_VALUE_S8: case GUM_MEMORY_VALUE_U8: length = 1; break; case GUM_MEMORY_VALUE_S16: case GUM_MEMORY_VALUE_U16: length = 2; break; case GUM_MEMORY_VALUE_S32: case GUM_MEMORY_VALUE_U32: case GUM_MEMORY_VALUE_FLOAT: length = 4; break; case GUM_MEMORY_VALUE_S64: case GUM_MEMORY_VALUE_U64: case GUM_MEMORY_VALUE_LONG: case GUM_MEMORY_VALUE_ULONG: case GUM_MEMORY_VALUE_DOUBLE: length = 8; break; default: break; } } if (length > 0) { guint8 * data; gpointer buffer_data; data = gum_kernel_read (address, length, &n_bytes_read); if (data == NULL) { _gum_duk_throw (ctx, "access violation reading 0x%" G_GINT64_MODIFIER "x", address); } switch (type) { case GUM_MEMORY_VALUE_S8: duk_push_number (ctx, *((gint8 *) data)); break; case GUM_MEMORY_VALUE_U8: duk_push_number (ctx, *((guint8 *) data)); break; case GUM_MEMORY_VALUE_S16: duk_push_number (ctx, *((gint16 *) data)); break; case GUM_MEMORY_VALUE_U16: duk_push_number (ctx, *((guint16 *) data)); break; case GUM_MEMORY_VALUE_S32: duk_push_number (ctx, *((gint32 *) data)); break; case GUM_MEMORY_VALUE_U32: duk_push_number (ctx, *((guint32 *) data)); break; case GUM_MEMORY_VALUE_S64: _gum_duk_push_int64 (ctx, *((gint64 *) data), core); break; case GUM_MEMORY_VALUE_U64: _gum_duk_push_uint64 (ctx, *((guint64 *) data), core); break; case GUM_MEMORY_VALUE_LONG: _gum_duk_push_int64 (ctx, *((glong *) data), core); break; case GUM_MEMORY_VALUE_ULONG: _gum_duk_push_uint64 (ctx, *((gulong *) data), core); break; case GUM_MEMORY_VALUE_FLOAT: duk_push_number (ctx, *((gfloat *) data)); break; case GUM_MEMORY_VALUE_DOUBLE: duk_push_number (ctx, *((gdouble *) data)); break; case GUM_MEMORY_VALUE_BYTE_ARRAY: { buffer_data = duk_push_fixed_buffer (ctx, n_bytes_read); memcpy (buffer_data, data, n_bytes_read); duk_push_buffer_object (ctx, -1, 0, n_bytes_read, DUK_BUFOBJ_ARRAYBUFFER); duk_swap (ctx, -2, -1); duk_pop (ctx); break; } case GUM_MEMORY_VALUE_C_STRING: { gchar * str; str = g_utf8_make_valid ((gchar *) data, length); duk_push_string (ctx, str); g_free (str); break; } case GUM_MEMORY_VALUE_UTF8_STRING: { const gchar * end; gchar * slice; if (!g_utf8_validate ((gchar *) data, length, &end)) { _gum_duk_throw (ctx, "can't decode byte 0x%02x in position %u", (guint8) *end, (guint) (end - (gchar *) data)); } slice = g_strndup ((gchar *) data, length); duk_push_string (ctx, slice); g_free (slice); break; } case GUM_MEMORY_VALUE_UTF16_STRING: { gunichar2 * str_utf16; gchar * str_utf8; glong size; str_utf16 = (gunichar2 *) data; str_utf8 = g_utf16_to_utf8 (str_utf16, length, NULL, &size, NULL); if (str_utf8 == NULL) _gum_duk_throw (ctx, "invalid string"); duk_push_string (ctx, str_utf8); g_free (str_utf8); break; } default: g_assert_not_reached (); } g_free (data); } else if (type == GUM_MEMORY_VALUE_BYTE_ARRAY) { duk_push_fixed_buffer (ctx, 0); } else { _gum_duk_throw (ctx, "please provide a length > 0"); } return 1; }
DUK_LOCAL duk_ret_t duk__error_getter_helper(duk_context *ctx, duk_small_int_t output_type) { duk_hthread *thr = (duk_hthread *) ctx; duk_idx_t idx_td; duk_small_int_t i; /* traceback depth fits into 16 bits */ duk_small_int_t t; /* stack type fits into 16 bits */ duk_small_int_t count_func = 0; /* traceback depth ensures fits into 16 bits */ const char *str_tailcall = " tailcall"; const char *str_strict = " strict"; const char *str_construct = " construct"; const char *str_prevyield = " preventsyield"; const char *str_directeval = " directeval"; const char *str_empty = ""; DUK_ASSERT_TOP(ctx, 0); /* fixed arg count */ DUK_UNREF(thr); duk_push_this(ctx); duk_get_prop_stridx_short(ctx, -1, DUK_STRIDX_INT_TRACEDATA); idx_td = duk_get_top_index(ctx); duk_push_hstring_stridx(ctx, DUK_STRIDX_NEWLINE_4SPACE); duk_push_this(ctx); /* [ ... this tracedata sep this ] */ /* XXX: skip null filename? */ if (duk_check_type(ctx, idx_td, DUK_TYPE_OBJECT)) { /* Current tracedata contains 2 entries per callstack entry. */ for (i = 0; ; i += 2) { duk_int_t pc; duk_int_t line; duk_int_t flags; duk_double_t d; const char *funcname; const char *filename; duk_hobject *h_func; duk_hstring *h_name; duk_require_stack(ctx, 5); duk_get_prop_index(ctx, idx_td, i); duk_get_prop_index(ctx, idx_td, i + 1); d = duk_to_number_m1(ctx); pc = (duk_int_t) DUK_FMOD(d, DUK_DOUBLE_2TO32); flags = (duk_int_t) DUK_FLOOR(d / DUK_DOUBLE_2TO32); t = (duk_small_int_t) duk_get_type(ctx, -2); if (t == DUK_TYPE_OBJECT || t == DUK_TYPE_LIGHTFUNC) { /* * Ecmascript/native function call or lightfunc call */ count_func++; /* [ ... v1(func) v2(pc+flags) ] */ h_func = duk_get_hobject(ctx, -2); /* NULL for lightfunc */ /* These may be systematically omitted by Duktape * with certain config options, but allow user to * set them on a case-by-case basis. */ duk_get_prop_stridx_short(ctx, -2, DUK_STRIDX_NAME); duk_get_prop_stridx_short(ctx, -3, DUK_STRIDX_FILE_NAME); #if defined(DUK_USE_PC2LINE) line = duk_hobject_pc2line_query(ctx, -4, (duk_uint_fast32_t) pc); #else line = 0; #endif /* [ ... v1 v2 name filename ] */ /* When looking for .fileName/.lineNumber, blame first * function which has a .fileName. */ if (duk_is_string_notsymbol(ctx, -1)) { if (output_type == DUK__OUTPUT_TYPE_FILENAME) { return 1; } else if (output_type == DUK__OUTPUT_TYPE_LINENUMBER) { duk_push_int(ctx, line); return 1; } } /* XXX: Change 'anon' handling here too, to use empty string for anonymous functions? */ /* XXX: Could be improved by coercing to a readable duk_tval (especially string escaping) */ h_name = duk_get_hstring_notsymbol(ctx, -2); /* may be NULL */ funcname = (h_name == NULL || h_name == DUK_HTHREAD_STRING_EMPTY_STRING(thr)) ? "[anon]" : (const char *) DUK_HSTRING_GET_DATA(h_name); filename = duk_get_string_notsymbol(ctx, -1); filename = filename ? filename : ""; DUK_ASSERT(funcname != NULL); DUK_ASSERT(filename != NULL); if (h_func == NULL) { duk_push_sprintf(ctx, "at %s light%s%s%s%s%s", (const char *) funcname, (const char *) ((flags & DUK_ACT_FLAG_STRICT) ? str_strict : str_empty), (const char *) ((flags & DUK_ACT_FLAG_TAILCALLED) ? str_tailcall : str_empty), (const char *) ((flags & DUK_ACT_FLAG_CONSTRUCT) ? str_construct : str_empty), (const char *) ((flags & DUK_ACT_FLAG_DIRECT_EVAL) ? str_directeval : str_empty), (const char *) ((flags & DUK_ACT_FLAG_PREVENT_YIELD) ? str_prevyield : str_empty)); } else if (DUK_HOBJECT_HAS_NATFUNC(h_func)) { duk_push_sprintf(ctx, "at %s (%s) native%s%s%s%s%s", (const char *) funcname, (const char *) filename, (const char *) ((flags & DUK_ACT_FLAG_STRICT) ? str_strict : str_empty), (const char *) ((flags & DUK_ACT_FLAG_TAILCALLED) ? str_tailcall : str_empty), (const char *) ((flags & DUK_ACT_FLAG_CONSTRUCT) ? str_construct : str_empty), (const char *) ((flags & DUK_ACT_FLAG_DIRECT_EVAL) ? str_directeval : str_empty), (const char *) ((flags & DUK_ACT_FLAG_PREVENT_YIELD) ? str_prevyield : str_empty)); } else { duk_push_sprintf(ctx, "at %s (%s:%ld)%s%s%s%s%s", (const char *) funcname, (const char *) filename, (long) line, (const char *) ((flags & DUK_ACT_FLAG_STRICT) ? str_strict : str_empty), (const char *) ((flags & DUK_ACT_FLAG_TAILCALLED) ? str_tailcall : str_empty), (const char *) ((flags & DUK_ACT_FLAG_CONSTRUCT) ? str_construct : str_empty), (const char *) ((flags & DUK_ACT_FLAG_DIRECT_EVAL) ? str_directeval : str_empty), (const char *) ((flags & DUK_ACT_FLAG_PREVENT_YIELD) ? str_prevyield : str_empty)); } duk_replace(ctx, -5); /* [ ... v1 v2 name filename str ] -> [ ... str v2 name filename ] */ duk_pop_3(ctx); /* -> [ ... str ] */ } else if (t == DUK_TYPE_STRING) { const char *str_file; /* * __FILE__ / __LINE__ entry, here 'pc' is line number directly. * Sometimes __FILE__ / __LINE__ is reported as the source for * the error (fileName, lineNumber), sometimes not. */ /* [ ... v1(filename) v2(line+flags) ] */ /* When looking for .fileName/.lineNumber, blame compilation * or C call site unless flagged not to do so. */ if (!(flags & DUK_TB_FLAG_NOBLAME_FILELINE)) { if (output_type == DUK__OUTPUT_TYPE_FILENAME) { duk_pop(ctx); return 1; } else if (output_type == DUK__OUTPUT_TYPE_LINENUMBER) { duk_push_int(ctx, pc); return 1; } } /* Tracedata is trusted but avoid any risk of using a NULL * for %s format because it has undefined behavior. Symbols * don't need to be explicitly rejected as they pose no memory * safety issues. */ str_file = (const char *) duk_get_string(ctx, -2); duk_push_sprintf(ctx, "at [anon] (%s:%ld) internal", (const char *) (str_file ? str_file : "null"), (long) pc); duk_replace(ctx, -3); /* [ ... v1 v2 str ] -> [ ... str v2 ] */ duk_pop(ctx); /* -> [ ... str ] */ } else { /* unknown, ignore */ duk_pop_2(ctx); break; } } if (count_func >= DUK_USE_TRACEBACK_DEPTH) { /* Possibly truncated; there is no explicit truncation * marker so this is the best we can do. */ duk_push_hstring_stridx(ctx, DUK_STRIDX_BRACKETED_ELLIPSIS); } } /* [ ... this tracedata sep this str1 ... strN ] */ if (output_type != DUK__OUTPUT_TYPE_TRACEBACK) { return 0; } else { /* The 'this' after 'sep' will get ToString() coerced by * duk_join() automatically. We don't want to do that * coercion when providing .fileName or .lineNumber (GH-254). */ duk_join(ctx, duk_get_top(ctx) - (idx_td + 2) /*count, not including sep*/); 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; }
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; }
DUK_INTERNAL duk_ret_t duk_bi_array_prototype_join_shared(duk_context *ctx) { duk_uint32_t len, count; duk_uint32_t idx; duk_small_int_t to_locale_string = duk_get_current_magic(ctx); duk_idx_t valstack_required; /* For join(), nargs is 1. For toLocaleString(), nargs is 0 and * setting the top essentially pushes an undefined to the stack, * thus defaulting to a comma separator. */ duk_set_top(ctx, 1); if (duk_is_undefined(ctx, 0)) { duk_pop(ctx); duk_push_hstring_stridx(ctx, DUK_STRIDX_COMMA); } else { duk_to_string(ctx, 0); } len = duk__push_this_obj_len_u32(ctx); /* [ sep ToObject(this) len ] */ DUK_DDD(DUK_DDDPRINT("sep=%!T, this=%!T, len=%lu", (duk_tval *) duk_get_tval(ctx, 0), (duk_tval *) duk_get_tval(ctx, 1), (unsigned long) len)); /* The extra (+4) is tight. */ valstack_required = (len >= DUK__ARRAY_MID_JOIN_LIMIT ? DUK__ARRAY_MID_JOIN_LIMIT : len) + 4; duk_require_stack(ctx, valstack_required); duk_dup(ctx, 0); /* [ sep ToObject(this) len sep ] */ count = 0; idx = 0; for (;;) { if (count >= DUK__ARRAY_MID_JOIN_LIMIT || /* intermediate join to avoid valstack overflow */ idx >= len) { /* end of loop (careful with len==0) */ /* [ sep ToObject(this) len sep str0 ... str(count-1) ] */ DUK_DDD(DUK_DDDPRINT("mid/final join, count=%ld, idx=%ld, len=%ld", (long) count, (long) idx, (long) len)); duk_join(ctx, (duk_idx_t) count); /* -> [ sep ToObject(this) len str ] */ duk_dup(ctx, 0); /* -> [ sep ToObject(this) len str sep ] */ duk_insert(ctx, -2); /* -> [ sep ToObject(this) len sep str ] */ count = 1; } if (idx >= len) { /* if true, the stack already contains the final result */ break; } duk_get_prop_index(ctx, 1, (duk_uarridx_t) idx); if (duk_is_null_or_undefined(ctx, -1)) { duk_pop(ctx); duk_push_hstring_stridx(ctx, DUK_STRIDX_EMPTY_STRING); } else { if (to_locale_string) { duk_to_object(ctx, -1); duk_get_prop_stridx(ctx, -1, DUK_STRIDX_TO_LOCALE_STRING); duk_insert(ctx, -2); /* -> [ ... toLocaleString ToObject(val) ] */ duk_call_method(ctx, 0); duk_to_string(ctx, -1); } else { duk_to_string(ctx, -1); } } count++; idx++; } /* [ sep ToObject(this) len sep result ] */ return 1; }
static int sys1_bind(duk_context *ctx) { int rc; int port = 0; int fd = duk_to_int(ctx, 0); union { struct sockaddr dst; struct sockaddr_in dst4; struct sockaddr_in6 dst6; struct sockaddr_un dstun; } addr; socklen_t addrlen = 0; memset(&addr, 0, sizeof(addr)); duk_get_prop_string(ctx, 1, "port"); if(duk_check_type(ctx, 2, DUK_TYPE_NUMBER)) { port = duk_to_int(ctx, 2); } duk_pop(ctx); duk_get_prop_string(ctx, 1, "in"); if(duk_check_type(ctx, 2, DUK_TYPE_STRING)) { addrlen = sizeof(addr.dst4); inet_aton(duk_to_string(ctx, 2), &addr.dst4.sin_addr); addr.dst4.sin_port = htons(port); addr.dst4.sin_family = AF_INET; } duk_pop(ctx); duk_get_prop_string(ctx, 1, "un"); if(duk_check_type(ctx, 2, DUK_TYPE_STRING)) { const char *path; addrlen = sizeof(addr.dstun); path = duk_to_string(ctx, 2); if(strlen(path) > sizeof(addr.dstun.sun_path)) { duk_pop(ctx); rc=-1; goto out; } memcpy(addr.dstun.sun_path, path, strlen(path)); addr.dstun.sun_family = AF_LOCAL; } duk_pop(ctx); duk_get_prop_string(ctx, 1, "una"); if(duk_check_type(ctx, 2, DUK_TYPE_STRING)) { const char *path; addrlen = sizeof(addr.dstun); path = duk_to_string(ctx, 2); if(strlen(path) > (sizeof(addr.dstun.sun_path)-1)) { duk_pop(ctx); rc=-1; goto out; } memcpy(addr.dstun.sun_path+1, path, strlen(path)); addr.dstun.sun_family = AF_LOCAL; } duk_pop(ctx); duk_get_prop_string(ctx, 1, "in6"); if(duk_check_type(ctx, 2, DUK_TYPE_STRING)) { addrlen = sizeof(addr.dst6); inet_pton(AF_INET6, duk_to_string(ctx, 2), &addr.dst6.sin6_addr); addr.dst6.sin6_port = htons(port); addr.dst6.sin6_family = AF_INET6; } duk_pop(ctx); rc = bind(fd, &addr.dst, addrlen); out: duk_push_number(ctx, rc); return 1; }
DUK_LOCAL duk_small_int_t duk__array_sort_compare(duk_context *ctx, duk_int_t idx1, duk_int_t idx2) { duk_bool_t have1, have2; duk_bool_t undef1, undef2; duk_small_int_t ret; duk_idx_t idx_obj = 1; /* fixed offsets in valstack */ duk_idx_t idx_fn = 0; duk_hstring *h1, *h2; /* Fast exit if indices are identical. This is valid for a non-existent property, * for an undefined value, and almost always for ToString() coerced comparison of * arbitrary values (corner cases where this is not the case include e.g. a an * object with varying ToString() coercion). * * The specification does not prohibit "caching" of values read from the array, so * assuming equality for comparing an index with itself falls into the category of * "caching". * * Also, compareFn may be inconsistent, so skipping a call to compareFn here may * have an effect on the final result. The specification does not require any * specific behavior for inconsistent compare functions, so again, this fast path * is OK. */ if (idx1 == idx2) { DUK_DDD(DUK_DDDPRINT("duk__array_sort_compare: idx1=%ld, idx2=%ld -> indices identical, quick exit", (long) idx1, (long) idx2)); return 0; } have1 = duk_get_prop_index(ctx, idx_obj, (duk_uarridx_t) idx1); have2 = duk_get_prop_index(ctx, idx_obj, (duk_uarridx_t) idx2); DUK_DDD(DUK_DDDPRINT("duk__array_sort_compare: idx1=%ld, idx2=%ld, have1=%ld, have2=%ld, val1=%!T, val2=%!T", (long) idx1, (long) idx2, (long) have1, (long) have2, (duk_tval *) duk_get_tval(ctx, -2), (duk_tval *) duk_get_tval(ctx, -1))); if (have1) { if (have2) { ; } else { ret = -1; goto pop_ret; } } else { if (have2) { ret = 1; goto pop_ret; } else { ret = 0; goto pop_ret; } } undef1 = duk_is_undefined(ctx, -2); undef2 = duk_is_undefined(ctx, -1); if (undef1) { if (undef2) { ret = 0; goto pop_ret; } else { ret = 1; goto pop_ret; } } else { if (undef2) { ret = -1; goto pop_ret; } else { ; } } if (!duk_is_undefined(ctx, idx_fn)) { duk_double_t d; /* no need to check callable; duk_call() will do that */ duk_dup(ctx, idx_fn); /* -> [ ... x y fn ] */ duk_insert(ctx, -3); /* -> [ ... fn x y ] */ duk_call(ctx, 2); /* -> [ ... res ] */ /* The specification is a bit vague what to do if the return * value is not a number. Other implementations seem to * tolerate non-numbers but e.g. V8 won't apparently do a * ToNumber(). */ /* XXX: best behavior for real world compatibility? */ d = duk_to_number(ctx, -1); if (d < 0.0) { ret = -1; } else if (d > 0.0) { ret = 1; } else { ret = 0; } duk_pop(ctx); DUK_DDD(DUK_DDDPRINT("-> result %ld (from comparefn, after coercion)", (long) ret)); return ret; } /* string compare is the default (a bit oddly) */ h1 = duk_to_hstring(ctx, -2); h2 = duk_to_hstring(ctx, -1); DUK_ASSERT(h1 != NULL); DUK_ASSERT(h2 != NULL); ret = duk_js_string_compare(h1, h2); /* retval is directly usable */ goto pop_ret; pop_ret: duk_pop_2(ctx); DUK_DDD(DUK_DDDPRINT("-> result %ld", (long) ret)); return ret; }
int main(int argc, char *argv[]) { duk_context *ctx = duk_create_heap_default(); duk_int_t rc; (void) argc; (void) argv; /* suppress warning */ printf("*** test1 - duk_pcall()\n"); duk_push_c_function(ctx, test1, 0); rc = duk_pcall(ctx, 0); printf("--> rc=%ld (%s)\n", (long) rc, duk_safe_to_string(ctx, -1)); duk_pop(ctx); printf("\n"); printf("*** test1 - duk_safe_call()\n"); rc = duk_safe_call(ctx, test1, 0, 1); printf("--> rc=%ld (%s)\n", (long) rc, duk_safe_to_string(ctx, -1)); duk_pop(ctx); printf("\n"); printf("*** test1 - ecmascript try-catch\n"); duk_push_c_function(ctx, test1, 0); duk_put_global_string(ctx, "test1"); duk_eval_string_noresult(ctx, "try {\n" " test1();\n" "} catch (e) {\n" " print(e.stack || e);\n" "}\n"); printf("\n"); printf("*** test2 - duk_pcall()\n"); duk_push_c_function(ctx, test2, 0); rc = duk_pcall(ctx, 0); printf("--> rc=%ld (%s)\n", (long) rc, duk_safe_to_string(ctx, -1)); duk_pop(ctx); printf("\n"); printf("*** test2 - duk_safe_call()\n"); rc = duk_safe_call(ctx, test2, 0, 1); printf("--> rc=%ld (%s)\n", (long) rc, duk_safe_to_string(ctx, -1)); duk_pop(ctx); printf("\n"); printf("*** test2 - ecmascript try-catch\n"); duk_push_c_function(ctx, test2, 0); duk_put_global_string(ctx, "test2"); duk_eval_string_noresult(ctx, "try {\n" " test2();\n" "} catch (e) {\n" " print(e.stack || e);\n" "}\n"); printf("\n"); printf("*** test3 - duk_pcall()\n"); duk_push_c_function(ctx, test3, 0); rc = duk_pcall(ctx, 0); printf("--> rc=%ld (%s)\n", (long) rc, duk_safe_to_string(ctx, -1)); duk_pop(ctx); printf("\n"); printf("*** test3 - duk_safe_call()\n"); rc = duk_safe_call(ctx, test3, 0, 1); printf("--> rc=%ld (%s)\n", (long) rc, duk_safe_to_string(ctx, -1)); duk_pop(ctx); printf("\n"); printf("*** test3 - ecmascript try-catch\n"); duk_push_c_function(ctx, test3, 0); duk_put_global_string(ctx, "test3"); duk_eval_string_noresult(ctx, "try {\n" " test3();\n" "} catch (e) {\n" " print(e.stack || e);\n" "}\n"); printf("\n"); printf("*** test4 - duk_pcall()\n"); duk_push_c_function(ctx, test4, 0); rc = duk_pcall(ctx, 0); printf("--> rc=%ld (%s)\n", (long) rc, duk_safe_to_string(ctx, -1)); duk_pop(ctx); printf("\n"); printf("*** test4 - duk_safe_call()\n"); rc = duk_safe_call(ctx, test4, 0, 1); printf("--> rc=%ld (%s)\n", (long) rc, duk_safe_to_string(ctx, -1)); duk_pop(ctx); printf("\n"); printf("*** test4 - ecmascript try-catch\n"); duk_push_c_function(ctx, test4, 0); duk_put_global_string(ctx, "test4"); duk_eval_string_noresult(ctx, "try {\n" " test4();\n" "} catch (e) {\n" " print(e.stack || e);\n" "}\n"); printf("\n"); printf("*** test5 - duk_pcall()\n"); duk_push_c_function(ctx, test5, 0); rc = duk_pcall(ctx, 0); printf("--> rc=%ld (%s)\n", (long) rc, duk_safe_to_string(ctx, -1)); duk_pop(ctx); printf("\n"); printf("*** test5 - duk_safe_call()\n"); rc = duk_safe_call(ctx, test5, 0, 1); printf("--> rc=%ld (%s)\n", (long) rc, duk_safe_to_string(ctx, -1)); duk_pop(ctx); printf("\n"); printf("*** test5 - ecmascript try-catch\n"); duk_push_c_function(ctx, test5, 0); duk_put_global_string(ctx, "test5"); duk_eval_string_noresult(ctx, "try {\n" " test5();\n" "} catch (e) {\n" " print(e.stack || e);\n" "}\n"); printf("\n"); printf("*** done\n"); duk_destroy_heap(ctx); return 0; }
static int Serializer_Write(duk_context* ctx) { duk_int_t magic = duk_get_current_magic(ctx); duk_push_this(ctx); // safe cast based on type check above Serializer* serial = CastToSerializer(ctx, duk_get_top_index(ctx)); duk_pop(ctx); if (!serial) { duk_push_boolean(ctx, 0); return 1; } const char* str; size_t length; IO_MAGIC_TYPE v = (IO_MAGIC_TYPE) magic; bool success = false; switch(v) { case IO_MAGIC_INT: success = serial->WriteInt((int) duk_require_number(ctx, 0)); break; case IO_MAGIC_STRING: str = duk_require_string(ctx, 0); length = strlen(str); success = serial->Write(str, length); /* if (length) { buffer.Resize(length); for (size_t i = 0; i < length; i++) buffer[i] = str[i]; serial->WriteBuffer(buffer); } */ break; case IO_MAGIC_ZEROSTRING: success = serial->WriteString(duk_require_string(ctx, 0)); break; case IO_MAGIC_BINARY: str = (const char*) duk_require_buffer_data(ctx, 0, &length); success = serial->Write(str, length); break; default: break; } duk_push_boolean(ctx, success ? 1 : 0); return 1; }
DUK_LOCAL void duk__err_augment_builtin_throw(duk_hthread *thr, duk_hthread *thr_callstack, const char *c_filename, duk_int_t c_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_INT_TRACEDATA(thr))) { DUK_DDD(DUK_DDDPRINT("error value already has a '_Tracedata' property, not modifying it")); } else { duk__add_traceback(thr, thr_callstack, c_filename, c_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 (thr->compile_ctx != NULL && thr->compile_ctx->h_filename != NULL) { /* Compiler SyntaxError (or other error) gets the primary blame. */ duk_push_hstring(ctx, thr->compile_ctx->h_filename); duk_xdef_prop_stridx(ctx, -2, DUK_STRIDX_FILE_NAME, DUK_PROPDESC_FLAGS_WC | DUK_PROPDESC_FLAG_NO_OVERWRITE); duk_push_uint(ctx, (duk_uint_t) thr->compile_ctx->curr_token.start_line); duk_xdef_prop_stridx(ctx, -2, DUK_STRIDX_LINE_NUMBER, DUK_PROPDESC_FLAGS_WC | DUK_PROPDESC_FLAG_NO_OVERWRITE); } else if (c_filename && !noblame_fileline) { /* XXX: file/line is disabled in minimal builds, so disable this too * when appropriate. */ duk_push_string(ctx, c_filename); duk_xdef_prop_stridx(ctx, -2, DUK_STRIDX_FILE_NAME, DUK_PROPDESC_FLAGS_WC | DUK_PROPDESC_FLAG_NO_OVERWRITE); duk_push_int(ctx, c_line); duk_xdef_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 = DUK_ACT_GET_FUNC(act); if (func) { duk_uint32_t pc; /* 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_xdef_prop_stridx(ctx, -3, DUK_STRIDX_FILE_NAME, DUK_PROPDESC_FLAGS_WC | DUK_PROPDESC_FLAG_NO_OVERWRITE); #if defined(DUK_USE_PC2LINE) if (DUK_HOBJECT_IS_COMPILEDFUNCTION(func)) { duk_uint32_t ecma_line; #if 0 duk_push_u32(ctx, pc); duk_xdef_prop_stridx(ctx, -3, DUK_STRIDX_PC, DUK_PROPDESC_FLAGS_WC | DUK_PROPDESC_FLAGS_NO_OVERWRITE); #endif ecma_line = duk_hobject_pc2line_query(ctx, -1, (duk_uint_fast32_t) pc); if (ecma_line > 0) { duk_push_u32(ctx, (duk_uint32_t) ecma_line); /* -> [ ... error func line ] */ duk_xdef_prop_stridx(ctx, -3, DUK_STRIDX_LINE_NUMBER, DUK_PROPDESC_FLAGS_WC | DUK_PROPDESC_FLAG_NO_OVERWRITE); } } else { /* Native function, no relevant lineNumber. */ } #endif /* DUK_USE_PC2LINE */ duk_pop(ctx); } } #endif /* DUK_USE_TRACEBACKS */ #ifdef DUK_USE_ASSERTIONS DUK_ASSERT(duk_get_top(ctx) == entry_top); #endif }
DUK_INTERNAL duk_ret_t duk_bi_string_prototype_replace(duk_context *ctx) { duk_hthread *thr = (duk_hthread *) ctx; duk_hstring *h_input; duk_hstring *h_match; duk_hstring *h_search; duk_hobject *h_re; duk_bufwriter_ctx bw_alloc; duk_bufwriter_ctx *bw; #ifdef DUK_USE_REGEXP_SUPPORT duk_bool_t is_regexp; duk_bool_t is_global; #endif duk_bool_t is_repl_func; duk_uint32_t match_start_coff, match_start_boff; #ifdef DUK_USE_REGEXP_SUPPORT duk_int_t match_caps; #endif duk_uint32_t prev_match_end_boff; const duk_uint8_t *r_start, *r_end, *r; /* repl string scan */ duk_size_t tmp_sz; DUK_ASSERT_TOP(ctx, 2); h_input = duk_push_this_coercible_to_string(ctx); DUK_ASSERT(h_input != NULL); bw = &bw_alloc; DUK_BW_INIT_PUSHBUF(thr, bw, DUK_HSTRING_GET_BYTELEN(h_input)); /* input size is good output starting point */ DUK_ASSERT_TOP(ctx, 4); /* stack[0] = search value * stack[1] = replace value * stack[2] = input string * stack[3] = result buffer */ h_re = duk_get_hobject_with_class(ctx, 0, DUK_HOBJECT_CLASS_REGEXP); if (h_re) { #ifdef DUK_USE_REGEXP_SUPPORT is_regexp = 1; is_global = duk_get_prop_stridx_boolean(ctx, 0, DUK_STRIDX_GLOBAL, NULL); if (is_global) { /* start match from beginning */ duk_push_int(ctx, 0); duk_put_prop_stridx(ctx, 0, DUK_STRIDX_LAST_INDEX); } #else /* DUK_USE_REGEXP_SUPPORT */ DUK_DCERROR_UNSUPPORTED(thr); #endif /* DUK_USE_REGEXP_SUPPORT */ } else { duk_to_string(ctx, 0); #ifdef DUK_USE_REGEXP_SUPPORT is_regexp = 0; is_global = 0; #endif } if (duk_is_function(ctx, 1)) { is_repl_func = 1; r_start = NULL; r_end = NULL; } else { duk_hstring *h_repl; is_repl_func = 0; h_repl = duk_to_hstring(ctx, 1); DUK_ASSERT(h_repl != NULL); r_start = DUK_HSTRING_GET_DATA(h_repl); r_end = r_start + DUK_HSTRING_GET_BYTELEN(h_repl); } prev_match_end_boff = 0; for (;;) { /* * If matching with a regexp: * - non-global RegExp: lastIndex not touched on a match, zeroed * on a non-match * - global RegExp: on match, lastIndex will be updated by regexp * executor to point to next char after the matching part (so that * characters in the matching part are not matched again) * * If matching with a string: * - always non-global match, find first occurrence * * We need: * - The character offset of start-of-match for the replacer function * - The byte offsets for start-of-match and end-of-match to implement * the replacement values $&, $`, and $', and to copy non-matching * input string portions (including header and trailer) verbatim. * * NOTE: the E5.1 specification is a bit vague how the RegExp should * behave in the replacement process; e.g. is matching done first for * all matches (in the global RegExp case) before any replacer calls * are made? See: test-bi-string-proto-replace.js for discussion. */ DUK_ASSERT_TOP(ctx, 4); #ifdef DUK_USE_REGEXP_SUPPORT if (is_regexp) { duk_dup_0(ctx); duk_dup_2(ctx); duk_regexp_match(thr); /* [ ... regexp input ] -> [ res_obj ] */ if (!duk_is_object(ctx, -1)) { duk_pop(ctx); break; } duk_get_prop_stridx(ctx, -1, DUK_STRIDX_INDEX); DUK_ASSERT(duk_is_number(ctx, -1)); match_start_coff = duk_get_int(ctx, -1); duk_pop(ctx); duk_get_prop_index(ctx, -1, 0); DUK_ASSERT(duk_is_string(ctx, -1)); h_match = duk_known_hstring(ctx, -1); duk_pop(ctx); /* h_match is borrowed, remains reachable through match_obj */ if (DUK_HSTRING_GET_BYTELEN(h_match) == 0) { /* This should be equivalent to match() algorithm step 8.f.iii.2: * detect an empty match and allow it, but don't allow it twice. */ duk_uint32_t last_index; duk_get_prop_stridx(ctx, 0, DUK_STRIDX_LAST_INDEX); last_index = (duk_uint32_t) duk_get_uint(ctx, -1); DUK_DDD(DUK_DDDPRINT("empty match, bump lastIndex: %ld -> %ld", (long) last_index, (long) (last_index + 1))); duk_pop(ctx); duk_push_int(ctx, last_index + 1); duk_put_prop_stridx(ctx, 0, DUK_STRIDX_LAST_INDEX); } DUK_ASSERT(duk_get_length(ctx, -1) <= DUK_INT_MAX); /* string limits */ match_caps = (duk_int_t) duk_get_length(ctx, -1); } else { #else /* DUK_USE_REGEXP_SUPPORT */ { /* unconditionally */ #endif /* DUK_USE_REGEXP_SUPPORT */ const duk_uint8_t *p_start, *p_end, *p; /* input string scan */ const duk_uint8_t *q_start; /* match string */ duk_size_t q_blen; #ifdef DUK_USE_REGEXP_SUPPORT DUK_ASSERT(!is_global); /* single match always */ #endif p_start = DUK_HSTRING_GET_DATA(h_input); p_end = p_start + DUK_HSTRING_GET_BYTELEN(h_input); p = p_start; h_search = duk_known_hstring(ctx, 0); q_start = DUK_HSTRING_GET_DATA(h_search); q_blen = (duk_size_t) DUK_HSTRING_GET_BYTELEN(h_search); p_end -= q_blen; /* ensure full memcmp() fits in while */ match_start_coff = 0; while (p <= p_end) { DUK_ASSERT(p + q_blen <= DUK_HSTRING_GET_DATA(h_input) + DUK_HSTRING_GET_BYTELEN(h_input)); if (DUK_MEMCMP((const void *) p, (const void *) q_start, (size_t) q_blen) == 0) { duk_dup_0(ctx); h_match = duk_known_hstring(ctx, -1); #ifdef DUK_USE_REGEXP_SUPPORT match_caps = 0; #endif goto found; } /* track utf-8 non-continuation bytes */ if ((p[0] & 0xc0) != 0x80) { match_start_coff++; } p++; } /* not found */ break; } found: /* stack[0] = search value * stack[1] = replace value * stack[2] = input string * stack[3] = result buffer * stack[4] = regexp match OR match string */ match_start_boff = duk_heap_strcache_offset_char2byte(thr, h_input, match_start_coff); tmp_sz = (duk_size_t) (match_start_boff - prev_match_end_boff); DUK_BW_WRITE_ENSURE_BYTES(thr, bw, DUK_HSTRING_GET_DATA(h_input) + prev_match_end_boff, tmp_sz); prev_match_end_boff = match_start_boff + DUK_HSTRING_GET_BYTELEN(h_match); if (is_repl_func) { duk_idx_t idx_args; duk_hstring *h_repl; /* regexp res_obj is at index 4 */ duk_dup_1(ctx); idx_args = duk_get_top(ctx); #ifdef DUK_USE_REGEXP_SUPPORT if (is_regexp) { duk_int_t idx; duk_require_stack(ctx, match_caps + 2); for (idx = 0; idx < match_caps; idx++) { /* match followed by capture(s) */ duk_get_prop_index(ctx, 4, idx); } } else { #else /* DUK_USE_REGEXP_SUPPORT */ { /* unconditionally */ #endif /* DUK_USE_REGEXP_SUPPORT */ /* match == search string, by definition */ duk_dup_0(ctx); } duk_push_int(ctx, match_start_coff); duk_dup_2(ctx); /* [ ... replacer match [captures] match_char_offset input ] */ duk_call(ctx, duk_get_top(ctx) - idx_args); h_repl = duk_to_hstring(ctx, -1); /* -> [ ... repl_value ] */ DUK_ASSERT(h_repl != NULL); DUK_BW_WRITE_ENSURE_HSTRING(thr, bw, h_repl); duk_pop(ctx); /* repl_value */ } else { r = r_start; while (r < r_end) { duk_int_t ch1; duk_int_t ch2; #ifdef DUK_USE_REGEXP_SUPPORT duk_int_t ch3; #endif duk_size_t left; ch1 = *r++; if (ch1 != DUK_ASC_DOLLAR) { goto repl_write; } left = r_end - r; if (left <= 0) { goto repl_write; } ch2 = r[0]; switch (ch2) { case DUK_ASC_DOLLAR: { ch1 = (1 << 8) + DUK_ASC_DOLLAR; goto repl_write; } case DUK_ASC_AMP: { DUK_BW_WRITE_ENSURE_HSTRING(thr, bw, h_match); r++; continue; } case DUK_ASC_GRAVE: { tmp_sz = (duk_size_t) match_start_boff; DUK_BW_WRITE_ENSURE_BYTES(thr, bw, DUK_HSTRING_GET_DATA(h_input), tmp_sz); r++; continue; } case DUK_ASC_SINGLEQUOTE: { duk_uint32_t match_end_boff; /* Use match charlen instead of bytelen, just in case the input and * match codepoint encodings would have different lengths. */ match_end_boff = duk_heap_strcache_offset_char2byte(thr, h_input, match_start_coff + DUK_HSTRING_GET_CHARLEN(h_match)); tmp_sz = (duk_size_t) (DUK_HSTRING_GET_BYTELEN(h_input) - match_end_boff); DUK_BW_WRITE_ENSURE_BYTES(thr, bw, DUK_HSTRING_GET_DATA(h_input) + match_end_boff, tmp_sz); r++; continue; } default: { #ifdef DUK_USE_REGEXP_SUPPORT duk_int_t capnum, captmp, capadv; /* XXX: optional check, match_caps is zero if no regexp, * so dollar will be interpreted literally anyway. */ if (!is_regexp) { goto repl_write; } if (!(ch2 >= DUK_ASC_0 && ch2 <= DUK_ASC_9)) { goto repl_write; } capnum = ch2 - DUK_ASC_0; capadv = 1; if (left >= 2) { ch3 = r[1]; if (ch3 >= DUK_ASC_0 && ch3 <= DUK_ASC_9) { captmp = capnum * 10 + (ch3 - DUK_ASC_0); if (captmp < match_caps) { capnum = captmp; capadv = 2; } } } if (capnum > 0 && capnum < match_caps) { DUK_ASSERT(is_regexp != 0); /* match_caps == 0 without regexps */ /* regexp res_obj is at offset 4 */ duk_get_prop_index(ctx, 4, (duk_uarridx_t) capnum); if (duk_is_string(ctx, -1)) { duk_hstring *h_tmp_str; h_tmp_str = duk_known_hstring(ctx, -1); DUK_BW_WRITE_ENSURE_HSTRING(thr, bw, h_tmp_str); } else { /* undefined -> skip (replaced with empty) */ } duk_pop(ctx); r += capadv; continue; } else { goto repl_write; } #else /* DUK_USE_REGEXP_SUPPORT */ goto repl_write; /* unconditionally */ #endif /* DUK_USE_REGEXP_SUPPORT */ } /* default case */ } /* switch (ch2) */ repl_write: /* ch1 = (r_increment << 8) + byte */ DUK_BW_WRITE_ENSURE_U8(thr, bw, (duk_uint8_t) (ch1 & 0xff)); r += ch1 >> 8; } /* while repl */ } /* if (is_repl_func) */ duk_pop(ctx); /* pop regexp res_obj or match string */ #ifdef DUK_USE_REGEXP_SUPPORT if (!is_global) { #else { /* unconditionally; is_global==0 */ #endif break; } } /* trailer */ tmp_sz = (duk_size_t) (DUK_HSTRING_GET_BYTELEN(h_input) - prev_match_end_boff); DUK_BW_WRITE_ENSURE_BYTES(thr, bw, DUK_HSTRING_GET_DATA(h_input) + prev_match_end_boff, tmp_sz); DUK_ASSERT_TOP(ctx, 4); DUK_BW_COMPACT(thr, bw); (void) duk_buffer_to_string(ctx, -1); return 1; } /* * split() */ /* XXX: very messy now, but works; clean up, remove unused variables (nomimally * used so compiler doesn't complain). */ DUK_INTERNAL duk_ret_t duk_bi_string_prototype_split(duk_context *ctx) { duk_hthread *thr = (duk_hthread *) ctx; duk_hstring *h_input; duk_hstring *h_sep; duk_uint32_t limit; duk_uint32_t arr_idx; #ifdef DUK_USE_REGEXP_SUPPORT duk_bool_t is_regexp; #endif duk_bool_t matched; /* set to 1 if any match exists (needed for empty input special case) */ duk_uint32_t prev_match_end_coff, prev_match_end_boff; duk_uint32_t match_start_boff, match_start_coff; duk_uint32_t match_end_boff, match_end_coff; DUK_UNREF(thr); h_input = duk_push_this_coercible_to_string(ctx); DUK_ASSERT(h_input != NULL); duk_push_array(ctx); if (duk_is_undefined(ctx, 1)) { limit = 0xffffffffUL; } else { limit = duk_to_uint32(ctx, 1); } if (limit == 0) { return 1; } /* If the separator is a RegExp, make a "clone" of it. The specification * algorithm calls [[Match]] directly for specific indices; we emulate this * by tweaking lastIndex and using a "force global" variant of duk_regexp_match() * which will use global-style matching even when the RegExp itself is non-global. */ if (duk_is_undefined(ctx, 0)) { /* The spec algorithm first does "R = ToString(separator)" before checking * whether separator is undefined. Since this is side effect free, we can * skip the ToString() here. */ duk_dup_2(ctx); duk_put_prop_index(ctx, 3, 0); return 1; } else if (duk_get_hobject_with_class(ctx, 0, DUK_HOBJECT_CLASS_REGEXP) != NULL) { #ifdef DUK_USE_REGEXP_SUPPORT duk_push_hobject_bidx(ctx, DUK_BIDX_REGEXP_CONSTRUCTOR); duk_dup_0(ctx); duk_new(ctx, 1); /* [ ... RegExp val ] -> [ ... res ] */ duk_replace(ctx, 0); /* lastIndex is initialized to zero by new RegExp() */ is_regexp = 1; #else DUK_DCERROR_UNSUPPORTED(thr); #endif } else { duk_to_string(ctx, 0); #ifdef DUK_USE_REGEXP_SUPPORT is_regexp = 0; #endif } /* stack[0] = separator (string or regexp) * stack[1] = limit * stack[2] = input string * stack[3] = result array */ prev_match_end_boff = 0; prev_match_end_coff = 0; arr_idx = 0; matched = 0; for (;;) { /* * The specification uses RegExp [[Match]] to attempt match at specific * offsets. We don't have such a primitive, so we use an actual RegExp * and tweak lastIndex. Since the RegExp may be non-global, we use a * special variant which forces global-like behavior for matching. */ DUK_ASSERT_TOP(ctx, 4); #ifdef DUK_USE_REGEXP_SUPPORT if (is_regexp) { duk_dup_0(ctx); duk_dup_2(ctx); duk_regexp_match_force_global(thr); /* [ ... regexp input ] -> [ res_obj ] */ if (!duk_is_object(ctx, -1)) { duk_pop(ctx); break; } matched = 1; duk_get_prop_stridx(ctx, -1, DUK_STRIDX_INDEX); DUK_ASSERT(duk_is_number(ctx, -1)); match_start_coff = duk_get_int(ctx, -1); match_start_boff = duk_heap_strcache_offset_char2byte(thr, h_input, match_start_coff); duk_pop(ctx); if (match_start_coff == DUK_HSTRING_GET_CHARLEN(h_input)) { /* don't allow an empty match at the end of the string */ duk_pop(ctx); break; } duk_get_prop_stridx(ctx, 0, DUK_STRIDX_LAST_INDEX); DUK_ASSERT(duk_is_number(ctx, -1)); match_end_coff = duk_get_int(ctx, -1); match_end_boff = duk_heap_strcache_offset_char2byte(thr, h_input, match_end_coff); duk_pop(ctx); /* empty match -> bump and continue */ if (prev_match_end_boff == match_end_boff) { duk_push_int(ctx, match_end_coff + 1); duk_put_prop_stridx(ctx, 0, DUK_STRIDX_LAST_INDEX); duk_pop(ctx); continue; } } else { #else /* DUK_USE_REGEXP_SUPPORT */ { /* unconditionally */ #endif /* DUK_USE_REGEXP_SUPPORT */ const duk_uint8_t *p_start, *p_end, *p; /* input string scan */ const duk_uint8_t *q_start; /* match string */ duk_size_t q_blen, q_clen; p_start = DUK_HSTRING_GET_DATA(h_input); p_end = p_start + DUK_HSTRING_GET_BYTELEN(h_input); p = p_start + prev_match_end_boff; h_sep = duk_known_hstring(ctx, 0); q_start = DUK_HSTRING_GET_DATA(h_sep); q_blen = (duk_size_t) DUK_HSTRING_GET_BYTELEN(h_sep); q_clen = (duk_size_t) DUK_HSTRING_GET_CHARLEN(h_sep); p_end -= q_blen; /* ensure full memcmp() fits in while */ match_start_coff = prev_match_end_coff; if (q_blen == 0) { /* Handle empty separator case: it will always match, and always * triggers the check in step 13.c.iii initially. Note that we * must skip to either end of string or start of first codepoint, * skipping over any continuation bytes! * * Don't allow an empty string to match at the end of the input. */ matched = 1; /* empty separator can always match */ match_start_coff++; p++; while (p < p_end) { if ((p[0] & 0xc0) != 0x80) { goto found; } p++; } goto not_found; } DUK_ASSERT(q_blen > 0 && q_clen > 0); while (p <= p_end) { DUK_ASSERT(p + q_blen <= DUK_HSTRING_GET_DATA(h_input) + DUK_HSTRING_GET_BYTELEN(h_input)); DUK_ASSERT(q_blen > 0); /* no issues with empty memcmp() */ if (DUK_MEMCMP((const void *) p, (const void *) q_start, (size_t) q_blen) == 0) { /* never an empty match, so step 13.c.iii can't be triggered */ goto found; } /* track utf-8 non-continuation bytes */ if ((p[0] & 0xc0) != 0x80) { match_start_coff++; } p++; } not_found: /* not found */ break; found: matched = 1; match_start_boff = (duk_uint32_t) (p - p_start); match_end_coff = (duk_uint32_t) (match_start_coff + q_clen); /* constrained by string length */ match_end_boff = (duk_uint32_t) (match_start_boff + q_blen); /* ditto */ /* empty match (may happen with empty separator) -> bump and continue */ if (prev_match_end_boff == match_end_boff) { prev_match_end_boff++; prev_match_end_coff++; continue; } } /* if (is_regexp) */ /* stack[0] = separator (string or regexp) * stack[1] = limit * stack[2] = input string * stack[3] = result array * stack[4] = regexp res_obj (if is_regexp) */ DUK_DDD(DUK_DDDPRINT("split; match_start b=%ld,c=%ld, match_end b=%ld,c=%ld, prev_end b=%ld,c=%ld", (long) match_start_boff, (long) match_start_coff, (long) match_end_boff, (long) match_end_coff, (long) prev_match_end_boff, (long) prev_match_end_coff)); duk_push_lstring(ctx, (const char *) (DUK_HSTRING_GET_DATA(h_input) + prev_match_end_boff), (duk_size_t) (match_start_boff - prev_match_end_boff)); duk_put_prop_index(ctx, 3, arr_idx); arr_idx++; if (arr_idx >= limit) { goto hit_limit; } #ifdef DUK_USE_REGEXP_SUPPORT if (is_regexp) { duk_size_t i, len; len = duk_get_length(ctx, 4); for (i = 1; i < len; i++) { DUK_ASSERT(i <= DUK_UARRIDX_MAX); /* cannot have >4G captures */ duk_get_prop_index(ctx, 4, (duk_uarridx_t) i); duk_put_prop_index(ctx, 3, arr_idx); arr_idx++; if (arr_idx >= limit) { goto hit_limit; } } duk_pop(ctx); /* lastIndex already set up for next match */ } else { #else /* DUK_USE_REGEXP_SUPPORT */ { /* unconditionally */ #endif /* DUK_USE_REGEXP_SUPPORT */ /* no action */ } prev_match_end_boff = match_end_boff; prev_match_end_coff = match_end_coff; continue; } /* for */ /* Combined step 11 (empty string special case) and 14-15. */ DUK_DDD(DUK_DDDPRINT("split trailer; prev_end b=%ld,c=%ld", (long) prev_match_end_boff, (long) prev_match_end_coff)); if (DUK_HSTRING_GET_CHARLEN(h_input) > 0 || !matched) { /* Add trailer if: * a) non-empty input * b) empty input and no (zero size) match found (step 11) */ duk_push_lstring(ctx, (const char *) DUK_HSTRING_GET_DATA(h_input) + prev_match_end_boff, (duk_size_t) (DUK_HSTRING_GET_BYTELEN(h_input) - prev_match_end_boff)); duk_put_prop_index(ctx, 3, arr_idx); /* No arr_idx update or limit check */ } return 1; hit_limit: #ifdef DUK_USE_REGEXP_SUPPORT if (is_regexp) { duk_pop(ctx); } #endif return 1; } /* * Various */ #ifdef DUK_USE_REGEXP_SUPPORT DUK_LOCAL void duk__to_regexp_helper(duk_context *ctx, duk_idx_t idx, duk_bool_t force_new) { duk_hobject *h; /* Shared helper for match() steps 3-4, search() steps 3-4. */ DUK_ASSERT(idx >= 0); if (force_new) { goto do_new; } h = duk_get_hobject_with_class(ctx, idx, DUK_HOBJECT_CLASS_REGEXP); if (!h) { goto do_new; } return; do_new: duk_push_hobject_bidx(ctx, DUK_BIDX_REGEXP_CONSTRUCTOR); duk_dup(ctx, idx); duk_new(ctx, 1); /* [ ... RegExp val ] -> [ ... res ] */ duk_replace(ctx, idx); } #endif /* DUK_USE_REGEXP_SUPPORT */ #ifdef DUK_USE_REGEXP_SUPPORT DUK_INTERNAL duk_ret_t duk_bi_string_prototype_search(duk_context *ctx) { duk_hthread *thr = (duk_hthread *) ctx; /* Easiest way to implement the search required by the specification * is to do a RegExp test() with lastIndex forced to zero. To avoid * side effects on the argument, "clone" the RegExp if a RegExp was * given as input. * * The global flag of the RegExp should be ignored; setting lastIndex * to zero (which happens when "cloning" the RegExp) should have an * equivalent effect. */ DUK_ASSERT_TOP(ctx, 1); (void) duk_push_this_coercible_to_string(ctx); /* at index 1 */ duk__to_regexp_helper(ctx, 0 /*index*/, 1 /*force_new*/); /* stack[0] = regexp * stack[1] = string */ /* Avoid using RegExp.prototype methods, as they're writable and * configurable and may have been changed. */ duk_dup_0(ctx); duk_dup_1(ctx); /* [ ... re_obj input ] */ duk_regexp_match(thr); /* -> [ ... res_obj ] */ if (!duk_is_object(ctx, -1)) { duk_push_int(ctx, -1); return 1; } duk_get_prop_stridx(ctx, -1, DUK_STRIDX_INDEX); DUK_ASSERT(duk_is_number(ctx, -1)); return 1; } #endif /* DUK_USE_REGEXP_SUPPORT */ #ifdef DUK_USE_REGEXP_SUPPORT DUK_INTERNAL duk_ret_t duk_bi_string_prototype_match(duk_context *ctx) { duk_hthread *thr = (duk_hthread *) ctx; duk_bool_t global; duk_int_t prev_last_index; duk_int_t this_index; duk_int_t arr_idx; DUK_ASSERT_TOP(ctx, 1); (void) duk_push_this_coercible_to_string(ctx); duk__to_regexp_helper(ctx, 0 /*index*/, 0 /*force_new*/); global = duk_get_prop_stridx_boolean(ctx, 0, DUK_STRIDX_GLOBAL, NULL); DUK_ASSERT_TOP(ctx, 2); /* stack[0] = regexp * stack[1] = string */ if (!global) { duk_regexp_match(thr); /* -> [ res_obj ] */ return 1; /* return 'res_obj' */ } /* Global case is more complex. */ /* [ regexp string ] */ duk_push_int(ctx, 0); duk_put_prop_stridx(ctx, 0, DUK_STRIDX_LAST_INDEX); duk_push_array(ctx); /* [ regexp string res_arr ] */ prev_last_index = 0; arr_idx = 0; for (;;) { DUK_ASSERT_TOP(ctx, 3); duk_dup_0(ctx); duk_dup_1(ctx); duk_regexp_match(thr); /* -> [ ... regexp string ] -> [ ... res_obj ] */ if (!duk_is_object(ctx, -1)) { duk_pop(ctx); break; } duk_get_prop_stridx(ctx, 0, DUK_STRIDX_LAST_INDEX); DUK_ASSERT(duk_is_number(ctx, -1)); this_index = duk_get_int(ctx, -1); duk_pop(ctx); if (this_index == prev_last_index) { this_index++; duk_push_int(ctx, this_index); duk_put_prop_stridx(ctx, 0, DUK_STRIDX_LAST_INDEX); } prev_last_index = this_index; duk_get_prop_index(ctx, -1, 0); /* match string */ duk_put_prop_index(ctx, 2, arr_idx); arr_idx++; duk_pop(ctx); /* res_obj */ } if (arr_idx == 0) { duk_push_null(ctx); } return 1; /* return 'res_arr' or 'null' */ }
static int duk__traceback_getter_helper(duk_context *ctx, int output_type) { duk_hthread *thr = (duk_hthread *) ctx; int idx_td; int i; const char *str_tailcalled = " tailcalled"; const char *str_strict = " strict"; const char *str_construct = " construct"; const char *str_prevyield = " preventsyield"; const char *str_directeval = " directeval"; const char *str_empty = ""; DUK_ASSERT_TOP(ctx, 0); /* fixed arg count */ duk_push_this(ctx); duk_get_prop_stridx(ctx, -1, DUK_STRIDX_TRACEDATA); idx_td = duk_get_top_index(ctx); duk_push_hstring_stridx(ctx, DUK_STRIDX_NEWLINE_TAB); duk_push_this(ctx); duk_to_string(ctx, -1); /* [ ... this tracedata sep ToString(this) ] */ /* FIXME: skip null filename? */ if (duk_check_type(ctx, idx_td, DUK_TYPE_OBJECT)) { int t; /* Current tracedata contains 2 entries per callstack entry. */ for (i = 0; ; i += 2) { int pc; int line; int flags; double d; const char *funcname; duk_hobject *h_func; duk_hstring *h_name; duk_hbuffer_fixed *pc2line; duk_require_stack(ctx, 5); duk_get_prop_index(ctx, idx_td, i); duk_get_prop_index(ctx, idx_td, i + 1); d = duk_to_number(ctx, -1); pc = (int) fmod(d, DUK_DOUBLE_2TO32); flags = (int) floor(d / DUK_DOUBLE_2TO32); t = duk_get_type(ctx, -2); if (t == DUK_TYPE_OBJECT) { /* * Ecmascript/native function call */ /* [ ... v1(func) v2(pc+flags) ] */ h_func = duk_get_hobject(ctx, -2); DUK_ASSERT(h_func != NULL); duk_get_prop_stridx(ctx, -2, DUK_STRIDX_NAME); duk_get_prop_stridx(ctx, -3, DUK_STRIDX_FILE_NAME); duk_get_prop_stridx(ctx, -4, 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)); line = duk_hobject_pc2line_query(pc2line, (duk_uint_fast32_t) pc); } else { line = 0; } duk_pop(ctx); /* [ ... v1 v2 name filename ] */ if (output_type == DUK__OUTPUT_TYPE_FILENAME) { return 1; } else if (output_type == DUK__OUTPUT_TYPE_LINENUMBER) { duk_push_int(ctx, line); return 1; } h_name = duk_get_hstring(ctx, -2); /* may be NULL */ funcname = (h_name == NULL || h_name == DUK_HTHREAD_STRING_EMPTY_STRING(thr)) ? "anon" : (const char *) DUK_HSTRING_GET_DATA(h_name); if (DUK_HOBJECT_HAS_NATIVEFUNCTION(h_func)) { duk_push_sprintf(ctx, "%s %s native%s%s%s%s%s", funcname, duk_get_string(ctx, -1), (flags & DUK_ACT_FLAG_STRICT) ? str_strict : str_empty, (flags & DUK_ACT_FLAG_TAILCALLED) ? str_tailcalled : str_empty, (flags & DUK_ACT_FLAG_CONSTRUCT) ? str_construct : str_empty, (flags & DUK_ACT_FLAG_DIRECT_EVAL) ? str_directeval : str_empty, (flags & DUK_ACT_FLAG_PREVENT_YIELD) ? str_prevyield : str_empty); } else { duk_push_sprintf(ctx, "%s %s:%d%s%s%s%s%s", funcname, duk_get_string(ctx, -1), line, (flags & DUK_ACT_FLAG_STRICT) ? str_strict : str_empty, (flags & DUK_ACT_FLAG_TAILCALLED) ? str_tailcalled : str_empty, (flags & DUK_ACT_FLAG_CONSTRUCT) ? str_construct : str_empty, (flags & DUK_ACT_FLAG_DIRECT_EVAL) ? str_directeval : str_empty, (flags & DUK_ACT_FLAG_PREVENT_YIELD) ? str_prevyield : str_empty); } duk_replace(ctx, -5); /* [ ... v1 v2 name filename str ] -> [ ... str v2 name filename ] */ duk_pop_n(ctx, 3); /* -> [ ... str ] */ } else if (t == DUK_TYPE_STRING) { /* * __FILE__ / __LINE__ entry, here 'pc' is line number directly. * Sometimes __FILE__ / __LINE__ is reported as the source for * the error (fileName, lineNumber), sometimes not. */ /* [ ... v1(filename) v2(line+flags) ] */ if (!(flags & DUK_TB_FLAG_NOBLAME_FILELINE)) { if (output_type == DUK__OUTPUT_TYPE_FILENAME) { duk_pop(ctx); return 1; } else if (output_type == DUK__OUTPUT_TYPE_LINENUMBER) { duk_push_int(ctx, pc); return 1; } } duk_push_sprintf(ctx, "%s:%d", duk_get_string(ctx, -2), pc); duk_replace(ctx, -3); /* [ ... v1 v2 str ] -> [ ... str v2 ] */ duk_pop(ctx); /* -> [ ... str ] */ } else { /* unknown, ignore */ duk_pop_2(ctx); break; } } if (i >= DUK_USE_TRACEBACK_DEPTH * 2) { /* Possibly truncated; there is no explicit truncation * marker so this is the best we can do. */ duk_push_hstring_stridx(ctx, DUK_STRIDX_BRACKETED_ELLIPSIS); } } /* [ ... this tracedata sep ToString(this) str1 ... strN ] */ if (output_type != DUK__OUTPUT_TYPE_TRACEBACK) { return 0; } else { duk_join(ctx, duk_get_top(ctx) - (idx_td + 2) /*count, not including sep*/); return 1; } }
DUK_INTERNAL 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; duk_small_uint_t i, j; DUK_D(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_DD(DUK_DDPRINT("create empty built-ins")); DUK_ASSERT_TOP(ctx, 0); for (i = 0; i < DUK_NUM_BUILTINS; i++) { duk_small_uint_t class_num; duk_small_int_t len = -1; /* must be signed */ class_num = (duk_small_uint_t) duk_bd_decode(bd, DUK__CLASS_BITS); len = (duk_small_int_t) duk_bd_decode_flagged(bd, DUK__LENGTH_PROP_BITS, (duk_int32_t) -1 /*def_value*/); if (class_num == DUK_HOBJECT_CLASS_FUNCTION) { duk_small_uint_t natidx; duk_small_uint_t stridx; duk_int_t c_nargs; /* must hold DUK_VARARGS */ duk_c_function c_func; duk_int16_t magic; DUK_DDD(DUK_DDDPRINT("len=%ld", (long) len)); DUK_ASSERT(len >= 0); natidx = (duk_small_uint_t) duk_bd_decode(bd, DUK__NATIDX_BITS); stridx = (duk_small_uint_t) duk_bd_decode(bd, DUK__STRIDX_BITS); c_func = duk_bi_native_functions[natidx]; c_nargs = (duk_small_uint_t) duk_bd_decode_flagged(bd, DUK__NARGS_BITS, len /*def_value*/); if (c_nargs == DUK__NARGS_VARARGS_MARKER) { c_nargs = DUK_VARARGS; } /* XXX: set magic directly here? (it could share the c_nargs arg) */ duk_push_c_function_noexotic(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)); /* XXX: function properties */ duk_push_hstring_stridx(ctx, stridx); duk_xdef_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 { /* XXX: 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_xdef_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 exotic behaviors last */ if (class_num == DUK_HOBJECT_CLASS_ARRAY) { DUK_HOBJECT_SET_EXOTIC_ARRAY(h); } if (class_num == DUK_HOBJECT_CLASS_STRING) { DUK_HOBJECT_SET_EXOTIC_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_EXOTIC_ARRAY varies */ /* DUK_HOBJECT_FLAG_EXOTIC_STRINGOBJ varies */ DUK_ASSERT(!DUK_HOBJECT_HAS_EXOTIC_ARGUMENTS(h)); DUK_DDD(DUK_DDDPRINT("created built-in %ld, class=%ld, length=%ld", (long) i, (long) class_num, (long) len)); } /* * Then decode the builtins init data (see genbuiltins.py) to * init objects */ DUK_DD(DUK_DDPRINT("initialize built-in object properties")); for (i = 0; i < DUK_NUM_BUILTINS; i++) { duk_small_uint_t t; duk_small_uint_t num; DUK_DDD(DUK_DDDPRINT("initializing built-in object at index %ld", (long) i)); h = thr->builtins[i]; t = (duk_small_uint_t) duk_bd_decode(bd, DUK__BIDX_BITS); if (t != DUK__NO_BIDX_MARKER) { DUK_DDD(DUK_DDDPRINT("set internal prototype: built-in %ld", (long) t)); DUK_HOBJECT_SET_PROTOTYPE_UPDREF(thr, h, thr->builtins[t]); } t = (duk_small_uint_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_DDD(DUK_DDDPRINT("set external prototype: built-in %ld", (long) t)); duk_xdef_prop_stridx_builtin(ctx, i, DUK_STRIDX_PROTOTYPE, t, DUK_PROPDESC_FLAGS_NONE); } t = (duk_small_uint_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_DDD(DUK_DDDPRINT("set external constructor: built-in %ld", (long) t)); duk_xdef_prop_stridx_builtin(ctx, i, DUK_STRIDX_CONSTRUCTOR, t, DUK_PROPDESC_FLAGS_WC); } /* normal valued properties */ num = (duk_small_uint_t) duk_bd_decode(bd, DUK__NUM_NORMAL_PROPS_BITS); DUK_DDD(DUK_DDDPRINT("built-in object %ld, %ld normal valued properties", (long) i, (long) num)); for (j = 0; j < num; j++) { duk_small_uint_t stridx; duk_small_uint_t prop_flags; stridx = (duk_small_uint_t) 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_small_uint_t) 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_small_uint_t) duk_bd_decode(bd, DUK__PROP_TYPE_BITS); DUK_DDD(DUK_DDDPRINT("built-in %ld, normal-valued property %ld, stridx %ld, flags 0x%02lx, type %ld", (long) i, (long) j, (long) stridx, (unsigned long) prop_flags, (long) t)); switch (t) { case DUK__PROP_TYPE_DOUBLE: { duk_double_union du; duk_small_uint_t 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: { duk_small_uint_t n; duk_small_uint_t k; duk_uint8_t *p; n = (duk_small_uint_t) duk_bd_decode(bd, DUK__STRING_LENGTH_BITS); p = (duk_uint8_t *) duk_push_fixed_buffer(ctx, n); for (k = 0; k < n; k++) { *p++ = (duk_uint8_t) duk_bd_decode(bd, DUK__STRING_CHAR_BITS); } duk_to_string(ctx, -1); break; } case DUK__PROP_TYPE_STRIDX: { duk_small_uint_t n; n = (duk_small_uint_t) duk_bd_decode(bd, DUK__STRIDX_BITS); DUK_ASSERT_DISABLE(n >= 0); /* unsigned */ DUK_ASSERT(n < DUK_HEAP_NUM_STRINGS); duk_push_hstring_stridx(ctx, n); break; } case DUK__PROP_TYPE_BUILTIN: { duk_small_uint_t bidx; bidx = (duk_small_uint_t) duk_bd_decode(bd, DUK__BIDX_BITS); DUK_ASSERT(bidx != DUK__NO_BIDX_MARKER); duk_dup(ctx, (duk_idx_t) 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: { duk_small_uint_t natidx_getter = (duk_small_uint_t) duk_bd_decode(bd, DUK__NATIDX_BITS); duk_small_uint_t natidx_setter = (duk_small_uint_t) 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_DDD(DUK_DDDPRINT("built-in accessor property: objidx=%ld, stridx=%ld, getteridx=%ld, setteridx=%ld, flags=0x%04lx", (long) i, (long) stridx, (long) natidx_getter, (long) natidx_setter, (unsigned long) 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_noexotic(ctx, c_func_getter, 0); /* always 0 args */ duk_push_c_function_noconstruct_noexotic(ctx, c_func_setter, 1); /* always 1 arg */ /* XXX: 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_xdef_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_small_uint_t) duk_bd_decode(bd, DUK__NUM_FUNC_PROPS_BITS); DUK_DDD(DUK_DDDPRINT("built-in object %ld, %ld function valued properties", (long) i, (long) num)); for (j = 0; j < num; j++) { duk_small_uint_t stridx; duk_small_uint_t natidx; duk_int_t c_nargs; /* must hold DUK_VARARGS */ duk_small_uint_t c_length; duk_int16_t magic; duk_c_function c_func; duk_hnativefunction *h_func; #if defined(DUK_USE_LIGHTFUNC_BUILTINS) duk_small_int_t lightfunc_eligible; #endif stridx = (duk_small_uint_t) duk_bd_decode(bd, DUK__STRIDX_BITS); natidx = (duk_small_uint_t) duk_bd_decode(bd, DUK__NATIDX_BITS); c_length = (duk_small_uint_t) duk_bd_decode(bd, DUK__LENGTH_PROP_BITS); c_nargs = (duk_int_t) 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_DDD(DUK_DDDPRINT("built-in %ld, function-valued property %ld, stridx %ld, natidx %ld, length %ld, nargs %ld", (long) i, (long) j, (long) stridx, (long) natidx, (long) c_length, (c_nargs == DUK_VARARGS ? (long) -1 : (long) c_nargs))); /* Cast converts magic to 16-bit signed value */ magic = (duk_int16_t) duk_bd_decode_flagged(bd, DUK__MAGIC_BITS, 0); #if defined(DUK_USE_LIGHTFUNC_BUILTINS) lightfunc_eligible = ((c_nargs >= DUK_LFUNC_NARGS_MIN && c_nargs <= DUK_LFUNC_NARGS_MAX) || (c_nargs == DUK_VARARGS)) && (c_length <= DUK_LFUNC_LENGTH_MAX) && (magic >= DUK_LFUNC_MAGIC_MIN && magic <= DUK_LFUNC_MAGIC_MAX); if (stridx == DUK_STRIDX_EVAL || stridx == DUK_STRIDX_YIELD || stridx == DUK_STRIDX_RESUME || stridx == DUK_STRIDX_REQUIRE) { /* These functions have trouble working as lightfuncs. * Some of them have specific asserts and some may have * additional properties (e.g. 'require.id' may be written). */ DUK_D(DUK_DPRINT("reject as lightfunc: stridx=%d, i=%d, j=%d", (int) stridx, (int) i, (int) j)); lightfunc_eligible = 0; } if (lightfunc_eligible) { duk_tval tv_lfunc; duk_small_uint_t lf_nargs = (c_nargs == DUK_VARARGS ? DUK_LFUNC_NARGS_VARARGS : c_nargs); duk_small_uint_t lf_flags = DUK_LFUNC_FLAGS_PACK(magic, c_length, lf_nargs); DUK_TVAL_SET_LIGHTFUNC(&tv_lfunc, c_func, lf_flags); duk_push_tval(ctx, &tv_lfunc); DUK_D(DUK_DPRINT("built-in function eligible as light function: i=%d, j=%d c_length=%ld, c_nargs=%ld, magic=%ld -> %!iT", (int) i, (int) j, (long) c_length, (long) c_nargs, (long) magic, duk_get_tval(ctx, -1))); goto lightfunc_skip; } DUK_D(DUK_DPRINT("built-in function NOT ELIGIBLE as light function: i=%d, j=%d c_length=%ld, c_nargs=%ld, magic=%ld", (int) i, (int) j, (long) c_length, (long) c_nargs, (long) magic)); #endif /* DUK_USE_LIGHTFUNC_BUILTINS */ /* [ (builtin objects) ] */ duk_push_c_function_noconstruct_noexotic(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)); /* XXX: any way to avoid decoding magic bit; there are quite * many function properties and relatively few with magic values. */ h_func->magic = magic; /* [ (builtin objects) func ] */ duk_push_int(ctx, c_length); duk_xdef_prop_stridx(ctx, -2, DUK_STRIDX_LENGTH, DUK_PROPDESC_FLAGS_NONE); duk_push_hstring_stridx(ctx, stridx); duk_xdef_prop_stridx(ctx, -2, DUK_STRIDX_NAME, DUK_PROPDESC_FLAGS_NONE); /* XXX: other properties of function instances; 'arguments', 'caller'. */ DUK_DD(DUK_DDPRINT("built-in object %ld, function property %ld -> %!T", (long) i, (long) j, (duk_tval *) duk_get_tval(ctx, -1))); /* [ (builtin objects) func ] */ /* * The default property attributes are correct for all * function valued properties of built-in objects now. */ #if defined(DUK_USE_LIGHTFUNC_BUILTINS) lightfunc_skip: #endif duk_xdef_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. * * - Possibly remove some properties (values or methods) which are not * desirable with current feature options but are not currently * conditional in init data. */ duk_get_prop_stridx(ctx, DUK_BIDX_DATE_PROTOTYPE, DUK_STRIDX_TO_UTC_STRING); duk_xdef_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); #if !defined(DUK_USE_ES6_OBJECT_PROTO_PROPERTY) DUK_DD(DUK_DDPRINT("delete Object.prototype.__proto__ built-in which is not enabled in features")); (void) duk_hobject_delprop_raw(thr, thr->builtins[DUK_BIDX_OBJECT_PROTOTYPE], DUK_HTHREAD_STRING___PROTO__(thr), DUK_DELPROP_FLAG_THROW); #endif #if !defined(DUK_USE_ES6_OBJECT_SETPROTOTYPEOF) DUK_DD(DUK_DDPRINT("delete Object.setPrototypeOf built-in which is not enabled in features")); (void) duk_hobject_delprop_raw(thr, thr->builtins[DUK_BIDX_OBJECT_CONSTRUCTOR], DUK_HTHREAD_STRING_SET_PROTOTYPE_OF(thr), DUK_DELPROP_FLAG_THROW); #endif duk_push_string(ctx, /* Endianness indicator */ #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 " " /* Packed or unpacked tval */ #if defined(DUK_USE_PACKED_TVAL) "p" #else "u" #endif #if defined(DUK_USE_FASTINT) "f" #endif " " /* Low memory options */ #if defined(DUK_USE_STRTAB_CHAIN) "c" /* chain */ #elif defined(DUK_USE_STRTAB_PROBE) "p" /* probe */ #else "?" #endif #if !defined(DUK_USE_HEAPPTR16) && !defined(DUK_DATAPTR16) && !defined(DUK_FUNCPTR16) "n" #endif #if defined(DUK_USE_HEAPPTR16) "h" #endif #if defined(DUK_USE_DATAPTR16) "d" #endif #if defined(DUK_USE_FUNCPTR16) "f" #endif #if defined(DUK_USE_REFCOUNT16) "R" #endif #if defined(DUK_USE_STRHASH16) "H" #endif #if defined(DUK_USE_STRLEN16) "S" #endif #if defined(DUK_USE_BUFLEN16) "B" #endif #if defined(DUK_USE_OBJSIZES16) "O" #endif #if defined(DUK_USE_LIGHTFUNC_BUILTINS) "L" #endif " " /* Object property allocation layout */ #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 " " /* Alignment guarantee */ #if defined(DUK_USE_ALIGN_4) "a4" #elif defined(DUK_USE_ALIGN_8) "a8" #else "a1" #endif " " /* Architecture, OS, and compiler strings */ DUK_USE_ARCH_STRING " " DUK_USE_OS_STRING " " DUK_USE_COMPILER_STRING); duk_xdef_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_BUILTIN_INITJS /* XXX: compression */ DUK_DD(DUK_DDPRINT("running built-in initjs")); duk_eval_string(ctx, (const char *) duk_initjs_data); /* initjs data is NUL terminated */ duk_pop(ctx); #endif /* DUK_USE_BUILTIN_INITJS */ #ifdef DUK_USE_USER_INITJS /* XXX: compression (as an option) */ DUK_DD(DUK_DDPRINT("running user initjs")); duk_eval_string_noresult(ctx, (const char *) DUK_USE_USER_INITJS); #endif /* DUK_USE_USER_INITJS */ /* * Since built-ins are not often extended, compact them. */ DUK_DD(DUK_DDPRINT("compact built-ins")); for (i = 0; i < DUK_NUM_BUILTINS; i++) { duk_hobject_compact_props(thr, thr->builtins[i]); } DUK_D(DUK_DPRINT("INITBUILTINS END")); #ifdef DUK_USE_DDPRINT for (i = 0; i < DUK_NUM_BUILTINS; i++) { DUK_DD(DUK_DDPRINT("built-in object %ld after initialization and compacting: %!@iO", (long) i, (duk_heaphdr *) 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); }
DUK_INTERNAL duk_ret_t duk_bi_array_prototype_indexof_shared(duk_context *ctx) { duk_idx_t nargs; duk_int_t i, len; duk_int_t from_index; duk_small_int_t idx_step = duk_get_current_magic(ctx); /* idx_step is +1 for indexOf, -1 for lastIndexOf */ /* lastIndexOf() needs to be a vararg function because we must distinguish * between an undefined fromIndex and a "not given" fromIndex; indexOf() is * made vararg for symmetry although it doesn't strictly need to be. */ nargs = duk_get_top(ctx); duk_set_top(ctx, 2); /* XXX: must be able to represent -len */ len = (duk_int_t) duk__push_this_obj_len_u32_limited(ctx); if (len == 0) { goto not_found; } /* Index clamping is a bit tricky, we must ensure that we'll only iterate * through elements that exist and that the specific requirements from E5.1 * Sections 15.4.4.14 and 15.4.4.15 are fulfilled; especially: * * - indexOf: clamp to [-len,len], negative handling -> [0,len], * if clamped result is len, for-loop bails out immediately * * - lastIndexOf: clamp to [-len-1, len-1], negative handling -> [-1, len-1], * if clamped result is -1, for-loop bails out immediately * * If fromIndex is not given, ToInteger(undefined) = 0, which is correct * for indexOf() but incorrect for lastIndexOf(). Hence special handling, * and why lastIndexOf() needs to be a vararg function. */ if (nargs >= 2) { /* indexOf: clamp fromIndex to [-len, len] * (if fromIndex == len, for-loop terminates directly) * * lastIndexOf: clamp fromIndex to [-len - 1, len - 1] * (if clamped to -len-1 -> fromIndex becomes -1, terminates for-loop directly) */ from_index = duk_to_int_clamped(ctx, 1, (idx_step > 0 ? -len : -len - 1), (idx_step > 0 ? len : len - 1)); if (from_index < 0) { /* for lastIndexOf, result may be -1 (mark immediate termination) */ from_index = len + from_index; } } else { /* for indexOf, ToInteger(undefined) would be 0, i.e. correct, but * handle both indexOf and lastIndexOf specially here. */ if (idx_step > 0) { from_index = 0; } else { from_index = len - 1; } } /* stack[0] = searchElement * stack[1] = fromIndex * stack[2] = object * stack[3] = length (not needed, but not popped above) */ for (i = from_index; i >= 0 && i < len; i += idx_step) { DUK_ASSERT_TOP(ctx, 4); if (duk_get_prop_index(ctx, 2, (duk_uarridx_t) i)) { DUK_ASSERT_TOP(ctx, 5); if (duk_strict_equals(ctx, 0, 4)) { duk_push_int(ctx, i); return 1; } } duk_pop(ctx); } not_found: duk_push_int(ctx, -1); return 1; }
static void remove_handlers(duk_context *ctx) { duk_eval_string(ctx, "delete Duktape.errCreate; delete Duktape.errThrow;"); duk_pop(ctx); }
DUK_INTERNAL duk_ret_t duk_bi_array_prototype_iter_shared(duk_context *ctx) { duk_uint32_t len; duk_uint32_t i; duk_uarridx_t k; duk_bool_t bval; duk_small_int_t iter_type = duk_get_current_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); duk_require_callable(ctx, 0); /* 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, (duk_uarridx_t) i)) { #if defined(DUK_USE_NONSTD_ARRAY_MAP_TRAILER) /* Real world behavior for map(): trailing non-existent * elements don't invoke the user callback, but are still * counted towards result 'length'. */ if (iter_type == DUK__ITER_MAP) { res_length = i + 1; } #else /* Standard behavior for map(): trailing non-existent * elements don't invoke the user callback and are not * counted towards result 'length'. */ #endif 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_u32(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_xdef_prop_index_wec(ctx, 4, (duk_uarridx_t) i); /* 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_xdef_prop_index_wec(ctx, 4, (duk_uarridx_t) k); 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_u32(ctx, res_length); duk_xdef_prop_stridx(ctx, -2, DUK_STRIDX_LENGTH, DUK_PROPDESC_FLAGS_W); break; default: DUK_UNREACHABLE(); break; } return 1; }
//----------------------------------------------------------------------------- void AdTiledManager::Load(const char* pName) { Unload(); char pFN[FILENAME_MAX]; sprintf(pFN, MAP_LOCATION, pName); duk_context* ctx = s_pJSCtx; duk_push_string_file(ctx, pFN); duk_json_decode(ctx, -1); if(!duk_is_object(ctx, -1)) { fprintf(stderr, "NOTE: Failed parse %s.json!\n", pName); return; } duk_get_prop_string(ctx, -1, "width"); m_iWidth = duk_to_int(ctx, -1); duk_pop(ctx); duk_get_prop_string(ctx, -1, "height"); m_iHeight = duk_to_int(ctx, -1); duk_pop(ctx); duk_get_prop_string(ctx, -1, "layers"); if(duk_is_array(ctx, -1)) { m_nLayers = duk_get_length(ctx, -1); m_pIndices = (int**) calloc(m_nLayers, sizeof(int*)); for(int j=0; j<m_nLayers; ++j) { duk_get_prop_index(ctx, -1, j); // NOTE: if this is a tile layer grab the indices, otherwise set // to NULL duk_get_prop_string(ctx, -1, "data"); if(duk_is_array(ctx, -1)) { int size = duk_get_length(ctx, -1); if((m_iWidth*m_iHeight) == size) { m_pIndices[j] = (int*) malloc(size*sizeof(int)); for(int i=0; i<size; ++i) { duk_get_prop_index(ctx, -1, i); m_pIndices[j][i] = duk_to_int(ctx, -1); duk_pop(ctx); } } } duk_pop(ctx); // NOTE: loop over the map entities and add them to the array of // entities duk_get_prop_string(ctx, -1, "name"); if(duk_is_string(ctx, -1)) { if(!strcmp(duk_get_string(ctx, -1), "entities")) { // NOTE: -2 referring back to object and not the string duk_get_prop_string(ctx, -2, "objects"); if(duk_is_array(ctx, -1)) { int size = duk_get_length(ctx, -1); for(int e=0; e<size; ++e) { duk_get_prop_index(ctx, -1, e); duk_get_prop_string(ctx, -1, "type"); const char* type = duk_get_string(ctx, -1); AdEntity* pEnt = NULL; if(!strcmp(type, "NPC-PLAYER")) { duk_pop(ctx); pEnt = new AdPlayer(); pEnt->Load(ctx); } else if(!strcmp(type, "NPC-TEST")) { duk_pop(ctx); pEnt = new AdMoveable(); pEnt->Load(ctx); } else if(!strcmp(type, "TEST")) { duk_pop(ctx); pEnt = new AdEntity(); pEnt->Load(ctx); } else { duk_pop(ctx); } if(pEnt) { m_pEntities = (AdEntity**) realloc( m_pEntities, ++m_nEntities*sizeof(AdEntity*) ); m_pEntities[m_nEntities-1] = pEnt; } duk_pop(ctx); } } duk_pop(ctx); } } duk_pop_2(ctx); } } duk_pop_2(ctx); }
DUK_INTERNAL duk_ret_t duk_bi_array_prototype_concat(duk_context *ctx) { duk_idx_t i, n; duk_uarridx_t idx, idx_last; duk_uarridx_t j, len; 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_xdef_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_xdef_prop_index_wec(ctx, -2, idx++); idx_last = idx; continue; } /* [ ToObject(this) item1 ... itemN arr item(i) ] */ /* XXX: an array can have length higher than 32 bits; this is not handled * correctly now. */ len = (duk_uarridx_t) 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_xdef_prop_index_wec(ctx, -3, idx++); idx_last = idx; } else { idx++; duk_pop(ctx); #if defined(DUK_USE_NONSTD_ARRAY_CONCAT_TRAILER) /* According to E5.1 Section 15.4.4.4 nonexistent trailing * elements do not affect 'length' of the result. Test262 * and other engines disagree, so update idx_last here too. */ idx_last = idx; #else /* Strict standard behavior, ignore trailing elements for * result 'length'. */ #endif } } duk_pop(ctx); } /* The E5.1 Section 15.4.4.4 algorithm doesn't set the length explicitly * in the end, but because we're operating with an internal value which * is known to be an array, this should be equivalent. */ duk_push_uarridx(ctx, idx_last); duk_xdef_prop_stridx(ctx, -2, DUK_STRIDX_LENGTH, DUK_PROPDESC_FLAGS_W); DUK_ASSERT_TOP(ctx, n + 1); return 1; }
duk_ret_t dukky_audio_track___proto(duk_context *ctx) { /* Add readonly property */ duk_dup(ctx, 0); duk_push_string(ctx, "id"); duk_push_c_function(ctx, dukky_audio_track_id_getter, 0); duk_def_prop(ctx, -3, DUK_DEFPROP_HAVE_GETTER | DUK_DEFPROP_HAVE_ENUMERABLE | DUK_DEFPROP_ENUMERABLE | DUK_DEFPROP_HAVE_CONFIGURABLE); duk_pop(ctx); /* Add readonly property */ duk_dup(ctx, 0); duk_push_string(ctx, "kind"); duk_push_c_function(ctx, dukky_audio_track_kind_getter, 0); duk_def_prop(ctx, -3, DUK_DEFPROP_HAVE_GETTER | DUK_DEFPROP_HAVE_ENUMERABLE | DUK_DEFPROP_ENUMERABLE | DUK_DEFPROP_HAVE_CONFIGURABLE); duk_pop(ctx); /* Add readonly property */ duk_dup(ctx, 0); duk_push_string(ctx, "label"); duk_push_c_function(ctx, dukky_audio_track_label_getter, 0); duk_def_prop(ctx, -3, DUK_DEFPROP_HAVE_GETTER | DUK_DEFPROP_HAVE_ENUMERABLE | DUK_DEFPROP_ENUMERABLE | DUK_DEFPROP_HAVE_CONFIGURABLE); duk_pop(ctx); /* Add readonly property */ duk_dup(ctx, 0); duk_push_string(ctx, "language"); duk_push_c_function(ctx, dukky_audio_track_language_getter, 0); duk_def_prop(ctx, -3, DUK_DEFPROP_HAVE_GETTER | DUK_DEFPROP_HAVE_ENUMERABLE | DUK_DEFPROP_ENUMERABLE | DUK_DEFPROP_HAVE_CONFIGURABLE); duk_pop(ctx); /* Add read/write property */ duk_dup(ctx, 0); duk_push_string(ctx, "enabled"); duk_push_c_function(ctx, dukky_audio_track_enabled_getter, 0); duk_push_c_function(ctx, dukky_audio_track_enabled_setter, 1); duk_def_prop(ctx, -4, DUK_DEFPROP_HAVE_GETTER | DUK_DEFPROP_HAVE_SETTER | DUK_DEFPROP_HAVE_ENUMERABLE | DUK_DEFPROP_ENUMERABLE | DUK_DEFPROP_HAVE_CONFIGURABLE); duk_pop(ctx); /* Set the destructor */ duk_dup(ctx, 0); duk_push_c_function(ctx, dukky_audio_track___destructor, 1); duk_set_finalizer(ctx, -2); duk_pop(ctx); /* Set the constructor */ duk_dup(ctx, 0); duk_push_c_function(ctx, dukky_audio_track___constructor, 1); duk_put_prop_string(ctx, -2, "\xFF\xFFNETSURF_DUKTAPE_INIT"); duk_pop(ctx); return 1; /* The prototype object */ }
DUK_INTERNAL duk_ret_t duk_bi_string_prototype_match(duk_context *ctx) { duk_hthread *thr = (duk_hthread *) ctx; duk_bool_t global; duk_int_t prev_last_index; duk_int_t this_index; duk_int_t arr_idx; DUK_ASSERT_TOP(ctx, 1); (void) duk_push_this_coercible_to_string(ctx); duk__to_regexp_helper(ctx, 0 /*index*/, 0 /*force_new*/); global = duk_get_prop_stridx_boolean(ctx, 0, DUK_STRIDX_GLOBAL, NULL); DUK_ASSERT_TOP(ctx, 2); /* stack[0] = regexp * stack[1] = string */ if (!global) { duk_regexp_match(thr); /* -> [ res_obj ] */ return 1; /* return 'res_obj' */ } /* Global case is more complex. */ /* [ regexp string ] */ duk_push_int(ctx, 0); duk_put_prop_stridx(ctx, 0, DUK_STRIDX_LAST_INDEX); duk_push_array(ctx); /* [ regexp string res_arr ] */ prev_last_index = 0; arr_idx = 0; for (;;) { DUK_ASSERT_TOP(ctx, 3); duk_dup(ctx, 0); duk_dup(ctx, 1); duk_regexp_match(thr); /* -> [ ... regexp string ] -> [ ... res_obj ] */ if (!duk_is_object(ctx, -1)) { duk_pop(ctx); break; } duk_get_prop_stridx(ctx, 0, DUK_STRIDX_LAST_INDEX); DUK_ASSERT(duk_is_number(ctx, -1)); this_index = duk_get_int(ctx, -1); duk_pop(ctx); if (this_index == prev_last_index) { this_index++; duk_push_int(ctx, this_index); duk_put_prop_stridx(ctx, 0, DUK_STRIDX_LAST_INDEX); } prev_last_index = this_index; duk_get_prop_index(ctx, -1, 0); /* match string */ duk_put_prop_index(ctx, 2, arr_idx); arr_idx++; duk_pop(ctx); /* res_obj */ } if (arr_idx == 0) { duk_push_null(ctx); } return 1; /* return 'res_arr' or 'null' */ }