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.
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 at the moment is whether the spec should be changed to, instead of having the [[TemplateMap]]
be global state, to instead have it be per-source-text-position. e.g. right now
var id = v => v;
id`tpl` === id`tpl` // true
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.
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.