4
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:

  1. Let realm be the current Realm Record.
  2. 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.

Mike Samuel
  • 109,453
  • 27
  • 204
  • 234
  • But how would you actually get something from the Map? Same templateStrings does not mean that the array it is stored in has the same reference. Or am i missing something? – Jonas Wilms Jan 30 '18 at 18:41
  • @JonasW. My understanding of step 4 from the spec (If e.[[Strings]] and rawStrings contain the same values in the same order, then Return e.[[Array]].) combined with step 14 means that it is the same array. – Mike Samuel Jan 30 '18 at 18:42
  • Interesting. Seems like you are right. However, it doesnt feel very javascriptish – Jonas Wilms Jan 30 '18 at 18:48

2 Answers2

6

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.

loganfsmyth
  • 135,356
  • 25
  • 296
  • 231
  • 2
    +1 for digging up that issue! – Bergi Jan 30 '18 at 18:57
  • Ah, I see. IIRC, when I drafted the strawman that I wrote it in terms of a declaration hoisted to the top of the compilation unit, but a per call-site array would complicate inlining and loop unrolling by tools like ClosureCompiler. – Mike Samuel Jan 30 '18 at 19:25
  • It would complicate that, true, but you could always wrap it in a function and leave it to the JIT to inline the function. I'm definitely not a fan of the hoisting because of the leak concerns. – loganfsmyth Jan 30 '18 at 19:47
1

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.

Bergi
  • 513,640
  • 108
  • 821
  • 1,164