Gitweb links:

...log 
http://git.netsurf-browser.org/netsurf.git/shortlog/770c277ccc182f504be0a316390d59c05a59e041
...commit 
http://git.netsurf-browser.org/netsurf.git/commit/770c277ccc182f504be0a316390d59c05a59e041
...tree 
http://git.netsurf-browser.org/netsurf.git/tree/770c277ccc182f504be0a316390d59c05a59e041

The branch, master has been updated
       via  770c277ccc182f504be0a316390d59c05a59e041 (commit)
       via  539d5da5c4b814322cfac07b825a2b222951dacd (commit)
       via  7c03ae91fd8a99576cd5f0b8e5311f990ae5739b (commit)
      from  b227be8cd2108405793e117fa150808dd6120f29 (commit)

Those revisions listed above that are new to this repository have
not appeared on any other notification email; so we list those
revisions in full, below.

- Log -----------------------------------------------------------------
commitdiff 
http://git.netsurf-browser.org/netsurf.git/commit/?id=770c277ccc182f504be0a316390d59c05a59e041
commit 770c277ccc182f504be0a316390d59c05a59e041
Author: Michael Drake <[email protected]>
Commit: Michael Drake <[email protected]>

    Duktape: Prevent clang static analysis.
    
    Restore 336326af3aab93f31474fa6de28782457ae4a1c0 for 1.6.0 import.

diff --git a/content/handlers/javascript/duktape/duktape.c 
b/content/handlers/javascript/duktape/duktape.c
index 732cb80..b64383b 100644
--- a/content/handlers/javascript/duktape/duktape.c
+++ b/content/handlers/javascript/duktape/duktape.c
@@ -1,3 +1,5 @@
+/* Omit from static analysis. */
+#ifndef __clang_analyzer__
 /*
  *  Single source autogenerated distributable for Duktape 1.6.0.
  *
@@ -86753,3 +86755,4 @@ DUK_INTERNAL duk_double_t 
duk_util_tinyrandom_get_double(duk_hthread *thr) {
 
        return t;
 }
+#endif


commitdiff 
http://git.netsurf-browser.org/netsurf.git/commit/?id=539d5da5c4b814322cfac07b825a2b222951dacd
commit 539d5da5c4b814322cfac07b825a2b222951dacd
Author: Michael Drake <[email protected]>
Commit: Michael Drake <[email protected]>

    Duktape: Make declarations match definitions for duk_raw_read_xxx_be
    
    Restore 6d63f7959af64a45b0643d0610fcbdb0c07bfbc4 for 1.6.0 import.

diff --git a/content/handlers/javascript/duktape/duktape.c 
b/content/handlers/javascript/duktape/duktape.c
index ca17277..732cb80 100644
--- a/content/handlers/javascript/duktape/duktape.c
+++ b/content/handlers/javascript/duktape/duktape.c
@@ -2248,12 +2248,12 @@ DUK_INTERNAL_DECL duk_uint8_t 
*duk_bw_insert_ensure_area(duk_hthread *thr, duk_b
 DUK_INTERNAL_DECL void duk_bw_remove_raw_slice(duk_hthread *thr, 
duk_bufwriter_ctx *bw, duk_size_t off, duk_size_t len);
 /* No duk_bw_remove_ensure_slice(), functionality would be identical. */
 
-DUK_INTERNAL_DECL duk_uint16_t duk_raw_read_u16_be(duk_uint8_t **p);
-DUK_INTERNAL_DECL duk_uint32_t duk_raw_read_u32_be(duk_uint8_t **p);
-DUK_INTERNAL_DECL duk_double_t duk_raw_read_double_be(duk_uint8_t **p);
-DUK_INTERNAL_DECL void duk_raw_write_u16_be(duk_uint8_t **p, duk_uint16_t val);
-DUK_INTERNAL_DECL void duk_raw_write_u32_be(duk_uint8_t **p, duk_uint32_t val);
-DUK_INTERNAL_DECL void duk_raw_write_double_be(duk_uint8_t **p, duk_double_t 
val);
+DUK_INTERNAL_DECL DUK_INLINE duk_uint16_t duk_raw_read_u16_be(duk_uint8_t **p);
+DUK_INTERNAL_DECL DUK_INLINE duk_uint32_t duk_raw_read_u32_be(duk_uint8_t **p);
+DUK_INTERNAL_DECL DUK_INLINE duk_double_t duk_raw_read_double_be(duk_uint8_t 
**p);
+DUK_INTERNAL_DECL DUK_INLINE void duk_raw_write_u16_be(duk_uint8_t **p, 
duk_uint16_t val);
+DUK_INTERNAL_DECL DUK_INLINE void duk_raw_write_u32_be(duk_uint8_t **p, 
duk_uint32_t val);
+DUK_INTERNAL_DECL DUK_INLINE void duk_raw_write_double_be(duk_uint8_t **p, 
duk_double_t val);
 
 #if defined(DUK_USE_DEBUGGER_SUPPORT)  /* For now only needed by the debugger. 
*/
 DUK_INTERNAL void duk_byteswap_bytes(duk_uint8_t *p, duk_small_uint_t len);


commitdiff 
http://git.netsurf-browser.org/netsurf.git/commit/?id=7c03ae91fd8a99576cd5f0b8e5311f990ae5739b
commit 7c03ae91fd8a99576cd5f0b8e5311f990ae5739b
Author: Michael Drake <[email protected]>
Commit: Michael Drake <[email protected]>

    Duktape: Update to version 1.6.0.

diff --git a/content/handlers/javascript/duktape/duk_config.h 
b/content/handlers/javascript/duktape/duk_config.h
index c336603..f9c95ae 100644
--- a/content/handlers/javascript/duktape/duk_config.h
+++ b/content/handlers/javascript/duktape/duk_config.h
@@ -1,8 +1,8 @@
 /*
  *  duk_config.h configuration header generated by genconfig.py.
  *
- *  Git commit: 2cc76e9ff1f64869e1146ad7317d8cbe33bbd27e
- *  Git describe: v1.5.1
+ *  Git commit: 17e3d86cf8b4788bd0d37658f833ab440ce43a1c
+ *  Git describe: v1.6.0
  *  Git branch: HEAD
  *
  *  Supported platforms:
diff --git a/content/handlers/javascript/duktape/duktape.c 
b/content/handlers/javascript/duktape/duktape.c
index e1867c0..ca17277 100644
--- a/content/handlers/javascript/duktape/duktape.c
+++ b/content/handlers/javascript/duktape/duktape.c
@@ -1,9 +1,7 @@
-/* Omit from static analysis. */
-#ifndef __clang_analyzer__
 /*
- *  Single source autogenerated distributable for Duktape 1.5.1.
+ *  Single source autogenerated distributable for Duktape 1.6.0.
  *
- *  Git commit 2cc76e9ff1f64869e1146ad7317d8cbe33bbd27e (v1.5.1).
+ *  Git commit 17e3d86cf8b4788bd0d37658f833ab440ce43a1c (v1.6.0).
  *  Git branch HEAD.
  *
  *  See Duktape AUTHORS.rst and LICENSE.txt for copyright and
@@ -1631,9 +1629,9 @@ DUK_INTERNAL_DECL const duk_c_function 
duk_bi_native_functions[149];
 #endif  /* !DUK_SINGLE_FILE */
 #if defined(DUK_USE_BUILTIN_INITJS)
 #if !defined(DUK_SINGLE_FILE)
-DUK_INTERNAL_DECL const duk_uint8_t duk_initjs_data[187];
+DUK_INTERNAL_DECL const duk_uint8_t duk_initjs_data[204];
 #endif  /* !DUK_SINGLE_FILE */
-#define DUK_BUILTIN_INITJS_DATA_LENGTH                                187
+#define DUK_BUILTIN_INITJS_DATA_LENGTH                                204
 #endif  /* DUK_USE_BUILTIN_INITJS */
 #define DUK_BIDX_GLOBAL                                               0
 #define DUK_BIDX_GLOBAL_ENV                                           1
