From 40c9814e053395c9dbfe8b8ddfb610c941e16f04 Mon Sep 17 00:00:00 2001 From: Sami Vaarala Date: Wed, 30 Nov 2016 21:17:34 +0200 Subject: [PATCH 1/5] Expose internal duk_set_length() in public API --- src-input/duk_api_internal.h | 3 --- src-input/duk_api_public.h.in | 1 + src-input/duk_api_stack.c | 41 +++++++++++++++++++++-------------- src-input/duk_bi_json.c | 2 +- src-input/duk_hobject.h | 4 +--- src-input/duk_hobject_props.c | 29 +++++-------------------- src-input/duk_js_compiler.c | 14 +++++------- src-input/duk_js_executor.c | 2 +- 8 files changed, 40 insertions(+), 56 deletions(-) diff --git a/src-input/duk_api_internal.h b/src-input/duk_api_internal.h index dffe838b..910c5de0 100644 --- a/src-input/duk_api_internal.h +++ b/src-input/duk_api_internal.h @@ -258,9 +258,6 @@ DUK_INTERNAL_DECL void duk_xdef_prop_stridx_builtin(duk_context *ctx, duk_idx_t DUK_INTERNAL_DECL void duk_xdef_prop_stridx_thrower(duk_context *ctx, duk_idx_t obj_idx, duk_small_uint_t stridx); /* [] -> [] */ -/* Set object 'length'. */ -DUK_INTERNAL_DECL void duk_set_length(duk_context *ctx, duk_idx_t idx, duk_size_t length); - DUK_INTERNAL_DECL void duk_pack(duk_context *ctx, duk_idx_t count); #if 0 DUK_INTERNAL_DECL void duk_unpack(duk_context *ctx); diff --git a/src-input/duk_api_public.h.in b/src-input/duk_api_public.h.in index 44c81e33..fb882ae9 100644 --- a/src-input/duk_api_public.h.in +++ b/src-input/duk_api_public.h.in @@ -642,6 +642,7 @@ DUK_EXTERNAL_DECL duk_c_function duk_get_c_function(duk_context *ctx, duk_idx_t DUK_EXTERNAL_DECL duk_context *duk_get_context(duk_context *ctx, duk_idx_t idx); DUK_EXTERNAL_DECL void *duk_get_heapptr(duk_context *ctx, duk_idx_t idx); DUK_EXTERNAL_DECL duk_size_t duk_get_length(duk_context *ctx, duk_idx_t idx); +DUK_EXTERNAL_DECL void duk_set_length(duk_context *ctx, duk_idx_t idx, duk_size_t len); /* * Require operations: no coercion, throw error if index or type diff --git a/src-input/duk_api_stack.c b/src-input/duk_api_stack.c index eca65ce9..c43de420 100644 --- a/src-input/duk_api_stack.c +++ b/src-input/duk_api_stack.c @@ -1827,16 +1827,26 @@ DUK_EXTERNAL duk_size_t duk_get_length(duk_context *ctx, duk_idx_t idx) { case DUK_TAG_BOOLEAN: case DUK_TAG_POINTER: return 0; +#if defined(DUK_USE_PREFER_SIZE) + /* All of these types (besides object) have a virtual, non-configurable + * .length property which is within size_t range so we can just look it + * up without specific type checks. + */ + case DUK_TAG_STRING: + case DUK_TAG_BUFFER: + case DUK_TAG_LIGHTFUNC: { + duk_size_t ret; + duk_get_prop_stridx(ctx, idx, DUK_STRIDX_LENGTH); + ret = (duk_size_t) duk_to_number_m1(ctx); + duk_pop(ctx); + return ret; + } +#else /* DUK_USE_PREFER_SIZE */ case DUK_TAG_STRING: { duk_hstring *h = DUK_TVAL_GET_STRING(tv); DUK_ASSERT(h != NULL); return (duk_size_t) DUK_HSTRING_GET_CHARLEN(h); } - case DUK_TAG_OBJECT: { - duk_hobject *h = DUK_TVAL_GET_OBJECT(tv); - DUK_ASSERT(h != NULL); - return (duk_size_t) duk_hobject_get_length((duk_hthread *) ctx, h); - } case DUK_TAG_BUFFER: { duk_hbuffer *h = DUK_TVAL_GET_BUFFER(tv); DUK_ASSERT(h != NULL); @@ -1847,6 +1857,12 @@ DUK_EXTERNAL duk_size_t duk_get_length(duk_context *ctx, duk_idx_t idx) { lf_flags = DUK_TVAL_GET_LIGHTFUNC_FLAGS(tv); return (duk_size_t) DUK_LFUNC_FLAGS_GET_LENGTH(lf_flags); } +#endif /* DUK_USE_PREFER_SIZE */ + case DUK_TAG_OBJECT: { + duk_hobject *h = DUK_TVAL_GET_OBJECT(tv); + DUK_ASSERT(h != NULL); + return (duk_size_t) duk_hobject_get_length((duk_hthread *) ctx, h); + } #if defined(DUK_USE_FASTINT) case DUK_TAG_FASTINT: #endif @@ -1859,7 +1875,6 @@ DUK_EXTERNAL duk_size_t duk_get_length(duk_context *ctx, duk_idx_t idx) { DUK_UNREACHABLE(); } - /* * duk_known_xxx() helpers * @@ -1913,18 +1928,12 @@ DUK_INTERNAL duk_hnatfunc *duk_known_hnatfunc(duk_context *ctx, duk_idx_t idx) { return (duk_hnatfunc *) duk__known_heaphdr(ctx, idx); } -DUK_INTERNAL void duk_set_length(duk_context *ctx, duk_idx_t idx, duk_size_t length) { - duk_hthread *thr = (duk_hthread *) ctx; - duk_hobject *h; - +DUK_EXTERNAL void duk_set_length(duk_context *ctx, duk_idx_t idx, duk_size_t len) { DUK_ASSERT_CTX_VALID(ctx); - h = duk_get_hobject(ctx, idx); - if (!h) { - return; - } - - duk_hobject_set_length(thr, h, (duk_uint32_t) length); /* XXX: typing */ + idx = duk_normalize_index(ctx, idx); + duk_push_uint(ctx, (duk_uint_t) len); + duk_put_prop_stridx(ctx, idx, DUK_STRIDX_LENGTH); } /* diff --git a/src-input/duk_bi_json.c b/src-input/duk_bi_json.c index 076e1e7d..814fd52f 100644 --- a/src-input/duk_bi_json.c +++ b/src-input/duk_bi_json.c @@ -2472,7 +2472,7 @@ DUK_LOCAL duk_bool_t duk__json_stringify_fast_value(duk_json_enc_ctx *js_ctx, du goto abort_fastpath; } - arr_len = (duk_uint_fast32_t) duk_hobject_get_length(js_ctx->thr, obj); + arr_len = (duk_uint_fast32_t) ((duk_harray *) obj)->length; asize = (duk_uint_fast32_t) DUK_HOBJECT_GET_ASIZE(obj); /* Array part may be larger than 'length'; if so, iterate * only up to array 'length'. Array part may also be smaller diff --git a/src-input/duk_hobject.h b/src-input/duk_hobject.h index cada3c0a..138e5935 100644 --- a/src-input/duk_hobject.h +++ b/src-input/duk_hobject.h @@ -887,9 +887,7 @@ DUK_INTERNAL_DECL duk_bool_t duk_hobject_delprop_raw(duk_hthread *thr, duk_hobje DUK_INTERNAL_DECL duk_bool_t duk_hobject_hasprop_raw(duk_hthread *thr, duk_hobject *obj, duk_hstring *key); DUK_INTERNAL_DECL void duk_hobject_define_property_internal(duk_hthread *thr, duk_hobject *obj, duk_hstring *key, duk_small_uint_t flags); DUK_INTERNAL_DECL void duk_hobject_define_property_internal_arridx(duk_hthread *thr, duk_hobject *obj, duk_uarridx_t arr_idx, duk_small_uint_t flags); -DUK_INTERNAL_DECL void duk_hobject_set_length(duk_hthread *thr, duk_hobject *obj, duk_uint32_t length); /* XXX: duk_uarridx_t? */ -DUK_INTERNAL_DECL void duk_hobject_set_length_zero(duk_hthread *thr, duk_hobject *obj); -DUK_INTERNAL_DECL duk_uint32_t duk_hobject_get_length(duk_hthread *thr, duk_hobject *obj); /* XXX: duk_uarridx_t? */ +DUK_INTERNAL_DECL duk_size_t duk_hobject_get_length(duk_hthread *thr, duk_hobject *obj); /* helpers for defineProperty() and defineProperties() */ DUK_INTERNAL_DECL diff --git a/src-input/duk_hobject_props.c b/src-input/duk_hobject_props.c index 73606422..48b157b6 100644 --- a/src-input/duk_hobject_props.c +++ b/src-input/duk_hobject_props.c @@ -4745,26 +4745,7 @@ DUK_INTERNAL void duk_hobject_define_property_internal_arridx(duk_hthread *thr, * Internal helpers for managing object 'length' */ -/* XXX: awkward helpers */ - -DUK_INTERNAL void duk_hobject_set_length(duk_hthread *thr, duk_hobject *obj, duk_uint32_t length) { - duk_context *ctx = (duk_context *) thr; - duk_push_hobject(ctx, obj); - duk_push_hstring_stridx(ctx, DUK_STRIDX_LENGTH); - duk_push_u32(ctx, length); - (void) duk_hobject_putprop(thr, - DUK_GET_TVAL_NEGIDX(ctx, -3), - DUK_GET_TVAL_NEGIDX(ctx, -2), - DUK_GET_TVAL_NEGIDX(ctx, -1), - 0); - duk_pop_3(ctx); -} - -DUK_INTERNAL void duk_hobject_set_length_zero(duk_hthread *thr, duk_hobject *obj) { - duk_hobject_set_length(thr, obj, 0); -} - -DUK_INTERNAL duk_uint32_t duk_hobject_get_length(duk_hthread *thr, duk_hobject *obj) { +DUK_INTERNAL duk_size_t duk_hobject_get_length(duk_hthread *thr, duk_hobject *obj) { duk_context *ctx = (duk_context *) thr; duk_double_t val; @@ -4785,9 +4766,11 @@ DUK_INTERNAL duk_uint32_t duk_hobject_get_length(duk_hthread *thr, duk_hobject * val = duk_to_number_m1(ctx); duk_pop_3(ctx); - /* XXX: better check */ - if (val >= 0.0 && val < DUK_DOUBLE_2TO32) { - return (duk_uint32_t) val; + /* This isn't part of Ecmascript semantics; return a value within + * duk_size_t range, or 0 otherwise. + */ + if (val >= 0.0 && val <= (duk_double_t) DUK_SIZE_MAX) { + return (duk_size_t) val; } return 0; } diff --git a/src-input/duk_js_compiler.c b/src-input/duk_js_compiler.c index 43502ebf..a75f9422 100644 --- a/src-input/duk_js_compiler.c +++ b/src-input/duk_js_compiler.c @@ -585,11 +585,11 @@ DUK_LOCAL void duk__reset_func_for_pass2(duk_compiler_ctx *comp_ctx) { */ DUK_BW_RESET_SIZE(thr, &func->bw_code); - duk_hobject_set_length_zero(thr, func->h_consts); + duk_set_length(ctx, func->consts_idx, 0); /* keep func->h_funcs; inner functions are not reparsed to avoid O(depth^2) parsing */ func->fnum_next = 0; - /* duk_hobject_set_length_zero(thr, func->h_funcs); */ - duk_hobject_set_length_zero(thr, func->h_labelnames); + /* duk_set_length(ctx, func->funcs_idx, 0); */ + duk_set_length(ctx, func->labelnames_idx, 0); duk_hbuffer_reset(thr, func->h_labelinfos); /* keep func->h_argnames; it is fixed for all passes */ @@ -2760,13 +2760,9 @@ DUK_LOCAL void duk__lookup_active_label(duk_compiler_ctx *comp_ctx, duk_hstring DUK_LOCAL void duk__reset_labels_to_length(duk_compiler_ctx *comp_ctx, duk_int_t len) { duk_hthread *thr = comp_ctx->thr; duk_context *ctx = (duk_context *) thr; - duk_size_t new_size; - /* XXX: duk_set_length */ - new_size = sizeof(duk_labelinfo) * (duk_size_t) len; - duk_push_int(ctx, len); - duk_put_prop_stridx(ctx, comp_ctx->curr_func.labelnames_idx, DUK_STRIDX_LENGTH); - duk_hbuffer_resize(thr, comp_ctx->curr_func.h_labelinfos, new_size); + duk_set_length(ctx, comp_ctx->curr_func.labelnames_idx, (duk_size_t) len); + duk_hbuffer_resize(thr, comp_ctx->curr_func.h_labelinfos, sizeof(duk_labelinfo) * (duk_size_t) len); } /* diff --git a/src-input/duk_js_executor.c b/src-input/duk_js_executor.c index 5f58c3c8..dfb107c9 100644 --- a/src-input/duk_js_executor.c +++ b/src-input/duk_js_executor.c @@ -4787,7 +4787,7 @@ DUK_LOCAL DUK_NOINLINE void duk__js_execute_bytecode_inner(duk_hthread *entry_th * ToUint32() which is odd but happens now as a side effect of * 'arr_idx' type. */ - duk_hobject_set_length(thr, duk_known_hobject(ctx, obj_idx), (duk_uint32_t) arr_idx); + duk_set_length(thr, obj_idx, (duk_size_t) (duk_uarridx_t) arr_idx); break; } From b1ebc56aab156f4650c6490b05c55910420e15c6 Mon Sep 17 00:00:00 2001 From: Sami Vaarala Date: Sun, 18 Dec 2016 00:56:56 +0200 Subject: [PATCH 2/5] Testcase for duk_set_length() --- tests/api/test-get-length.c | 8 +++--- tests/api/test-set-length.c | 50 +++++++++++++++++++++++++++++++++++++ 2 files changed, 54 insertions(+), 4 deletions(-) create mode 100644 tests/api/test-set-length.c diff --git a/tests/api/test-get-length.c b/tests/api/test-get-length.c index f2f8744c..2a053cab 100644 --- a/tests/api/test-get-length.c +++ b/tests/api/test-get-length.c @@ -59,13 +59,13 @@ void test(duk_context *ctx) { duk_put_prop_string(ctx, -2, "length"); /* 8 */ - duk_push_object(ctx); /* length: outside 32-bit range but within range after ToInteger() */ - duk_push_number(ctx, 4294967295.9); + duk_push_object(ctx); /* length: just within 32-bit range */ + duk_push_number(ctx, (duk_double_t) 0xffffffffUL); duk_put_prop_string(ctx, -2, "length"); /* 9 */ - duk_push_object(ctx); /* length: outside 32-bit range */ - duk_push_number(ctx, 4294967296); + duk_push_object(ctx); /* length: outside size_t range */ + duk_push_number(ctx, (duk_double_t) DUK_SIZE_MAX + 1.0); duk_put_prop_string(ctx, -2, "length"); /* 10 */ diff --git a/tests/api/test-set-length.c b/tests/api/test-set-length.c new file mode 100644 index 00000000..860fe959 --- /dev/null +++ b/tests/api/test-set-length.c @@ -0,0 +1,50 @@ +/*=== +*** test_basic (duk_safe_call) +["foo","bar","quux","bax"] +["foo"] +["foo",null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null] +{"foo":123} +{"foo":123,"length":123} +final top: 0 +==> rc=0, result='undefined' +===*/ + +static duk_ret_t test_basic(duk_context *ctx, void *udata) { + (void) udata; + + duk_eval_string(ctx, "[ 'foo', 'bar', 'quux', 'bax' ]"); + duk_dup(ctx, -1); + printf("%s\n", duk_json_encode(ctx, -1)); + duk_pop(ctx); + + duk_set_length(ctx, -1, 1); + duk_dup(ctx, -1); + printf("%s\n", duk_json_encode(ctx, -1)); + duk_pop(ctx); + + duk_set_length(ctx, -1, 100); + duk_dup(ctx, -1); + printf("%s\n", duk_json_encode(ctx, -1)); + duk_pop(ctx); + + duk_pop(ctx); + + duk_eval_string(ctx, "({ foo: 123 })"); + duk_dup(ctx, -1); + printf("%s\n", duk_json_encode(ctx, -1)); + duk_pop(ctx); + + duk_set_length(ctx, -1, 123); + duk_dup(ctx, -1); + printf("%s\n", duk_json_encode(ctx, -1)); + duk_pop(ctx); + + duk_pop(ctx); + + printf("final top: %ld\n", (long) duk_get_top(ctx)); + return 0; +} + +void test(duk_context *ctx) { + TEST_SAFE_CALL(test_basic); +} From a450898c475a6fd4e7101edecda759d7b2b31cf9 Mon Sep 17 00:00:00 2001 From: Sami Vaarala Date: Sun, 18 Dec 2016 00:57:18 +0200 Subject: [PATCH 3/5] API doc updates for duk_{get,set}_length() --- website/api/duk_get_length.yaml | 5 ++++- website/api/duk_set_length.yaml | 23 +++++++++++++++++++++++ 2 files changed, 27 insertions(+), 1 deletion(-) create mode 100644 website/api/duk_set_length.yaml diff --git a/website/api/duk_get_length.yaml b/website/api/duk_get_length.yaml index ff7682f1..b4624ff0 100644 --- a/website/api/duk_get_length.yaml +++ b/website/api/duk_get_length.yaml @@ -11,7 +11,7 @@ summary: |
  • String: character length of string (not byte length)
  • Object: Math.floor(ToNumber(obj.length)) if result within - 32-bit unsigned range; otherwise 0
  • + duk_size_t unsigned range; otherwise 0
  • Buffer: byte length of buffer
  • Other type or invalid stack index: 0
@@ -28,4 +28,7 @@ example: | tags: - stack +seealso: + - duk_set_length + introduced: 1.0.0 diff --git a/website/api/duk_set_length.yaml b/website/api/duk_set_length.yaml new file mode 100644 index 00000000..530ea4cd --- /dev/null +++ b/website/api/duk_set_length.yaml @@ -0,0 +1,23 @@ +name: duk_set_length + +proto: | + void duk_set_length(duk_context *ctx, duk_idx_t idx, duk_size_t len); + +stack: | + [ ... val! ... ] + +summary: | +

Set "length" for value at idx. Equivalent to the Ecmascript + statement obj.length = len;.

+ +example: | + /* Set array length to zero, deleting elements as a side effect. */ + duk_set_length(ctx, -3, 0); + +tags: + - stack + +seealso: + - duk_get_length + +introduced: 2.0.0 From be429d01e639f21c5acd1cf56cafc0b50920e7ea Mon Sep 17 00:00:00 2001 From: Sami Vaarala Date: Sun, 18 Dec 2016 00:58:53 +0200 Subject: [PATCH 4/5] 2.0 migration note for duk_get_length() --- doc/release-notes-v2-0.rst | 3 +++ 1 file changed, 3 insertions(+) diff --git a/doc/release-notes-v2-0.rst b/doc/release-notes-v2-0.rst index 83fee7c0..2aaed3a1 100644 --- a/doc/release-notes-v2-0.rst +++ b/doc/release-notes-v2-0.rst @@ -1044,6 +1044,9 @@ Other incompatible changes previously an error was thrown. This situation never occurs for standard Ecmascript strings or valid UTF-8 strings. +* ``duk_get_length()`` now allows the ``size_t`` rather than the unsigned 32-bit + integer range for the target value's ``.length``. + * Legacy octal literal handling has been improved to match more closely with ES6 Annex B. Octal look-alike decimal literals like "0778" and "0778.123" are now allowed. From 91304edd4a258bff2be47139ba13e1119741d960 Mon Sep 17 00:00:00 2001 From: Sami Vaarala Date: Sun, 18 Dec 2016 00:57:59 +0200 Subject: [PATCH 5/5] Releases: duk_set_length() --- RELEASES.rst | 3 +++ 1 file changed, 3 insertions(+) diff --git a/RELEASES.rst b/RELEASES.rst index f3a10803..1c685b2b 100644 --- a/RELEASES.rst +++ b/RELEASES.rst @@ -1976,6 +1976,9 @@ Planned * Add duk_inspect_callstack_entry() to provide internal information about a callstack entry; the output matches Duktape.act() (GH-1128) +* Add duk_set_length() API call and change duk_get_length() limits from + uint32 to size_t supported range (GH-1123) + * Add ability to perform an indirect debugger Eval with non-empty callstack by sending null for the callstack level (GH-747)