• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1// Copyright 2014 the V8 project authors. All rights reserved.
2// Use of this source code is governed by a BSD-style license that can be
3// found in the LICENSE file.
4
5var num = 5;
6var str = "str";
7function fn() { return "result"; }
8var obj = {
9  num: num,
10  str: str,
11  fn: function() { return "result"; }
12};
13
14(function testBasicExpressions() {
15  assertEquals("foo 5 bar", `foo ${num} bar`);
16  assertEquals("foo str bar", `foo ${str} bar`);
17  assertEquals("foo [object Object] bar", `foo ${obj} bar`);
18  assertEquals("foo result bar", `foo ${fn()} bar`);
19  assertEquals("foo 5 bar", `foo ${obj.num} bar`);
20  assertEquals("foo str bar", `foo ${obj.str} bar`);
21  assertEquals("foo result bar", `foo ${obj.fn()} bar`);
22})();
23
24(function testExpressionsContainingTemplates() {
25  assertEquals("foo bar 5", `foo ${`bar ${num}`}`);
26})();
27
28(function testMultilineTemplates() {
29  assertEquals("foo\n    bar\n    baz", `foo
30    bar
31    baz`);
32
33  assertEquals("foo\n  bar\n  baz", eval("`foo\r\n  bar\r  baz`"));
34})();
35
36(function testLineContinuation() {
37  assertEquals("\n", `\
38
39`);
40})();
41
42(function testTaggedTemplates() {
43  var calls = 0;
44  (function(s) {
45    calls++;
46  })`test`;
47  assertEquals(1, calls);
48
49  calls = 0;
50  // assert tag is invoked in right context
51  obj = {
52    fn: function() {
53      calls++;
54      assertEquals(obj, this);
55    }
56  };
57
58  obj.fn`test`;
59  assertEquals(1, calls);
60
61  calls = 0;
62  // Simple templates only have a callSiteObj
63  (function(s) {
64    calls++;
65    assertEquals(1, arguments.length);
66  })`test`;
67  assertEquals(1, calls);
68
69  // Templates containing expressions have the values of evaluated expressions
70  calls = 0;
71  (function(site, n, s, o, f, r) {
72    calls++;
73    assertEquals(6, arguments.length);
74    assertEquals("number", typeof n);
75    assertEquals("string", typeof s);
76    assertEquals("object", typeof o);
77    assertEquals("function", typeof f);
78    assertEquals("result", r);
79  })`${num}${str}${obj}${fn}${fn()}`;
80  assertEquals(1, calls);
81
82  // The TV and TRV of NoSubstitutionTemplate :: `` is the empty code unit
83  // sequence.
84  calls = 0;
85  (function(s) {
86    calls++;
87    assertEquals(1, s.length);
88    assertEquals(1, s.raw.length);
89    assertEquals("", s[0]);
90
91    // Failure: expected <""> found <"foo  barfoo  barfoo foo foo foo testtest">
92    assertEquals("", s.raw[0]);
93  })``;
94  assertEquals(1, calls);
95
96  // The TV and TRV of TemplateHead :: `${ is the empty code unit sequence.
97  calls = 0;
98  (function(s) {
99    calls++;
100    assertEquals(2, s.length);
101    assertEquals(2, s.raw.length);
102    assertEquals("", s[0]);
103    assertEquals("", s.raw[0]);
104  })`${1}`;
105  assertEquals(1, calls);
106
107  // The TV and TRV of TemplateMiddle :: }${ is the empty code unit sequence.
108  calls = 0;
109  (function(s) {
110    calls++;
111    assertEquals(3, s.length);
112    assertEquals(3, s.raw.length);
113    assertEquals("", s[1]);
114    assertEquals("", s.raw[1]);
115  })`${1}${2}`;
116  assertEquals(1, calls);
117
118  // The TV and TRV of TemplateTail :: }` is the empty code unit sequence.
119  calls = 0;
120  (function(s) {
121    calls++;
122    assertEquals(2, s.length);
123    assertEquals(2, s.raw.length);
124    assertEquals("", s[1]);
125    assertEquals("", s.raw[1]);
126  })`${1}`;
127  assertEquals(1, calls);
128
129  // The TV of NoSubstitutionTemplate :: ` TemplateCharacters ` is the TV of
130  // TemplateCharacters.
131  calls = 0;
132  (function(s) { calls++; assertEquals("foo", s[0]); })`foo`;
133  assertEquals(1, calls);
134
135  // The TV of TemplateHead :: ` TemplateCharacters ${ is the TV of
136  // TemplateCharacters.
137  calls = 0;
138  (function(s) { calls++; assertEquals("foo", s[0]); })`foo${1}`;
139  assertEquals(1, calls);
140
141  // The TV of TemplateMiddle :: } TemplateCharacters ${ is the TV of
142  // TemplateCharacters.
143  calls = 0;
144  (function(s) { calls++; assertEquals("foo", s[1]); })`${1}foo${2}`;
145  assertEquals(1, calls);
146
147  // The TV of TemplateTail :: } TemplateCharacters ` is the TV of
148  // TemplateCharacters.
149  calls = 0;
150  (function(s) { calls++; assertEquals("foo", s[1]); })`${1}foo`;
151  assertEquals(1, calls);
152
153  // The TV of TemplateCharacters :: TemplateCharacter is the TV of
154  // TemplateCharacter.
155  calls = 0;
156  (function(s) { calls++; assertEquals("f", s[0]); })`f`;
157  assertEquals(1, calls);
158
159  // The TV of TemplateCharacter :: $ is the code unit value 0x0024.
160  calls = 0;
161  (function(s) { calls++; assertEquals("$", s[0]); })`$`;
162  assertEquals(1, calls);
163
164  // The TV of TemplateCharacter :: \ EscapeSequence is the CV of
165  // EscapeSequence.
166  calls = 0;
167  (function(s) { calls++; assertEquals("안녕", s[0]); })`\uc548\uB155`;
168  (function(s) { calls++; assertEquals("\xff", s[0]); })`\xff`;
169  (function(s) { calls++; assertEquals("\n", s[0]); })`\n`;
170  assertEquals(3, calls);
171
172  // The TV of TemplateCharacter :: LineContinuation is the TV of
173  // LineContinuation. The TV of LineContinuation :: \ LineTerminatorSequence is
174  // the empty code unit sequence.
175  calls = 0;
176  (function(s) { calls++; assertEquals("", s[0]); })`\
177`;
178  assertEquals(1, calls);
179
180  // The TRV of NoSubstitutionTemplate :: ` TemplateCharacters ` is the TRV of
181  // TemplateCharacters.
182  calls = 0;
183  (function(s) { calls++; assertEquals("test", s.raw[0]); })`test`;
184  assertEquals(1, calls);
185
186  // The TRV of TemplateHead :: ` TemplateCharacters ${ is the TRV of
187  // TemplateCharacters.
188  calls = 0;
189  (function(s) { calls++; assertEquals("test", s.raw[0]); })`test${1}`;
190  assertEquals(1, calls);
191
192  // The TRV of TemplateMiddle :: } TemplateCharacters ${ is the TRV of
193  // TemplateCharacters.
194  calls = 0;
195  (function(s) { calls++; assertEquals("test", s.raw[1]); })`${1}test${2}`;
196  assertEquals(1, calls);
197
198  // The TRV of TemplateTail :: } TemplateCharacters ` is the TRV of
199  // TemplateCharacters.
200  calls = 0;
201  (function(s) { calls++; assertEquals("test", s.raw[1]); })`${1}test`;
202  assertEquals(1, calls);
203
204  // The TRV of TemplateCharacters :: TemplateCharacter is the TRV of
205  // TemplateCharacter.
206  calls = 0;
207  (function(s) { calls++; assertEquals("f", s.raw[0]); })`f`;
208  assertEquals(1, calls);
209
210  // The TRV of TemplateCharacter :: $ is the code unit value 0x0024.
211  calls = 0;
212  (function(s) { calls++; assertEquals("\u0024", s.raw[0]); })`$`;
213  assertEquals(1, calls);
214
215  // The TRV of EscapeSequence :: 0 is the code unit value 0x0030.
216  calls = 0;
217  (function(s) { calls++; assertEquals("\u005C\u0030", s.raw[0]); })`\0`;
218  assertEquals(1, calls);
219
220  // The TRV of TemplateCharacter :: \ EscapeSequence is the sequence consisting
221  // of the code unit value 0x005C followed by the code units of TRV of
222  // EscapeSequence.
223
224  //   The TRV of EscapeSequence :: HexEscapeSequence is the TRV of the
225  //   HexEscapeSequence.
226  calls = 0;
227  (function(s) { calls++; assertEquals("\u005Cxff", s.raw[0]); })`\xff`;
228  assertEquals(1, calls);
229
230  //   The TRV of EscapeSequence :: UnicodeEscapeSequence is the TRV of the
231  //   UnicodeEscapeSequence.
232  calls = 0;
233  (function(s) { calls++; assertEquals("\u005Cuc548", s.raw[0]); })`\uc548`;
234  assertEquals(1, calls);
235
236  //   The TRV of CharacterEscapeSequence :: SingleEscapeCharacter is the TRV of
237  //   the SingleEscapeCharacter.
238  calls = 0;
239  (function(s) { calls++; assertEquals("\u005C\u0027", s.raw[0]); })`\'`;
240  (function(s) { calls++; assertEquals("\u005C\u0022", s.raw[0]); })`\"`;
241  (function(s) { calls++; assertEquals("\u005C\u005C", s.raw[0]); })`\\`;
242  (function(s) { calls++; assertEquals("\u005Cb", s.raw[0]); })`\b`;
243  (function(s) { calls++; assertEquals("\u005Cf", s.raw[0]); })`\f`;
244  (function(s) { calls++; assertEquals("\u005Cn", s.raw[0]); })`\n`;
245  (function(s) { calls++; assertEquals("\u005Cr", s.raw[0]); })`\r`;
246  (function(s) { calls++; assertEquals("\u005Ct", s.raw[0]); })`\t`;
247  (function(s) { calls++; assertEquals("\u005Cv", s.raw[0]); })`\v`;
248  (function(s) { calls++; assertEquals("\u005C`", s.raw[0]); })`\``;
249  assertEquals(10, calls);
250
251  //   The TRV of CharacterEscapeSequence :: NonEscapeCharacter is the CV of the
252  //   NonEscapeCharacter.
253  calls = 0;
254  (function(s) { calls++; assertEquals("\u005Cz", s.raw[0]); })`\z`;
255  assertEquals(1, calls);
256
257  // The TRV of LineTerminatorSequence :: <LF> is the code unit value 0x000A.
258  // The TRV of LineTerminatorSequence :: <CR> is the code unit value 0x000A.
259  // The TRV of LineTerminatorSequence :: <CR><LF> is the sequence consisting of
260  // the code unit value 0x000A.
261  calls = 0;
262  function testRawLineNormalization(cs) {
263    calls++;
264    assertEquals(cs.raw[0], "\n\n\n");
265    assertEquals(cs.raw[1], "\n\n\n");
266  }
267  eval("testRawLineNormalization`\r\n\n\r${1}\r\n\n\r`");
268  assertEquals(1, calls);
269
270  // The TRV of LineContinuation :: \ LineTerminatorSequence is the sequence
271  // consisting of the code unit value 0x005C followed by the code units of TRV
272  // of LineTerminatorSequence.
273  calls = 0;
274  function testRawLineContinuation(cs) {
275    calls++;
276    assertEquals(cs.raw[0], "\u005C\n\u005C\n\u005C\n");
277    assertEquals(cs.raw[1], "\u005C\n\u005C\n\u005C\n");
278  }
279  eval("testRawLineContinuation`\\\r\n\\\n\\\r${1}\\\r\n\\\n\\\r`");
280  assertEquals(1, calls);
281})();
282
283
284(function testCallSiteObj() {
285  var calls = 0;
286  function tag(cs) {
287    calls++;
288    assertTrue(cs.hasOwnProperty("raw"));
289    assertTrue(Object.isFrozen(cs));
290    assertTrue(Object.isFrozen(cs.raw));
291    var raw = Object.getOwnPropertyDescriptor(cs, "raw");
292    assertFalse(raw.writable);
293    assertFalse(raw.configurable);
294    assertFalse(raw.enumerable);
295    assertEquals(Array.prototype, Object.getPrototypeOf(cs.raw));
296    assertTrue(Array.isArray(cs.raw));
297    assertEquals(Array.prototype, Object.getPrototypeOf(cs));
298    assertTrue(Array.isArray(cs));
299
300    var cooked0 = Object.getOwnPropertyDescriptor(cs, "0");
301    assertFalse(cooked0.writable);
302    assertFalse(cooked0.configurable);
303    assertTrue(cooked0.enumerable);
304
305    var raw0 = Object.getOwnPropertyDescriptor(cs.raw, "0");
306    assertFalse(raw0.writable);
307    assertFalse(raw0.configurable);
308    assertTrue(raw0.enumerable);
309
310    var length = Object.getOwnPropertyDescriptor(cs, "length");
311    assertFalse(length.writable);
312    assertFalse(length.configurable);
313    assertFalse(length.enumerable);
314
315    length = Object.getOwnPropertyDescriptor(cs.raw, "length");
316    assertFalse(length.writable);
317    assertFalse(length.configurable);
318    assertFalse(length.enumerable);
319  }
320  tag`${1}`;
321  assertEquals(1, calls);
322})();
323
324
325(function testUTF16ByteOrderMark() {
326  assertEquals("\uFEFFtest", `\uFEFFtest`);
327  assertEquals("\uFEFFtest", eval("`\uFEFFtest`"));
328})();
329
330
331(function testStringRawAsTagFn() {
332  assertEquals("\\u0065\\`\\r\\r\\n\\ntestcheck",
333               String.raw`\u0065\`\r\r\n\n${"test"}check`);
334  assertEquals("\\\n\\\n\\\n", eval("String.raw`\\\r\\\r\n\\\n`"));
335  assertEquals("", String.raw``);
336})();
337
338
339(function testCallSiteCaching() {
340  var callSites = [];
341  function tag(cs) { callSites.push(cs); }
342  var a = 1;
343  var b = 2;
344
345  tag`head${a}tail`;
346  tag`head${b}tail`;
347
348  assertEquals(2, callSites.length);
349  assertSame(callSites[0], callSites[1]);
350
351  eval("tag`head${a}tail`");
352  assertEquals(3, callSites.length);
353  assertSame(callSites[1], callSites[2]);
354
355  eval("tag`head${b}tail`");
356  assertEquals(4, callSites.length);
357  assertSame(callSites[2], callSites[3]);
358
359  (new Function("tag", "a", "b", "return tag`head${a}tail`;"))(tag, 1, 2);
360  assertEquals(5, callSites.length);
361  assertSame(callSites[3], callSites[4]);
362
363  (new Function("tag", "a", "b", "return tag`head${b}tail`;"))(tag, 1, 2);
364  assertEquals(6, callSites.length);
365  assertSame(callSites[4], callSites[5]);
366
367  callSites = [];
368
369  tag`foo${a}bar`;
370  tag`foo\${.}bar`;
371  assertEquals(2, callSites.length);
372  assertEquals(2, callSites[0].length);
373  assertEquals(1, callSites[1].length);
374
375  callSites = [];
376
377  eval("tag`\\\r\n\\\n\\\r`");
378  eval("tag`\\\r\n\\\n\\\r`");
379  assertEquals(2, callSites.length);
380  assertSame(callSites[0], callSites[1]);
381  assertEquals("", callSites[0][0]);
382  assertEquals("\\\n\\\n\\\n", callSites[0].raw[0]);
383
384  callSites = [];
385
386  tag`\uc548\ub155`;
387  tag`\uc548\ub155`;
388  assertEquals(2, callSites.length);
389  assertSame(callSites[0], callSites[1]);
390  assertEquals("안녕", callSites[0][0]);
391  assertEquals("\\uc548\\ub155", callSites[0].raw[0]);
392
393  callSites = [];
394
395  tag`\uc548\ub155`;
396  tag`안녕`;
397  assertEquals(2, callSites.length);
398  assertTrue(callSites[0] !== callSites[1]);
399  assertEquals("안녕", callSites[0][0]);
400  assertEquals("\\uc548\\ub155", callSites[0].raw[0]);
401  assertEquals("안녕", callSites[1][0]);
402  assertEquals("안녕", callSites[1].raw[0]);
403
404  // Extra-thorough UTF8 decoding test.
405  callSites = [];
406
407  tag`Iñtërnâtiônàlizætiøn\u2603\uD83D\uDCA9`;
408  tag`Iñtërnâtiônàlizætiøn☃��`;
409
410  assertEquals(2, callSites.length);
411  assertTrue(callSites[0] !== callSites[1]);
412  assertEquals("Iñtërnâtiônàlizætiøn☃��", callSites[0][0]);
413  assertEquals(
414      "Iñtërnâtiônàlizætiøn\\u2603\\uD83D\\uDCA9", callSites[0].raw[0]);
415  assertEquals("Iñtërnâtiônàlizætiøn☃��", callSites[1][0]);
416  assertEquals("Iñtërnâtiônàlizætiøn☃��", callSites[1].raw[0]);
417})();
418
419
420(function testExtendedArrayPrototype() {
421  Object.defineProperty(Array.prototype, 0, {
422    set: function() {
423      assertUnreachable();
424    },
425    configurable: true
426  });
427  function tag(){}
428  tag`a${1}b`;
429  delete Array.prototype[0];
430})();
431
432
433(function testRawLineNormalization() {
434  function raw0(callSiteObj) {
435    return callSiteObj.raw[0];
436  }
437  assertEquals(eval("raw0`\r`"), "\n");
438  assertEquals(eval("raw0`\r\n`"), "\n");
439  assertEquals(eval("raw0`\r\r\n`"), "\n\n");
440  assertEquals(eval("raw0`\r\n\r\n`"), "\n\n");
441  assertEquals(eval("raw0`\r\r\r\n`"), "\n\n\n");
442})();
443
444
445(function testHarmonyUnicode() {
446  function raw0(callSiteObj) {
447    return callSiteObj.raw[0];
448  }
449  assertEquals(raw0`a\u{62}c`, "a\\u{62}c");
450  assertEquals(raw0`a\u{000062}c`, "a\\u{000062}c");
451  assertEquals(raw0`a\u{0}c`, "a\\u{0}c");
452
453  assertEquals(`a\u{62}c`, "abc");
454  assertEquals(`a\u{000062}c`, "abc");
455})();
456
457
458(function testLiteralAfterRightBrace() {
459  // Regression test for https://code.google.com/p/v8/issues/detail?id=3734
460  function f() {}
461  `abc`;
462
463  function g() {}`def`;
464
465  {
466    // block
467  }
468  `ghi`;
469
470  {
471    // block
472  }`jkl`;
473})();
474
475
476(function testLegacyOctal() {
477  assertEquals('\u0000', `\0`);
478  assertEquals('\u0000a', `\0a`);
479  for (var i = 0; i < 8; i++) {
480    var code = "`\\0" + i + "`";
481    assertThrows(code, SyntaxError);
482    code = "(function(){})" + code;
483    assertThrows(code, SyntaxError);
484  }
485
486  assertEquals('\\0', String.raw`\0`);
487})();
488
489
490(function testSyntaxErrorsNonEscapeCharacter() {
491  assertThrows("`\\x`", SyntaxError);
492  assertThrows("`\\u`", SyntaxError);
493  for (var i = 1; i < 8; i++) {
494    var code = "`\\" + i + "`";
495    assertThrows(code, SyntaxError);
496    code = "(function(){})" + code;
497    assertThrows(code, SyntaxError);
498  }
499})();
500
501
502(function testValidNumericEscapes() {
503  assertEquals("8", `\8`);
504  assertEquals("9", `\9`);
505  assertEquals("\u00008", `\08`);
506  assertEquals("\u00009", `\09`);
507})();
508
509
510(function testLegacyOctalEscapesInExpressions() {
511  // Allowed in sloppy expression
512  assertEquals("\x07", `${"\07"}`);
513
514  // Disallowed in template tail
515  assertThrows("`${\"\\07\"}\\07`", SyntaxError);
516
517  // Disallowed in strict expression
518  assertThrows("`${(function() { \"use strict\"; return \"\\07\"; })()}`",
519               SyntaxError);
520})();
521
522
523var global = this;
524(function testCallNew() {
525  "use strict";
526  var called = false;
527  var calledWith;
528  global.log = function(x) { called = true; calledWith = x; }
529
530  assertInstanceof(new Function`log("test")`, Object);
531  assertTrue(called);
532  assertSame("test", calledWith);
533  delete global.log;
534})();
535
536
537(function testCallNew2() {
538  "use strict";
539  var log = [];
540  function tag(x) {
541    log.push(x);
542    if (!(this instanceof tag)) {
543      return tag;
544    }
545    this.x = x === void 0 ? null : x;
546    return this;
547  }
548  // No arguments passed to constructor
549  var instance = new tag`x``y``z`;
550  assertInstanceof(instance, tag);
551  assertSame(tag.prototype, Object.getPrototypeOf(instance));
552  assertEquals({ x: null }, instance);
553  assertEquals([["x"], ["y"], ["z"], undefined], log);
554
555  // Arguments passed to constructor
556  log.length = 0;
557  instance = new tag`x2` `y2` `z2` (`test`);
558  assertInstanceof(instance, tag);
559  assertSame(tag.prototype, Object.getPrototypeOf(instance));
560  assertEquals({ x: "test" }, instance);
561  assertEquals([["x2"], ["y2"], ["z2"], "test"], log);
562})();
563
564
565(function testCallResultOfTagFn() {
566  "use strict";
567  var i = 0;
568  var raw = [];
569  function tag(cs) {
570    var args = Array.prototype.slice.call(arguments);
571    var text = String.raw.apply(null, args);
572    if (i++ < 2) {
573      raw.push("tag;" + text);
574      return tag;
575    }
576
577    raw.push("raw;" + text);
578    return text;
579  }
580  assertEquals("test3", tag`test1``test2``test3`);
581  assertEquals([
582    "tag;test1",
583    "tag;test2",
584    "raw;test3"
585  ], raw);
586})();
587
588
589(function testReturnValueAsTagFn() {
590  "use strict";
591  var i = 0;
592  function makeTag() {
593    return function tag(cs) {
594      var args = Array.prototype.slice.call(arguments, 1);
595      var rcs = [];
596      rcs.raw = cs.map(function(s) {
597        return '!' + s + '!';
598      });
599      args.unshift(rcs);
600      return String.raw.apply(null, args);
601    }
602  }
603  assertEquals('!hi!', makeTag()`hi`);
604  assertEquals('!test!0!test!', makeTag()`test${0}test`);
605  assertEquals('!!', makeTag()``);
606});
607
608
609(function testToStringSubstitutions() {
610  var a = {
611    toString: function() { return "a"; },
612    valueOf: function() { return "-a-"; }
613  };
614  var b = {
615    toString: function() { return "b"; },
616    valueOf: function() { return "-b-"; }
617  };
618  assertEquals("a", `${a}`);
619  assertEquals("ab", `${a}${b}`);
620  assertEquals("-a--b-", `${a + b}`);
621  assertEquals("-a-", `${a + ""}`);
622  assertEquals("1a", `1${a}`);
623  assertEquals("1a2", `1${a}2`);
624  assertEquals("1a2b", `1${a}2${b}`);
625  assertEquals("1a2b3", `1${a}2${b}3`);
626})();
627
628
629(function testToStringSubstitutionsOrder() {
630  var subs = [];
631  var log = [];
632  function getter(name, value) {
633    return {
634      get: function() {
635        log.push("get" + name);
636        return value;
637      },
638      set: function(v) {
639        log.push("set" + name);
640      }
641    };
642  }
643  Object.defineProperties(subs, {
644    0: getter(0, "a"),
645    1: getter(1, "b"),
646    2: getter(2, "c")
647  });
648
649  assertEquals("-a-b-c-", `-${subs[0]}-${subs[1]}-${subs[2]}-`);
650  assertArrayEquals(["get0", "get1", "get2"], log);
651})();
652
653
654(function testTaggedToStringSubstitutionsOrder() {
655  var subs = [];
656  var log = [];
657  var tagged = [];
658  function getter(name, value) {
659    return {
660      get: function() {
661        log.push("get" + name);
662        return value;
663      },
664      set: function(v) {
665        log.push("set" + name);
666      }
667    };
668  }
669  Object.defineProperties(subs, {
670    0: getter(0, 1),
671    1: getter(1, 2),
672    2: getter(2, 3)
673  });
674
675  function tag(cs) {
676    var n_substitutions = arguments.length - 1;
677    var n_cooked = cs.length;
678    var e = cs[0];
679    var i = 0;
680    assertEquals(n_cooked, n_substitutions + 1);
681    while (i < n_substitutions) {
682      var sub = arguments[i++ + 1];
683      var tail = cs[i];
684      tagged.push(sub);
685      e = e.concat(sub, tail);
686    }
687    return e;
688  }
689
690  assertEquals("-1-2-3-", tag`-${subs[0]}-${subs[1]}-${subs[2]}-`);
691  assertArrayEquals(["get0", "get1", "get2"], log);
692  assertArrayEquals([1, 2, 3], tagged);
693
694  tagged.length = 0;
695  log.length = 0;
696  assertEquals("-1-", tag`-${subs[0]}-`);
697  assertArrayEquals(["get0"], log);
698  assertArrayEquals([1], tagged);
699})();
700
701
702// Since the first argument to the tag function is always an array,
703// eval calls will always just return that array.
704(function testEvalTagStrict() {
705  "use strict";
706  var f = (x) => eval`a${x}b`;
707  var result = f();
708  assertEquals(["a", "b"], result);
709  assertSame(result, f());
710})();
711
712
713(function testEvalTagSloppy() {
714  var f = (x) => eval`a${x}b`;
715  var result = f();
716  assertEquals(["a", "b"], result);
717  assertSame(result, f());
718})();
719