@@ -2250,12 +2248,12 @@ DUK_INTERNAL_DECL duk_uint8_t 
*duk_bw_insert_ensure_area(duk_hthread *thr, duk_b
 DUK_INTERNAL_DECL void duk_bw_remove_raw_slice(duk_hthread *thr, 
duk_bufwriter_ctx *bw, duk_size_t off, duk_size_t len);
 /* No duk_bw_remove_ensure_slice(), functionality would be identical. */
 
-DUK_INTERNAL_DECL DUK_INLINE duk_uint16_t duk_raw_read_u16_be(duk_uint8_t **p);
-DUK_INTERNAL_DECL DUK_INLINE duk_uint32_t duk_raw_read_u32_be(duk_uint8_t **p);
-DUK_INTERNAL_DECL DUK_INLINE duk_double_t duk_raw_read_double_be(duk_uint8_t 
**p);
-DUK_INTERNAL_DECL DUK_INLINE void duk_raw_write_u16_be(duk_uint8_t **p, 
duk_uint16_t val);
-DUK_INTERNAL_DECL DUK_INLINE void duk_raw_write_u32_be(duk_uint8_t **p, 
duk_uint32_t val);
-DUK_INTERNAL_DECL DUK_INLINE void duk_raw_write_double_be(duk_uint8_t **p, 
duk_double_t val);
+DUK_INTERNAL_DECL duk_uint16_t duk_raw_read_u16_be(duk_uint8_t **p);
+DUK_INTERNAL_DECL duk_uint32_t duk_raw_read_u32_be(duk_uint8_t **p);
+DUK_INTERNAL_DECL duk_double_t duk_raw_read_double_be(duk_uint8_t **p);
+DUK_INTERNAL_DECL void duk_raw_write_u16_be(duk_uint8_t **p, duk_uint16_t val);
+DUK_INTERNAL_DECL void duk_raw_write_u32_be(duk_uint8_t **p, duk_uint32_t val);
+DUK_INTERNAL_DECL void duk_raw_write_double_be(duk_uint8_t **p, duk_double_t 
val);
 
 #if defined(DUK_USE_DEBUGGER_SUPPORT)  /* For now only needed by the debugger. 
*/
 DUK_INTERNAL void duk_byteswap_bytes(duk_uint8_t *p, duk_small_uint_t len);
@@ -9221,7 +9219,7 @@ DUK_INTERNAL const duk_c_function 
duk_bi_native_functions[149] = {
        duk_bi_typedarray_set,
 };
 #if defined(DUK_USE_BUILTIN_INITJS)
-DUK_INTERNAL const duk_uint8_t duk_initjs_data[187] = {
+DUK_INTERNAL const duk_uint8_t duk_initjs_data[204] = {
 40,102,117,110,99,116,105,111,110,40,100,44,97,41,123,102,117,110,99,116,
 105,111,110,32,98,40,97,44,98,44,99,41,123,79,98,106,101,99,116,46,100,101,
 102,105,110,101,80,114,111,112,101,114,116,121,40,97,44,98,44,123,118,97,
@@ -9229,8 +9227,9 @@ DUK_INTERNAL const duk_uint8_t duk_initjs_data[187] = {
 109,101,114,97,98,108,101,58,33,49,44,99,111,110,102,105,103,117,114,97,98,
 108,101,58,33,48,125,41,125,98,40,97,46,76,111,103,103,101,114,44,34,99,
 108,111,103,34,44,110,101,119,32,97,46,76,111,103,103,101,114,40,34,67,34,
-41,41,59,98,40,97,44,34,109,111,100,76,111,97,100,101,100,34,44,123,125,41,
-125,41,40,116,104,105,115,44,68,117,107,116,97,112,101,41,59,10,0,
+41,41,59,98,40,97,44,34,109,111,100,76,111,97,100,101,100,34,44,79,98,106,
+101,99,116,46,99,114,101,97,116,101,40,110,117,108,108,41,41,125,41,40,116,
+104,105,115,44,68,117,107,116,97,112,101,41,59,10,0,
 };
 #endif  /* DUK_USE_BUILTIN_INITJS */
 #if defined(DUK_USE_DOUBLE_LE)
@@ -9354,7 +9353,7 @@ DUK_INTERNAL const duk_uint8_t duk_builtins_data[3833] = {
 88,119,100,223,181,68,16,94,91,250,238,200,160,80,0,152,31,61,59,148,10,0,
 21,4,231,199,151,69,2,128,5,192,250,97,220,160,80,0,192,127,255,128,20,23,
 134,30,92,242,164,34,19,207,167,45,59,179,233,205,229,37,129,127,255,0,0,
-191,255,128,0,63,255,197,131,246,203,203,158,157,251,160,0,0,0,0,64,65,98,
+191,255,128,0,63,255,197,131,246,203,203,158,157,251,160,0,0,0,0,0,90,98,
 32,3,166,156,30,53,32,249,165,131,76,223,159,62,94,70,172,114,16,176,144,
 60,56,250,19,18,5,159,25,89,32,121,180,238,42,30,129,229,221,140,164,122,7,
 147,46,50,129,232,62,61,251,120,97,199,208,156,129,83,127,0,50,250,69,3,
@@ -9534,7 +9533,7 @@ DUK_INTERNAL const duk_uint8_t duk_builtins_data[3833] = {
 88,119,100,223,181,68,16,94,91,250,238,200,160,80,0,152,31,61,59,148,10,0,
 21,4,231,199,151,69,2,128,5,192,250,97,220,160,80,0,192,127,255,128,20,23,
 134,30,92,242,164,34,19,207,167,45,59,179,233,205,229,37,129,127,255,0,0,
-191,255,128,0,63,255,197,131,246,203,203,158,157,251,160,32,98,65,64,0,0,0,
+191,255,128,0,63,255,197,131,246,203,203,158,157,251,160,32,98,90,0,0,0,0,
 0,3,166,156,30,53,32,249,165,131,76,223,159,62,94,70,172,114,16,176,144,60,
 56,250,19,18,5,159,25,89,32,121,180,238,42,30,129,229,221,140,164,122,7,
 147,46,50,129,232,62,61,251,120,97,199,208,156,129,83,127,0,50,250,69,3,
@@ -9714,7 +9713,7 @@ DUK_INTERNAL const duk_uint8_t duk_builtins_data[3833] = {
 88,119,100,223,181,68,16,94,91,250,238,200,160,80,0,152,31,61,59,148,10,0,
 21,4,231,199,151,69,2,128,5,192,250,97,220,160,80,0,192,127,255,128,20,23,
 134,30,92,242,164,34,19,207,167,45,59,179,233,205,229,37,129,127,255,0,0,
-191,255,128,0,63,255,197,131,246,203,203,158,157,251,160,64,65,98,32,0,0,0,
+191,255,128,0,63,255,197,131,246,203,203,158,157,251,160,0,90,98,32,0,0,0,
 0,3,166,156,30,53,32,249,165,131,76,223,159,62,94,70,172,114,16,176,144,60,
 56,250,19,18,5,159,25,89,32,121,180,238,42,30,129,229,221,140,164,122,7,
 147,46,50,129,232,62,61,251,120,97,199,208,156,129,83,127,0,50,250,69,3,
@@ -14047,6 +14046,15 @@ DUK_EXTERNAL void duk_debugger_pause(duk_context *ctx) 
{
 
 /* include removed: duk_internal.h */
 
+typedef struct duk_internal_thread_state duk_internal_thread_state;
+
+struct duk_internal_thread_state {
+       duk_ljstate lj;
+       duk_bool_t handling_error;
+       duk_hthread *curr_thread;
+       duk_int_t call_recursion_depth;
+};
+
 DUK_EXTERNAL
 duk_context *duk_create_heap(duk_alloc_function alloc_func,
                              duk_realloc_function realloc_func,
@@ -14112,6 +14120,57 @@ DUK_EXTERNAL void duk_destroy_heap(duk_context *ctx) {
        duk_heap_free(heap);
 }
 
+DUK_EXTERNAL void duk_suspend(duk_context *ctx, duk_thread_state *state) {
+       duk_hthread *thr = (duk_hthread *) ctx;
+       duk_internal_thread_state *snapshot = (duk_internal_thread_state *) 
(void *) state;
+       duk_heap *heap;
+       duk_ljstate *lj;
+
+       DUK_ASSERT_CTX_VALID(ctx);
+       DUK_ASSERT(thr != NULL);
+       DUK_ASSERT(thr->heap != NULL);
+       DUK_ASSERT(state != NULL);  /* unvalidated */
+
+       heap = thr->heap;
+       lj = &heap->lj;
+
+       duk_push_tval(ctx, &lj->value1);
+       duk_push_tval(ctx, &lj->value2);
+
+       DUK_MEMCPY((void *) &snapshot->lj, (const void *) lj, 
sizeof(duk_ljstate));
+       snapshot->handling_error = heap->handling_error;
+       snapshot->curr_thread = heap->curr_thread;
+       snapshot->call_recursion_depth = heap->call_recursion_depth;
+
+       lj->jmpbuf_ptr = NULL;
+       lj->type = DUK_LJ_TYPE_UNKNOWN;
+       DUK_TVAL_SET_UNDEFINED(&lj->value1);
+       DUK_TVAL_SET_UNDEFINED(&lj->value2);
+       heap->handling_error = 0;
+       heap->curr_thread = NULL;
+       heap->call_recursion_depth = 0;
+}
+
+DUK_EXTERNAL void duk_resume(duk_context *ctx, const duk_thread_state *state) {
+       duk_hthread *thr = (duk_hthread *) ctx;
+       const duk_internal_thread_state *snapshot = (const 
duk_internal_thread_state *) (const void *) state;
+       duk_heap *heap;
+
+       DUK_ASSERT_CTX_VALID(ctx);
+       DUK_ASSERT(thr != NULL);
+       DUK_ASSERT(thr->heap != NULL);
+       DUK_ASSERT(state != NULL);  /* unvalidated */
+
+       heap = thr->heap;
+
+       DUK_MEMCPY((void *) &heap->lj, (const void *) &snapshot->lj, 
sizeof(duk_ljstate));
+       heap->handling_error = snapshot->handling_error;
+       heap->curr_thread = snapshot->curr_thread;
+       heap->call_recursion_depth = snapshot->call_recursion_depth;
+
+       duk_pop_2(ctx);
+}
+
 /* XXX: better place for this */
 DUK_EXTERNAL void duk_set_global_object(duk_context *ctx) {
        duk_hthread *thr = (duk_hthread *) ctx;
@@ -14417,7 +14476,7 @@ DUK_INTERNAL duk_bool_t 
duk_get_prop_stridx_boolean(duk_context *ctx, duk_idx_t
        return rc;
 }
 
-DUK_EXTERNAL duk_bool_t duk_put_prop(duk_context *ctx, duk_idx_t obj_index) {
+DUK_LOCAL duk_bool_t duk__put_prop_shared(duk_context *ctx, duk_idx_t obj_idx, 
duk_idx_t idx_key) {
        duk_hthread *thr = (duk_hthread *) ctx;
        duk_tval *tv_obj;
        duk_tval *tv_key;
@@ -14425,16 +14484,19 @@ DUK_EXTERNAL duk_bool_t duk_put_prop(duk_context 
*ctx, duk_idx_t obj_index) {
        duk_small_int_t throw_flag;
        duk_bool_t rc;
 
-       DUK_ASSERT_CTX_VALID(ctx);
-
        /* Note: copying tv_obj and tv_key to locals to shield against a 
valstack
         * resize is not necessary for a property put right now (putprop 
protects
         * against it internally).
         */
 
-       tv_obj = duk_require_tval(ctx, obj_index);
-       tv_key = duk_require_tval(ctx, -2);
-       tv_val = duk_require_tval(ctx, -1);
+       /* Key and value indices are either (-2, -1) or (-1, -2).  Given 
idx_key,
+        * idx_val is always (idx_key ^ 0x01).
+        */
+       DUK_ASSERT((idx_key == -2 && (idx_key ^ 1) == -1) ||
+                  (idx_key == -1 && (idx_key ^ 1) == -2));
+       tv_obj = duk_require_tval(ctx, obj_idx);
+       tv_key = duk_require_tval(ctx, idx_key);
+       tv_val = duk_require_tval(ctx, idx_key ^ 1);
        throw_flag = duk_is_strict_call(ctx);
 
        rc = duk_hobject_putprop(thr, tv_obj, tv_key, tv_val, throw_flag);
@@ -14444,26 +14506,33 @@ DUK_EXTERNAL duk_bool_t duk_put_prop(duk_context 
*ctx, duk_idx_t obj_index) {
        return rc;  /* 1 if property found, 0 otherwise */
 }
 
-DUK_EXTERNAL duk_bool_t duk_put_prop_string(duk_context *ctx, duk_idx_t 
obj_index, const char *key) {
+DUK_EXTERNAL duk_bool_t duk_put_prop(duk_context *ctx, duk_idx_t obj_idx) {
+       DUK_ASSERT_CTX_VALID(ctx);
+       return duk__put_prop_shared(ctx, obj_idx, -2);
+}
+
+DUK_EXTERNAL duk_bool_t duk_put_prop_string(duk_context *ctx, duk_idx_t 
obj_idx, const char *key) {
        DUK_ASSERT_CTX_VALID(ctx);
        DUK_ASSERT(key != NULL);
 
-       obj_index = duk_require_normalize_index(ctx, obj_index);
-       duk_push_string(ctx, key);
-       duk_swap_top(ctx, -2);  /* [val key] -> [key val] */
-       return duk_put_prop(ctx, obj_index);
+       /* Careful here and with other duk_put_prop_xxx() helpers: the
+        * target object and the property value may be in the same value
+        * stack slot (unusual, but still conceptually clear).
+        */
+       obj_idx = duk_normalize_index(ctx, obj_idx);
+       (void) duk_push_string(ctx, key);
+       return duk__put_prop_shared(ctx, obj_idx, -1);
 }
 
-DUK_EXTERNAL duk_bool_t duk_put_prop_index(duk_context *ctx, duk_idx_t 
obj_index, duk_uarridx_t arr_index) {
+DUK_EXTERNAL duk_bool_t duk_put_prop_index(duk_context *ctx, duk_idx_t 
obj_idx, duk_uarridx_t arr_idx) {
        DUK_ASSERT_CTX_VALID(ctx);
 
-       obj_index = duk_require_normalize_index(ctx, obj_index);
-       duk_push_uarridx(ctx, arr_index);
-       duk_swap_top(ctx, -2);  /* [val key] -> [key val] */
-       return duk_put_prop(ctx, obj_index);
+       obj_idx = duk_require_normalize_index(ctx, obj_idx);
+       duk_push_uarridx(ctx, arr_idx);
+       return duk__put_prop_shared(ctx, obj_idx, -1);
 }
 
-DUK_INTERNAL duk_bool_t duk_put_prop_stridx(duk_context *ctx, duk_idx_t 
obj_index, duk_small_int_t stridx) {
+DUK_INTERNAL duk_bool_t duk_put_prop_stridx(duk_context *ctx, duk_idx_t 
obj_idx, duk_small_int_t stridx) {
        duk_hthread *thr = (duk_hthread *) ctx;
 
        DUK_ASSERT_CTX_VALID(ctx);
@@ -14471,10 +14540,9 @@ DUK_INTERNAL duk_bool_t 
duk_put_prop_stridx(duk_context *ctx, duk_idx_t obj_inde
        DUK_ASSERT(stridx < DUK_HEAP_NUM_STRINGS);
        DUK_UNREF(thr);
 
-       obj_index = duk_require_normalize_index(ctx, obj_index);
+       obj_idx = duk_require_normalize_index(ctx, obj_idx);
        duk_push_hstring(ctx, DUK_HTHREAD_GET_STRING(thr, stridx));
-       duk_swap_top(ctx, -2);  /* [val key] -> [key val] */
-       return duk_put_prop(ctx, obj_index);
+       return duk__put_prop_shared(ctx, obj_idx, -1);
 }
 
 DUK_EXTERNAL duk_bool_t duk_del_prop(duk_context *ctx, duk_idx_t obj_index) {
@@ -31391,12 +31459,17 @@ DUK_LOCAL duk_bool_t 
duk__json_stringify_fast_value(duk_json_enc_ctx *js_ctx, du
                                         * standard JSON (and no JX/JC support 
here now).
                                         */
                                        DUK_D(DUK_DPRINT("gap in array, no 
conflicting inherited property, remain on fast path"));
+#if defined(DUK_USE_JX)
+                                       DUK__EMIT_STRIDX(js_ctx, 
js_ctx->stridx_custom_undefined);
+#else
                                        DUK__EMIT_STRIDX(js_ctx, 
DUK_STRIDX_LC_NULL);
+#endif
                                } else {
                                        if 
(duk__json_stringify_fast_value(js_ctx, tv_val) == 0) {
                                                DUK__EMIT_STRIDX(js_ctx, 
DUK_STRIDX_LC_NULL);
                                        }
                                }
+
                                DUK__EMIT_1(js_ctx, DUK_ASC_COMMA);
                                emitted = 1;
                        }
@@ -31729,6 +31802,7 @@ void duk_bi_json_stringify_helper(duk_context *ctx,
         * combinations properly.
         */
 #if defined(DUK_USE_JX) || defined(DUK_USE_JC)
+       js_ctx->stridx_custom_undefined = DUK_STRIDX_LC_NULL;  /* standard 
JSON; array gaps */
 #if defined(DUK_USE_JX)
        if (flags & DUK_JSON_FLAG_EXT_CUSTOM) {
                js_ctx->stridx_custom_undefined = DUK_STRIDX_LC_UNDEFINED;
@@ -33931,7 +34005,7 @@ DUK_INTERNAL duk_ret_t 
duk_bi_string_constructor_from_char_code(duk_context *ctx
                cp = (duk_ucodepoint_t) duk_to_uint32(ctx, i);
                DUK_BW_WRITE_ENSURE_XUTF8(thr, bw, cp);
 #else
-               cp = (duk_ucodepoint_t) duk_to_uint32(ctx, i);
+               cp = (duk_ucodepoint_t) duk_to_uint16(ctx, i);
                DUK_BW_WRITE_ENSURE_CESU8(thr, bw, cp);
 #endif
        }
@@ -42036,18 +42110,23 @@ DUK_LOCAL void duk__mark_hobject(duk_heap *heap, 
duk_hobject *h) {
 
                duk__mark_heaphdr(heap, (duk_heaphdr *) 
DUK_HCOMPILEDFUNCTION_GET_DATA(heap, f));
 
-               tv = DUK_HCOMPILEDFUNCTION_GET_CONSTS_BASE(heap, f);
-               tv_end = DUK_HCOMPILEDFUNCTION_GET_CONSTS_END(heap, f);
-               while (tv < tv_end) {
-                       duk__mark_tval(heap, tv);
-                       tv++;
-               }
+               if (DUK_HCOMPILEDFUNCTION_GET_DATA(heap, f) != NULL) {
+                       tv = DUK_HCOMPILEDFUNCTION_GET_CONSTS_BASE(heap, f);
+                       tv_end = DUK_HCOMPILEDFUNCTION_GET_CONSTS_END(heap, f);
+                       while (tv < tv_end) {
+                               duk__mark_tval(heap, tv);
+                               tv++;
+                       }
 
-               fn = DUK_HCOMPILEDFUNCTION_GET_FUNCS_BASE(heap, f);
-               fn_end = DUK_HCOMPILEDFUNCTION_GET_FUNCS_END(heap, f);
-               while (fn < fn_end) {
-                       duk__mark_heaphdr(heap, (duk_heaphdr *) *fn);
-                       fn++;
+                       fn = DUK_HCOMPILEDFUNCTION_GET_FUNCS_BASE(heap, f);
+                       fn_end = DUK_HCOMPILEDFUNCTION_GET_FUNCS_END(heap, f);
+                       while (fn < fn_end) {
+                               duk__mark_heaphdr(heap, (duk_heaphdr *) *fn);
+                               fn++;
+                       }
+               } else {
+                       /* May happen in some out-of-memory corner cases. */
+                       DUK_D(DUK_DPRINT("duk_hcompiledfunction 'data' is NULL, 
skipping marking"));
                }
        } else if (DUK_HOBJECT_IS_NATIVEFUNCTION(h)) {
                duk_hnativefunction *f = (duk_hnativefunction *) h;
@@ -43927,20 +44006,23 @@ DUK_LOCAL void 
duk__refcount_finalize_hobject(duk_hthread *thr, duk_hobject *h)
                duk_tval *tv, *tv_end;
                duk_hobject **funcs, **funcs_end;
 
-               DUK_ASSERT(DUK_HCOMPILEDFUNCTION_GET_DATA(thr->heap, f) != 
NULL);  /* compiled functions must be created 'atomically' */
-
-               tv = DUK_HCOMPILEDFUNCTION_GET_CONSTS_BASE(thr->heap, f);
-               tv_end = DUK_HCOMPILEDFUNCTION_GET_CONSTS_END(thr->heap, f);
-               while (tv < tv_end) {
-                       duk_tval_decref(thr, tv);
-                       tv++;
-               }
+               if (DUK_HCOMPILEDFUNCTION_GET_DATA(thr->heap, f) != NULL) {
+                       tv = DUK_HCOMPILEDFUNCTION_GET_CONSTS_BASE(thr->heap, 
f);
+                       tv_end = 
DUK_HCOMPILEDFUNCTION_GET_CONSTS_END(thr->heap, f);
+                       while (tv < tv_end) {
+                               duk_tval_decref(thr, tv);
+                               tv++;
+                       }
 
-               funcs = DUK_HCOMPILEDFUNCTION_GET_FUNCS_BASE(thr->heap, f);
-               funcs_end = DUK_HCOMPILEDFUNCTION_GET_FUNCS_END(thr->heap, f);
-               while (funcs < funcs_end) {
-                       duk_heaphdr_decref(thr, (duk_heaphdr *) *funcs);
-                       funcs++;
+                       funcs = DUK_HCOMPILEDFUNCTION_GET_FUNCS_BASE(thr->heap, 
f);
+                       funcs_end = 
DUK_HCOMPILEDFUNCTION_GET_FUNCS_END(thr->heap, f);
+                       while (funcs < funcs_end) {
+                               duk_heaphdr_decref(thr, (duk_heaphdr *) *funcs);
+                               funcs++;
+                       }
+               } else {
+                       /* May happen in some out-of-memory corner cases. */
+                       DUK_D(DUK_DPRINT("duk_hcompiledfunction 'data' is NULL, 
skipping decref"));
                }
 
                duk_heaphdr_decref(thr, (duk_heaphdr *) 
DUK_HCOMPILEDFUNCTION_GET_DATA(thr->heap, f));
@@ -46870,8 +46952,13 @@ DUK_INTERNAL duk_bool_t 
duk_hobject_prototype_chain_contains(duk_hthread *thr, d
        duk_uint_t sanity;
 
        DUK_ASSERT(thr != NULL);
-       DUK_ASSERT(h != NULL);
-       /* allow 'p' to be NULL; then the result is always false */
+
+       /* False if the object is NULL or the prototype 'p' is NULL.
+        * In particular, false if both are NULL (don't compare equal).
+        */
+       if (h == NULL || p == NULL) {
+               return 0;
+       }
 
        sanity = DUK_HOBJECT_PROTOTYPE_CHAIN_SANITY;
        do {
@@ -51358,8 +51445,17 @@ DUK_INTERNAL duk_bool_t 
duk_hobject_delprop_raw(duk_hthread *thr, duk_hobject *o
                DUK_TVAL_SET_UNUSED_UPDREF(thr, tv);  /* side effects */
                goto success;
        } else {
+               duk_hobject *h_get = NULL;
+               duk_hobject *h_set = NULL;
+               duk_tval tv_tmp;
+
                DUK_ASSERT(desc.a_idx < 0);
 
+               /* Set property slot to an empty state.  Careful not to invoke
+                * any side effects while using desc.e_idx so that it doesn't
+                * get invalidated by a finalizer mutating our object.
+                */
+
                /* remove hash entry (no decref) */
 #if defined(DUK_USE_HOBJECT_HASH_PART)
                if (desc.h_idx >= 0) {
@@ -51380,21 +51476,17 @@ DUK_INTERNAL duk_bool_t 
duk_hobject_delprop_raw(duk_hthread *thr, duk_hobject *o
                DUK_DDD(DUK_DDDPRINT("before removing value, e_idx %ld, key %p, 
key at slot %p",
                                     (long) desc.e_idx, (void *) key, (void *) 
DUK_HOBJECT_E_GET_KEY(thr->heap, obj, desc.e_idx)));
                DUK_DDD(DUK_DDDPRINT("removing value at e_idx %ld", (long) 
desc.e_idx));
+               DUK_MEMSET((void *) &tv_tmp, 0, sizeof(tv_tmp));
+               DUK_TVAL_SET_UNDEFINED(&tv_tmp);
                if (DUK_HOBJECT_E_SLOT_IS_ACCESSOR(thr->heap, obj, desc.e_idx)) 
{
-                       duk_hobject *tmp;
-
-                       tmp = DUK_HOBJECT_E_GET_VALUE_GETTER(thr->heap, obj, 
desc.e_idx);
+                       h_get = DUK_HOBJECT_E_GET_VALUE_GETTER(thr->heap, obj, 
desc.e_idx);
+                       h_set = DUK_HOBJECT_E_GET_VALUE_SETTER(thr->heap, obj, 
desc.e_idx);
                        DUK_HOBJECT_E_SET_VALUE_GETTER(thr->heap, obj, 
desc.e_idx, NULL);
-                       DUK_UNREF(tmp);
-                       DUK_HOBJECT_DECREF_ALLOWNULL(thr, tmp);  /* side 
effects */
-
-                       tmp = DUK_HOBJECT_E_GET_VALUE_SETTER(thr->heap, obj, 
desc.e_idx);
                        DUK_HOBJECT_E_SET_VALUE_SETTER(thr->heap, obj, 
desc.e_idx, NULL);
-                       DUK_UNREF(tmp);
-                       DUK_HOBJECT_DECREF_ALLOWNULL(thr, tmp);  /* side 
effects */
                } else {
                        tv = DUK_HOBJECT_E_GET_VALUE_TVAL_PTR(thr->heap, obj, 
desc.e_idx);
-                       DUK_TVAL_SET_UNDEFINED_UPDREF(thr, tv);  /* side 
effects */
+                       DUK_TVAL_SET_TVAL(&tv_tmp, tv);
+                       DUK_TVAL_SET_UNDEFINED(tv);
                }
 #if 0
                /* Not strictly necessary because if key == NULL, flag MUST be 
ignored. */
@@ -51407,7 +51499,14 @@ DUK_INTERNAL duk_bool_t 
duk_hobject_delprop_raw(duk_hthread *thr, duk_hobject *o
                DUK_DDD(DUK_DDDPRINT("removing key at e_idx %ld", (long) 
desc.e_idx));
                DUK_ASSERT(key == DUK_HOBJECT_E_GET_KEY(thr->heap, obj, 
desc.e_idx));
                DUK_HOBJECT_E_SET_KEY(thr->heap, obj, desc.e_idx, NULL);
-               DUK_HSTRING_DECREF(thr, key);  /* side effects */
+
+               /* Do decrefs only with safe pointers to avoid side effects
+                * disturbing e_idx.
+                */
+               DUK_TVAL_DECREF(thr, &tv_tmp);
+               DUK_HOBJECT_DECREF_ALLOWNULL(thr, h_get);
+               DUK_HOBJECT_DECREF_ALLOWNULL(thr, h_set);
+               DUK_HSTRING_DECREF(thr, key);
                goto success;
        }
 
@@ -52581,6 +52680,7 @@ void duk_hobject_define_property_helper(duk_context 
*ctx,
                } else {
                        duk_bool_t rc;
                        duk_tval *tv1;
+                       duk_tval tv_tmp;
 
                        /* curr is data, desc is accessor */
                        if (!(curr.flags & DUK_PROPDESC_FLAG_CONFIGURABLE) && 
!force_flag) {
@@ -52600,9 +52700,12 @@ void duk_hobject_define_property_helper(duk_context 
*ctx,
 
                        DUK_ASSERT(!DUK_HOBJECT_E_SLOT_IS_ACCESSOR(thr->heap, 
obj, curr.e_idx));
 
+                       /* Avoid side effects that might disturb curr.e_idx 
until
+                        * we're done editing the slot.
+                        */
                        tv1 = DUK_HOBJECT_E_GET_VALUE_TVAL_PTR(thr->heap, obj, 
curr.e_idx);
-                       /* XXX: just decref */
-                       DUK_TVAL_SET_UNDEFINED_UPDREF(thr, tv1);  /* side 
effects */
+                       DUK_TVAL_SET_TVAL(&tv_tmp, tv1);
+                       DUK_TVAL_SET_UNDEFINED(tv1);
 
                        DUK_HOBJECT_E_SET_VALUE_GETTER(thr->heap, obj, 
curr.e_idx, NULL);
                        DUK_HOBJECT_E_SET_VALUE_SETTER(thr->heap, obj, 
curr.e_idx, NULL);
@@ -52612,6 +52715,8 @@ void duk_hobject_define_property_helper(duk_context 
*ctx,
                        DUK_DDD(DUK_DDDPRINT("flags after data->accessor 
conversion: 0x%02lx",
                                             (unsigned long) 
DUK_HOBJECT_E_GET_FLAGS(thr->heap, obj, curr.e_idx)));
 
+                       DUK_TVAL_DECREF(thr, &tv_tmp);  /* side effects */
+
                        /* re-lookup to update curr.flags
                         * XXX: would be faster to update directly
                         */
@@ -52627,7 +52732,8 @@ void duk_hobject_define_property_helper(duk_context 
*ctx,
 
                if (curr.flags & DUK_PROPDESC_FLAG_ACCESSOR) {
                        duk_bool_t rc;
-                       duk_hobject *tmp;
+                       duk_hobject *h_get;
+                       duk_hobject *h_set;
 
                        /* curr is accessor, desc is data */
                        if (!(curr.flags & DUK_PROPDESC_FLAG_CONFIGURABLE) && 
!force_flag) {
@@ -52639,15 +52745,14 @@ void duk_hobject_define_property_helper(duk_context 
*ctx,
 
                        DUK_DDD(DUK_DDDPRINT("convert property to data 
property"));
 
+                       /* Avoid side effects that might disturb curr.e_idx 
until
+                        * we're done editing the slot.
+                        */
                        DUK_ASSERT(DUK_HOBJECT_E_SLOT_IS_ACCESSOR(thr->heap, 
obj, curr.e_idx));
-                       tmp = DUK_HOBJECT_E_GET_VALUE_GETTER(thr->heap, obj, 
curr.e_idx);
-                       DUK_UNREF(tmp);
+                       h_get = DUK_HOBJECT_E_GET_VALUE_GETTER(thr->heap, obj, 
curr.e_idx);
                        DUK_HOBJECT_E_SET_VALUE_GETTER(thr->heap, obj, 
curr.e_idx, NULL);
-                       DUK_HOBJECT_DECREF_ALLOWNULL(thr, tmp);  /* side 
effects */
-                       tmp = DUK_HOBJECT_E_GET_VALUE_SETTER(thr->heap, obj, 
curr.e_idx);
-                       DUK_UNREF(tmp);
+                       h_set = DUK_HOBJECT_E_GET_VALUE_SETTER(thr->heap, obj, 
curr.e_idx);
                        DUK_HOBJECT_E_SET_VALUE_SETTER(thr->heap, obj, 
curr.e_idx, NULL);
-                       DUK_HOBJECT_DECREF_ALLOWNULL(thr, tmp);  /* side 
effects */
 
                        
DUK_TVAL_SET_UNDEFINED(DUK_HOBJECT_E_GET_VALUE_TVAL_PTR(thr->heap, obj, 
curr.e_idx));
                        DUK_HOBJECT_E_SLOT_CLEAR_WRITABLE(thr->heap, obj, 
curr.e_idx);
@@ -52656,6 +52761,9 @@ void duk_hobject_define_property_helper(duk_context 
*ctx,
                        DUK_DDD(DUK_DDDPRINT("flags after accessor->data 
conversion: 0x%02lx",
                                             (unsigned long) 
DUK_HOBJECT_E_GET_FLAGS(thr->heap, obj, curr.e_idx)));
 
+                       DUK_HOBJECT_DECREF_ALLOWNULL(thr, h_get);  /* side 
effects */
+                       DUK_HOBJECT_DECREF_ALLOWNULL(thr, h_set);  /* side 
effects */
+
                        /* re-lookup to update curr.flags
                         * XXX: would be faster to update directly
                         */
@@ -59766,6 +59874,7 @@ DUK_LOCAL void duk__ivalue_toplain_raw(duk_compiler_ctx 
*comp_ctx, duk_ivalue *x
        }
        case DUK_IVAL_NONE:
        default: {
+               DUK_D(DUK_DPRINT("invalid ivalue type: %ld", (long) x->t));
                break;
        }
        }
@@ -61735,13 +61844,24 @@ DUK_LOCAL void duk__expr_led(duk_compiler_ctx 
*comp_ctx, duk_ivalue *left, duk_i
         *  left-hand-side values (e.g. as in "f() = 1") must NOT cause a
         *  SyntaxError, but rather a run-time ReferenceError.
         *
-        *  Assignment expression value is conceptually the LHS/RHS value
-        *  copied into a fresh temporary so that it won't change even if
-        *  LHS/RHS values change (e.g. when they're identifiers).  Doing this
-        *  concretely produces inefficient bytecode, so we try to avoid the
-        *  extra temporary for some known-to-be-safe cases.  Currently the
-        *  only safe case we detect is a "top level assignment", for example
-        *  "x = y + z;", where the assignment expression value is ignored.
+        *  When evaluating X <op>= Y, the LHS (X) is conceptually evaluated
+        *  to a temporary first.  The RHS is then evaluated.  Finally, the
+        *  <op> is applied to the initial value of RHS (not the value after
+        *  RHS evaluation), and written to X.  Doing so concretely generates
+        *  inefficient code so we'd like to avoid the temporary when possible.
+        *  See: https://github.com/svaarala/duktape/pull/992.
+        *
+        *  The expression value (final LHS value, written to RHS) is
+        *  conceptually copied into a fresh temporary so that it won't
+        *  change even if the LHS/RHS values change in outer expressions.
+        *  For example, it'd be generally incorrect for the expression value
+        *  to be the RHS register binding, unless there's a guarantee that it
+        *  won't change during further expression evaluation.  Using the
+        *  temporary concretely produces inefficient bytecode, so we try to
+        *  avoid the extra temporary for some known-to-be-safe cases.
+        *  Currently the only safe case we detect is a "top level assignment",
+        *  for example "x = y + z;", where the assignment expression value is
+        *  ignored.
         *  See: test-dev-assign-expr.js and test-bug-assign-mutate-gh381.js.
         */
 
@@ -61761,7 +61881,9 @@ DUK_LOCAL void duk__expr_led(duk_compiler_ctx 
*comp_ctx, duk_ivalue *left, duk_i
                 * is a reg-bound identifier.  The RHS ('res') is right 
associative
                 * so it has consumed all other assignment level operations; the
                 * only relevant lower binding power construct is comma operator
-                * which will ignore the expression value provided here.
+                * which will ignore the expression value provided here.  
Usually
+                * the top level assignment expression value is ignored, but it
+                * is relevant for e.g. eval code.
                 */
                toplevel_assign = (comp_ctx->curr_func.nud_count == 1 && /* one 
token before */
                                   comp_ctx->curr_func.led_count == 1);  /* one 
operator (= assign) */
@@ -61777,23 +61899,17 @@ DUK_LOCAL void duk__expr_led(duk_compiler_ctx 
*comp_ctx, duk_ivalue *left, duk_i
 
                        DUK_ASSERT(left->x1.t == DUK_ISPEC_VALUE);  /* LHS is 
already side effect free */
 
-                       /* Keep the RHS as an unresolved ivalue for now, so it
-                        * can be a plain value or a unary/binary operation 
here.
-                        * We resolve it before finishing but doing it later 
allows
-                        * better bytecode in some cases.
-                        */
-                       duk__expr(comp_ctx, res, args_rbp /*rbp_flags*/);
-
                        h_varname = duk_get_hstring(ctx, left->x1.valstack_idx);
                        DUK_ASSERT(h_varname != NULL);
                        if 
(duk__hstring_is_eval_or_arguments_in_strict_mode(comp_ctx, h_varname)) {
-                               /* E5 Section 11.13.1 (and others for other 
assignments), step 4 */
+                               /* E5 Section 11.13.1 (and others for other 
assignments), step 4. */
                                goto syntax_error_lvalue;
                        }
                        duk_dup(ctx, left->x1.valstack_idx);
                        (void) duk__lookup_lhs(comp_ctx, &reg_varbind, 
&rc_varname);
 
                        if (args_op == DUK_OP_NONE) {
+                               duk__expr(comp_ctx, res, args_rbp 
/*rbp_flags*/);
                                if (toplevel_assign) {
                                        /* Any 'res' will do. */
                                        DUK_DDD(DUK_DDDPRINT("plain assignment, 
toplevel assign, use as is"));
@@ -61807,42 +61923,98 @@ DUK_LOCAL void duk__expr_led(duk_compiler_ctx 
*comp_ctx, duk_ivalue *left, duk_i
                                        }
                                }
                        } else {
-                               duk__ivalue_toregconst(comp_ctx, res);
-                               DUK_ASSERT(res->t == DUK_IVAL_PLAIN && 
res->x1.t == DUK_ISPEC_REGCONST);
+                               /* For X <op>= Y we need to evaluate the pre-op
+                                * value of X before evaluating the RHS: the RHS
+                                * can change X, but when we do <op> we must use
+                                * the pre-op value.
+                                */
+                               duk_reg_t reg_temp;
+
+                               reg_temp = DUK__ALLOCTEMP(comp_ctx);
 
                                if (reg_varbind >= 0) {
                                        duk_reg_t reg_res;
+                                       duk_reg_t reg_src;
+                                       duk_int_t pc_temp_load;
+                                       duk_int_t pc_before_rhs;
+                                       duk_int_t pc_after_rhs;
 
                                        if (toplevel_assign) {
                                                /* 'reg_varbind' is the 
operation result and can also
                                                 * become the expression value 
for top level assignments
                                                 * such as: "var x; x += y;".
                                                 */
+                                               DUK_DD(DUK_DDPRINT("<op>= 
expression is top level, write directly to reg_varbind"));
                                                reg_res = reg_varbind;
                                        } else {
                                                /* Not safe to use 
'reg_varbind' as assignment expression
                                                 * value, so go through a temp.
                                                 */
-                                               reg_res = 
DUK__ALLOCTEMP(comp_ctx);
+                                               DUK_DD(DUK_DDPRINT("<op>= 
expression is not top level, write to reg_temp"));
+                                               reg_res = reg_temp;  /* reg_res 
should be smallest possible */
+                                               reg_temp = 
DUK__ALLOCTEMP(comp_ctx);
+                                       }
+
+                                       /* Try to optimize X <op>= Y for 
reg-bound
+                                        * variables.  Detect side-effect free 
RHS
+                                        * narrowly by seeing whether it emits 
code.
+                                        * If not, rewind the code emitter and 
overwrite
+                                        * the unnecessary temp reg load.
+                                        */
+
+                                       pc_temp_load = 
duk__get_current_pc(comp_ctx);
+                                       duk__emit_a_bc(comp_ctx,
+                                                      DUK_OP_LDREG,
+                                                      (duk_regconst_t) 
reg_temp,
+                                                      reg_varbind);
+
+                                       pc_before_rhs = 
duk__get_current_pc(comp_ctx);
+                                       duk__expr_toregconst(comp_ctx, res, 
args_rbp /*rbp_flags*/);
+                                       DUK_ASSERT(res->t == DUK_IVAL_PLAIN && 
res->x1.t == DUK_ISPEC_REGCONST);
+                                       pc_after_rhs = 
duk__get_current_pc(comp_ctx);
+
+                                       DUK_DD(DUK_DDPRINT("pc_temp_load=%ld, 
pc_before_rhs=%ld, pc_after_rhs=%ld",
+                                                          (long) pc_temp_load, 
(long) pc_before_rhs,
+                                                          (long) 
pc_after_rhs));
+
+                                       if (pc_after_rhs == pc_before_rhs) {
+                                               /* Note: if the reg_temp load 
generated shuffling
+                                                * instructions, we may need to 
rewind more than
+                                                * one instruction, so use 
explicit PC computation.
+                                                */
+                                               DUK_DD(DUK_DDPRINT("rhs is side 
effect free, rewind and avoid unnecessary temp for reg-based <op>="));
+                                               DUK_BW_ADD_PTR(comp_ctx->thr, 
&comp_ctx->curr_func.bw_code, (pc_temp_load - pc_before_rhs) * 
sizeof(duk_compiler_instr));
+                                               reg_src = reg_varbind;
+                                       } else {
+                                               DUK_DD(DUK_DDPRINT("rhs 
evaluation emitted code, not sure if rhs is side effect free; use temp reg for 
LHS"));
+                                               reg_src = reg_temp;
                                        }
 
                                        duk__emit_a_b_c(comp_ctx,
                                                        args_op,
                                                        (duk_regconst_t) 
reg_res,
-                                                       (duk_regconst_t) 
reg_varbind,
+                                                       (duk_regconst_t) 
reg_src,
                                                        res->x1.regconst);
+
                                        res->x1.regconst = (duk_regconst_t) 
reg_res;
+
+                                       /* Ensure compact use of temps. */
+                                       if (DUK__ISTEMP(comp_ctx, reg_res)) {
+                                               DUK__SETTEMP(comp_ctx, reg_res 
+ 1);
+                                       }
                                } else {
                                        /* When LHS is not register bound, 
always go through a
                                         * temporary.  No optimization for top 
level assignment.
                                         */
-                                       duk_reg_t reg_temp;
-                                       reg_temp = DUK__ALLOCTEMP(comp_ctx);
 
                                        duk__emit_a_bc(comp_ctx,
                                                       DUK_OP_GETVAR,
                                                       (duk_regconst_t) 
reg_temp,
                                                       rc_varname);
+
+                                       duk__expr_toregconst(comp_ctx, res, 
args_rbp /*rbp_flags*/);
+                                       DUK_ASSERT(res->t == DUK_IVAL_PLAIN && 
res->x1.t == DUK_ISPEC_REGCONST);
+
                                        duk__emit_a_b_c(comp_ctx,
                                                        args_op,
                                                        (duk_regconst_t) 
reg_temp,
@@ -61928,10 +62100,10 @@ DUK_LOCAL void duk__expr_led(duk_compiler_ctx 
*comp_ctx, duk_ivalue *left, duk_i
                                                           
DUK__IVAL_FLAG_REQUIRE_TEMP | DUK__IVAL_FLAG_ALLOW_CONST /*flags*/);
 
                        /* Evaluate RHS only when LHS is safe. */
-                       duk__expr_toregconst(comp_ctx, res, args_rbp 
/*rbp_flags*/);
-                       DUK_ASSERT(res->t == DUK_IVAL_PLAIN && res->x1.t == 
DUK_ISPEC_REGCONST);
 
                        if (args_op == DUK_OP_NONE) {
+                               duk__expr_toregconst(comp_ctx, res, args_rbp 
/*rbp_flags*/);
+                               DUK_ASSERT(res->t == DUK_IVAL_PLAIN && 
res->x1.t == DUK_ISPEC_REGCONST);
                                rc_res = res->x1.regconst;
                        } else {
                                reg_temp = DUK__ALLOCTEMP(comp_ctx);
@@ -61940,6 +62112,10 @@ DUK_LOCAL void duk__expr_led(duk_compiler_ctx 
*comp_ctx, duk_ivalue *left, duk_i
                                                (duk_regconst_t) reg_temp,
                                                (duk_regconst_t) reg_obj,
                                                rc_key);
+
+                               duk__expr_toregconst(comp_ctx, res, args_rbp 
/*rbp_flags*/);
+                               DUK_ASSERT(res->t == DUK_IVAL_PLAIN && 
res->x1.t == DUK_ISPEC_REGCONST);
+
                                duk__emit_a_b_c(comp_ctx,
                                                args_op,
                                                (duk_regconst_t) reg_temp,
@@ -61971,17 +62147,18 @@ DUK_LOCAL void duk__expr_led(duk_compiler_ctx 
*comp_ctx, duk_ivalue *left, duk_i
 
                        duk_regconst_t rc_res;
 
-                       /* first evaluate LHS fully to ensure all side effects 
are out */
+                       /* First evaluate LHS fully to ensure all side effects 
are out. */
                        duk__ivalue_toplain_ignore(comp_ctx, left);
 
-                       /* then evaluate RHS fully (its value becomes the 
expression value too) */
+                       /* Then evaluate RHS fully (its value becomes the 
expression value too).
+                        * Technically we'd need the side effect safety check 
here too, but because
+                        * we always throw using INVLHS the result doesn't 
matter.
+                        */
                        rc_res = duk__expr_toregconst(comp_ctx, res, args_rbp 
/*rbp_flags*/);
 
                        duk__emit_extraop_only(comp_ctx,
                                               DUK_EXTRAOP_INVLHS);
 
-                       /* XXX: this value is irrelevant because of INVLHS? */
-
                        res->t = DUK_IVAL_PLAIN;
                        res->x1.t = DUK_ISPEC_REGCONST;
                        res->x1.regconst = rc_res;
@@ -71183,7 +71360,12 @@ DUK_LOCAL void 
duk__inc_data_inner_refcounts(duk_hthread *thr, duk_hcompiledfunc
        duk_tval *tv, *tv_end;
        duk_hobject **funcs, **funcs_end;
 
-       DUK_ASSERT(DUK_HCOMPILEDFUNCTION_GET_DATA(thr->heap, f) != NULL);  /* 
compiled functions must be created 'atomically' */
+       /* If function creation fails due to out-of-memory, the data buffer
+        * pointer may be NULL in some cases.  That's actually possible for
+        * GC code, but shouldn't be possible here because the incomplete
+        * function will be unwound from the value stack and never instantiated.
+        */
+       DUK_ASSERT(DUK_HCOMPILEDFUNCTION_GET_DATA(thr->heap, f) != NULL);
        DUK_UNREF(thr);
 
        tv = DUK_HCOMPILEDFUNCTION_GET_CONSTS_BASE(thr->heap, f);
@@ -78997,6 +79179,7 @@ DUK_LOCAL void duk__regexp_match_helper(duk_hthread 
*thr, duk_small_int_t force_
        duk_hobject *h_regexp;
        duk_hstring *h_bytecode;
        duk_hstring *h_input;
+       duk_uint8_t *p_buf;
        const duk_uint8_t *pc;
        const duk_uint8_t *sp;
        duk_small_int_t match = 0;
@@ -79067,17 +79250,21 @@ DUK_LOCAL void duk__regexp_match_helper(duk_hthread 
*thr, duk_small_int_t force_
        DUK_ASSERT(re_ctx.nsaved >= 2);
        DUK_ASSERT((re_ctx.nsaved % 2) == 0);
 
-       duk_push_fixed_buffer(ctx, sizeof(duk_uint8_t *) * re_ctx.nsaved);
+       p_buf = (duk_uint8_t *) duk_push_fixed_buffer(ctx, sizeof(duk_uint8_t 
*) * re_ctx.nsaved);
+       DUK_UNREF(p_buf);
        re_ctx.saved = (const duk_uint8_t **) duk_get_buffer(ctx, -1, NULL);
        DUK_ASSERT(re_ctx.saved != NULL);
 
        /* [ ... re_obj input bc saved_buf ] */
 
-       /* buffer is automatically zeroed */
-#ifdef DUK_USE_EXPLICIT_NULL_INIT
+#if defined(DUK_USE_EXPLICIT_NULL_INIT)
        for (i = 0; i < re_ctx.nsaved; i++) {
                re_ctx.saved[i] = (duk_uint8_t *) NULL;
        }
+#elif defined(DUK_USE_ZERO_BUFFER_DATA)
+       /* buffer is automatically zeroed */
+#else
+       DUK_MEMZERO((void *) p_buf, sizeof(duk_uint8_t *) * re_ctx.nsaved);
 #endif
 
        DUK_DDD(DUK_DDDPRINT("regexp ctx initialized, flags=0x%08lx, 
nsaved=%ld, recursion_limit=%ld, steps_limit=%ld",
@@ -86566,4 +86753,3 @@ DUK_INTERNAL duk_double_t 
duk_util_tinyrandom_get_double(duk_hthread *thr) {
 
        return t;
 }
-#endif
diff --git a/content/handlers/javascript/duktape/duktape.h 
b/content/handlers/javascript/duktape/duktape.h
index 2cb9a50..eb47a70 100644
--- a/content/handlers/javascript/duktape/duktape.h
+++ b/content/handlers/javascript/duktape/duktape.h
@@ -1,12 +1,12 @@
 /*
- *  Duktape public API for Duktape 1.5.1.
+ *  Duktape public API for Duktape 1.6.0.
  *
  *  See the API reference for documentation on call semantics.
  *  The exposed API is inside the DUK_API_PUBLIC_H_INCLUDED
  *  include guard.  Other parts of the header are Duktape
  *  internal and related to platform/compiler/feature detection.
  *
- *  Git commit 2cc76e9ff1f64869e1146ad7317d8cbe33bbd27e (v1.5.1).
+ *  Git commit 17e3d86cf8b4788bd0d37658f833ab440ce43a1c (v1.6.0).
  *  Git branch HEAD.
  *
  *  See Duktape AUTHORS.rst and LICENSE.txt for copyright and
@@ -163,6 +163,7 @@ extern "C" {
  *  in Duktape web documentation.
  */
 
+struct duk_thread_state;
 struct duk_memory_functions;
 struct duk_function_list_entry;
 struct duk_number_list_entry;
@@ -170,6 +171,7 @@ struct duk_number_list_entry;
 /* duk_context is now defined in duk_config.h because it may also be
  * referenced there by prototypes.
  */
+typedef struct duk_thread_state duk_thread_state;
 typedef struct duk_memory_functions duk_memory_functions;
 typedef struct duk_function_list_entry duk_function_list_entry;
 typedef struct duk_number_list_entry duk_number_list_entry;
@@ -190,6 +192,14 @@ typedef void (*duk_debug_write_flush_function) (void 
*udata);
 typedef duk_idx_t (*duk_debug_request_function) (duk_context *ctx, void 
*udata, duk_idx_t nvalues);
 typedef void (*duk_debug_detached_function) (void *udata);
 
+struct duk_thread_state {
+       /* XXX: Enough space to hold internal suspend/resume structure.
+        * This is rather awkward and to be fixed when the internal
+        * structure is visible for the public API header.
+        */
+       char data[128];
+};
+
 struct duk_memory_functions {
        duk_alloc_function alloc_func;
        duk_realloc_function realloc_func;
@@ -218,15 +228,15 @@ struct duk_number_list_entry {
  * have 99 for patch level (e.g. 0.10.99 would be a development version
  * after 0.10.0 but before the next official release).
  */
-#define DUK_VERSION                       10501L
+#define DUK_VERSION                       10600L
 
 /* Git commit, describe, and branch for Duktape build.  Useful for
  * non-official snapshot builds so that application code can easily log
  * which Duktape snapshot was used.  Not available in the Ecmascript
  * environment.
  */
-#define DUK_GIT_COMMIT                    
"2cc76e9ff1f64869e1146ad7317d8cbe33bbd27e"
-#define DUK_GIT_DESCRIBE                  "v1.5.1"
+#define DUK_GIT_COMMIT                    
"17e3d86cf8b4788bd0d37658f833ab440ce43a1c"
+#define DUK_GIT_DESCRIBE                  "v1.6.0"
 #define DUK_GIT_BRANCH                    "HEAD"
 
 /* Duktape debug protocol version used by this build. */
@@ -397,6 +407,9 @@ duk_context *duk_create_heap(duk_alloc_function alloc_func,
                              duk_fatal_function fatal_handler);
 DUK_EXTERNAL_DECL void duk_destroy_heap(duk_context *ctx);
 
+DUK_EXTERNAL_DECL void duk_suspend(duk_context *ctx, duk_thread_state *state);
+DUK_EXTERNAL_DECL void duk_resume(duk_context *ctx, const duk_thread_state 
*state);
+
 #define duk_create_heap_default() \
        duk_create_heap(NULL, NULL, NULL, NULL, NULL)
 


-----------------------------------------------------------------------

Summary of changes:
 content/handlers/javascript/duktape/duk_config.h |    4 +-
 content/handlers/javascript/duktape/duktape.c    |  407 ++++++++++++++++------
 content/handlers/javascript/duktape/duktape.h    |   23 +-
 3 files changed, 318 insertions(+), 116 deletions(-)

diff --git a/content/handlers/javascript/duktape/duk_config.h 
b/content/handlers/javascript/duktape/duk_config.h
index c336603..f9c95ae 100644
--- a/content/handlers/javascript/duktape/duk_config.h
+++ b/content/handlers/javascript/duktape/duk_config.h
@@ -1,8 +1,8 @@
 /*
  *  duk_config.h configuration header generated by genconfig.py.
  *
- *  Git commit: 2cc76e9ff1f64869e1146ad7317d8cbe33bbd27e
- *  Git describe: v1.5.1
+ *  Git commit: 17e3d86cf8b4788bd0d37658f833ab440ce43a1c
+ *  Git describe: v1.6.0
  *  Git branch: HEAD
  *
  *  Supported platforms:
diff --git a/content/handlers/javascript/duktape/duktape.c 
b/content/handlers/javascript/duktape/duktape.c
index e1867c0..b64383b 100644
--- a/content/handlers/javascript/duktape/duktape.c
+++ b/content/handlers/javascript/duktape/duktape.c
@@ -1,9 +1,9 @@
 /* Omit from static analysis. */
 #ifndef __clang_analyzer__
 /*
- *  Single source autogenerated distributable for Duktape 1.5.1.
+ *  Single source autogenerated distributable for Duktape 1.6.0.
  *
- *  Git commit 2cc76e9ff1f64869e1146ad7317d8cbe33bbd27e (v1.5.1).
+ *  Git commit 17e3d86cf8b4788bd0d37658f833ab440ce43a1c (v1.6.0).
  *  Git branch HEAD.
  *
  *  See Duktape AUTHORS.rst and LICENSE.txt for copyright and
@@ -1631,9 +1631,9 @@ DUK_INTERNAL_DECL const duk_c_function 
duk_bi_native_functions[149];
 #endif  /* !DUK_SINGLE_FILE */
 #if defined(DUK_USE_BUILTIN_INITJS)
 #if !defined(DUK_SINGLE_FILE)
-DUK_INTERNAL_DECL const duk_uint8_t duk_initjs_data[187];
+DUK_INTERNAL_DECL const duk_uint8_t duk_initjs_data[204];
 #endif  /* !DUK_SINGLE_FILE */
-#define DUK_BUILTIN_INITJS_DATA_LENGTH                                187
+#define DUK_BUILTIN_INITJS_DATA_LENGTH                                204
 #endif  /* DUK_USE_BUILTIN_INITJS */
 #define DUK_BIDX_GLOBAL                                               0
 #define DUK_BIDX_GLOBAL_ENV                                           1
@@ -9221,7 +9221,7 @@ DUK_INTERNAL const duk_c_function 
duk_bi_native_functions[149] = {
        duk_bi_typedarray_set,
 };
 #if defined(DUK_USE_BUILTIN_INITJS)
-DUK_INTERNAL const duk_uint8_t duk_initjs_data[187] = {
+DUK_INTERNAL const duk_uint8_t duk_initjs_data[204] = {
 40,102,117,110,99,116,105,111,110,40,100,44,97,41,123,102,117,110,99,116,
 105,111,110,32,98,40,97,44,98,44,99,41,123,79,98,106,101,99,116,46,100,101,
 102,105,110,101,80,114,111,112,101,114,116,121,40,97,44,98,44,123,118,97,
@@ -9229,8 +9229,9 @@ DUK_INTERNAL const duk_uint8_t duk_initjs_data[187] = {
 109,101,114,97,98,108,101,58,33,49,44,99,111,110,102,105,103,117,114,97,98,
 108,101,58,33,48,125,41,125,98,40,97,46,76,111,103,103,101,114,44,34,99,
 108,111,103,34,44,110,101,119,32,97,46,76,111,103,103,101,114,40,34,67,34,
-41,41,59,98,40,97,44,34,109,111,100,76,111,97,100,101,100,34,44,123,125,41,
-125,41,40,116,104,105,115,44,68,117,107,116,97,112,101,41,59,10,0,
+41,41,59,98,40,97,44,34,109,111,100,76,111,97,100,101,100,34,44,79,98,106,
+101,99,116,46,99,114,101,97,116,101,40,110,117,108,108,41,41,125,41,40,116,
+104,105,115,44,68,117,107,116,97,112,101,41,59,10,0,
 };
 #endif  /* DUK_USE_BUILTIN_INITJS */
 #if defined(DUK_USE_DOUBLE_LE)
@@ -9354,7 +9355,7 @@ DUK_INTERNAL const duk_uint8_t duk_builtins_data[3833] = {
 88,119,100,223,181,68,16,94,91,250,238,200,160,80,0,152,31,61,59,148,10,0,
 21,4,231,199,151,69,2,128,5,192,250,97,220,160,80,0,192,127,255,128,20,23,
 134,30,92,242,164,34,19,207,167,45,59,179,233,205,229,37,129,127,255,0,0,
-191,255,128,0,63,255,197,131,246,203,203,158,157,251,160,0,0,0,0,64,65,98,
+191,255,128,0,63,255,197,131,246,203,203,158,157,251,160,0,0,0,0,0,90,98,
 32,3,166,156,30,53,32,249,165,131,76,223,159,62,94,70,172,114,16,176,144,
 60,56,250,19,18,5,159,25,89,32,121,180,238,42,30,129,229,221,140,164,122,7,
 147,46,50,129,232,62,61,251,120,97,199,208,156,129,83,127,0,50,250,69,3,
@@ -9534,7 +9535,7 @@ DUK_INTERNAL const duk_uint8_t duk_builtins_data[3833] = {
 88,119,100,223,181,68,16,94,91,250,238,200,160,80,0,152,31,61,59,148,10,0,
 21,4,231,199,151,69,2,128,5,192,250,97,220,160,80,0,192,127,255,128,20,23,
 134,30,92,242,164,34,19,207,167,45,59,179,233,205,229,37,129,127,255,0,0,
-191,255,128,0,63,255,197,131,246,203,203,158,157,251,160,32,98,65,64,0,0,0,
+191,255,128,0,63,255,197,131,246,203,203,158,157,251,160,32,98,90,0,0,0,0,
 0,3,166,156,30,53,32,249,165,131,76,223,159,62,94,70,172,114,16,176,144,60,
 56,250,19,18,5,159,25,89,32,121,180,238,42,30,129,229,221,140,164,122,7,
 147,46,50,129,232,62,61,251,120,97,199,208,156,129,83,127,0,50,250,69,3,
@@ -9714,7 +9715,7 @@ DUK_INTERNAL const duk_uint8_t duk_builtins_data[3833] = {
 88,119,100,223,181,68,16,94,91,250,238,200,160,80,0,152,31,61,59,148,10,0,
 21,4,231,199,151,69,2,128,5,192,250,97,220,160,80,0,192,127,255,128,20,23,
 134,30,92,242,164,34,19,207,167,45,59,179,233,205,229,37,129,127,255,0,0,
-191,255,128,0,63,255,197,131,246,203,203,158,157,251,160,64,65,98,32,0,0,0,
+191,255,128,0,63,255,197,131,246,203,203,158,157,251,160,0,90,98,32,0,0,0,
 0,3,166,156,30,53,32,249,165,131,76,223,159,62,94,70,172,114,16,176,144,60,
 56,250,19,18,5,159,25,89,32,121,180,238,42,30,129,229,221,140,164,122,7,
 147,46,50,129,232,62,61,251,120,97,199,208,156,129,83,127,0,50,250,69,3,
@@ -14047,6 +14048,15 @@ DUK_EXTERNAL void duk_debugger_pause(duk_context *ctx) 
{
 
 /* include removed: duk_internal.h */
 
+typedef struct duk_internal_thread_state duk_internal_thread_state;
+
+struct duk_internal_thread_state {
+       duk_ljstate lj;
+       duk_bool_t handling_error;
+       duk_hthread *curr_thread;
+       duk_int_t call_recursion_depth;
+};
+
 DUK_EXTERNAL
 duk_context *duk_create_heap(duk_alloc_function alloc_func,
                              duk_realloc_function realloc_func,
@@ -14112,6 +14122,57 @@ DUK_EXTERNAL void duk_destroy_heap(duk_context *ctx) {
        duk_heap_free(heap);
 }
 
+DUK_EXTERNAL void duk_suspend(duk_context *ctx, duk_thread_state *state) {
+       duk_hthread *thr = (duk_hthread *) ctx;
+       duk_internal_thread_state *snapshot = (duk_internal_thread_state *) 
(void *) state;
+       duk_heap *heap;
+       duk_ljstate *lj;
+
+       DUK_ASSERT_CTX_VALID(ctx);
+       DUK_ASSERT(thr != NULL);
+       DUK_ASSERT(thr->heap != NULL);
+       DUK_ASSERT(state != NULL);  /* unvalidated */
+
+       heap = thr->heap;
+       lj = &heap->lj;
+
+       duk_push_tval(ctx, &lj->value1);
+       duk_push_tval(ctx, &lj->value2);
+
+       DUK_MEMCPY((void *) &snapshot->lj, (const void *) lj, 
sizeof(duk_ljstate));
+       snapshot->handling_error = heap->handling_error;
+       snapshot->curr_thread = heap->curr_thread;
+       snapshot->call_recursion_depth = heap->call_recursion_depth;
+
+       lj->jmpbuf_ptr = NULL;
+       lj->type = DUK_LJ_TYPE_UNKNOWN;
+       DUK_TVAL_SET_UNDEFINED(&lj->value1);
+       DUK_TVAL_SET_UNDEFINED(&lj->value2);
+       heap->handling_error = 0;
+       heap->curr_thread = NULL;
+       heap->call_recursion_depth = 0;
+}
+
+DUK_EXTERNAL void duk_resume(duk_context *ctx, const duk_thread_state *state) {
+       duk_hthread *thr = (duk_hthread *) ctx;
+       const duk_internal_thread_state *snapshot = (const 
duk_internal_thread_state *) (const void *) state;
+       duk_heap *heap;
+
+       DUK_ASSERT_CTX_VALID(ctx);
+       DUK_ASSERT(thr != NULL);
+       DUK_ASSERT(thr->heap != NULL);
+       DUK_ASSERT(state != NULL);  /* unvalidated */
+
+       heap = thr->heap;
+
+       DUK_MEMCPY((void *) &heap->lj, (const void *) &snapshot->lj, 
sizeof(duk_ljstate));
+       heap->handling_error = snapshot->handling_error;
+       heap->curr_thread = snapshot->curr_thread;
+       heap->call_recursion_depth = snapshot->call_recursion_depth;
+
+       duk_pop_2(ctx);
+}
+
 /* XXX: better place for this */
 DUK_EXTERNAL void duk_set_global_object(duk_context *ctx) {
        duk_hthread *thr = (duk_hthread *) ctx;
@@ -14417,7 +14478,7 @@ DUK_INTERNAL duk_bool_t 
duk_get_prop_stridx_boolean(duk_context *ctx, duk_idx_t
        return rc;
 }
 
-DUK_EXTERNAL duk_bool_t duk_put_prop(duk_context *ctx, duk_idx_t obj_index) {
+DUK_LOCAL duk_bool_t duk__put_prop_shared(duk_context *ctx, duk_idx_t obj_idx, 
duk_idx_t idx_key) {
        duk_hthread *thr = (duk_hthread *) ctx;
        duk_tval *tv_obj;
        duk_tval *tv_key;
@@ -14425,16 +14486,19 @@ DUK_EXTERNAL duk_bool_t duk_put_prop(duk_context 
*ctx, duk_idx_t obj_index) {
        duk_small_int_t throw_flag;
        duk_bool_t rc;
 
-       DUK_ASSERT_CTX_VALID(ctx);
-
        /* Note: copying tv_obj and tv_key to locals to shield against a 
valstack
         * resize is not necessary for a property put right now (putprop 
protects
         * against it internally).
         */
 
-       tv_obj = duk_require_tval(ctx, obj_index);
-       tv_key = duk_require_tval(ctx, -2);
-       tv_val = duk_require_tval(ctx, -1);
+       /* Key and value indices are either (-2, -1) or (-1, -2).  Given 
idx_key,
+        * idx_val is always (idx_key ^ 0x01).
+        */
+       DUK_ASSERT((idx_key == -2 && (idx_key ^ 1) == -1) ||
+                  (idx_key == -1 && (idx_key ^ 1) == -2));
+       tv_obj = duk_require_tval(ctx, obj_idx);
+       tv_key = duk_require_tval(ctx, idx_key);
+       tv_val = duk_require_tval(ctx, idx_key ^ 1);
        throw_flag = duk_is_strict_call(ctx);
 
        rc = duk_hobject_putprop(thr, tv_obj, tv_key, tv_val, throw_flag);
@@ -14444,26 +14508,33 @@ DUK_EXTERNAL duk_bool_t duk_put_prop(duk_context 
*ctx, duk_idx_t obj_index) {
        return rc;  /* 1 if property found, 0 otherwise */
 }
 
-DUK_EXTERNAL duk_bool_t duk_put_prop_string(duk_context *ctx, duk_idx_t 
obj_index, const char *key) {
+DUK_EXTERNAL duk_bool_t duk_put_prop(duk_context *ctx, duk_idx_t obj_idx) {
+       DUK_ASSERT_CTX_VALID(ctx);
+       return duk__put_prop_shared(ctx, obj_idx, -2);
+}
+
+DUK_EXTERNAL duk_bool_t duk_put_prop_string(duk_context *ctx, duk_idx_t 
obj_idx, const char *key) {
        DUK_ASSERT_CTX_VALID(ctx);
        DUK_ASSERT(key != NULL);
 
-       obj_index = duk_require_normalize_index(ctx, obj_index);
-       duk_push_string(ctx, key);
-       duk_swap_top(ctx, -2);  /* [val key] -> [key val] */
-       return duk_put_prop(ctx, obj_index);
+       /* Careful here and with other duk_put_prop_xxx() helpers: the
+        * target object and the property value may be in the same value
+        * stack slot (unusual, but still conceptually clear).
+        */
+       obj_idx = duk_normalize_index(ctx, obj_idx);
+       (void) duk_push_string(ctx, key);
+       return duk__put_prop_shared(ctx, obj_idx, -1);
 }
 
-DUK_EXTERNAL duk_bool_t duk_put_prop_index(duk_context *ctx, duk_idx_t 
obj_index, duk_uarridx_t arr_index) {
+DUK_EXTERNAL duk_bool_t duk_put_prop_index(duk_context *ctx, duk_idx_t 
obj_idx, duk_uarridx_t arr_idx) {
        DUK_ASSERT_CTX_VALID(ctx);
 
-       obj_index = duk_require_normalize_index(ctx, obj_index);
-       duk_push_uarridx(ctx, arr_index);
-       duk_swap_top(ctx, -2);  /* [val key] -> [key val] */
-       return duk_put_prop(ctx, obj_index);
+       obj_idx = duk_require_normalize_index(ctx, obj_idx);
+       duk_push_uarridx(ctx, arr_idx);
+       return duk__put_prop_shared(ctx, obj_idx, -1);
 }
 
-DUK_INTERNAL duk_bool_t duk_put_prop_stridx(duk_context *ctx, duk_idx_t 
obj_index, duk_small_int_t stridx) {
+DUK_INTERNAL duk_bool_t duk_put_prop_stridx(duk_context *ctx, duk_idx_t 
obj_idx, duk_small_int_t stridx) {
        duk_hthread *thr = (duk_hthread *) ctx;
 
        DUK_ASSERT_CTX_VALID(ctx);
@@ -14471,10 +14542,9 @@ DUK_INTERNAL duk_bool_t 
duk_put_prop_stridx(duk_context *ctx, duk_idx_t obj_inde
        DUK_ASSERT(stridx < DUK_HEAP_NUM_STRINGS);
        DUK_UNREF(thr);
 
-       obj_index = duk_require_normalize_index(ctx, obj_index);
+       obj_idx = duk_require_normalize_index(ctx, obj_idx);
        duk_push_hstring(ctx, DUK_HTHREAD_GET_STRING(thr, stridx));
-       duk_swap_top(ctx, -2);  /* [val key] -> [key val] */
-       return duk_put_prop(ctx, obj_index);
+       return duk__put_prop_shared(ctx, obj_idx, -1);
 }
 
 DUK_EXTERNAL duk_bool_t duk_del_prop(duk_context *ctx, duk_idx_t obj_index) {
@@ -31391,12 +31461,17 @@ DUK_LOCAL duk_bool_t 
duk__json_stringify_fast_value(duk_json_enc_ctx *js_ctx, du
                                         * standard JSON (and no JX/JC support 
here now).
                                         */
                                        DUK_D(DUK_DPRINT("gap in array, no 
conflicting inherited property, remain on fast path"));
+#if defined(DUK_USE_JX)
+                                       DUK__EMIT_STRIDX(js_ctx, 
js_ctx->stridx_custom_undefined);
+#else
                                        DUK__EMIT_STRIDX(js_ctx, 
DUK_STRIDX_LC_NULL);
+#endif
                                } else {
                                        if 
(duk__json_stringify_fast_value(js_ctx, tv_val) == 0) {
                                                DUK__EMIT_STRIDX(js_ctx, 
DUK_STRIDX_LC_NULL);
                                        }
                                }
+
                                DUK__EMIT_1(js_ctx, DUK_ASC_COMMA);
                                emitted = 1;
                        }
@@ -31729,6 +31804,7 @@ void duk_bi_json_stringify_helper(duk_context *ctx,
         * combinations properly.
         */
 #if defined(DUK_USE_JX) || defined(DUK_USE_JC)
+       js_ctx->stridx_custom_undefined = DUK_STRIDX_LC_NULL;  /* standard 
JSON; array gaps */
 #if defined(DUK_USE_JX)
        if (flags & DUK_JSON_FLAG_EXT_CUSTOM) {
                js_ctx->stridx_custom_undefined = DUK_STRIDX_LC_UNDEFINED;
@@ -33931,7 +34007,7 @@ DUK_INTERNAL duk_ret_t 
duk_bi_string_constructor_from_char_code(duk_context *ctx
                cp = (duk_ucodepoint_t) duk_to_uint32(ctx, i);
                DUK_BW_WRITE_ENSURE_XUTF8(thr, bw, cp);
 #else
-               cp = (duk_ucodepoint_t) duk_to_uint32(ctx, i);
+               cp = (duk_ucodepoint_t) duk_to_uint16(ctx, i);
                DUK_BW_WRITE_ENSURE_CESU8(thr, bw, cp);
 #endif
        }
@@ -42036,18 +42112,23 @@ DUK_LOCAL void duk__mark_hobject(duk_heap *heap, 
duk_hobject *h) {
 
                duk__mark_heaphdr(heap, (duk_heaphdr *) 
DUK_HCOMPILEDFUNCTION_GET_DATA(heap, f));
 
-               tv = DUK_HCOMPILEDFUNCTION_GET_CONSTS_BASE(heap, f);
-               tv_end = DUK_HCOMPILEDFUNCTION_GET_CONSTS_END(heap, f);
-               while (tv < tv_end) {
-                       duk__mark_tval(heap, tv);
-                       tv++;
-               }
+               if (DUK_HCOMPILEDFUNCTION_GET_DATA(heap, f) != NULL) {
+                       tv = DUK_HCOMPILEDFUNCTION_GET_CONSTS_BASE(heap, f);
+                       tv_end = DUK_HCOMPILEDFUNCTION_GET_CONSTS_END(heap, f);
+                       while (tv < tv_end) {
+                               duk__mark_tval(heap, tv);
+                               tv++;
+                       }
 
-               fn = DUK_HCOMPILEDFUNCTION_GET_FUNCS_BASE(heap, f);
-               fn_end = DUK_HCOMPILEDFUNCTION_GET_FUNCS_END(heap, f);
-               while (fn < fn_end) {
-                       duk__mark_heaphdr(heap, (duk_heaphdr *) *fn);
-                       fn++;
+                       fn = DUK_HCOMPILEDFUNCTION_GET_FUNCS_BASE(heap, f);
+                       fn_end = DUK_HCOMPILEDFUNCTION_GET_FUNCS_END(heap, f);
+                       while (fn < fn_end) {
+                               duk__mark_heaphdr(heap, (duk_heaphdr *) *fn);
+                               fn++;
+                       }
+               } else {
+                       /* May happen in some out-of-memory corner cases. */
+                       DUK_D(DUK_DPRINT("duk_hcompiledfunction 'data' is NULL, 
skipping marking"));
                }
        } else if (DUK_HOBJECT_IS_NATIVEFUNCTION(h)) {
                duk_hnativefunction *f = (duk_hnativefunction *) h;
@@ -43927,20 +44008,23 @@ DUK_LOCAL void 
duk__refcount_finalize_hobject(duk_hthread *thr, duk_hobject *h)
                duk_tval *tv, *tv_end;
                duk_hobject **funcs, **funcs_end;
 
-               DUK_ASSERT(DUK_HCOMPILEDFUNCTION_GET_DATA(thr->heap, f) != 
NULL);  /* compiled functions must be created 'atomically' */
-
-               tv = DUK_HCOMPILEDFUNCTION_GET_CONSTS_BASE(thr->heap, f);
-               tv_end = DUK_HCOMPILEDFUNCTION_GET_CONSTS_END(thr->heap, f);
-               while (tv < tv_end) {
-                       duk_tval_decref(thr, tv);
-                       tv++;
-               }
+               if (DUK_HCOMPILEDFUNCTION_GET_DATA(thr->heap, f) != NULL) {
+                       tv = DUK_HCOMPILEDFUNCTION_GET_CONSTS_BASE(thr->heap, 
f);
+                       tv_end = 
DUK_HCOMPILEDFUNCTION_GET_CONSTS_END(thr->heap, f);
+                       while (tv < tv_end) {
+                               duk_tval_decref(thr, tv);
+                               tv++;
+                       }
 
-               funcs = DUK_HCOMPILEDFUNCTION_GET_FUNCS_BASE(thr->heap, f);
-               funcs_end = DUK_HCOMPILEDFUNCTION_GET_FUNCS_END(thr->heap, f);
-               while (funcs < funcs_end) {
-                       duk_heaphdr_decref(thr, (duk_heaphdr *) *funcs);
-                       funcs++;
+                       funcs = DUK_HCOMPILEDFUNCTION_GET_FUNCS_BASE(thr->heap, 
f);
+                       funcs_end = 
DUK_HCOMPILEDFUNCTION_GET_FUNCS_END(thr->heap, f);
+                       while (funcs < funcs_end) {
+                               duk_heaphdr_decref(thr, (duk_heaphdr *) *funcs);
+                               funcs++;
+                       }
+               } else {
+                       /* May happen in some out-of-memory corner cases. */
+                       DUK_D(DUK_DPRINT("duk_hcompiledfunction 'data' is NULL, 
skipping decref"));
                }
 
                duk_heaphdr_decref(thr, (duk_heaphdr *) 
DUK_HCOMPILEDFUNCTION_GET_DATA(thr->heap, f));
@@ -46870,8 +46954,13 @@ DUK_INTERNAL duk_bool_t 
duk_hobject_prototype_chain_contains(duk_hthread *thr, d
        duk_uint_t sanity;
 
        DUK_ASSERT(thr != NULL);
-       DUK_ASSERT(h != NULL);
-       /* allow 'p' to be NULL; then the result is always false */
+
+       /* False if the object is NULL or the prototype 'p' is NULL.
+        * In particular, false if both are NULL (don't compare equal).
+        */
+       if (h == NULL || p == NULL) {
+               return 0;
+       }
 
        sanity = DUK_HOBJECT_PROTOTYPE_CHAIN_SANITY;
        do {
@@ -51358,8 +51447,17 @@ DUK_INTERNAL duk_bool_t 
duk_hobject_delprop_raw(duk_hthread *thr, duk_hobject *o
                DUK_TVAL_SET_UNUSED_UPDREF(thr, tv);  /* side effects */
                goto success;
        } else {
+               duk_hobject *h_get = NULL;
+               duk_hobject *h_set = NULL;
+               duk_tval tv_tmp;
+
                DUK_ASSERT(desc.a_idx < 0);
 
+               /* Set property slot to an empty state.  Careful not to invoke
+                * any side effects while using desc.e_idx so that it doesn't
+                * get invalidated by a finalizer mutating our object.
+                */
+
                /* remove hash entry (no decref) */
 #if defined(DUK_USE_HOBJECT_HASH_PART)
                if (desc.h_idx >= 0) {
@@ -51380,21 +51478,17 @@ DUK_INTERNAL duk_bool_t 
duk_hobject_delprop_raw(duk_hthread *thr, duk_hobject *o
                DUK_DDD(DUK_DDDPRINT("before removing value, e_idx %ld, key %p, 
key at slot %p",
                                     (long) desc.e_idx, (void *) key, (void *) 
DUK_HOBJECT_E_GET_KEY(thr->heap, obj, desc.e_idx)));
                DUK_DDD(DUK_DDDPRINT("removing value at e_idx %ld", (long) 
desc.e_idx));
+               DUK_MEMSET((void *) &tv_tmp, 0, sizeof(tv_tmp));
+               DUK_TVAL_SET_UNDEFINED(&tv_tmp);
                if (DUK_HOBJECT_E_SLOT_IS_ACCESSOR(thr->heap, obj, desc.e_idx)) 
{
-                       duk_hobject *tmp;
-
-                       tmp = DUK_HOBJECT_E_GET_VALUE_GETTER(thr->heap, obj, 
desc.e_idx);
+                       h_get = DUK_HOBJECT_E_GET_VALUE_GETTER(thr->heap, obj, 
desc.e_idx);
+                       h_set = DUK_HOBJECT_E_GET_VALUE_SETTER(thr->heap, obj, 
desc.e_idx);
                        DUK_HOBJECT_E_SET_VALUE_GETTER(thr->heap, obj, 
desc.e_idx, NULL);
-                       DUK_UNREF(tmp);
-                       DUK_HOBJECT_DECREF_ALLOWNULL(thr, tmp);  /* side 
effects */
-
-                       tmp = DUK_HOBJECT_E_GET_VALUE_SETTER(thr->heap, obj, 
desc.e_idx);
                        DUK_HOBJECT_E_SET_VALUE_SETTER(thr->heap, obj, 
desc.e_idx, NULL);
-                       DUK_UNREF(tmp);
-                       DUK_HOBJECT_DECREF_ALLOWNULL(thr, tmp);  /* side 
effects */
                } else {
                        tv = DUK_HOBJECT_E_GET_VALUE_TVAL_PTR(thr->heap, obj, 
desc.e_idx);
-                       DUK_TVAL_SET_UNDEFINED_UPDREF(thr, tv);  /* side 
effects */
+                       DUK_TVAL_SET_TVAL(&tv_tmp, tv);
+                       DUK_TVAL_SET_UNDEFINED(tv);
                }
 #if 0
                /* Not strictly necessary because if key == NULL, flag MUST be 
ignored. */
@@ -51407,7 +51501,14 @@ DUK_INTERNAL duk_bool_t 
duk_hobject_delprop_raw(duk_hthread *thr, duk_hobject *o
                DUK_DDD(DUK_DDDPRINT("removing key at e_idx %ld", (long) 
desc.e_idx));
                DUK_ASSERT(key == DUK_HOBJECT_E_GET_KEY(thr->heap, obj, 
desc.e_idx));
                DUK_HOBJECT_E_SET_KEY(thr->heap, obj, desc.e_idx, NULL);
-               DUK_HSTRING_DECREF(thr, key);  /* side effects */
+
+               /* Do decrefs only with safe pointers to avoid side effects
+                * disturbing e_idx.
+                */
+               DUK_TVAL_DECREF(thr, &tv_tmp);
+               DUK_HOBJECT_DECREF_ALLOWNULL(thr, h_get);
+               DUK_HOBJECT_DECREF_ALLOWNULL(thr, h_set);
+               DUK_HSTRING_DECREF(thr, key);
                goto success;
        }
 
@@ -52581,6 +52682,7 @@ void duk_hobject_define_property_helper(duk_context 
*ctx,
                } else {
                        duk_bool_t rc;
                        duk_tval *tv1;
+                       duk_tval tv_tmp;
 
                        /* curr is data, desc is accessor */
                        if (!(curr.flags & DUK_PROPDESC_FLAG_CONFIGURABLE) && 
!force_flag) {
@@ -52600,9 +52702,12 @@ void duk_hobject_define_property_helper(duk_context 
*ctx,
 
                        DUK_ASSERT(!DUK_HOBJECT_E_SLOT_IS_ACCESSOR(thr->heap, 
obj, curr.e_idx));
 
+                       /* Avoid side effects that might disturb curr.e_idx 
until
+                        * we're done editing the slot.
+                        */
                        tv1 = DUK_HOBJECT_E_GET_VALUE_TVAL_PTR(thr->heap, obj, 
curr.e_idx);
-                       /* XXX: just decref */
-                       DUK_TVAL_SET_UNDEFINED_UPDREF(thr, tv1);  /* side 
effects */
+                       DUK_TVAL_SET_TVAL(&tv_tmp, tv1);
+                       DUK_TVAL_SET_UNDEFINED(tv1);
 
                        DUK_HOBJECT_E_SET_VALUE_GETTER(thr->heap, obj, 
curr.e_idx, NULL);
                        DUK_HOBJECT_E_SET_VALUE_SETTER(thr->heap, obj, 
curr.e_idx, NULL);
@@ -52612,6 +52717,8 @@ void duk_hobject_define_property_helper(duk_context 
*ctx,
                        DUK_DDD(DUK_DDDPRINT("flags after data->accessor 
conversion: 0x%02lx",
                                             (unsigned long) 
DUK_HOBJECT_E_GET_FLAGS(thr->heap, obj, curr.e_idx)));
 
+                       DUK_TVAL_DECREF(thr, &tv_tmp);  /* side effects */
+
                        /* re-lookup to update curr.flags
                         * XXX: would be faster to update directly
                         */
@@ -52627,7 +52734,8 @@ void duk_hobject_define_property_helper(duk_context 
*ctx,
 
                if (curr.flags & DUK_PROPDESC_FLAG_ACCESSOR) {
                        duk_bool_t rc;
-                       duk_hobject *tmp;
+                       duk_hobject *h_get;
+                       duk_hobject *h_set;
 
                        /* curr is accessor, desc is data */
                        if (!(curr.flags & DUK_PROPDESC_FLAG_CONFIGURABLE) && 
!force_flag) {
@@ -52639,15 +52747,14 @@ void duk_hobject_define_property_helper(duk_context 
*ctx,
 
                        DUK_DDD(DUK_DDDPRINT("convert property to data 
property"));
 
+                       /* Avoid side effects that might disturb curr.e_idx 
until
+                        * we're done editing the slot.
+                        */
                        DUK_ASSERT(DUK_HOBJECT_E_SLOT_IS_ACCESSOR(thr->heap, 
obj, curr.e_idx));
-                       tmp = DUK_HOBJECT_E_GET_VALUE_GETTER(thr->heap, obj, 
curr.e_idx);
-                       DUK_UNREF(tmp);
+                       h_get = DUK_HOBJECT_E_GET_VALUE_GETTER(thr->heap, obj, 
curr.e_idx);
                        DUK_HOBJECT_E_SET_VALUE_GETTER(thr->heap, obj, 
curr.e_idx, NULL);
-                       DUK_HOBJECT_DECREF_ALLOWNULL(thr, tmp);  /* side 
effects */
-                       tmp = DUK_HOBJECT_E_GET_VALUE_SETTER(thr->heap, obj, 
curr.e_idx);
-                       DUK_UNREF(tmp);
+                       h_set = DUK_HOBJECT_E_GET_VALUE_SETTER(thr->heap, obj, 
curr.e_idx);
                        DUK_HOBJECT_E_SET_VALUE_SETTER(thr->heap, obj, 
curr.e_idx, NULL);
-                       DUK_HOBJECT_DECREF_ALLOWNULL(thr, tmp);  /* side 
effects */
 
                        
DUK_TVAL_SET_UNDEFINED(DUK_HOBJECT_E_GET_VALUE_TVAL_PTR(thr->heap, obj, 
curr.e_idx));
                        DUK_HOBJECT_E_SLOT_CLEAR_WRITABLE(thr->heap, obj, 
curr.e_idx);
@@ -52656,6 +52763,9 @@ void duk_hobject_define_property_helper(duk_context 
*ctx,
                        DUK_DDD(DUK_DDDPRINT("flags after accessor->data 
conversion: 0x%02lx",
                                             (unsigned long) 
DUK_HOBJECT_E_GET_FLAGS(thr->heap, obj, curr.e_idx)));
 
+                       DUK_HOBJECT_DECREF_ALLOWNULL(thr, h_get);  /* side 
effects */
+                       DUK_HOBJECT_DECREF_ALLOWNULL(thr, h_set);  /* side 
effects */
+
                        /* re-lookup to update curr.flags
                         * XXX: would be faster to update directly
                         */
@@ -59766,6 +59876,7 @@ DUK_LOCAL void duk__ivalue_toplain_raw(duk_compiler_ctx 
*comp_ctx, duk_ivalue *x
        }
        case DUK_IVAL_NONE:
        default: {
+               DUK_D(DUK_DPRINT("invalid ivalue type: %ld", (long) x->t));
                break;
        }
        }
@@ -61735,13 +61846,24 @@ DUK_LOCAL void duk__expr_led(duk_compiler_ctx 
*comp_ctx, duk_ivalue *left, duk_i
         *  left-hand-side values (e.g. as in "f() = 1") must NOT cause a
         *  SyntaxError, but rather a run-time ReferenceError.
         *
-        *  Assignment expression value is conceptually the LHS/RHS value
-        *  copied into a fresh temporary so that it won't change even if
-        *  LHS/RHS values change (e.g. when they're identifiers).  Doing this
-        *  concretely produces inefficient bytecode, so we try to avoid the
-        *  extra temporary for some known-to-be-safe cases.  Currently the
-        *  only safe case we detect is a "top level assignment", for example
-        *  "x = y + z;", where the assignment expression value is ignored.
+        *  When evaluating X <op>= Y, the LHS (X) is conceptually evaluated
+        *  to a temporary first.  The RHS is then evaluated.  Finally, the
+        *  <op> is applied to the initial value of RHS (not the value after
+        *  RHS evaluation), and written to X.  Doing so concretely generates
+        *  inefficient code so we'd like to avoid the temporary when possible.
+        *  See: https://github.com/svaarala/duktape/pull/992.
+        *
+        *  The expression value (final LHS value, written to RHS) is
+        *  conceptually copied into a fresh temporary so that it won't
+        *  change even if the LHS/RHS values change in outer expressions.
+        *  For example, it'd be generally incorrect for the expression value
+        *  to be the RHS register binding, unless there's a guarantee that it
+        *  won't change during further expression evaluation.  Using the
+        *  temporary concretely produces inefficient bytecode, so we try to
+        *  avoid the extra temporary for some known-to-be-safe cases.
+        *  Currently the only safe case we detect is a "top level assignment",
+        *  for example "x = y + z;", where the assignment expression value is
+        *  ignored.
         *  See: test-dev-assign-expr.js and test-bug-assign-mutate-gh381.js.
         */
 
@@ -61761,7 +61883,9 @@ DUK_LOCAL void duk__expr_led(duk_compiler_ctx 
*comp_ctx, duk_ivalue *left, duk_i
                 * is a reg-bound identifier.  The RHS ('res') is right 
associative
                 * so it has consumed all other assignment level operations; the
                 * only relevant lower binding power construct is comma operator
-                * which will ignore the expression value provided here.
+                * which will ignore the expression value provided here.  
Usually
+                * the top level assignment expression value is ignored, but it
+                * is relevant for e.g. eval code.
                 */
                toplevel_assign = (comp_ctx->curr_func.nud_count == 1 && /* one 
token before */
                                   comp_ctx->curr_func.led_count == 1);  /* one 
operator (= assign) */
@@ -61777,23 +61901,17 @@ DUK_LOCAL void duk__expr_led(duk_compiler_ctx 
*comp_ctx, duk_ivalue *left, duk_i
 
                        DUK_ASSERT(left->x1.t == DUK_ISPEC_VALUE);  /* LHS is 
already side effect free */
 
-                       /* Keep the RHS as an unresolved ivalue for now, so it
-                        * can be a plain value or a unary/binary operation 
here.
-                        * We resolve it before finishing but doing it later 
allows
-                        * better bytecode in some cases.
-                        */
-                       duk__expr(comp_ctx, res, args_rbp /*rbp_flags*/);
-
                        h_varname = duk_get_hstring(ctx, left->x1.valstack_idx);
                        DUK_ASSERT(h_varname != NULL);
                        if 
(duk__hstring_is_eval_or_arguments_in_strict_mode(comp_ctx, h_varname)) {
-                               /* E5 Section 11.13.1 (and others for other 
assignments), step 4 */
+                               /* E5 Section 11.13.1 (and others for other 
assignments), step 4. */
                                goto syntax_error_lvalue;
                        }
                        duk_dup(ctx, left->x1.valstack_idx);
                        (void) duk__lookup_lhs(comp_ctx, &reg_varbind, 
&rc_varname);
 
                        if (args_op == DUK_OP_NONE) {
+                               duk__expr(comp_ctx, res, args_rbp 
/*rbp_flags*/);
                                if (toplevel_assign) {
                                        /* Any 'res' will do. */
                                        DUK_DDD(DUK_DDDPRINT("plain assignment, 
toplevel assign, use as is"));
@@ -61807,42 +61925,98 @@ DUK_LOCAL void duk__expr_led(duk_compiler_ctx 
*comp_ctx, duk_ivalue *left, duk_i
                                        }
                                }
                        } else {
-                               duk__ivalue_toregconst(comp_ctx, res);
-                               DUK_ASSERT(res->t == DUK_IVAL_PLAIN && 
res->x1.t == DUK_ISPEC_REGCONST);
+                               /* For X <op>= Y we need to evaluate the pre-op
+                                * value of X before evaluating the RHS: the RHS
+                                * can change X, but when we do <op> we must use
+                                * the pre-op value.
+                                */
+                               duk_reg_t reg_temp;
+
+                               reg_temp = DUK__ALLOCTEMP(comp_ctx);
 
                                if (reg_varbind >= 0) {
                                        duk_reg_t reg_res;
+                                       duk_reg_t reg_src;
+                                       duk_int_t pc_temp_load;
+                                       duk_int_t pc_before_rhs;
+                                       duk_int_t pc_after_rhs;
 
                                        if (toplevel_assign) {
                                                /* 'reg_varbind' is the 
operation result and can also
                                                 * become the expression value 
for top level assignments
                                                 * such as: "var x; x += y;".
                                                 */
+                                               DUK_DD(DUK_DDPRINT("<op>= 
expression is top level, write directly to reg_varbind"));
                                                reg_res = reg_varbind;
                                        } else {
                                                /* Not safe to use 
'reg_varbind' as assignment expression
                                                 * value, so go through a temp.
                                                 */
-                                               reg_res = 
DUK__ALLOCTEMP(comp_ctx);
+                                               DUK_DD(DUK_DDPRINT("<op>= 
expression is not top level, write to reg_temp"));
+                                               reg_res = reg_temp;  /* reg_res 
should be smallest possible */
+                                               reg_temp = 
DUK__ALLOCTEMP(comp_ctx);
+                                       }
+
+                                       /* Try to optimize X <op>= Y for 
reg-bound
+                                        * variables.  Detect side-effect free 
RHS
+                                        * narrowly by seeing whether it emits 
code.
+                                        * If not, rewind the code emitter and 
overwrite
+                                        * the unnecessary temp reg load.
+                                        */
+
+                                       pc_temp_load = 
duk__get_current_pc(comp_ctx);
+                                       duk__emit_a_bc(comp_ctx,
+                                                      DUK_OP_LDREG,
+                                                      (duk_regconst_t) 
reg_temp,
+                                                      reg_varbind);
+
+                                       pc_before_rhs = 
duk__get_current_pc(comp_ctx);
+                                       duk__expr_toregconst(comp_ctx, res, 
args_rbp /*rbp_flags*/);
+                                       DUK_ASSERT(res->t == DUK_IVAL_PLAIN && 
res->x1.t == DUK_ISPEC_REGCONST);
+                                       pc_after_rhs = 
duk__get_current_pc(comp_ctx);
+
+                                       DUK_DD(DUK_DDPRINT("pc_temp_load=%ld, 
pc_before_rhs=%ld, pc_after_rhs=%ld",
+                                                          (long) pc_temp_load, 
(long) pc_before_rhs,
+                                                          (long) 
pc_after_rhs));
+
+                                       if (pc_after_rhs == pc_before_rhs) {
+                                               /* Note: if the reg_temp load 
generated shuffling
+                                                * instructions, we may need to 
rewind more than
+                                                * one instruction, so use 
explicit PC computation.
+                                                */
+                                               DUK_DD(DUK_DDPRINT("rhs is side 
effect free, rewind and avoid unnecessary temp for reg-based <op>="));
+                                               DUK_BW_ADD_PTR(comp_ctx->thr, 
&comp_ctx->curr_func.bw_code, (pc_temp_load - pc_before_rhs) * 
sizeof(duk_compiler_instr));
+                                               reg_src = reg_varbind;
+                                       } else {
+                                               DUK_DD(DUK_DDPRINT("rhs 
evaluation emitted code, not sure if rhs is side effect free; use temp reg for 
LHS"));
+                                               reg_src = reg_temp;
                                        }
 
                                        duk__emit_a_b_c(comp_ctx,
                                                        args_op,
                                                        (duk_regconst_t) 
reg_res,
-                                                       (duk_regconst_t) 
reg_varbind,
+                                                       (duk_regconst_t) 
reg_src,
                                                        res->x1.regconst);
+
                                        res->x1.regconst = (duk_regconst_t) 
reg_res;
+
+                                       /* Ensure compact use of temps. */
+                                       if (DUK__ISTEMP(comp_ctx, reg_res)) {
+                                               DUK__SETTEMP(comp_ctx, reg_res 
+ 1);
+                                       }
                                } else {
                                        /* When LHS is not register bound, 
always go through a
                                         * temporary.  No optimization for top 
level assignment.
                                         */
-                                       duk_reg_t reg_temp;
-                                       reg_temp = DUK__ALLOCTEMP(comp_ctx);
 
                                        duk__emit_a_bc(comp_ctx,
                                                       DUK_OP_GETVAR,
                                                       (duk_regconst_t) 
reg_temp,
                                                       rc_varname);
+
+                                       duk__expr_toregconst(comp_ctx, res, 
args_rbp /*rbp_flags*/);
+                                       DUK_ASSERT(res->t == DUK_IVAL_PLAIN && 
res->x1.t == DUK_ISPEC_REGCONST);
+
                                        duk__emit_a_b_c(comp_ctx,
                                                        args_op,
                                                        (duk_regconst_t) 
reg_temp,
@@ -61928,10 +62102,10 @@ DUK_LOCAL void duk__expr_led(duk_compiler_ctx 
*comp_ctx, duk_ivalue *left, duk_i
                                                           
DUK__IVAL_FLAG_REQUIRE_TEMP | DUK__IVAL_FLAG_ALLOW_CONST /*flags*/);
 
                        /* Evaluate RHS only when LHS is safe. */
-                       duk__expr_toregconst(comp_ctx, res, args_rbp 
/*rbp_flags*/);
-                       DUK_ASSERT(res->t == DUK_IVAL_PLAIN && res->x1.t == 
DUK_ISPEC_REGCONST);
 
                        if (args_op == DUK_OP_NONE) {
+                               duk__expr_toregconst(comp_ctx, res, args_rbp 
/*rbp_flags*/);
+                               DUK_ASSERT(res->t == DUK_IVAL_PLAIN && 
res->x1.t == DUK_ISPEC_REGCONST);
                                rc_res = res->x1.regconst;
                        } else {
                                reg_temp = DUK__ALLOCTEMP(comp_ctx);
@@ -61940,6 +62114,10 @@ DUK_LOCAL void duk__expr_led(duk_compiler_ctx 
*comp_ctx, duk_ivalue *left, duk_i
                                                (duk_regconst_t) reg_temp,
                                                (duk_regconst_t) reg_obj,
                                                rc_key);
+
+                               duk__expr_toregconst(comp_ctx, res, args_rbp 
/*rbp_flags*/);
+                               DUK_ASSERT(res->t == DUK_IVAL_PLAIN && 
res->x1.t == DUK_ISPEC_REGCONST);
+
                                duk__emit_a_b_c(comp_ctx,
                                                args_op,
                                                (duk_regconst_t) reg_temp,
@@ -61971,17 +62149,18 @@ DUK_LOCAL void duk__expr_led(duk_compiler_ctx 
*comp_ctx, duk_ivalue *left, duk_i
 
                        duk_regconst_t rc_res;
 
-                       /* first evaluate LHS fully to ensure all side effects 
are out */
+                       /* First evaluate LHS fully to ensure all side effects 
are out. */
                        duk__ivalue_toplain_ignore(comp_ctx, left);
 
-                       /* then evaluate RHS fully (its value becomes the 
expression value too) */
+                       /* Then evaluate RHS fully (its value becomes the 
expression value too).
+                        * Technically we'd need the side effect safety check 
here too, but because
+                        * we always throw using INVLHS the result doesn't 
matter.
+                        */
                        rc_res = duk__expr_toregconst(comp_ctx, res, args_rbp 
/*rbp_flags*/);
 
                        duk__emit_extraop_only(comp_ctx,
                                               DUK_EXTRAOP_INVLHS);
 
-                       /* XXX: this value is irrelevant because of INVLHS? */
-
                        res->t = DUK_IVAL_PLAIN;
                        res->x1.t = DUK_ISPEC_REGCONST;
                        res->x1.regconst = rc_res;
@@ -71183,7 +71362,12 @@ DUK_LOCAL void 
duk__inc_data_inner_refcounts(duk_hthread *thr, duk_hcompiledfunc
        duk_tval *tv, *tv_end;
        duk_hobject **funcs, **funcs_end;
 
-       DUK_ASSERT(DUK_HCOMPILEDFUNCTION_GET_DATA(thr->heap, f) != NULL);  /* 
compiled functions must be created 'atomically' */
+       /* If function creation fails due to out-of-memory, the data buffer
+        * pointer may be NULL in some cases.  That's actually possible for
+        * GC code, but shouldn't be possible here because the incomplete
+        * function will be unwound from the value stack and never instantiated.
+        */
+       DUK_ASSERT(DUK_HCOMPILEDFUNCTION_GET_DATA(thr->heap, f) != NULL);
        DUK_UNREF(thr);
 
        tv = DUK_HCOMPILEDFUNCTION_GET_CONSTS_BASE(thr->heap, f);
@@ -78997,6 +79181,7 @@ DUK_LOCAL void duk__regexp_match_helper(duk_hthread 
*thr, duk_small_int_t force_
        duk_hobject *h_regexp;
        duk_hstring *h_bytecode;
        duk_hstring *h_input;
+       duk_uint8_t *p_buf;
        const duk_uint8_t *pc;
        const duk_uint8_t *sp;
        duk_small_int_t match = 0;
@@ -79067,17 +79252,21 @@ DUK_LOCAL void duk__regexp_match_helper(duk_hthread 
*thr, duk_small_int_t force_
        DUK_ASSERT(re_ctx.nsaved >= 2);
        DUK_ASSERT((re_ctx.nsaved % 2) == 0);
 
-       duk_push_fixed_buffer(ctx, sizeof(duk_uint8_t *) * re_ctx.nsaved);
+       p_buf = (duk_uint8_t *) duk_push_fixed_buffer(ctx, sizeof(duk_uint8_t 
*) * re_ctx.nsaved);
+       DUK_UNREF(p_buf);
        re_ctx.saved = (const duk_uint8_t **) duk_get_buffer(ctx, -1, NULL);
        DUK_ASSERT(re_ctx.saved != NULL);
 
        /* [ ... re_obj input bc saved_buf ] */
 
-       /* buffer is automatically zeroed */
-#ifdef DUK_USE_EXPLICIT_NULL_INIT
+#if defined(DUK_USE_EXPLICIT_NULL_INIT)
        for (i = 0; i < re_ctx.nsaved; i++) {
                re_ctx.saved[i] = (duk_uint8_t *) NULL;
        }
+#elif defined(DUK_USE_ZERO_BUFFER_DATA)
+       /* buffer is automatically zeroed */
+#else
+       DUK_MEMZERO((void *) p_buf, sizeof(duk_uint8_t *) * re_ctx.nsaved);
 #endif
 
        DUK_DDD(DUK_DDDPRINT("regexp ctx initialized, flags=0x%08lx, 
nsaved=%ld, recursion_limit=%ld, steps_limit=%ld",
diff --git a/content/handlers/javascript/duktape/duktape.h 
b/content/handlers/javascript/duktape/duktape.h
index 2cb9a50..eb47a70 100644
--- a/content/handlers/javascript/duktape/duktape.h
+++ b/content/handlers/javascript/duktape/duktape.h
@@ -1,12 +1,12 @@
 /*
- *  Duktape public API for Duktape 1.5.1.
+ *  Duktape public API for Duktape 1.6.0.
  *
  *  See the API reference for documentation on call semantics.
  *  The exposed API is inside the DUK_API_PUBLIC_H_INCLUDED
  *  include guard.  Other parts of the header are Duktape
  *  internal and related to platform/compiler/feature detection.
  *
- *  Git commit 2cc76e9ff1f64869e1146ad7317d8cbe33bbd27e (v1.5.1).
+ *  Git commit 17e3d86cf8b4788bd0d37658f833ab440ce43a1c (v1.6.0).
  *  Git branch HEAD.
  *
  *  See Duktape AUTHORS.rst and LICENSE.txt for copyright and
@@ -163,6 +163,7 @@ extern "C" {
  *  in Duktape web documentation.
  */
 
+struct duk_thread_state;
 struct duk_memory_functions;
 struct duk_function_list_entry;
 struct duk_number_list_entry;
@@ -170,6 +171,7 @@ struct duk_number_list_entry;
 /* duk_context is now defined in duk_config.h because it may also be
  * referenced there by prototypes.
  */
+typedef struct duk_thread_state duk_thread_state;
 typedef struct duk_memory_functions duk_memory_functions;
 typedef struct duk_function_list_entry duk_function_list_entry;
 typedef struct duk_number_list_entry duk_number_list_entry;
@@ -190,6 +192,14 @@ typedef void (*duk_debug_write_flush_function) (void 
*udata);
 typedef duk_idx_t (*duk_debug_request_function) (duk_context *ctx, void 
*udata, duk_idx_t nvalues);
 typedef void (*duk_debug_detached_function) (void *udata);
 
+struct duk_thread_state {
+       /* XXX: Enough space to hold internal suspend/resume structure.
+        * This is rather awkward and to be fixed when the internal
+        * structure is visible for the public API header.
+        */
+       char data[128];
+};
+
 struct duk_memory_functions {
        duk_alloc_function alloc_func;
        duk_realloc_function realloc_func;
@@ -218,15 +228,15 @@ struct duk_number_list_entry {
  * have 99 for patch level (e.g. 0.10.99 would be a development version
  * after 0.10.0 but before the next official release).
  */
-#define DUK_VERSION                       10501L
+#define DUK_VERSION                       10600L
 
 /* Git commit, describe, and branch for Duktape build.  Useful for
  * non-official snapshot builds so that application code can easily log
  * which Duktape snapshot was used.  Not available in the Ecmascript
  * environment.
  */
-#define DUK_GIT_COMMIT                    
"2cc76e9ff1f64869e1146ad7317d8cbe33bbd27e"
-#define DUK_GIT_DESCRIBE                  "v1.5.1"
+#define DUK_GIT_COMMIT                    
"17e3d86cf8b4788bd0d37658f833ab440ce43a1c"
+#define DUK_GIT_DESCRIBE                  "v1.6.0"
 #define DUK_GIT_BRANCH                    "HEAD"
 
 /* Duktape debug protocol version used by this build. */
@@ -397,6 +407,9 @@ duk_context *duk_create_heap(duk_alloc_function alloc_func,
                              duk_fatal_function fatal_handler);
 DUK_EXTERNAL_DECL void duk_destroy_heap(duk_context *ctx);
 
+DUK_EXTERNAL_DECL void duk_suspend(duk_context *ctx, duk_thread_state *state);
+DUK_EXTERNAL_DECL void duk_resume(duk_context *ctx, const duk_thread_state 
*state);
+
 #define duk_create_heap_default() \
        duk_create_heap(NULL, NULL, NULL, NULL, NULL)
 


-- 
NetSurf Browser

_______________________________________________
netsurf-commits mailing list
[email protected]
http://listmaster.pepperfish.net/cgi-bin/mailman/listinfo/netsurf-commits-netsurf-browser.org

Reply via email to