I’m looking for help understanding why some code I...
# javascript
t
I’m looking for help understanding why some code I’ve written – which relies on weak references to attest garbage has been collected – works as expected when using the Legacy backend, but fails when using the IR backend. More details inside the thread.
Context I’m working on an open-source multi-platform library that aims to provide utilities to assert that code under test does not keep references to objects it no longer needs. Structure There are actually several (tiny) libraries involved: 1. com.tomuvak.testing-gc-core. This library provides a multi-platform way to (attempt and) trigger garbage collection. The main function works by calling the platform-given way of triggering garbage collection where the platform provides such a way (
System.gc()
on JVM,
kotlin.native.internal.GC.collect()
on Native), and by running a memory-intensive task in the hope that this will trigger garbage collection otherwise (= on JS). The specifics of the task used on JS (including the nature of the task itself as well as the argument values) have been determined in a longish process of trial and error, with a lot of tweaking, finally leading to a state where the functionality does seem to work (at least in the environments I tested it on), and also not take too much time. Note that I use
delay
in the process – it seems garbage collection isn’t triggered otherwise – which forces the function to be a
suspend
function, and code which uses it to run from a coroutine. 2. com.tomuvak.weak-reference. This library provides a unified multi-platform interface to weak references (which do exist for JVM, Native, and JS, but each with a different interface). Note that the tests do pass on all platforms, including on JS with the IR backend (see GitHub workflow). 3. com.tomuvak.testing-gc. This library uses the libraries above to provide higher-level constructs that help in verifying code under test does not keep references (the idea is that tests using it obtain weak references to objects that are fed to the code under test, and after the code is executed the tests verify that the weak references’ targets are reclaimable – that is that once garbage collection is triggered the weak references can no longer return their original targets). It is this library where the difference between the Legacy and the IR backends for JS is observable. 4. com.tomuvak.testing-coroutines. This library provides a multi-platform way to run tests that use
suspend
functions. The other libraries use this one to run their tests. (`kotlinx-coroutines-test`’s runTest doesn’t work because it skips `delay`s, and it seems the actual `delay`s are crucial for garbage collection to actually be observed.) The issue While all tests for
com.tomuvak.testing-gc
pass on JVM, Native and JS with the Legacy backend, some of them fail on JS with the IR backend. I’ve got no idea what this difference in behaviour stems from. Maybe the code that tries to trigger garbage collection in JS, which relies on a mechanism which has proven itself empirically on JS Legacy, changes in JS IR in a way which makes it not work anymore? Or maybe the way the tests generate their weak references somehow change in JS IR in a way that maintains a (hidden) strong reference somewhere? Or something else? What I need If anyone understands how these things work and can figure out what causes the tests to fail on JS IR (and, ideally, how to rewrite it so that it works on all platforms and backends) I’ll be extremely grateful!