dash icon indicating copy to clipboard operation
dash copied to clipboard

Marking roots on the native stack

Open y21 opened this issue 1 year ago • 1 comments

A major problem with the current GC is that the vm has no way of telling that a variable in a native function is holding a reference to a JavaScript object. If a GC cycle happens to trigger (e.g. due to calling a JS function), the object referenced by the variable is (mistakenly) deallocated.

Fixing this likely requires major changes wrt how values are represented all over the vm and how native code works with these. One possible way to fix this is to have a separate vector of "external references" (i.e. objects referenced by bindings on the native stack). The current vm actually already has something similar, but it requires manually adding values to it. It would be nice if we could make this sort of pattern a compile error (pseudocode):

fn native_function() {
  let o = create_object();
  js_function(); // calls some JavaScript function, GC triggers in here, `o` is not marked and gets deallocated
  print(o); // using deallocated object
}

We could perhaps make some kind of macro that also adds the assigned value to the external refs vector, used like so:

fn native_function() {
  letroot!(o = create_object()); // allocate object AND add to vector of external refs
  js_function(); // GC triggers, but `o` is in external refs vector and is not deallocated
  print(o); // ok
}

y21 avatar Apr 29 '23 03:04 y21

Now that we have an Unrooted API, one thing we could explore is using custom lints that check for how these are used. In particular, one issue that remains is that one can carry an Unrooted object, call into JavaScript (which causes the unrooted value to get GC'd), then root the value, but at that point it is too late. We ideally would want to make this a compile error, so that you have to root all your values before doing anything with the Vm if you want to be able to carry them across JS boundaries, but I don't think it's possible to express that with the type system or lifetimes. The easier option for now would be to simply have some kind of deny lint that ensures that you must root Unrooted things as early as possible

y21 avatar Jul 31 '23 20:07 y21