Mjs – An interpreter for ECMAScript 1st edition written in C++17
github.com
github.com
I suspect the hardest part is getting garbage collection correct around lexical scope. The browsers struggled with this for years.
Multiple browsers had bugs with garbage collection of a basic part of JavaScript for years? What was the problem? I wonder what made them specific to lexical scopes, because a lexical scope is either just more entries on the stack, which is scanned for local variables in the same way, or objects on the heap, which is scanned for fields in the same way.
There are still a few cases to worry about. For example, during function activation: if you allocate anything between the time you switched to a new lexical scope, but before saving the old one somewhere in the root set you are in trouble. This is easy to do, because you want to just save it in a local C variable until you have allocated the thing where you are ultimately going to save it.
https://github.com/jhallen/ivy-lang
In general gc bugs (like all allocation related bugs) are a nightmare because the effect is always delayed from the bug.
All pointers are tracked automatically (except for specially optimized classes), so that part isn't so bad, but it's still (too) easy to accidentally use a stale pointer value.
At the moment I can safely collect after every statement, but there is still work to be done before I can claim to support collecting after every expression (like I'd ideally be able to - so I can collect when I run out of heap).
function f(){
let a = {};
return function(){};
}
In all (IIRC) major implementations, the object allocated for `a` will be held on to after the function returns because it exists in the lexical scope of the function returned (even though it isn't—and never can be—reached, as static analysis can prove).Basically all the other bugs I'm aware of are cases of C++ code not treating things as roots when necessary, mostly in DOM code.
If no enclosing scope references or maybe references it, you can treat it differently to those that are referenced. You don't even need to do actual escape analysis to get 99% of the cases.
In truth there are myriad ways to very casually and accidentally create pools of references that can't be garbage collected. I'll leave finding these as an exercise for the reader to google.
here's an old article by Douglas Crockford about a particular memory leak in IE's garbage collector that persisted for a very long time:
It isn't, per-se, a bug in the JS VM, given the JS VM's GC is merely acting on what the external code tells it (especially given IE used a separate heap for DOM objects).
//global variable
let closure;
{ // block
let x = function a (b) {
let c = b + 5;
return function (d) {
return d + c;
}
}
closure = a(5);
}
The variable named closure is in the global scope. The functions are long destroyed after the block closes. When does variable c get garbage collected? It is still needed by the global closure variable. All of that mess should be garbage collected when closure is garbage collected, but it can continue to live on as a different variable when used in an object or array.Arguably, they still do.
function f() {
var some = [];
while(some.length < 1e6) {
some.push(some.length);
}
function g() { some; }
return function() {};
}
var a = [];
var interval = setInterval(function() {
var len = a.push(f());
if(len >= 500) {
clearInterval(interval);
}
}, 10);
uses a very large amount of memory in all browsers. function f() {
var some = [];
while(some.length < 1e6) {
some.push(some.length);
}
function g() { some; }
}
var a = [];
var interval = setInterval(function() {
var len = a.push(f());
if(len >= 500) {
clearInterval(interval);
}
}, 10);
uses very little.https://stackoverflow.com/questions/19798803/how-javascript-...
https://blog.meteor.com/an-interesting-kind-of-javascript-me...
Yesterday I finished an article (https://mras0.github.io/mjs/doc/gc/initial.html) on how I implemented the GC and some of the challenges, if you're curious.
My favorite is that the language spec was written as quite regular pseudo-code... so at least as of some years ago, and after some manual massage to clean up irregularities, one could automagically transliterate the text of the spec into code. And if the target language had label gotos (because the spec was full of 'then go to step N'), and permited arbitrary identifiers, then the code could read just like the spec. Making code-review proof reading much easier.
That's one of my canaries for detecting when a non-crippling programming language finally exists - someone generates a not-necessarily performant but fully spec-compliant js implementation with some strikingly tiny effort. A few days.
But I've tried to keep the code reasonably small and (hopefully) readable - which is why it's currently targeting ES1997 - rather than having lots of features, so it's probably more understandable than V8.
The one I was talking about is https://github.com/cesanta/mjs
... yea, two JS interpreters for C++ with the same name. How could that be confusing?