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

  • From: "Eduard Suica" <dmarc-noreply@xxxxxxxxxxxxx> (Redacted sender "rokempes" for DMARC)
  • To: <quickjs-devel@xxxxxxxxxxxxx>
  • Date: Fri, 10 Jan 2020 12:47:01 +0000 (UTC)

 Thank you. Great project, thank you for sharing it.
GE.

    On Friday, January 10, 2020, 11:41:24 AM GMT+2, Fabrice Bellard 
<fabrice@xxxxxxxxxxx> wrote:  
 
 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: