|  | // Copyright 2014 the V8 project authors. All rights reserved. | 
|  | // Use of this source code is governed by a BSD-style license that can be | 
|  | // found in the LICENSE file. | 
|  |  | 
|  | var num = 5; | 
|  | var str = "str"; | 
|  | function fn() { return "result"; } | 
|  | var obj = { | 
|  | num: num, | 
|  | str: str, | 
|  | fn: function() { return "result"; } | 
|  | }; | 
|  |  | 
|  | (function testBasicExpressions() { | 
|  | assertEquals("foo 5 bar", `foo ${num} bar`); | 
|  | assertEquals("foo str bar", `foo ${str} bar`); | 
|  | assertEquals("foo [object Object] bar", `foo ${obj} bar`); | 
|  | assertEquals("foo result bar", `foo ${fn()} bar`); | 
|  | assertEquals("foo 5 bar", `foo ${obj.num} bar`); | 
|  | assertEquals("foo str bar", `foo ${obj.str} bar`); | 
|  | assertEquals("foo result bar", `foo ${obj.fn()} bar`); | 
|  | })(); | 
|  |  | 
|  | (function testExpressionsContainingTemplates() { | 
|  | assertEquals("foo bar 5", `foo ${`bar ${num}`}`); | 
|  | })(); | 
|  |  | 
|  | (function testMultilineTemplates() { | 
|  | assertEquals("foo\n    bar\n    baz", `foo | 
|  | bar | 
|  | baz`); | 
|  |  | 
|  | assertEquals("foo\n  bar\n  baz", eval("`foo\r\n  bar\r  baz`")); | 
|  | })(); | 
|  |  | 
|  | (function testLineContinuation() { | 
|  | assertEquals("\n", `\ | 
|  |  | 
|  | `); | 
|  | })(); | 
|  |  | 
|  | (function testTaggedTemplates() { | 
|  | var calls = 0; | 
|  | (function(s) { | 
|  | calls++; | 
|  | })`test`; | 
|  | assertEquals(1, calls); | 
|  |  | 
|  | calls = 0; | 
|  | // assert tag is invoked in right context | 
|  | obj = { | 
|  | fn: function() { | 
|  | calls++; | 
|  | assertEquals(obj, this); | 
|  | } | 
|  | }; | 
|  |  | 
|  | obj.fn`test`; | 
|  | assertEquals(1, calls); | 
|  |  | 
|  | calls = 0; | 
|  | // Simple templates only have a callSiteObj | 
|  | (function(s) { | 
|  | calls++; | 
|  | assertEquals(1, arguments.length); | 
|  | })`test`; | 
|  | assertEquals(1, calls); | 
|  |  | 
|  | // Templates containing expressions have the values of evaluated expressions | 
|  | calls = 0; | 
|  | (function(site, n, s, o, f, r) { | 
|  | calls++; | 
|  | assertEquals(6, arguments.length); | 
|  | assertEquals("number", typeof n); | 
|  | assertEquals("string", typeof s); | 
|  | assertEquals("object", typeof o); | 
|  | assertEquals("function", typeof f); | 
|  | assertEquals("result", r); | 
|  | })`${num}${str}${obj}${fn}${fn()}`; | 
|  | assertEquals(1, calls); | 
|  |  | 
|  | // The TV and TRV of NoSubstitutionTemplate :: `` is the empty code unit | 
|  | // sequence. | 
|  | calls = 0; | 
|  | (function(s) { | 
|  | calls++; | 
|  | assertEquals(1, s.length); | 
|  | assertEquals(1, s.raw.length); | 
|  | assertEquals("", s[0]); | 
|  |  | 
|  | // Failure: expected <""> found <"foo  barfoo  barfoo foo foo foo testtest"> | 
|  | assertEquals("", s.raw[0]); | 
|  | })``; | 
|  | assertEquals(1, calls); | 
|  |  | 
|  | // The TV and TRV of TemplateHead :: `${ is the empty code unit sequence. | 
|  | calls = 0; | 
|  | (function(s) { | 
|  | calls++; | 
|  | assertEquals(2, s.length); | 
|  | assertEquals(2, s.raw.length); | 
|  | assertEquals("", s[0]); | 
|  | assertEquals("", s.raw[0]); | 
|  | })`${1}`; | 
|  | assertEquals(1, calls); | 
|  |  | 
|  | // The TV and TRV of TemplateMiddle :: }${ is the empty code unit sequence. | 
|  | calls = 0; | 
|  | (function(s) { | 
|  | calls++; | 
|  | assertEquals(3, s.length); | 
|  | assertEquals(3, s.raw.length); | 
|  | assertEquals("", s[1]); | 
|  | assertEquals("", s.raw[1]); | 
|  | })`${1}${2}`; | 
|  | assertEquals(1, calls); | 
|  |  | 
|  | // The TV and TRV of TemplateTail :: }` is the empty code unit sequence. | 
|  | calls = 0; | 
|  | (function(s) { | 
|  | calls++; | 
|  | assertEquals(2, s.length); | 
|  | assertEquals(2, s.raw.length); | 
|  | assertEquals("", s[1]); | 
|  | assertEquals("", s.raw[1]); | 
|  | })`${1}`; | 
|  | assertEquals(1, calls); | 
|  |  | 
|  | // The TV of NoSubstitutionTemplate :: ` TemplateCharacters ` is the TV of | 
|  | // TemplateCharacters. | 
|  | calls = 0; | 
|  | (function(s) { calls++; assertEquals("foo", s[0]); })`foo`; | 
|  | assertEquals(1, calls); | 
|  |  | 
|  | // The TV of TemplateHead :: ` TemplateCharacters ${ is the TV of | 
|  | // TemplateCharacters. | 
|  | calls = 0; | 
|  | (function(s) { calls++; assertEquals("foo", s[0]); })`foo${1}`; | 
|  | assertEquals(1, calls); | 
|  |  | 
|  | // The TV of TemplateMiddle :: } TemplateCharacters ${ is the TV of | 
|  | // TemplateCharacters. | 
|  | calls = 0; | 
|  | (function(s) { calls++; assertEquals("foo", s[1]); })`${1}foo${2}`; | 
|  | assertEquals(1, calls); | 
|  |  | 
|  | // The TV of TemplateTail :: } TemplateCharacters ` is the TV of | 
|  | // TemplateCharacters. | 
|  | calls = 0; | 
|  | (function(s) { calls++; assertEquals("foo", s[1]); })`${1}foo`; | 
|  | assertEquals(1, calls); | 
|  |  | 
|  | // The TV of TemplateCharacters :: TemplateCharacter is the TV of | 
|  | // TemplateCharacter. | 
|  | calls = 0; | 
|  | (function(s) { calls++; assertEquals("f", s[0]); })`f`; | 
|  | assertEquals(1, calls); | 
|  |  | 
|  | // The TV of TemplateCharacter :: $ is the code unit value 0x0024. | 
|  | calls = 0; | 
|  | (function(s) { calls++; assertEquals("$", s[0]); })`$`; | 
|  | assertEquals(1, calls); | 
|  |  | 
|  | // The TV of TemplateCharacter :: \ EscapeSequence is the CV of | 
|  | // EscapeSequence. | 
|  | calls = 0; | 
|  | (function(s) { calls++; assertEquals("안녕", s[0]); })`\uc548\uB155`; | 
|  | (function(s) { calls++; assertEquals("\xff", s[0]); })`\xff`; | 
|  | (function(s) { calls++; assertEquals("\n", s[0]); })`\n`; | 
|  | assertEquals(3, calls); | 
|  |  | 
|  | // The TV of TemplateCharacter :: LineContinuation is the TV of | 
|  | // LineContinuation. The TV of LineContinuation :: \ LineTerminatorSequence is | 
|  | // the empty code unit sequence. | 
|  | calls = 0; | 
|  | (function(s) { calls++; assertEquals("", s[0]); })`\ | 
|  | `; | 
|  | assertEquals(1, calls); | 
|  |  | 
|  | // The TRV of NoSubstitutionTemplate :: ` TemplateCharacters ` is the TRV of | 
|  | // TemplateCharacters. | 
|  | calls = 0; | 
|  | (function(s) { calls++; assertEquals("test", s.raw[0]); })`test`; | 
|  | assertEquals(1, calls); | 
|  |  | 
|  | // The TRV of TemplateHead :: ` TemplateCharacters ${ is the TRV of | 
|  | // TemplateCharacters. | 
|  | calls = 0; | 
|  | (function(s) { calls++; assertEquals("test", s.raw[0]); })`test${1}`; | 
|  | assertEquals(1, calls); | 
|  |  | 
|  | // The TRV of TemplateMiddle :: } TemplateCharacters ${ is the TRV of | 
|  | // TemplateCharacters. | 
|  | calls = 0; | 
|  | (function(s) { calls++; assertEquals("test", s.raw[1]); })`${1}test${2}`; | 
|  | assertEquals(1, calls); | 
|  |  | 
|  | // The TRV of TemplateTail :: } TemplateCharacters ` is the TRV of | 
|  | // TemplateCharacters. | 
|  | calls = 0; | 
|  | (function(s) { calls++; assertEquals("test", s.raw[1]); })`${1}test`; | 
|  | assertEquals(1, calls); | 
|  |  | 
|  | // The TRV of TemplateCharacters :: TemplateCharacter is the TRV of | 
|  | // TemplateCharacter. | 
|  | calls = 0; | 
|  | (function(s) { calls++; assertEquals("f", s.raw[0]); })`f`; | 
|  | assertEquals(1, calls); | 
|  |  | 
|  | // The TRV of TemplateCharacter :: $ is the code unit value 0x0024. | 
|  | calls = 0; | 
|  | (function(s) { calls++; assertEquals("\u0024", s.raw[0]); })`$`; | 
|  | assertEquals(1, calls); | 
|  |  | 
|  | // The TRV of EscapeSequence :: 0 is the code unit value 0x0030. | 
|  | calls = 0; | 
|  | (function(s) { calls++; assertEquals("\u005C\u0030", s.raw[0]); })`\0`; | 
|  | assertEquals(1, calls); | 
|  |  | 
|  | // The TRV of TemplateCharacter :: \ EscapeSequence is the sequence consisting | 
|  | // of the code unit value 0x005C followed by the code units of TRV of | 
|  | // EscapeSequence. | 
|  |  | 
|  | //   The TRV of EscapeSequence :: HexEscapeSequence is the TRV of the | 
|  | //   HexEscapeSequence. | 
|  | calls = 0; | 
|  | (function(s) { calls++; assertEquals("\u005Cxff", s.raw[0]); })`\xff`; | 
|  | assertEquals(1, calls); | 
|  |  | 
|  | //   The TRV of EscapeSequence :: UnicodeEscapeSequence is the TRV of the | 
|  | //   UnicodeEscapeSequence. | 
|  | calls = 0; | 
|  | (function(s) { calls++; assertEquals("\u005Cuc548", s.raw[0]); })`\uc548`; | 
|  | assertEquals(1, calls); | 
|  |  | 
|  | //   The TRV of CharacterEscapeSequence :: SingleEscapeCharacter is the TRV of | 
|  | //   the SingleEscapeCharacter. | 
|  | calls = 0; | 
|  | (function(s) { calls++; assertEquals("\u005C\u0027", s.raw[0]); })`\'`; | 
|  | (function(s) { calls++; assertEquals("\u005C\u0022", s.raw[0]); })`\"`; | 
|  | (function(s) { calls++; assertEquals("\u005C\u005C", s.raw[0]); })`\\`; | 
|  | (function(s) { calls++; assertEquals("\u005Cb", s.raw[0]); })`\b`; | 
|  | (function(s) { calls++; assertEquals("\u005Cf", s.raw[0]); })`\f`; | 
|  | (function(s) { calls++; assertEquals("\u005Cn", s.raw[0]); })`\n`; | 
|  | (function(s) { calls++; assertEquals("\u005Cr", s.raw[0]); })`\r`; | 
|  | (function(s) { calls++; assertEquals("\u005Ct", s.raw[0]); })`\t`; | 
|  | (function(s) { calls++; assertEquals("\u005Cv", s.raw[0]); })`\v`; | 
|  | (function(s) { calls++; assertEquals("\u005C`", s.raw[0]); })`\``; | 
|  | assertEquals(10, calls); | 
|  |  | 
|  | //   The TRV of CharacterEscapeSequence :: NonEscapeCharacter is the CV of the | 
|  | //   NonEscapeCharacter. | 
|  | calls = 0; | 
|  | (function(s) { calls++; assertEquals("\u005Cz", s.raw[0]); })`\z`; | 
|  | assertEquals(1, calls); | 
|  |  | 
|  | // The TRV of LineTerminatorSequence :: <LF> is the code unit value 0x000A. | 
|  | // The TRV of LineTerminatorSequence :: <CR> is the code unit value 0x000A. | 
|  | // The TRV of LineTerminatorSequence :: <CR><LF> is the sequence consisting of | 
|  | // the code unit value 0x000A. | 
|  | calls = 0; | 
|  | function testRawLineNormalization(cs) { | 
|  | calls++; | 
|  | assertEquals(cs.raw[0], "\n\n\n"); | 
|  | assertEquals(cs.raw[1], "\n\n\n"); | 
|  | } | 
|  | eval("testRawLineNormalization`\r\n\n\r${1}\r\n\n\r`"); | 
|  | assertEquals(1, calls); | 
|  |  | 
|  | // The TRV of LineContinuation :: \ LineTerminatorSequence is the sequence | 
|  | // consisting of the code unit value 0x005C followed by the code units of TRV | 
|  | // of LineTerminatorSequence. | 
|  | calls = 0; | 
|  | function testRawLineContinuation(cs) { | 
|  | calls++; | 
|  | assertEquals(cs.raw[0], "\u005C\n\u005C\n\u005C\n"); | 
|  | assertEquals(cs.raw[1], "\u005C\n\u005C\n\u005C\n"); | 
|  | } | 
|  | eval("testRawLineContinuation`\\\r\n\\\n\\\r${1}\\\r\n\\\n\\\r`"); | 
|  | assertEquals(1, calls); | 
|  | })(); | 
|  |  | 
|  |  | 
|  | (function testCallSiteObj() { | 
|  | var calls = 0; | 
|  | function tag(cs) { | 
|  | calls++; | 
|  | assertTrue(cs.hasOwnProperty("raw")); | 
|  | assertTrue(Object.isFrozen(cs)); | 
|  | assertTrue(Object.isFrozen(cs.raw)); | 
|  | var raw = Object.getOwnPropertyDescriptor(cs, "raw"); | 
|  | assertFalse(raw.writable); | 
|  | assertFalse(raw.configurable); | 
|  | assertFalse(raw.enumerable); | 
|  | assertEquals(Array.prototype, Object.getPrototypeOf(cs.raw)); | 
|  | assertTrue(Array.isArray(cs.raw)); | 
|  | assertEquals(Array.prototype, Object.getPrototypeOf(cs)); | 
|  | assertTrue(Array.isArray(cs)); | 
|  |  | 
|  | var cooked0 = Object.getOwnPropertyDescriptor(cs, "0"); | 
|  | assertFalse(cooked0.writable); | 
|  | assertFalse(cooked0.configurable); | 
|  | assertTrue(cooked0.enumerable); | 
|  |  | 
|  | var raw0 = Object.getOwnPropertyDescriptor(cs.raw, "0"); | 
|  | assertFalse(raw0.writable); | 
|  | assertFalse(raw0.configurable); | 
|  | assertTrue(raw0.enumerable); | 
|  |  | 
|  | var length = Object.getOwnPropertyDescriptor(cs, "length"); | 
|  | assertFalse(length.writable); | 
|  | assertFalse(length.configurable); | 
|  | assertFalse(length.enumerable); | 
|  |  | 
|  | length = Object.getOwnPropertyDescriptor(cs.raw, "length"); | 
|  | assertFalse(length.writable); | 
|  | assertFalse(length.configurable); | 
|  | assertFalse(length.enumerable); | 
|  | } | 
|  | tag`${1}`; | 
|  | assertEquals(1, calls); | 
|  | })(); | 
|  |  | 
|  |  | 
|  | (function testUTF16ByteOrderMark() { | 
|  | assertEquals("\uFEFFtest", `\uFEFFtest`); | 
|  | assertEquals("\uFEFFtest", eval("`\uFEFFtest`")); | 
|  | })(); | 
|  |  | 
|  |  | 
|  | (function testStringRawAsTagFn() { | 
|  | assertEquals("\\u0065\\`\\r\\r\\n\\ntestcheck", | 
|  | String.raw`\u0065\`\r\r\n\n${"test"}check`); | 
|  | assertEquals("\\\n\\\n\\\n", eval("String.raw`\\\r\\\r\n\\\n`")); | 
|  | assertEquals("", String.raw``); | 
|  | })(); | 
|  |  | 
|  |  | 
|  | (function testCallSiteCaching() { | 
|  | var callSites = []; | 
|  | function tag(cs) { callSites.push(cs); } | 
|  | var a = 1; | 
|  | var b = 2; | 
|  |  | 
|  | // Call-sites are cached by ParseNode. Same tag call in a loop | 
|  | // means same template object | 
|  | for (var i = 0; i < 2; ++i) { | 
|  | tag`head${i == 0 ? a : b}tail`; | 
|  | } | 
|  | assertEquals(2, callSites.length); | 
|  | assertSame(callSites[0], callSites[1]); | 
|  |  | 
|  | // Tag calls within eval() never have the same ParseNode as the same tag | 
|  | // call from a different eval() invocation. | 
|  | for (var i = 0; i < 2; ++i) { | 
|  | eval("tag`head${i == 0 ? a : b}tail`"); | 
|  | } | 
|  | assertEquals(4, callSites.length); | 
|  | assertTrue(callSites[1] !== callSites[2]); | 
|  | assertTrue(callSites[2] !== callSites[3]); | 
|  |  | 
|  | (new Function("tag", "a", "b", "return tag`head${a}tail`;"))(tag, 1, 2); | 
|  | assertEquals(5, callSites.length); | 
|  | assertTrue(callSites[3] !== callSites[4]); | 
|  |  | 
|  | (new Function("tag", "a", "b", "return tag`head${b}tail`;"))(tag, 1, 2); | 
|  | assertEquals(6, callSites.length); | 
|  | assertTrue(callSites[4] !== callSites[5]); | 
|  |  | 
|  | callSites = []; | 
|  |  | 
|  | tag`foo${a}bar`; | 
|  | tag`foo\${.}bar`; | 
|  | assertEquals(2, callSites.length); | 
|  | assertEquals(2, callSites[0].length); | 
|  | assertEquals(1, callSites[1].length); | 
|  |  | 
|  | callSites = []; | 
|  |  | 
|  | for (var i = 0; i < 2; ++i) { | 
|  | eval("tag`\\\r\n\\\n\\\r`"); | 
|  | } | 
|  | assertEquals(2, callSites.length); | 
|  | assertTrue(callSites[0] !== callSites[1]); | 
|  | assertEquals("", callSites[0][0]); | 
|  | assertEquals("\\\n\\\n\\\n", callSites[0].raw[0]); | 
|  |  | 
|  | callSites = []; | 
|  |  | 
|  | for (var i = 0; i < 2; ++i) { | 
|  | tag`\uc548\ub155`; | 
|  | } | 
|  | assertEquals(2, callSites.length); | 
|  | assertSame(callSites[0], callSites[1]); | 
|  | assertEquals("안녕", callSites[0][0]); | 
|  | assertEquals("\\uc548\\ub155", callSites[0].raw[0]); | 
|  |  | 
|  | callSites = []; | 
|  |  | 
|  | tag`\uc548\ub155`; | 
|  | tag`안녕`; | 
|  | assertEquals(2, callSites.length); | 
|  | assertTrue(callSites[0] !== callSites[1]); | 
|  | assertEquals("안녕", callSites[0][0]); | 
|  | assertEquals("\\uc548\\ub155", callSites[0].raw[0]); | 
|  | assertEquals("안녕", callSites[1][0]); | 
|  | assertEquals("안녕", callSites[1].raw[0]); | 
|  |  | 
|  | // Extra-thorough UTF8 decoding test. | 
|  | callSites = []; | 
|  |  | 
|  | tag`Iñtërnâtiônàlizætiøn\u2603\uD83D\uDCA9`; | 
|  | tag`Iñtërnâtiônàlizætiøn☃💩`; | 
|  |  | 
|  | assertEquals(2, callSites.length); | 
|  | assertTrue(callSites[0] !== callSites[1]); | 
|  | assertEquals("Iñtërnâtiônàlizætiøn☃💩", callSites[0][0]); | 
|  | assertEquals( | 
|  | "Iñtërnâtiônàlizætiøn\\u2603\\uD83D\\uDCA9", callSites[0].raw[0]); | 
|  | assertEquals("Iñtërnâtiônàlizætiøn☃💩", callSites[1][0]); | 
|  | assertEquals("Iñtërnâtiônàlizætiøn☃💩", callSites[1].raw[0]); | 
|  | })(); | 
|  |  | 
|  |  | 
|  | (function testExtendedArrayPrototype() { | 
|  | Object.defineProperty(Array.prototype, 0, { | 
|  | set: function() { | 
|  | assertUnreachable(); | 
|  | }, | 
|  | configurable: true | 
|  | }); | 
|  | function tag(){} | 
|  | tag`a${1}b`; | 
|  | delete Array.prototype[0]; | 
|  | })(); | 
|  |  | 
|  |  | 
|  | (function testRawLineNormalization() { | 
|  | function raw0(callSiteObj) { | 
|  | return callSiteObj.raw[0]; | 
|  | } | 
|  | assertEquals(eval("raw0`\r`"), "\n"); | 
|  | assertEquals(eval("raw0`\r\n`"), "\n"); | 
|  | assertEquals(eval("raw0`\r\r\n`"), "\n\n"); | 
|  | assertEquals(eval("raw0`\r\n\r\n`"), "\n\n"); | 
|  | assertEquals(eval("raw0`\r\r\r\n`"), "\n\n\n"); | 
|  | })(); | 
|  |  | 
|  |  | 
|  | (function testHarmonyUnicode() { | 
|  | function raw0(callSiteObj) { | 
|  | return callSiteObj.raw[0]; | 
|  | } | 
|  | assertEquals(raw0`a\u{62}c`, "a\\u{62}c"); | 
|  | assertEquals(raw0`a\u{000062}c`, "a\\u{000062}c"); | 
|  | assertEquals(raw0`a\u{0}c`, "a\\u{0}c"); | 
|  |  | 
|  | assertEquals(`a\u{62}c`, "abc"); | 
|  | assertEquals(`a\u{000062}c`, "abc"); | 
|  | })(); | 
|  |  | 
|  |  | 
|  | (function testLiteralAfterRightBrace() { | 
|  | // Regression test for https://code.google.com/p/v8/issues/detail?id=3734 | 
|  | function f() {} | 
|  | `abc`; | 
|  |  | 
|  | function g() {}`def`; | 
|  |  | 
|  | { | 
|  | // block | 
|  | } | 
|  | `ghi`; | 
|  |  | 
|  | { | 
|  | // block | 
|  | }`jkl`; | 
|  | })(); | 
|  |  | 
|  |  | 
|  | (function testLegacyOctal() { | 
|  | assertEquals('\u0000', `\0`); | 
|  | assertEquals('\u0000a', `\0a`); | 
|  | for (var i = 0; i < 10; i++) { | 
|  | var code = "`\\0" + i + "`"; | 
|  | assertThrows(code, SyntaxError); | 
|  | // Not an error if tagged. | 
|  | code = "(function(){})" + code; | 
|  | assertDoesNotThrow(code, SyntaxError); | 
|  | } | 
|  |  | 
|  | assertEquals('\\0', String.raw`\0`); | 
|  | })(); | 
|  |  | 
|  |  | 
|  | (function testSyntaxErrorsNonEscapeCharacter() { | 
|  | assertThrows("`\\x`", SyntaxError); | 
|  | assertThrows("`\\u`", SyntaxError); | 
|  | for (var i = 1; i < 8; i++) { | 
|  | var code = "`\\" + i + "`"; | 
|  | assertThrows(code, SyntaxError); | 
|  | // Not an error if tagged. | 
|  | code = "(function(){})" + code; | 
|  | assertDoesNotThrow(code, SyntaxError); | 
|  | } | 
|  | })(); | 
|  |  | 
|  |  | 
|  | (function testInvalidNumericEscapes() { | 
|  | assertThrows(function() { eval("`\\8`"); }, SyntaxError) | 
|  | assertThrows(function() { eval("`\\9`"); }, SyntaxError) | 
|  | })(); | 
|  |  | 
|  |  | 
|  | (function testLegacyOctalEscapesInExpressions() { | 
|  | // Allowed in sloppy expression | 
|  | assertEquals("\x07", `${"\07"}`); | 
|  |  | 
|  | // Disallowed in template tail | 
|  | assertThrows("`${\"\\07\"}\\07`", SyntaxError); | 
|  |  | 
|  | // Disallowed in strict expression | 
|  | assertThrows("`${(function() { \"use strict\"; return \"\\07\"; })()}`", | 
|  | SyntaxError); | 
|  | })(); | 
|  |  | 
|  |  | 
|  | var global = this; | 
|  | (function testCallNew() { | 
|  | "use strict"; | 
|  | var called = false; | 
|  | var calledWith; | 
|  | global.log = function(x) { called = true; calledWith = x; } | 
|  |  | 
|  | assertInstanceof(new Function`log("test")`, Object); | 
|  | assertTrue(called); | 
|  | assertSame("test", calledWith); | 
|  | delete global.log; | 
|  | })(); | 
|  |  | 
|  |  | 
|  | (function testCallNew2() { | 
|  | "use strict"; | 
|  | var log = []; | 
|  | function tag(x) { | 
|  | log.push(x); | 
|  | if (!(this instanceof tag)) { | 
|  | return tag; | 
|  | } | 
|  | this.x = x === void 0 ? null : x; | 
|  | return this; | 
|  | } | 
|  | // No arguments passed to constructor | 
|  | var instance = new tag`x``y``z`; | 
|  | assertInstanceof(instance, tag); | 
|  | assertSame(tag.prototype, Object.getPrototypeOf(instance)); | 
|  | assertEquals({ x: null }, instance); | 
|  | assertEquals([["x"], ["y"], ["z"], undefined], log); | 
|  |  | 
|  | // Arguments passed to constructor | 
|  | log.length = 0; | 
|  | instance = new tag`x2` `y2` `z2` (`test`); | 
|  | assertInstanceof(instance, tag); | 
|  | assertSame(tag.prototype, Object.getPrototypeOf(instance)); | 
|  | assertEquals({ x: "test" }, instance); | 
|  | assertEquals([["x2"], ["y2"], ["z2"], "test"], log); | 
|  | })(); | 
|  |  | 
|  |  | 
|  | (function testCallResultOfTagFn() { | 
|  | "use strict"; | 
|  | var i = 0; | 
|  | var raw = []; | 
|  | function tag(cs) { | 
|  | var args = Array.prototype.slice.call(arguments); | 
|  | var text = String.raw.apply(null, args); | 
|  | if (i++ < 2) { | 
|  | raw.push("tag;" + text); | 
|  | return tag; | 
|  | } | 
|  |  | 
|  | raw.push("raw;" + text); | 
|  | return text; | 
|  | } | 
|  | assertEquals("test3", tag`test1``test2``test3`); | 
|  | assertEquals([ | 
|  | "tag;test1", | 
|  | "tag;test2", | 
|  | "raw;test3" | 
|  | ], raw); | 
|  | })(); | 
|  |  | 
|  |  | 
|  | (function testReturnValueAsTagFn() { | 
|  | "use strict"; | 
|  | var i = 0; | 
|  | function makeTag() { | 
|  | return function tag(cs) { | 
|  | var args = Array.prototype.slice.call(arguments, 1); | 
|  | var rcs = []; | 
|  | rcs.raw = cs.map(function(s) { | 
|  | return '!' + s + '!'; | 
|  | }); | 
|  | args.unshift(rcs); | 
|  | return String.raw.apply(null, args); | 
|  | } | 
|  | } | 
|  | assertEquals('!hi!', makeTag()`hi`); | 
|  | assertEquals('!test!0!test!', makeTag()`test${0}test`); | 
|  | assertEquals('!!', makeTag()``); | 
|  | }); | 
|  |  | 
|  |  | 
|  | (function testToStringSubstitutions() { | 
|  | var a = { | 
|  | toString: function() { return "a"; }, | 
|  | valueOf: function() { return "-a-"; } | 
|  | }; | 
|  | var b = { | 
|  | toString: function() { return "b"; }, | 
|  | valueOf: function() { return "-b-"; } | 
|  | }; | 
|  | assertEquals("a", `${a}`); | 
|  | assertEquals("ab", `${a}${b}`); | 
|  | assertEquals("-a--b-", `${a + b}`); | 
|  | assertEquals("-a-", `${a + ""}`); | 
|  | assertEquals("1a", `1${a}`); | 
|  | assertEquals("1a2", `1${a}2`); | 
|  | assertEquals("1a2b", `1${a}2${b}`); | 
|  | assertEquals("1a2b3", `1${a}2${b}3`); | 
|  | })(); | 
|  |  | 
|  |  | 
|  | (function testToStringSubstitutionsOrder() { | 
|  | var subs = []; | 
|  | var log = []; | 
|  | function getter(name, value) { | 
|  | return { | 
|  | get: function() { | 
|  | log.push("get" + name); | 
|  | return value; | 
|  | }, | 
|  | set: function(v) { | 
|  | log.push("set" + name); | 
|  | } | 
|  | }; | 
|  | } | 
|  | Object.defineProperties(subs, { | 
|  | 0: getter(0, "a"), | 
|  | 1: getter(1, "b"), | 
|  | 2: getter(2, "c") | 
|  | }); | 
|  |  | 
|  | assertEquals("-a-b-c-", `-${subs[0]}-${subs[1]}-${subs[2]}-`); | 
|  | assertArrayEquals(["get0", "get1", "get2"], log); | 
|  | })(); | 
|  |  | 
|  |  | 
|  | (function testTaggedToStringSubstitutionsOrder() { | 
|  | var subs = []; | 
|  | var log = []; | 
|  | var tagged = []; | 
|  | function getter(name, value) { | 
|  | return { | 
|  | get: function() { | 
|  | log.push("get" + name); | 
|  | return value; | 
|  | }, | 
|  | set: function(v) { | 
|  | log.push("set" + name); | 
|  | } | 
|  | }; | 
|  | } | 
|  | Object.defineProperties(subs, { | 
|  | 0: getter(0, 1), | 
|  | 1: getter(1, 2), | 
|  | 2: getter(2, 3) | 
|  | }); | 
|  |  | 
|  | function tag(cs) { | 
|  | var n_substitutions = arguments.length - 1; | 
|  | var n_cooked = cs.length; | 
|  | var e = cs[0]; | 
|  | var i = 0; | 
|  | assertEquals(n_cooked, n_substitutions + 1); | 
|  | while (i < n_substitutions) { | 
|  | var sub = arguments[i++ + 1]; | 
|  | var tail = cs[i]; | 
|  | tagged.push(sub); | 
|  | e = e.concat(sub, tail); | 
|  | } | 
|  | return e; | 
|  | } | 
|  |  | 
|  | assertEquals("-1-2-3-", tag`-${subs[0]}-${subs[1]}-${subs[2]}-`); | 
|  | assertArrayEquals(["get0", "get1", "get2"], log); | 
|  | assertArrayEquals([1, 2, 3], tagged); | 
|  |  | 
|  | tagged.length = 0; | 
|  | log.length = 0; | 
|  | assertEquals("-1-", tag`-${subs[0]}-`); | 
|  | assertArrayEquals(["get0"], log); | 
|  | assertArrayEquals([1], tagged); | 
|  | })(); | 
|  |  | 
|  |  | 
|  | // Since the first argument to the tag function is always an array, | 
|  | // eval calls will always just return that array. | 
|  | (function testEvalTagStrict() { | 
|  | "use strict"; | 
|  | var f = (x) => eval`a${x}b`; | 
|  | var result = f(); | 
|  | assertEquals(["a", "b"], result); | 
|  | assertSame(result, f()); | 
|  | })(); | 
|  |  | 
|  |  | 
|  | (function testEvalTagSloppy() { | 
|  | var f = (x) => eval`a${x}b`; | 
|  | var result = f(); | 
|  | assertEquals(["a", "b"], result); | 
|  | assertSame(result, f()); | 
|  | })(); | 
|  |  | 
|  | (function testTaggedTemplateInvalidAssignmentTargetStrict() { | 
|  | "use strict"; | 
|  | function f() {} | 
|  | assertThrows(() => Function("++f`foo`"), SyntaxError); | 
|  | assertThrows(() => Function("f`foo`++"), SyntaxError); | 
|  | assertThrows(() => Function("--f`foo`"), SyntaxError); | 
|  | assertThrows(() => Function("f`foo`--"), SyntaxError); | 
|  | assertThrows(() => Function("f`foo` = 1"), SyntaxError); | 
|  | })(); | 
|  |  | 
|  | (function testTaggedTemplateInvalidAssignmentTargetSloppy() { | 
|  | function f() {} | 
|  | assertThrows(() => Function("++f`foo`"), SyntaxError); | 
|  | assertThrows(() => Function("f`foo`++"), SyntaxError); | 
|  | assertThrows(() => Function("--f`foo`"), SyntaxError); | 
|  | assertThrows(() => Function("f`foo`--"), SyntaxError); | 
|  | assertThrows(() => Function("f`foo` = 1"), SyntaxError); | 
|  | })(); | 
|  |  | 
|  | // Disable eval caching if a tagged template occurs in a nested function | 
|  | var v = 0; | 
|  | var templates = []; | 
|  | function tag(callSite) { templates.push(callSite); } | 
|  | for (v = 0; v < 6; v += 2) { | 
|  | eval("(function() { for (var i = 0; i < 2; ++i) tag`Hello${v}world` })()"); | 
|  | assertSame(templates[v], templates[v + 1]); | 
|  | } | 
|  | assertNotSame(templates[0], templates[2]); | 
|  | assertNotSame(templates[0], templates[4]); | 
|  | assertNotSame(templates[1], templates[3]); | 
|  | assertNotSame(templates[1], templates[5]); | 
|  | assertNotSame(templates[2], templates[4]); | 
|  | assertNotSame(templates[3], templates[5]); | 
|  |  | 
|  | function makeSource1(id) { | 
|  | return `function f() { | 
|  | for (var i = 0; i < 2; ++i) tag\`Hello${id}world\`; | 
|  | } | 
|  | f();`; | 
|  | } | 
|  | templates = []; | 
|  | for (v = 0; v < 6; v += 2) { | 
|  | eval(makeSource1(v)); | 
|  | assertSame(templates[v], templates[v + 1]); | 
|  | } | 
|  | assertNotSame(templates[0], templates[2]); | 
|  | assertNotSame(templates[0], templates[4]); | 
|  | assertNotSame(templates[1], templates[3]); | 
|  | assertNotSame(templates[1], templates[5]); | 
|  | assertNotSame(templates[2], templates[4]); | 
|  | assertNotSame(templates[3], templates[5]); | 
|  |  | 
|  | templates = []; | 
|  | eval("(function() { for (var i = 0; i < 2; ++i) tag`Hello${1}world` })()"); | 
|  | eval("(function() { for (var i = 0; i < 2; ++i) tag`Hello${2}world` })()"); | 
|  | eval("(function() { for (var i = 0; i < 2; ++i) tag`Hello${2}world` })()"); | 
|  | assertSame(templates[0], templates[1]); | 
|  | assertNotSame(templates[0], templates[2]); | 
|  | assertNotSame(templates[0], templates[4]); | 
|  | assertNotSame(templates[1], templates[3]); | 
|  | assertNotSame(templates[1], templates[5]); | 
|  | assertSame(templates[2], templates[3]); | 
|  | assertNotSame(templates[2], templates[4]); | 
|  | assertNotSame(templates[3], templates[5]); | 
|  | assertSame(templates[4],templates[5]); | 
|  |  | 
|  | templates = []; | 
|  | eval(makeSource1(1)); | 
|  | eval(makeSource1(2)); | 
|  | eval(makeSource1(3)); | 
|  | assertSame(templates[0], templates[1]); | 
|  | assertNotSame(templates[0], templates[2]); | 
|  | assertNotSame(templates[0], templates[4]); | 
|  | assertNotSame(templates[1], templates[3]); | 
|  | assertNotSame(templates[1], templates[5]); | 
|  | assertSame(templates[2], templates[3]); | 
|  | assertNotSame(templates[2], templates[4]); | 
|  | assertNotSame(templates[3], templates[5]); | 
|  | assertSame(templates[4],templates[5]); | 
|  |  | 
|  | // Disable eval caching if a tagged template occurs in an even deeper nested function | 
|  | var v = 0; | 
|  | templates = []; | 
|  | for (v = 0; v < 6; v += 2) { | 
|  | eval("(function() { (function() { for (var i = 0; i < 2; ++i) tag`Hello${v}world` })() })()"); | 
|  | if (!v) continue; | 
|  | assertNotSame(templates[v], templates[v - 1]); | 
|  | } | 
|  | assertNotSame(templates[0], templates[2]); | 
|  | assertNotSame(templates[0], templates[4]); | 
|  | assertNotSame(templates[1], templates[3]); | 
|  | assertNotSame(templates[1], templates[5]); | 
|  | assertNotSame(templates[2], templates[4]); | 
|  | assertNotSame(templates[3], templates[5]); | 
|  |  | 
|  | function makeSource2(id) { | 
|  | return `function f() { | 
|  | function innerF() { | 
|  | for (var i = 0; i < 2; ++i) tag\`Hello${id}world\`; | 
|  | } | 
|  | return innerF(); | 
|  | } | 
|  | f();`; | 
|  | } | 
|  | templates = []; | 
|  | for (v = 0; v < 6; v += 2) { | 
|  | eval(makeSource2(v)); | 
|  | assertSame(templates[v], templates[v + 1]); | 
|  | } | 
|  | assertNotSame(templates[0], templates[2]); | 
|  | assertNotSame(templates[0], templates[4]); | 
|  | assertNotSame(templates[1], templates[3]); | 
|  | assertNotSame(templates[1], templates[5]); | 
|  | assertNotSame(templates[2], templates[4]); | 
|  | assertNotSame(templates[3], templates[5]); | 
|  |  | 
|  | templates = []; | 
|  | eval("(function() { (function() { for (var i = 0; i < 2; ++i) tag`Hello${1}world` })() })()"); | 
|  | eval("(function() { (function() { for (var i = 0; i < 2; ++i) tag`Hello${2}world` })() })()"); | 
|  | eval("(function() { (function() { for (var i = 0; i < 2; ++i) tag`Hello${2}world` })() })()"); | 
|  | assertSame(templates[0], templates[1]); | 
|  | assertNotSame(templates[0], templates[2]); | 
|  | assertNotSame(templates[0], templates[4]); | 
|  | assertNotSame(templates[1], templates[3]); | 
|  | assertNotSame(templates[1], templates[5]); | 
|  | assertSame(templates[2], templates[3]); | 
|  | assertNotSame(templates[2], templates[4]); | 
|  | assertNotSame(templates[3], templates[5]); | 
|  | assertSame(templates[4], templates[5]); | 
|  |  | 
|  | templates = []; | 
|  | eval(makeSource2(1)); | 
|  | eval(makeSource2(2)); | 
|  | eval(makeSource2(3)); | 
|  | assertSame(templates[0], templates[1]); | 
|  | assertNotSame(templates[0], templates[2]); | 
|  | assertNotSame(templates[0], templates[4]); | 
|  | assertNotSame(templates[1], templates[3]); | 
|  | assertNotSame(templates[1], templates[5]); | 
|  | assertSame(templates[2], templates[3]); | 
|  | assertNotSame(templates[2], templates[4]); | 
|  | assertNotSame(templates[3], templates[5]); | 
|  | assertSame(templates[4], templates[5]); |