'Are TemplateObject arrays for tagged template literals weakly referenced by their realm?
while (c) {
tag`str0 ${e} str1`
}
The JavaScript runtime creates a frozen array like Object.freeze(['str0 ', ' str1'])
but with an additional .raw
property.
Is it okay to use that object as a key in a WeakMap
to avoid having to redo work based on the array each time through the loop?
const memoTable = new WeakMap
function tag(templateStrings, ...values) {
let cached = memoTable.get(templateStrings)
if (!cached) {
// Compute cached and put it in the table for next time.
}
// Do something with cached and values
}
Section 12.2.9.3 Runtime Semantics: GetTemplateObject ( templateLiteral ) describes how this value is cached:
- Let realm be the current Realm Record.
- Let templateRegistry be realm.[[TemplateMap]].
so it should be the same from use to use of tag
in the loop above which would be a nice property for a key to have.
It seems to me that the [[TemplateMap]] would have to weakly reference the template object array because otherwise
for (let i = 0; i < 1e6; ++i) {
eval('(() => {})`' + i + '`');
}
would leak memory.
I don't see anything in the specification but is it the case for widely used JavaScript engines that WeakMap entries for tagged string template uses not in re-enterable scopes will eventually be collected?
I ask because I've implemented something based on this assumption but haven't figured out how to test it.
Solution 1:[1]
Is it okay to use that object as a key in a WeakMap to avoid having to redo work based on the array each time through the loop?
Yep, that's exactly what you're meant to do and it's one of the great features of template literals.
It seems to me that the [[TemplateMap]] would have to weakly reference the template object array because otherwise
for (let i = 0; i < 1e6; ++i) { eval('(() => {})`' + i + '`'); }
would leak memory.
It does leak in fact, since [[TemplateMap]]
is not weak. :(
This is an open point of discussion on the current spec. The discussion as of this writing is whether the spec should be changed to have template literals be be per-source-text-position instead of having the [[TemplateMap]]
be global state. e.g. as of this writing:
var id = v => v;
id`tpl` === id`tpl` // true
which is a bit weird.
Is it acceptable to break that such that two separate templates are created? If so, then there's at least the possibility that your eval
example could be allowed to collect the template.
You can see some discussion here, https://github.com/tc39/ecma262/issues/840, where at least tentatively this could be fixed.
EDIT:
The spec did indeed change, so templates are now per-source-location instead of per-realm, so two templates with the same content and different locations will be two different objects. This means that engines can potentially garbage-collect template literal object if their source file is no longer reachable.
Solution 2:[2]
It seems to me that the [[TemplateMap]] would have to weakly reference the template object array
I don't know what implementations actually do here, but the spec does indeed describe the realms [[TemplateMap]] as never being emptied. Evaling many different template tags in a loop would indeed heavily leak memory, so don't do that.
Is it okay to use that object as a key in a WeakMap to avoid having to redo work?
Yes, that's perfectly fine. If the global [[TemplateMap]] does leak memory, this will intensify the problem, but when it doesn't then by using a weak map you are not causing a problem.
Sources
This article follows the attribution requirements of Stack Overflow and is licensed under CC BY-SA 3.0.
Source: Stack Overflow
Solution | Source |
---|---|
Solution 1 | |
Solution 2 | Bergi |