]>
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) { | |
21 | DUK_ASSERT(ctx != NULL); | |
22 | ||
23 | DUK_DDD(DUK_DDDPRINT("protected finalization helper running")); | |
24 | ||
25 | /* [... obj] */ | |
26 | ||
27 | /* XXX: Finalizer lookup should traverse the prototype chain (to allow | |
28 | * inherited finalizers) but should not invoke accessors or proxy object | |
29 | * behavior. At the moment this lookup will invoke proxy behavior, so | |
30 | * caller must ensure that this function is not called if the target is | |
31 | * a Proxy. | |
32 | */ | |
33 | ||
34 | duk_get_prop_stridx(ctx, -1, DUK_STRIDX_INT_FINALIZER); /* -> [... obj finalizer] */ | |
35 | if (!duk_is_callable(ctx, -1)) { | |
36 | DUK_DDD(DUK_DDDPRINT("-> no finalizer or finalizer not callable")); | |
37 | return 0; | |
38 | } | |
39 | duk_dup(ctx, -2); /* -> [... obj finalizer obj] */ | |
40 | DUK_DDD(DUK_DDDPRINT("-> finalizer found, calling finalizer")); | |
41 | duk_call(ctx, 1); /* -> [... obj retval] */ | |
42 | DUK_DDD(DUK_DDDPRINT("finalizer finished successfully")); | |
43 | return 0; | |
44 | ||
45 | /* Note: we rely on duk_safe_call() to fix up the stack for the caller, | |
46 | * so we don't need to pop stuff here. There is no return value; | |
47 | * caller determines rescued status based on object refcount. | |
48 | */ | |
49 | } | |
50 | ||
51 | DUK_INTERNAL void duk_hobject_run_finalizer(duk_hthread *thr, duk_hobject *obj) { | |
52 | duk_context *ctx = (duk_context *) thr; | |
53 | duk_ret_t rc; | |
54 | #ifdef DUK_USE_ASSERTIONS | |
55 | duk_idx_t entry_top; | |
56 | #endif | |
57 | ||
58 | DUK_DDD(DUK_DDDPRINT("running object finalizer for object: %p", (void *) obj)); | |
59 | ||
60 | DUK_ASSERT(thr != NULL); | |
61 | DUK_ASSERT(ctx != NULL); | |
62 | DUK_ASSERT(obj != NULL); | |
63 | DUK_ASSERT_VALSTACK_SPACE(thr, 1); | |
64 | ||
65 | #ifdef DUK_USE_ASSERTIONS | |
66 | entry_top = duk_get_top(ctx); | |
67 | #endif | |
68 | /* | |
69 | * Get and call the finalizer. All of this must be wrapped | |
70 | * in a protected call, because even getting the finalizer | |
71 | * may trigger an error (getter may throw one, for instance). | |
72 | */ | |
73 | ||
74 | /* XXX: use a NULL error handler for the finalizer call? */ | |
75 | ||
76 | DUK_DDD(DUK_DDDPRINT("-> finalizer found, calling wrapped finalize helper")); | |
77 | duk_push_hobject(ctx, obj); /* this also increases refcount by one */ | |
78 | rc = duk_safe_call(ctx, duk__finalize_helper, 0 /*nargs*/, 1 /*nrets*/); /* -> [... obj retval/error] */ | |
79 | DUK_ASSERT_TOP(ctx, entry_top + 2); /* duk_safe_call discipline */ | |
80 | ||
81 | if (rc != DUK_EXEC_SUCCESS) { | |
82 | /* Note: we ask for one return value from duk_safe_call to get this | |
83 | * error debugging here. | |
84 | */ | |
85 | DUK_D(DUK_DPRINT("wrapped finalizer call failed for object %p (ignored); error: %!T", | |
86 | (void *) obj, (duk_tval *) duk_get_tval(ctx, -1))); | |
87 | } | |
88 | duk_pop_2(ctx); /* -> [...] */ | |
89 | ||
90 | DUK_ASSERT_TOP(ctx, entry_top); | |
91 | } |