GJS itself worked by creating a SpiderMonkey runtime and calling into that in order to bind GObject-introspected modules.
The solution was to use SpiderMonkey’s own debugging API to record what was going on during execution. This could then be dumped and written to a file that was LCOV compliant, from which reports could be generated.
The solution was to use the internal
AST.Parse mechanism available in
From there, the AST could be recursively traversed in order
to determine lines in which expressions could take place. The nature of
code execution could take place in a number of areas one might not expect
due to the presence of self-executing anonymous functions.
Effectively managing the code across this period of transition would not have been possible without a comprehensive series of automated tests. These tests exercised both the edge cases in terms of what lines needed to be covered and also the core coverage functionality.
Another key area was build performance. Running with the debugger turned on caused a slowdown, especially when files needed to be parsed. The coverage utilities thus include a caching system to ensure that files which have not changed are not re-parsed and information about their executable lines is directly re-used.