]>
Commit | Line | Data |
---|---|---|
7c673cae FG |
1 | /* |
2 | * Run an duk_hobject finalizer. Used for both reference counting | |
3 | * and mark-and-sweep algorithms. Must never throw an error. | |
4 | * | |
5 | * There is no return value. Any return value or error thrown by | |
6 | * the finalizer is ignored (although errors are debug logged). | |
7 | * | |
8 | * Notes: | |
9 | * | |
10 | * - The thread used for calling the finalizer is the same as the | |
11 | * 'thr' argument. This may need to change later. | |
12 | * | |
13 | * - The finalizer thread 'top' assertions are there because it is | |
14 | * critical that strict stack policy is observed (i.e. no cruft | |
15 | * left on the finalizer stack). | |
16 | */ | |
17 | ||
18 | #include "duk_internal.h" | |
19 | ||
20 | DUK_LOCAL duk_ret_t duk__finalize_helper(duk_context *ctx) { | |
11fdf7f2 TL |
21 | duk_hthread *thr; |
22 | ||
7c673cae | 23 | DUK_ASSERT(ctx != NULL); |
11fdf7f2 | 24 | thr = (duk_hthread *) ctx; |
7c673cae FG |
25 | |
26 | DUK_DDD(DUK_DDDPRINT("protected finalization helper running")); | |
27 | ||
28 | /* [... obj] */ | |
29 | ||
30 | /* XXX: Finalizer lookup should traverse the prototype chain (to allow | |
31 | * inherited finalizers) but should not invoke accessors or proxy object | |
32 | * behavior. At the moment this lookup will invoke proxy behavior, so | |
33 | * caller must ensure that this function is not called if the target is | |
34 | * a Proxy. | |
35 | */ | |
36 | ||
37 | duk_get_prop_stridx(ctx, -1, DUK_STRIDX_INT_FINALIZER); /* -> [... obj finalizer] */ | |
38 | if (!duk_is_callable(ctx, -1)) { | |
39 | DUK_DDD(DUK_DDDPRINT("-> no finalizer or finalizer not callable")); | |
40 | return 0; | |
41 | } | |
11fdf7f2 TL |
42 | duk_dup(ctx, -2); |
43 | duk_push_boolean(ctx, DUK_HEAP_HAS_FINALIZER_NORESCUE(thr->heap)); | |
7c673cae | 44 | DUK_DDD(DUK_DDDPRINT("-> finalizer found, calling finalizer")); |
11fdf7f2 | 45 | duk_call(ctx, 2); /* [ ... obj finalizer obj heapDestruct ] -> [ ... obj retval ] */ |
7c673cae FG |
46 | DUK_DDD(DUK_DDDPRINT("finalizer finished successfully")); |
47 | return 0; | |
48 | ||
49 | /* Note: we rely on duk_safe_call() to fix up the stack for the caller, | |
50 | * so we don't need to pop stuff here. There is no return value; | |
51 | * caller determines rescued status based on object refcount. | |
52 | */ | |
53 | } | |
54 | ||
55 | DUK_INTERNAL void duk_hobject_run_finalizer(duk_hthread *thr, duk_hobject *obj) { | |
56 | duk_context *ctx = (duk_context *) thr; | |
57 | duk_ret_t rc; | |
58 | #ifdef DUK_USE_ASSERTIONS | |
59 | duk_idx_t entry_top; | |
60 | #endif | |
61 | ||
62 | DUK_DDD(DUK_DDDPRINT("running object finalizer for object: %p", (void *) obj)); | |
63 | ||
64 | DUK_ASSERT(thr != NULL); | |
65 | DUK_ASSERT(ctx != NULL); | |
66 | DUK_ASSERT(obj != NULL); | |
67 | DUK_ASSERT_VALSTACK_SPACE(thr, 1); | |
68 | ||
69 | #ifdef DUK_USE_ASSERTIONS | |
70 | entry_top = duk_get_top(ctx); | |
71 | #endif | |
72 | /* | |
73 | * Get and call the finalizer. All of this must be wrapped | |
74 | * in a protected call, because even getting the finalizer | |
75 | * may trigger an error (getter may throw one, for instance). | |
76 | */ | |
77 | ||
11fdf7f2 TL |
78 | DUK_ASSERT(!DUK_HEAPHDR_HAS_READONLY((duk_heaphdr *) obj)); |
79 | if (DUK_HEAPHDR_HAS_FINALIZED((duk_heaphdr *) obj)) { | |
80 | DUK_D(DUK_DPRINT("object already finalized, avoid running finalizer twice: %!O", obj)); | |
81 | return; | |
82 | } | |
83 | DUK_HEAPHDR_SET_FINALIZED((duk_heaphdr *) obj); /* ensure never re-entered until rescue cycle complete */ | |
84 | if (DUK_HOBJECT_HAS_EXOTIC_PROXYOBJ(obj)) { | |
85 | /* This shouldn't happen; call sites should avoid looking up | |
86 | * _Finalizer "through" a Proxy, but ignore if we come here | |
87 | * with a Proxy to avoid finalizer re-entry. | |
88 | */ | |
89 | DUK_D(DUK_DPRINT("object is a proxy, skip finalizer call")); | |
90 | return; | |
91 | } | |
92 | ||
7c673cae FG |
93 | /* XXX: use a NULL error handler for the finalizer call? */ |
94 | ||
95 | DUK_DDD(DUK_DDDPRINT("-> finalizer found, calling wrapped finalize helper")); | |
96 | duk_push_hobject(ctx, obj); /* this also increases refcount by one */ | |
97 | rc = duk_safe_call(ctx, duk__finalize_helper, 0 /*nargs*/, 1 /*nrets*/); /* -> [... obj retval/error] */ | |
98 | DUK_ASSERT_TOP(ctx, entry_top + 2); /* duk_safe_call discipline */ | |
99 | ||
100 | if (rc != DUK_EXEC_SUCCESS) { | |
101 | /* Note: we ask for one return value from duk_safe_call to get this | |
102 | * error debugging here. | |
103 | */ | |
104 | DUK_D(DUK_DPRINT("wrapped finalizer call failed for object %p (ignored); error: %!T", | |
105 | (void *) obj, (duk_tval *) duk_get_tval(ctx, -1))); | |
106 | } | |
107 | duk_pop_2(ctx); /* -> [...] */ | |
108 | ||
109 | DUK_ASSERT_TOP(ctx, entry_top); | |
110 | } |