[quickjs-devel] Re: Defining a finalizalizer for an object

  • From: Fabrice Bellard <fabrice@xxxxxxxxxxx>
  • To: quickjs-devel@xxxxxxxxxxxxx
  • Date: Fri, 10 Jan 2020 10:41:05 +0100

Hi,

Calling JS code from a finalizer (e.g. with JS_Call) is not supported and will only lead to problems. Finalizers should only be used to free system resources and JS objects.

Best regards,

Fabrice.

On 1/10/20 8:08 AM, Eduard Suica (Redacted sender rokempes for DMARC) wrote:

Just tried this:

static void js_finalizer_finalizer(JSRuntime *rt, JSValue val) {
    JSValueConst *argv2 = (JSValueConst *)JS_GetOpaque(val, js_finalizer_class_id);
     if (argv2) {

// ==== > it crashes on the next line. It seems that argv2[0] and argv[1] get collected before the finalizer is called

        JS_FreeValueCheckException(js_ctx, JS_Call(js_ctx, argv2[1], argv2[0], 1, argv2));
         JS_FreeValueRT(rt, argv2[0]);
         JS_FreeValueRT(rt, argv2[1]);
         js_free(js_ctx, argv2);
     }
}

static JSValue js_finalizer_ctor(JSContext *ctx, JSValueConst new_target, int argc, JSValueConst *argv) {
     if (argc != 2) {
         JS_Error(ctx, "2 parameters expected");
     }
     JSValue proto = JS_GetPropertyStr(ctx, new_target, "prototype");
    JSValue obj = JS_NewObjectProtoClass(ctx, proto, js_finalizer_class_id);
     if (argc == 2) {
        JSValueConst *argv2 = (JSValueConst *)js_malloc(ctx, sizeof(JSValueConst) * 2);
         argv2[0] = JS_DupValueRT(JS_GetRuntime(ctx), argv[0]);
         argv2[1] = JS_DupValueRT(JS_GetRuntime(ctx), argv[1]);
         JS_SetOpaque(obj, argv2);
     }
     JS_FreeValue(ctx, proto);
     return obj;
}

static void js_finalizer_gc_mark(JSRuntime *rt, JSValueConst val, JS_MarkFunc *mark_func) {
    JSValueConst *argv2 = (JSValueConst *)JS_GetOpaque(val, js_finalizer_class_id);
     if (argv2) {
         JS_MarkValue(rt, argv2[0], mark_func);
         JS_MarkValue(rt, argv2[1], mark_func);
     }
}

static JSClassDef js_finalizer_class = {
     "__finalizerContainer",
     .finalizer = js_finalizer_finalizer,
     .gc_mark = js_finalizer_gc_mark,
};

The javascript code for testing (crashes):

var obj = { };
var a = new __finalizerContainer(obj, function(self) {
      console.log("finalize");
});

obj = undefined;
gc();


This code works:

function foo(self) {
      console.log("finalize");
}

var obj = { };
var a = new __finalizerContainer(obj, foo);

obj = undefined;
gc();


GE.

On Thursday, January 9, 2020, 9:45:40 PM GMT+2, Koushik Dutta <koush@xxxxxxxxxxxxxxxx> wrote:


It's not possible to do it for an arbitrary object, as far as I know. But my workaround is to create a custom class that is a "finalizer object", and then attach as a (hidden) property to any arbitrary object. I can then use the finalizer object to observe when the arbitrary object gets collected. The finalizer object gets collected at the same time as the arbitrary object; just ensure that the finalizer object is only referenced by that object.

Koush

On Thu, Jan 9, 2020 at 11:26 AM Eduard Suica <dmarc-noreply@xxxxxxxxxxxxx <mailto:dmarc-noreply@xxxxxxxxxxxxx>> wrote:

    Is possible to define a finalizer/destructor for an object?
    Something like Duktape's fin function:

    .fin(object, function(self) { ... } )

     From the documentation: "When defining a new JS class, it is
    possible to declare a finalizer which is called when the object is
    destroyed. ".

    Is this possible for an arbitrary object? If yes, how?

    Great project,
    GE.


Other related posts: