DUK_INTERNAL void duk_hobject_run_finalizer(duk_hthread *thr, duk_hobject *obj) { duk_context *ctx = (duk_context *) thr; duk_ret_t rc; #if defined(DUK_USE_ASSERTIONS) duk_idx_t entry_top; #endif DUK_DDD(DUK_DDDPRINT("running object finalizer for object: %p", (void *) obj)); DUK_ASSERT(thr != NULL); DUK_ASSERT(ctx != NULL); DUK_ASSERT(obj != NULL); DUK_ASSERT_VALSTACK_SPACE(thr, 1); #if defined(DUK_USE_ASSERTIONS) entry_top = duk_get_top(ctx); #endif /* * Get and call the finalizer. All of this must be wrapped * in a protected call, because even getting the finalizer * may trigger an error (getter may throw one, for instance). */ DUK_ASSERT(!DUK_HEAPHDR_HAS_READONLY((duk_heaphdr *) obj)); if (DUK_HEAPHDR_HAS_FINALIZED((duk_heaphdr *) obj)) { DUK_D(DUK_DPRINT("object already finalized, avoid running finalizer twice: %!O", obj)); return; } DUK_HEAPHDR_SET_FINALIZED((duk_heaphdr *) obj); /* ensure never re-entered until rescue cycle complete */ if (DUK_HOBJECT_HAS_EXOTIC_PROXYOBJ(obj)) { /* This shouldn't happen; call sites should avoid looking up * _Finalizer "through" a Proxy, but ignore if we come here * with a Proxy to avoid finalizer re-entry. */ DUK_D(DUK_DPRINT("object is a proxy, skip finalizer call")); return; } /* XXX: use a NULL error handler for the finalizer call? */ DUK_DDD(DUK_DDDPRINT("-> finalizer found, calling wrapped finalize helper")); duk_push_hobject(ctx, obj); /* this also increases refcount by one */ rc = duk_safe_call(ctx, duk__finalize_helper, NULL /*udata*/, 0 /*nargs*/, 1 /*nrets*/); /* -> [... obj retval/error] */ DUK_ASSERT_TOP(ctx, entry_top + 2); /* duk_safe_call discipline */ if (rc != DUK_EXEC_SUCCESS) { /* Note: we ask for one return value from duk_safe_call to get this * error debugging here. */ DUK_D(DUK_DPRINT("wrapped finalizer call failed for object %p (ignored); error: %!T", (void *) obj, (duk_tval *) duk_get_tval(ctx, -1))); } duk_pop_2(ctx); /* -> [...] */ DUK_ASSERT_TOP(ctx, entry_top); }
DUK_LOCAL void duk__err_augment_user(duk_hthread *thr, duk_small_uint_t stridx_cb) { duk_context *ctx = (duk_context *) thr; duk_tval *tv_hnd; duk_small_uint_t call_flags; duk_int_t rc; DUK_ASSERT(thr != NULL); DUK_ASSERT(thr->heap != NULL); DUK_ASSERT_DISABLE(stridx_cb >= 0); /* unsigned */ DUK_ASSERT(stridx_cb < DUK_HEAP_NUM_STRINGS); if (DUK_HEAP_HAS_ERRHANDLER_RUNNING(thr->heap)) { DUK_DD(DUK_DDPRINT("recursive call to error handler, ignore")); return; } /* * Check whether or not we have an error handler. * * We must be careful of not triggering an error when looking up the * property. For instance, if the property is a getter, we don't want * to call it, only plain values are allowed. The value, if it exists, * is not checked. If the value is not a function, a TypeError happens * when it is called and that error replaces the original one. */ DUK_ASSERT_VALSTACK_SPACE(thr, 4); /* 3 entries actually needed below */ /* [ ... errval ] */ if (thr->builtins[DUK_BIDX_DUKTAPE] == NULL) { /* When creating built-ins, some of the built-ins may not be set * and we want to tolerate that when throwing errors. */ DUK_DD(DUK_DDPRINT("error occurred when DUK_BIDX_DUKTAPE is NULL, ignoring")); return; } tv_hnd = duk_hobject_find_existing_entry_tval_ptr(thr->heap, thr->builtins[DUK_BIDX_DUKTAPE], DUK_HTHREAD_GET_STRING(thr, stridx_cb)); if (tv_hnd == NULL) { DUK_DD(DUK_DDPRINT("error handler does not exist or is not a plain value: %!T", (duk_tval *) tv_hnd)); return; } DUK_DDD(DUK_DDDPRINT("error handler dump (callability not checked): %!T", (duk_tval *) tv_hnd)); duk_push_tval(ctx, tv_hnd); /* [ ... errval errhandler ] */ duk_insert(ctx, -2); /* -> [ ... errhandler errval ] */ duk_push_undefined(ctx); duk_insert(ctx, -2); /* -> [ ... errhandler undefined(= this) errval ] */ /* [ ... errhandler undefined errval ] */ /* * DUK_CALL_FLAG_IGNORE_RECLIMIT causes duk_handle_call() to ignore C * recursion depth limit (and won't increase it either). This is * dangerous, but useful because it allows the error handler to run * even if the original error is caused by C recursion depth limit. * * The heap level DUK_HEAP_FLAG_ERRHANDLER_RUNNING is set for the * duration of the error handler and cleared afterwards. This flag * prevents the error handler from running recursively. The flag is * heap level so that the flag properly controls even coroutines * launched by an error handler. Since the flag is heap level, it is * critical to restore it correctly. * * We ignore errors now: a success return and an error value both * replace the original error value. (This would be easy to change.) */ DUK_ASSERT(!DUK_HEAP_HAS_ERRHANDLER_RUNNING(thr->heap)); /* since no recursive error handler calls */ DUK_HEAP_SET_ERRHANDLER_RUNNING(thr->heap); call_flags = DUK_CALL_FLAG_IGNORE_RECLIMIT; /* ignore reclimit, not constructor */ rc = duk_handle_call_protected(thr, 1, /* num args */ call_flags); /* call_flags */ DUK_UNREF(rc); /* no need to check now: both success and error are OK */ DUK_ASSERT(DUK_HEAP_HAS_ERRHANDLER_RUNNING(thr->heap)); DUK_HEAP_CLEAR_ERRHANDLER_RUNNING(thr->heap); /* [ ... errval ] */ }