[I].. discovered the possibility to create a really bad "memory leak"
This is technically not a memory leak. A leak would be to allocate memory and loose the pointer to it so it could not be freed. In this case the pointer is tracked but the memory not released.
The problem occurs when you happen to continually save to the state stack without restoring.
That is to be expected. Allocating memory without freeing it will accumulate allocated memory blocks.
How can you clear out or delete the state stack for a canvas context?
The only way is to either restore all saved states, or to reset the context by setting some size to the canvas element (ie. canvas.width = canvas.width
).
It's also safe to call restore()
more times than save()
(in which case it just returns without doing anything) so you could in theory run it through a loop of n
number of iterations. This latter would be more in the bad practice category though.
But with that being said: if there is a mismatch in numbers of save and restore when it's suppose to be equal, usually indicates a problem somewhere else in the code. Working around the problem with a reset or running multiple restores in post probably will only contribute to cover up the actual problem.
Here's an example on how to track the count of save/restore calls -
// NOTE: this code needs to run before a canvas context is created
CanvasRenderingContext2D.prototype.__save = CanvasRenderingContext2D.prototype.save;
CanvasRenderingContext2D.prototype.__restore = CanvasRenderingContext2D.prototype.restore;
// Our patch vectors
CanvasRenderingContext2D.prototype.__tracker = 0;
CanvasRenderingContext2D.prototype.save = function() {
this.__tracker++;
console.log("Track save:", this.__tracker);
this.__save()
}
CanvasRenderingContext2D.prototype.restore = function() {
this.__tracker--;
console.log("Track restore:", this.__tracker);
this.__restore()
}
// custom method to dump status
CanvasRenderingContext2D.prototype.trackstat = function() {
if (this.__tracker)
console.warn("Track stat:", this.__tracker);
else
console.log("Track stat: OK");
}
var ctx = document.createElement("canvas").getContext("2d");
ctx.save(); // do a couple of save()s
ctx.save();
ctx.restore(); // single restore()
ctx.trackstat(); // should report mismatch of 1
ctx.restore(); // last restore()
ctx.trackstat(); // should report OK