[ES6] Implement tagged templates
authorutatane.tea@gmail.com <utatane.tea@gmail.com@268f45cc-cd09-0410-ab3c-d52691b4dbfc>
Thu, 14 May 2015 16:07:54 +0000 (16:07 +0000)
committerutatane.tea@gmail.com <utatane.tea@gmail.com@268f45cc-cd09-0410-ab3c-d52691b4dbfc>
Thu, 14 May 2015 16:07:54 +0000 (16:07 +0000)
https://bugs.webkit.org/show_bug.cgi?id=143183

Reviewed by Oliver Hunt.

This patch implements ES6 tagged templates.
In tagged templates, the function takes the template object.

The template object contains the raw and cooked template strings,
so when parsing the tagged templates, we need to tokenize the raw and cooked strings.
While tagged templates require the both strings, the template literal only requires
the cooked strings. So when tokenizing under the template literal context,
we only builds the cooked strings.

As per ES6 spec, the template objects for the same raw strings are shared in the same realm.
The template objects is cached. And every time we evaluate the same tagged templates,
the same (cached) template objects are used.
Since the spec freezes this template objects completely,
we cannot attach some properties to it.
So we can say that it behaves as if the template objects are the primitive values (like JSString).
Since we cannot attach properties, the only way to test the identity of the template object is comparing. (===)
As the result, when there is no reference to the template object, we can garbage collect it
because the user has no way to test that the newly created template object does not equal
to the already collected template object.

So, to implement tagged templates, we implement the following components.

1. JSTemplateRegistryKey
It holds the template registry key and it does not exposed to users.
TemplateRegistryKey holds the vector of raw and cooked strings with the pre-computed hash value.
When obtaining the template object for the (statically, a.k.a. at the parsing time) given raw string vectors,
we use this JSTemplateRegistryKey as a key to the map and look up the template object from
TemplateRegistry.
JSTemplateRegistryKey is created at the bytecode compiling time and
stored in the CodeBlock as like as JSString content values.

2. TemplateRegistry
This manages the cached template objects.
It holds the weak map (JSTemplateRegistryKey -> the template object).
The template object is weakly referenced.
So if there is no reference to the template object,
the template object is automatically GC-ed.
When looking up the template object, it searches the cached template object.
If it is found, it is returned to the users.
If there is no cached template objects, it creates the new template object and
stores it with the given template registry key.

* CMakeLists.txt:
* JavaScriptCore.vcxproj/JavaScriptCore.vcxproj:
* JavaScriptCore.vcxproj/JavaScriptCore.vcxproj.filters:
* JavaScriptCore.xcodeproj/project.pbxproj:
* bytecompiler/BytecodeGenerator.cpp:
(JSC::BytecodeGenerator::addTemplateRegistryKeyConstant):
(JSC::BytecodeGenerator::emitGetTemplateObject):
* bytecompiler/BytecodeGenerator.h:
* bytecompiler/NodesCodegen.cpp:
(JSC::TaggedTemplateNode::emitBytecode):
(JSC::TemplateLiteralNode::emitBytecode): Deleted.
* parser/ASTBuilder.h:
(JSC::ASTBuilder::createTaggedTemplate):
(JSC::ASTBuilder::createTemplateLiteral): Deleted.
* parser/Lexer.cpp:
(JSC::Lexer<T>::setCode):
(JSC::Lexer<T>::parseTemplateLiteral):
(JSC::Lexer<T>::lex):
(JSC::Lexer<T>::scanTrailingTemplateString):
(JSC::Lexer<T>::clear):
* parser/Lexer.h:
(JSC::Lexer<T>::makeEmptyIdentifier):
* parser/NodeConstructors.h:
(JSC::TaggedTemplateNode::TaggedTemplateNode):
(JSC::TemplateLiteralNode::TemplateLiteralNode): Deleted.
* parser/Nodes.h:
(JSC::TemplateLiteralNode::templateStrings):
(JSC::TemplateLiteralNode::templateExpressions):
(JSC::TaggedTemplateNode::templateLiteral):
* parser/Parser.cpp:
(JSC::Parser<LexerType>::parseTemplateString):
(JSC::Parser<LexerType>::parseTemplateLiteral):
(JSC::Parser<LexerType>::parsePrimaryExpression):
(JSC::Parser<LexerType>::parseMemberExpression):
* parser/Parser.h:
* parser/ParserArena.h:
(JSC::IdentifierArena::makeEmptyIdentifier):
* parser/SyntaxChecker.h:
(JSC::SyntaxChecker::createTaggedTemplate):
(JSC::SyntaxChecker::createTemplateLiteral): Deleted.
* runtime/CommonIdentifiers.h:
* runtime/JSGlobalObject.cpp:
(JSC::getTemplateObject):
(JSC::JSGlobalObject::JSGlobalObject):
(JSC::JSGlobalObject::init):
* runtime/JSGlobalObject.h:
(JSC::JSGlobalObject::templateRegistry):
* runtime/JSTemplateRegistryKey.cpp: Added.
(JSC::JSTemplateRegistryKey::JSTemplateRegistryKey):
(JSC::JSTemplateRegistryKey::create):
(JSC::JSTemplateRegistryKey::destroy):
* runtime/JSTemplateRegistryKey.h: Added.
* runtime/ObjectConstructor.cpp:
(JSC::objectConstructorFreeze):
* runtime/ObjectConstructor.h:
* runtime/TemplateRegistry.cpp: Added.
(JSC::TemplateRegistry::TemplateRegistry):
(JSC::TemplateRegistry::getTemplateObject):
* runtime/TemplateRegistry.h: Added.
* runtime/TemplateRegistryKey.h: Added.
(JSC::TemplateRegistryKey::isDeletedValue):
(JSC::TemplateRegistryKey::isEmptyValue):
(JSC::TemplateRegistryKey::hash):
(JSC::TemplateRegistryKey::rawStrings):
(JSC::TemplateRegistryKey::cookedStrings):
(JSC::TemplateRegistryKey::operator==):
(JSC::TemplateRegistryKey::operator!=):
(JSC::TemplateRegistryKey::Hasher::hash):
(JSC::TemplateRegistryKey::Hasher::equal):
(JSC::TemplateRegistryKey::TemplateRegistryKey):
* runtime/VM.cpp:
(JSC::VM::VM):
* runtime/VM.h:
* tests/stress/tagged-templates-identity.js: Added.
(shouldBe):
* tests/stress/tagged-templates-raw-strings.js: Added.
(shouldBe):
(tag):
(testEval):
* tests/stress/tagged-templates-syntax.js: Added.
(tag):
(testSyntax):
(testSyntaxError):
* tests/stress/tagged-templates-template-object.js: Added.
(shouldBe):
(tag):
* tests/stress/tagged-templates-this.js: Added.
(shouldBe):
(tag):
* tests/stress/tagged-templates.js: Added.
(shouldBe):
(raw):
(cooked):
(Counter):

git-svn-id: https://svn.webkit.org/repository/webkit/trunk@184337 268f45cc-cd09-0410-ab3c-d52691b4dbfc

35 files changed:
Source/JavaScriptCore/CMakeLists.txt
Source/JavaScriptCore/ChangeLog
Source/JavaScriptCore/JavaScriptCore.vcxproj/JavaScriptCore.vcxproj
Source/JavaScriptCore/JavaScriptCore.vcxproj/JavaScriptCore.vcxproj.filters
Source/JavaScriptCore/JavaScriptCore.xcodeproj/project.pbxproj
Source/JavaScriptCore/bytecompiler/BytecodeGenerator.cpp
Source/JavaScriptCore/bytecompiler/BytecodeGenerator.h
Source/JavaScriptCore/bytecompiler/NodesCodegen.cpp
Source/JavaScriptCore/parser/ASTBuilder.h
Source/JavaScriptCore/parser/Lexer.cpp
Source/JavaScriptCore/parser/Lexer.h
Source/JavaScriptCore/parser/NodeConstructors.h
Source/JavaScriptCore/parser/Nodes.h
Source/JavaScriptCore/parser/Parser.cpp
Source/JavaScriptCore/parser/Parser.h
Source/JavaScriptCore/parser/ParserArena.h
Source/JavaScriptCore/parser/SyntaxChecker.h
Source/JavaScriptCore/runtime/CommonIdentifiers.h
Source/JavaScriptCore/runtime/JSGlobalObject.cpp
Source/JavaScriptCore/runtime/JSGlobalObject.h
Source/JavaScriptCore/runtime/JSTemplateRegistryKey.cpp [new file with mode: 0644]
Source/JavaScriptCore/runtime/JSTemplateRegistryKey.h [new file with mode: 0644]
Source/JavaScriptCore/runtime/ObjectConstructor.cpp
Source/JavaScriptCore/runtime/ObjectConstructor.h
Source/JavaScriptCore/runtime/TemplateRegistry.cpp [new file with mode: 0644]
Source/JavaScriptCore/runtime/TemplateRegistry.h [new file with mode: 0644]
Source/JavaScriptCore/runtime/TemplateRegistryKey.h [new file with mode: 0644]
Source/JavaScriptCore/runtime/VM.cpp
Source/JavaScriptCore/runtime/VM.h
Source/JavaScriptCore/tests/stress/tagged-templates-identity.js [new file with mode: 0644]
Source/JavaScriptCore/tests/stress/tagged-templates-raw-strings.js [new file with mode: 0644]
Source/JavaScriptCore/tests/stress/tagged-templates-syntax.js [new file with mode: 0644]
Source/JavaScriptCore/tests/stress/tagged-templates-template-object.js [new file with mode: 0644]
Source/JavaScriptCore/tests/stress/tagged-templates-this.js [new file with mode: 0644]
Source/JavaScriptCore/tests/stress/tagged-templates.js [new file with mode: 0644]

index e532154..c336de6 100644 (file)
@@ -512,6 +512,7 @@ set(JavaScriptCore_RUNTIME_SOURCES
     runtime/JSStringIterator.cpp
     runtime/JSStringJoiner.cpp
     runtime/JSSymbolTableObject.cpp
+    runtime/JSTemplateRegistryKey.cpp
     runtime/JSTypedArrayConstructors.cpp
     runtime/JSTypedArrayPrototypes.cpp
     runtime/JSTypedArrays.cpp
@@ -578,6 +579,7 @@ set(JavaScriptCore_RUNTIME_SOURCES
     runtime/SymbolObject.cpp
     runtime/SymbolPrototype.cpp
     runtime/SymbolTable.cpp
+    runtime/TemplateRegistry.cpp
     runtime/TestRunnerUtils.cpp
     runtime/TypeLocationCache.cpp
     runtime/TypeProfiler.cpp
index 2992f80..69bb280 100644 (file)
@@ -1,3 +1,147 @@
+2015-05-14  Yusuke Suzuki  <utatane.tea@gmail.com>
+
+        [ES6] Implement tagged templates
+        https://bugs.webkit.org/show_bug.cgi?id=143183
+
+        Reviewed by Oliver Hunt.
+
+        This patch implements ES6 tagged templates.
+        In tagged templates, the function takes the template object.
+
+        The template object contains the raw and cooked template strings,
+        so when parsing the tagged templates, we need to tokenize the raw and cooked strings.
+        While tagged templates require the both strings, the template literal only requires
+        the cooked strings. So when tokenizing under the template literal context,
+        we only builds the cooked strings.
+
+        As per ES6 spec, the template objects for the same raw strings are shared in the same realm.
+        The template objects is cached. And every time we evaluate the same tagged templates,
+        the same (cached) template objects are used.
+        Since the spec freezes this template objects completely,
+        we cannot attach some properties to it.
+        So we can say that it behaves as if the template objects are the primitive values (like JSString).
+        Since we cannot attach properties, the only way to test the identity of the template object is comparing. (===)
+        As the result, when there is no reference to the template object, we can garbage collect it
+        because the user has no way to test that the newly created template object does not equal
+        to the already collected template object.
+
+        So, to implement tagged templates, we implement the following components.
+
+        1. JSTemplateRegistryKey
+        It holds the template registry key and it does not exposed to users.
+        TemplateRegistryKey holds the vector of raw and cooked strings with the pre-computed hash value.
+        When obtaining the template object for the (statically, a.k.a. at the parsing time) given raw string vectors,
+        we use this JSTemplateRegistryKey as a key to the map and look up the template object from
+        TemplateRegistry.
+        JSTemplateRegistryKey is created at the bytecode compiling time and
+        stored in the CodeBlock as like as JSString content values.
+
+        2. TemplateRegistry
+        This manages the cached template objects.
+        It holds the weak map (JSTemplateRegistryKey -> the template object).
+        The template object is weakly referenced.
+        So if there is no reference to the template object,
+        the template object is automatically GC-ed.
+        When looking up the template object, it searches the cached template object.
+        If it is found, it is returned to the users.
+        If there is no cached template objects, it creates the new template object and
+        stores it with the given template registry key.
+
+        * CMakeLists.txt:
+        * JavaScriptCore.vcxproj/JavaScriptCore.vcxproj:
+        * JavaScriptCore.vcxproj/JavaScriptCore.vcxproj.filters:
+        * JavaScriptCore.xcodeproj/project.pbxproj:
+        * bytecompiler/BytecodeGenerator.cpp:
+        (JSC::BytecodeGenerator::addTemplateRegistryKeyConstant):
+        (JSC::BytecodeGenerator::emitGetTemplateObject):
+        * bytecompiler/BytecodeGenerator.h:
+        * bytecompiler/NodesCodegen.cpp:
+        (JSC::TaggedTemplateNode::emitBytecode):
+        (JSC::TemplateLiteralNode::emitBytecode): Deleted.
+        * parser/ASTBuilder.h:
+        (JSC::ASTBuilder::createTaggedTemplate):
+        (JSC::ASTBuilder::createTemplateLiteral): Deleted.
+        * parser/Lexer.cpp:
+        (JSC::Lexer<T>::setCode):
+        (JSC::Lexer<T>::parseTemplateLiteral):
+        (JSC::Lexer<T>::lex):
+        (JSC::Lexer<T>::scanTrailingTemplateString):
+        (JSC::Lexer<T>::clear):
+        * parser/Lexer.h:
+        (JSC::Lexer<T>::makeEmptyIdentifier):
+        * parser/NodeConstructors.h:
+        (JSC::TaggedTemplateNode::TaggedTemplateNode):
+        (JSC::TemplateLiteralNode::TemplateLiteralNode): Deleted.
+        * parser/Nodes.h:
+        (JSC::TemplateLiteralNode::templateStrings):
+        (JSC::TemplateLiteralNode::templateExpressions):
+        (JSC::TaggedTemplateNode::templateLiteral):
+        * parser/Parser.cpp:
+        (JSC::Parser<LexerType>::parseTemplateString):
+        (JSC::Parser<LexerType>::parseTemplateLiteral):
+        (JSC::Parser<LexerType>::parsePrimaryExpression):
+        (JSC::Parser<LexerType>::parseMemberExpression):
+        * parser/Parser.h:
+        * parser/ParserArena.h:
+        (JSC::IdentifierArena::makeEmptyIdentifier):
+        * parser/SyntaxChecker.h:
+        (JSC::SyntaxChecker::createTaggedTemplate):
+        (JSC::SyntaxChecker::createTemplateLiteral): Deleted.
+        * runtime/CommonIdentifiers.h:
+        * runtime/JSGlobalObject.cpp:
+        (JSC::getTemplateObject):
+        (JSC::JSGlobalObject::JSGlobalObject):
+        (JSC::JSGlobalObject::init):
+        * runtime/JSGlobalObject.h:
+        (JSC::JSGlobalObject::templateRegistry):
+        * runtime/JSTemplateRegistryKey.cpp: Added.
+        (JSC::JSTemplateRegistryKey::JSTemplateRegistryKey):
+        (JSC::JSTemplateRegistryKey::create):
+        (JSC::JSTemplateRegistryKey::destroy):
+        * runtime/JSTemplateRegistryKey.h: Added.
+        * runtime/ObjectConstructor.cpp:
+        (JSC::objectConstructorFreeze):
+        * runtime/ObjectConstructor.h:
+        * runtime/TemplateRegistry.cpp: Added.
+        (JSC::TemplateRegistry::TemplateRegistry):
+        (JSC::TemplateRegistry::getTemplateObject):
+        * runtime/TemplateRegistry.h: Added.
+        * runtime/TemplateRegistryKey.h: Added.
+        (JSC::TemplateRegistryKey::isDeletedValue):
+        (JSC::TemplateRegistryKey::isEmptyValue):
+        (JSC::TemplateRegistryKey::hash):
+        (JSC::TemplateRegistryKey::rawStrings):
+        (JSC::TemplateRegistryKey::cookedStrings):
+        (JSC::TemplateRegistryKey::operator==):
+        (JSC::TemplateRegistryKey::operator!=):
+        (JSC::TemplateRegistryKey::Hasher::hash):
+        (JSC::TemplateRegistryKey::Hasher::equal):
+        (JSC::TemplateRegistryKey::TemplateRegistryKey):
+        * runtime/VM.cpp:
+        (JSC::VM::VM):
+        * runtime/VM.h:
+        * tests/stress/tagged-templates-identity.js: Added.
+        (shouldBe):
+        * tests/stress/tagged-templates-raw-strings.js: Added.
+        (shouldBe):
+        (tag):
+        (testEval):
+        * tests/stress/tagged-templates-syntax.js: Added.
+        (tag):
+        (testSyntax):
+        (testSyntaxError):
+        * tests/stress/tagged-templates-template-object.js: Added.
+        (shouldBe):
+        (tag):
+        * tests/stress/tagged-templates-this.js: Added.
+        (shouldBe):
+        (tag):
+        * tests/stress/tagged-templates.js: Added.
+        (shouldBe):
+        (raw):
+        (cooked):
+        (Counter):
+
 2015-05-13  Ryosuke Niwa  <rniwa@webkit.org>
 
         REGRESSION(r180595): same-callee profiling no longer works
index 76bd886..fe0800d 100644 (file)
     <ClCompile Include="..\runtime\JSStringIterator.cpp" />
     <ClCompile Include="..\runtime\JSStringJoiner.cpp" />
     <ClCompile Include="..\runtime\JSSymbolTableObject.cpp" />
+    <ClCompile Include="..\runtime\JSTemplateRegistryKey.cpp" />
     <ClCompile Include="..\runtime\JSTypedArrayConstructors.cpp" />
     <ClCompile Include="..\runtime\JSTypedArrayPrototypes.cpp" />
     <ClCompile Include="..\runtime\JSTypedArrays.cpp" />
     <ClCompile Include="..\runtime\SymbolObject.cpp" />
     <ClCompile Include="..\runtime\SymbolPrototype.cpp" />
     <ClCompile Include="..\runtime\SymbolTable.cpp" />
+    <ClCompile Include="..\runtime\TemplateRegistry.cpp" />
     <ClCompile Include="..\runtime\TestRunnerUtils.cpp" />
     <ClCompile Include="..\runtime\TypedArrayController.cpp" />
     <ClCompile Include="..\runtime\TypedArrayType.cpp" />
     <ClInclude Include="..\runtime\JSStringIterator.h" />
     <ClInclude Include="..\runtime\JSStringJoiner.h" />
     <ClInclude Include="..\runtime\JSSymbolTableObject.h" />
+    <ClInclude Include="..\runtime\JSTemplateRegistryKey.h" />
     <ClInclude Include="..\runtime\JSType.h" />
     <ClInclude Include="..\runtime\JSTypeInfo.h" />
     <ClInclude Include="..\runtime\JSTypedArrayConstructors.h" />
     <ClInclude Include="..\runtime\SymbolObject.h" />
     <ClInclude Include="..\runtime\SymbolPrototype.h" />
     <ClInclude Include="..\runtime\SymbolTable.h" />
+    <ClInclude Include="..\runtime\TemplateRegistry.h" />
+    <ClInclude Include="..\runtime\TemplateRegistryKey.h" />
     <ClInclude Include="..\runtime\TestRunnerUtils.h" />
     <ClInclude Include="..\runtime\Tracing.h" />
     <ClInclude Include="..\runtime\ToNativeFromValue.h" />
index 538dae4..8859512 100644 (file)
     <ClCompile Include="..\runtime\JSSymbolTableObject.cpp">
       <Filter>runtime</Filter>
     </ClCompile>
+    <ClCompile Include="..\runtime\JSTemplateRegistryKey.cpp">
+      <Filter>runtime</Filter>
+    </ClCompile>
     <ClCompile Include="..\runtime\JSEnvironmentRecord.cpp">
       <Filter>runtime</Filter>
     </ClCompile>
     <ClCompile Include="..\runtime\SymbolTable.cpp">
       <Filter>runtime</Filter>
     </ClCompile>
+    <ClCompile Include="..\runtime\TemplateRegistry.cpp">
+      <Filter>runtime</Filter>
+    </ClCompile>
     <ClCompile Include="..\runtime\TypeLocationCache.cpp">
       <Filter>runtime</Filter>
     </ClCompile>
     <ClInclude Include="..\runtime\JSSymbolTableObject.h">
       <Filter>runtime</Filter>
     </ClInclude>
+    <ClInclude Include="..\runtime\JSTemplateRegistryKey.h">
+      <Filter>runtime</Filter>
+    </ClInclude>
     <ClInclude Include="..\runtime\JSType.h">
       <Filter>runtime</Filter>
     </ClInclude>
     <ClInclude Include="..\runtime\SymbolTable.h">
       <Filter>runtime</Filter>
     </ClInclude>
+    <ClInclude Include="..\runtime\TemplateRegistry.h">
+      <Filter>runtime</Filter>
+    </ClInclude>
+    <ClInclude Include="..\runtime\TemplateRegistryKey.h">
+      <Filter>runtime</Filter>
+    </ClInclude>
     <ClInclude Include="..\runtime\Tracing.h">
       <Filter>runtime</Filter>
     </ClInclude>
index 3192e69..5802f7c 100644 (file)
                70EC0EC51AA0D7DA00B6AAFA /* StringIteratorConstructor.h in Headers */ = {isa = PBXBuildFile; fileRef = 70EC0EBF1AA0D7DA00B6AAFA /* StringIteratorConstructor.h */; settings = {ATTRIBUTES = (Private, ); }; };
                70EC0EC61AA0D7DA00B6AAFA /* StringIteratorPrototype.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 70EC0EC01AA0D7DA00B6AAFA /* StringIteratorPrototype.cpp */; };
                70EC0EC71AA0D7DA00B6AAFA /* StringIteratorPrototype.h in Headers */ = {isa = PBXBuildFile; fileRef = 70EC0EC11AA0D7DA00B6AAFA /* StringIteratorPrototype.h */; settings = {ATTRIBUTES = (Private, ); }; };
+               70ECA6051AFDBEA200449739 /* JSTemplateRegistryKey.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 70ECA6001AFDBEA200449739 /* JSTemplateRegistryKey.cpp */; };
+               70ECA6061AFDBEA200449739 /* JSTemplateRegistryKey.h in Headers */ = {isa = PBXBuildFile; fileRef = 70ECA6011AFDBEA200449739 /* JSTemplateRegistryKey.h */; settings = {ATTRIBUTES = (Private, ); }; };
+               70ECA6071AFDBEA200449739 /* TemplateRegistry.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 70ECA6021AFDBEA200449739 /* TemplateRegistry.cpp */; };
+               70ECA6081AFDBEA200449739 /* TemplateRegistry.h in Headers */ = {isa = PBXBuildFile; fileRef = 70ECA6031AFDBEA200449739 /* TemplateRegistry.h */; settings = {ATTRIBUTES = (Private, ); }; };
+               70ECA6091AFDBEA200449739 /* TemplateRegistryKey.h in Headers */ = {isa = PBXBuildFile; fileRef = 70ECA6041AFDBEA200449739 /* TemplateRegistryKey.h */; settings = {ATTRIBUTES = (Private, ); }; };
                7C008CD2186F8A9300955C24 /* JSPromiseFunctions.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 7C008CD0186F8A9300955C24 /* JSPromiseFunctions.cpp */; };
                7C008CD3186F8A9300955C24 /* JSPromiseFunctions.h in Headers */ = {isa = PBXBuildFile; fileRef = 7C008CD1186F8A9300955C24 /* JSPromiseFunctions.h */; settings = {ATTRIBUTES = (Private, ); }; };
                7C008CDA187124BB00955C24 /* JSPromiseDeferred.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 7C008CD8187124BB00955C24 /* JSPromiseDeferred.cpp */; };
                70EC0EBF1AA0D7DA00B6AAFA /* StringIteratorConstructor.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = StringIteratorConstructor.h; sourceTree = "<group>"; };
                70EC0EC01AA0D7DA00B6AAFA /* StringIteratorPrototype.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = StringIteratorPrototype.cpp; sourceTree = "<group>"; };
                70EC0EC11AA0D7DA00B6AAFA /* StringIteratorPrototype.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = StringIteratorPrototype.h; sourceTree = "<group>"; };
+               70ECA6001AFDBEA200449739 /* JSTemplateRegistryKey.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = JSTemplateRegistryKey.cpp; sourceTree = "<group>"; };
+               70ECA6011AFDBEA200449739 /* JSTemplateRegistryKey.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = JSTemplateRegistryKey.h; sourceTree = "<group>"; };
+               70ECA6021AFDBEA200449739 /* TemplateRegistry.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = TemplateRegistry.cpp; sourceTree = "<group>"; };
+               70ECA6031AFDBEA200449739 /* TemplateRegistry.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = TemplateRegistry.h; sourceTree = "<group>"; };
+               70ECA6041AFDBEA200449739 /* TemplateRegistryKey.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = TemplateRegistryKey.h; sourceTree = "<group>"; };
                7C008CD0186F8A9300955C24 /* JSPromiseFunctions.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; lineEnding = 0; path = JSPromiseFunctions.cpp; sourceTree = "<group>"; xcLanguageSpecificationIdentifier = xcode.lang.cpp; };
                7C008CD1186F8A9300955C24 /* JSPromiseFunctions.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = JSPromiseFunctions.h; sourceTree = "<group>"; };
                7C008CD8187124BB00955C24 /* JSPromiseDeferred.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = JSPromiseDeferred.cpp; sourceTree = "<group>"; };
                                2600B5A5152BAAA70091EE5F /* JSStringJoiner.h */,
                                0F919D09157EE09D004A4E7D /* JSSymbolTableObject.cpp */,
                                0F919D0A157EE09D004A4E7D /* JSSymbolTableObject.h */,
+                               70ECA6001AFDBEA200449739 /* JSTemplateRegistryKey.cpp */,
+                               70ECA6011AFDBEA200449739 /* JSTemplateRegistryKey.h */,
                                14ABB454099C2A0F00E2A24F /* JSType.h */,
                                0F2B66CC17B6B5AB00A7AE3F /* JSTypedArrayConstructors.cpp */,
                                0F2B66CD17B6B5AB00A7AE3F /* JSTypedArrayConstructors.h */,
                                705B41AA1A6E501E00716757 /* SymbolPrototype.h */,
                                0F919D2715856770004A4E7D /* SymbolTable.cpp */,
                                14A396A60CD2933100B5B4FF /* SymbolTable.h */,
+                               70ECA6021AFDBEA200449739 /* TemplateRegistry.cpp */,
+                               70ECA6031AFDBEA200449739 /* TemplateRegistry.h */,
+                               70ECA6041AFDBEA200449739 /* TemplateRegistryKey.h */,
                                0FA2C17917D7CF84009D015F /* TestRunnerUtils.cpp */,
                                0FA2C17A17D7CF84009D015F /* TestRunnerUtils.h */,
                                0F55989717C86C5600A1E543 /* ToNativeFromValue.h */,
                                969A07970ED1D3AE00F1F681 /* CodeBlock.h in Headers */,
                                0F8F94411667633200D61971 /* CodeBlockHash.h in Headers */,
                                0FC97F34182020D7002C9B26 /* CodeBlockJettisoningWatchpoint.h in Headers */,
+                               70ECA6081AFDBEA200449739 /* TemplateRegistry.h in Headers */,
                                0FD8A31417D4326C00CA2C40 /* CodeBlockSet.h in Headers */,
                                0F96EBB316676EF6008BADE3 /* CodeBlockWithJITType.h in Headers */,
                                A77F1822164088B200640A47 /* CodeCache.h in Headers */,
                                BC02E9130E1839DB000F9297 /* NativeErrorPrototype.h in Headers */,
                                99CC0B6318BE9950006CEBCC /* CodeGeneratorReplayInputs.py in Headers */,
                                0FFB922016D033B70055A5DB /* NodeConstructors.h in Headers */,
+                               70ECA6091AFDBEA200449739 /* TemplateRegistryKey.h in Headers */,
                                BC18C43F0E16F5CD00B34460 /* Nodes.h in Headers */,
                                99E45A2818A1B2590026D88F /* NondeterministicInput.h in Headers */,
                                0FC3CD0219ADA411006AC72A /* DFGNaiveDominators.h in Headers */,
                                A1712B4111C7B235007A5315 /* RegExpKey.h in Headers */,
                                BC18C45B0E16F5CD00B34460 /* RegExpObject.h in Headers */,
                                BC18C52C0E16FCD200B34460 /* RegExpObject.lut.h in Headers */,
+                               70ECA6061AFDBEA200449739 /* JSTemplateRegistryKey.h in Headers */,
                                BCD202C40E1706A7002C7E82 /* RegExpPrototype.h in Headers */,
                                BC18C45D0E16F5CD00B34460 /* Register.h in Headers */,
                                969A072B0ED1CE6900F1F681 /* RegisterID.h in Headers */,
                                0FD8A32517D51F5700CA2C40 /* DFGOSREntrypointCreationPhase.cpp in Sources */,
                                FE5068671AE25E280009DAB7 /* DeferredSourceDump.cpp in Sources */,
                                0FC09791146A6F7100CF2442 /* DFGOSRExit.cpp in Sources */,
+                               70ECA6071AFDBEA200449739 /* TemplateRegistry.cpp in Sources */,
                                0F235BEB17178E7300690C7F /* DFGOSRExitBase.cpp in Sources */,
                                0FC09792146A6F7300CF2442 /* DFGOSRExitCompiler.cpp in Sources */,
                                0FC09776146943B000CF2442 /* DFGOSRExitCompiler32_64.cpp in Sources */,
                                0FB1058B1675483100F8AB6E /* ProfilerOSRExit.cpp in Sources */,
                                0FB1058D1675483700F8AB6E /* ProfilerOSRExitSite.cpp in Sources */,
                                0F13912B16771C3A009CCB07 /* ProfilerProfiledBytecodes.cpp in Sources */,
+                               70ECA6051AFDBEA200449739 /* JSTemplateRegistryKey.cpp in Sources */,
                                A7FB60A4103F7DC20017A286 /* PropertyDescriptor.cpp in Sources */,
                                14469DE8107EC7E700650446 /* PropertySlot.cpp in Sources */,
                                ADE39FFF16DD144B0003CD4A /* PropertyTable.cpp in Sources */,
index 5fe9e0e..14a1e97 100644 (file)
@@ -36,6 +36,7 @@
 #include "JSFunction.h"
 #include "JSLexicalEnvironment.h"
 #include "JSNameScope.h"
+#include "JSTemplateRegistryKey.h"
 #include "LowLevelInterpreter.h"
 #include "JSCInlines.h"
 #include "Options.h"
@@ -1717,6 +1718,16 @@ JSString* BytecodeGenerator::addStringConstant(const Identifier& identifier)
     return stringInMap;
 }
 
+JSTemplateRegistryKey* BytecodeGenerator::addTemplateRegistryKeyConstant(const TemplateRegistryKey& templateRegistryKey)
+{
+    JSTemplateRegistryKey*& templateRegistryKeyInMap = m_templateRegistryKeyMap.add(templateRegistryKey, nullptr).iterator->value;
+    if (!templateRegistryKeyInMap) {
+        templateRegistryKeyInMap = JSTemplateRegistryKey::create(*vm(), templateRegistryKey);
+        addConstantValue(templateRegistryKeyInMap);
+    }
+    return templateRegistryKeyInMap;
+}
+
 RegisterID* BytecodeGenerator::emitNewArray(RegisterID* dst, ElementNode* elements, unsigned length)
 {
 #if !ASSERT_DISABLED
@@ -2797,6 +2808,33 @@ void BytecodeGenerator::emitEnumeration(ThrowableExpressionData* node, Expressio
     emitLabel(loopDone.get());
 }
 
+RegisterID* BytecodeGenerator::emitGetTemplateObject(RegisterID* dst, TaggedTemplateNode* taggedTemplate)
+{
+    TemplateRegistryKey::StringVector rawStrings;
+    TemplateRegistryKey::StringVector cookedStrings;
+
+    TemplateStringListNode* templateString = taggedTemplate->templateLiteral()->templateStrings();
+    for (; templateString; templateString = templateString->next()) {
+        rawStrings.append(templateString->value()->raw().impl());
+        cookedStrings.append(templateString->value()->cooked().impl());
+    }
+
+    RefPtr<RegisterID> getTemplateObject = nullptr;
+    Variable var = variable(propertyNames().getTemplateObjectPrivateName);
+    if (RegisterID* local = var.local())
+        getTemplateObject = emitMove(newTemporary(), local);
+    else {
+        getTemplateObject = newTemporary();
+        RefPtr<RegisterID> scope = newTemporary();
+        moveToDestinationIfNeeded(scope.get(), emitResolveScope(scope.get(), var));
+        emitGetFromScope(getTemplateObject.get(), scope.get(), var, ThrowIfNotFound);
+    }
+
+    CallArguments arguments(*this, nullptr);
+    emitLoad(arguments.thisRegister(), JSValue(addTemplateRegistryKeyConstant(TemplateRegistryKey(rawStrings, cookedStrings))));
+    return emitCall(dst, getTemplateObject.get(), NoExpectedFunction, arguments, taggedTemplate->divot(), taggedTemplate->divotStart(), taggedTemplate->divotEnd());
+}
+
 RegisterID* BytecodeGenerator::emitGetEnumerableLength(RegisterID* dst, RegisterID* base)
 {
     emitOpcode(op_get_enumerable_length);
index 85dfb9e..46f8e25 100644 (file)
@@ -43,6 +43,7 @@
 #include "Debugger.h"
 #include "Nodes.h"
 #include "StaticPropertyAnalyzer.h"
+#include "TemplateRegistryKey.h"
 #include "UnlinkedCodeBlock.h"
 
 #include <functional>
@@ -55,6 +56,7 @@
 namespace JSC {
 
     class Identifier;
+    class JSTemplateRegistryKey;
 
     enum ExpectedFunction {
         NoExpectedFunction,
@@ -501,6 +503,7 @@ namespace JSC {
             RegisterID* valueRegister, RegisterID* getterRegister, RegisterID* setterRegister, unsigned options, const JSTextPosition&);
 
         void emitEnumeration(ThrowableExpressionData* enumerationNode, ExpressionNode* subjectNode, const std::function<void(BytecodeGenerator&, RegisterID*)>& callBack);
+        RegisterID* emitGetTemplateObject(RegisterID* dst, TaggedTemplateNode*);
         
         RegisterID* emitReturn(RegisterID* src);
         RegisterID* emitEnd(RegisterID* src) { return emitUnaryNoDstOp(op_end, src); }
@@ -622,6 +625,7 @@ namespace JSC {
 
         typedef HashMap<double, JSValue> NumberMap;
         typedef HashMap<StringImpl*, JSString*, IdentifierRepHash> IdentifierStringMap;
+        typedef HashMap<TemplateRegistryKey, JSTemplateRegistryKey*> TemplateRegistryKeyMap;
         
         // Helper for emitCall() and emitConstruct(). This works because the set of
         // expected functions have identical behavior for both call and construct
@@ -676,6 +680,7 @@ namespace JSC {
 
     public:
         JSString* addStringConstant(const Identifier&);
+        JSTemplateRegistryKey* addTemplateRegistryKeyConstant(const TemplateRegistryKey&);
 
         Vector<UnlinkedInstruction, 0, UnsafeVectorOverflow>& instructions() { return m_instructions; }
 
@@ -765,6 +770,7 @@ namespace JSC {
         typedef HashMap<EncodedJSValueWithRepresentation, unsigned, EncodedJSValueWithRepresentationHash, EncodedJSValueWithRepresentationHashTraits> JSValueMap;
         JSValueMap m_jsValueMap;
         IdentifierStringMap m_stringMap;
+        TemplateRegistryKeyMap m_templateRegistryKeyMap;
 
         StaticPropertyAnalyzer m_staticPropertyAnalyzer { &m_instructions };
 
index ff210c3..e97d393 100644 (file)
 #include "LabelScope.h"
 #include "Lexer.h"
 #include "JSCInlines.h"
+#include "JSTemplateRegistryKey.h"
 #include "Parser.h"
 #include "PropertyNameArray.h"
 #include "RegExpCache.h"
 #include "RegExpObject.h"
 #include "SamplingTool.h"
 #include "StackAlignment.h"
+#include "TemplateRegistryKey.h"
 #include <wtf/Assertions.h>
 #include <wtf/RefCountedLeakCounter.h>
 #include <wtf/Threading.h>
@@ -260,6 +262,64 @@ RegisterID* TemplateLiteralNode::emitBytecode(BytecodeGenerator& generator, Regi
 
     return generator.emitStrcat(generator.finalDestination(dst, temporaryRegisters[0].get()), temporaryRegisters[0].get(), temporaryRegisters.size());
 }
+
+// ------------------------------ TaggedTemplateNode -----------------------------------
+
+RegisterID* TaggedTemplateNode::emitBytecode(BytecodeGenerator& generator, RegisterID* dst)
+{
+    ExpectedFunction expectedFunction = NoExpectedFunction;
+    RefPtr<RegisterID> tag = nullptr;
+    RefPtr<RegisterID> base = nullptr;
+    if (!m_tag->isLocation()) {
+        tag = generator.emitNode(generator.newTemporary(), m_tag);
+    } else if (m_tag->isResolveNode()) {
+        ResolveNode* resolve = static_cast<ResolveNode*>(m_tag);
+        const Identifier& identifier = resolve->identifier();
+        expectedFunction = generator.expectedFunctionForIdentifier(identifier);
+
+        Variable var = generator.variable(identifier);
+        if (RegisterID* local = var.local())
+            tag = generator.emitMove(generator.newTemporary(), local);
+        else {
+            tag = generator.newTemporary();
+            base = generator.newTemporary();
+
+            JSTextPosition newDivot = divotStart() + identifier.length();
+            generator.emitExpressionInfo(newDivot, divotStart(), newDivot);
+            generator.moveToDestinationIfNeeded(base.get(), generator.emitResolveScope(base.get(), var));
+            generator.emitGetFromScope(tag.get(), base.get(), var, ThrowIfNotFound);
+        }
+    } else if (m_tag->isBracketAccessorNode()) {
+        BracketAccessorNode* bracket = static_cast<BracketAccessorNode*>(m_tag);
+        base = generator.emitNode(generator.newTemporary(), bracket->base());
+        RefPtr<RegisterID> property = generator.emitNode(bracket->subscript());
+        tag = generator.emitGetByVal(generator.newTemporary(), base.get(), property.get());
+    } else {
+        ASSERT(m_tag->isDotAccessorNode());
+        DotAccessorNode* dot = static_cast<DotAccessorNode*>(m_tag);
+        base = generator.emitNode(generator.newTemporary(), dot->base());
+        tag = generator.emitGetById(generator.newTemporary(), base.get(), dot->identifier());
+    }
+
+    RefPtr<RegisterID> templateObject = generator.emitGetTemplateObject(generator.newTemporary(), this);
+
+    unsigned expressionsCount = 0;
+    for (TemplateExpressionListNode* templateExpression = m_templateLiteral->templateExpressions(); templateExpression; templateExpression = templateExpression->next())
+        ++expressionsCount;
+
+    CallArguments callArguments(generator, nullptr, 1 + expressionsCount);
+    if (base)
+        generator.emitMove(callArguments.thisRegister(), base.get());
+    else
+        generator.emitLoad(callArguments.thisRegister(), jsUndefined());
+
+    unsigned argumentIndex = 0;
+    generator.emitMove(callArguments.argumentRegister(argumentIndex++), templateObject.get());
+    for (TemplateExpressionListNode* templateExpression = m_templateLiteral->templateExpressions(); templateExpression; templateExpression = templateExpression->next())
+        generator.emitNode(callArguments.argumentRegister(argumentIndex++), templateExpression->value());
+
+    return generator.emitCall(generator.finalDestination(dst, tag.get()), tag.get(), expectedFunction, callArguments, divot(), divotStart(), divotEnd());
+}
 #endif
 
 // ------------------------------ ArrayNode ------------------------------------
index c494536..fbcbe26 100644 (file)
@@ -288,6 +288,13 @@ public:
     {
         return new (m_parserArena) TemplateLiteralNode(location, templateStringList, templateExpressionList);
     }
+
+    ExpressionNode* createTaggedTemplate(const JSTokenLocation& location, ExpressionNode* base, TemplateLiteralNode* templateLiteral, const JSTextPosition& start, const JSTextPosition& divot, const JSTextPosition& end)
+    {
+        auto node = new (m_parserArena) TaggedTemplateNode(location, base, templateLiteral);
+        setExceptionLocation(node, start, divot, end);
+        return node;
+    }
 #endif
 
     ExpressionNode* createRegExp(const JSTokenLocation& location, const Identifier& pattern, const Identifier& flags, const JSTextPosition& start)
index 96eb7aa..7b119ba 100644 (file)
@@ -569,6 +569,7 @@ void Lexer<T>::setCode(const SourceCode& source, ParserArena* arena)
     
     m_buffer8.reserveInitialCapacity(initialReadBufferCapacity);
     m_buffer16.reserveInitialCapacity((m_codeEnd - m_code) / 2);
+    m_bufferForRawTemplateString16.reserveInitialCapacity(initialReadBufferCapacity);
     
     if (LIKELY(m_code < m_codeEnd))
         m_current = *m_code;
@@ -1374,7 +1375,7 @@ private:
 };
 
 template <typename T>
-template <bool shouldBuildStrings> typename Lexer<T>::StringParseResult Lexer<T>::parseTemplateLiteral(JSTokenData* tokenData)
+template <bool shouldBuildStrings> typename Lexer<T>::StringParseResult Lexer<T>::parseTemplateLiteral(JSTokenData* tokenData, RawStringsBuildMode rawStringsBuildMode)
 {
     const T* stringStart = currentSourcePtr();
     const T* rawStringStart = currentSourcePtr();
@@ -1435,10 +1436,16 @@ template <bool shouldBuildStrings> typename Lexer<T>::StringParseResult Lexer<T>
             if (isLineTerminator(m_current)) {
                 if (m_current == '\r') {
                     // Normalize <CR>, <CR><LF> to <LF>.
-                    if (stringStart != currentSourcePtr() && shouldBuildStrings)
-                        append16(stringStart, currentSourcePtr() - stringStart);
-                    if (shouldBuildStrings)
+                    if (shouldBuildStrings) {
+                        if (stringStart != currentSourcePtr())
+                            append16(stringStart, currentSourcePtr() - stringStart);
+                        if (rawStringStart != currentSourcePtr() && rawStringsBuildMode == RawStringsBuildMode::BuildRawStrings)
+                            m_bufferForRawTemplateString16.append(rawStringStart, currentSourcePtr() - rawStringStart);
+
                         record16('\n');
+                        if (rawStringsBuildMode == RawStringsBuildMode::BuildRawStrings)
+                            m_bufferForRawTemplateString16.append('\n');
+                    }
                     lineNumberAdder.add(m_current);
                     shift();
                     if (m_current == '\n') {
@@ -1446,6 +1453,7 @@ template <bool shouldBuildStrings> typename Lexer<T>::StringParseResult Lexer<T>
                         shift();
                     }
                     stringStart = currentSourcePtr();
+                    rawStringStart = currentSourcePtr();
                 } else {
                     lineNumberAdder.add(m_current);
                     shift();
@@ -1461,24 +1469,28 @@ template <bool shouldBuildStrings> typename Lexer<T>::StringParseResult Lexer<T>
 
     bool isTail = m_current == '`';
 
-    if (currentSourcePtr() != stringStart && shouldBuildStrings)
-        append16(stringStart, currentSourcePtr() - stringStart);
+    if (shouldBuildStrings) {
+        if (currentSourcePtr() != stringStart)
+            append16(stringStart, currentSourcePtr() - stringStart);
+        if (rawStringStart != currentSourcePtr() && rawStringsBuildMode == RawStringsBuildMode::BuildRawStrings)
+            m_bufferForRawTemplateString16.append(rawStringStart, currentSourcePtr() - rawStringStart);
+    }
 
     if (shouldBuildStrings) {
         tokenData->cooked = makeIdentifier(m_buffer16.data(), m_buffer16.size());
-        // TODO: While line terminator normalization (e.g. <CR> => <LF>) should be applied to both the raw and cooked representations,
-        // this raw implementation just slices the source string. As a result, line terminators appear in the raw representation without normalization.
-        // For example, when parsing `<CR>`, <CR> appears in the raw representation.
-        // While non-tagged template literals don't use the raw representation, tagged templates use the raw representation.
-        // So line terminator normalization should be applied to the raw representation when implementing tagged templates.
-        tokenData->raw = makeIdentifier(rawStringStart, currentSourcePtr() - rawStringStart);
+        // Line terminator normalization (e.g. <CR> => <LF>) should be applied to both the raw and cooked representations.
+        if (rawStringsBuildMode == RawStringsBuildMode::BuildRawStrings)
+            tokenData->raw = makeIdentifier(m_bufferForRawTemplateString16.data(), m_bufferForRawTemplateString16.size());
+        else
+            tokenData->raw = makeEmptyIdentifier();
     } else {
-        tokenData->cooked = nullptr;
-        tokenData->raw = nullptr;
+        tokenData->cooked = makeEmptyIdentifier();
+        tokenData->raw = makeEmptyIdentifier();
     }
     tokenData->isTail = isTail;
 
     m_buffer16.shrink(0);
+    m_bufferForRawTemplateString16.shrink(0);
 
     if (isTail) {
         // Skip `
@@ -2125,9 +2137,9 @@ inNumberAfterDecimalPoint:
         shift();
         StringParseResult result = StringCannotBeParsed;
         if (lexerFlags & LexerFlagsDontBuildStrings)
-            result = parseTemplateLiteral<false>(tokenData);
+            result = parseTemplateLiteral<false>(tokenData, RawStringsBuildMode::BuildRawStrings);
         else
-            result = parseTemplateLiteral<true>(tokenData);
+            result = parseTemplateLiteral<true>(tokenData, RawStringsBuildMode::BuildRawStrings);
 
         if (UNLIKELY(result != StringParsedSuccessfully)) {
             token = result == StringUnterminated ? UNTERMINATED_TEMPLATE_LITERAL_ERRORTOK : INVALID_TEMPLATE_LITERAL_ERRORTOK;
@@ -2331,7 +2343,7 @@ bool Lexer<T>::skipRegExp()
 
 #if ENABLE(ES6_TEMPLATE_LITERAL_SYNTAX)
 template <typename T>
-JSTokenType Lexer<T>::scanTrailingTemplateString(JSToken* tokenRecord)
+JSTokenType Lexer<T>::scanTrailingTemplateString(JSToken* tokenRecord, RawStringsBuildMode rawStringsBuildMode)
 {
     JSTokenData* tokenData = &tokenRecord->m_data;
     JSTokenLocation* tokenLocation = &tokenRecord->m_location;
@@ -2340,7 +2352,7 @@ JSTokenType Lexer<T>::scanTrailingTemplateString(JSToken* tokenRecord)
 
     // Leading closing brace } is already shifted in the previous token scan.
     // So in this re-scan phase, shift() is not needed here.
-    StringParseResult result = parseTemplateLiteral<true>(tokenData);
+    StringParseResult result = parseTemplateLiteral<true>(tokenData, rawStringsBuildMode);
     JSTokenType token = ERRORTOK;
     if (UNLIKELY(result != StringParsedSuccessfully)) {
         token = result == StringUnterminated ? UNTERMINATED_TEMPLATE_LITERAL_ERRORTOK : INVALID_TEMPLATE_LITERAL_ERRORTOK;
@@ -2374,6 +2386,9 @@ void Lexer<T>::clear()
     Vector<UChar> newBuffer16;
     m_buffer16.swap(newBuffer16);
 
+    Vector<UChar> newBufferForRawTemplateString16;
+    m_bufferForRawTemplateString16.swap(newBufferForRawTemplateString16);
+
     m_isReparsing = false;
 }
 
index 8afef26..cab7d52 100644 (file)
@@ -102,7 +102,8 @@ public:
     bool prevTerminator() const { return m_terminator; }
     bool scanRegExp(const Identifier*& pattern, const Identifier*& flags, UChar patternPrefix = 0);
 #if ENABLE(ES6_TEMPLATE_LITERAL_SYNTAX)
-    JSTokenType scanTrailingTemplateString(JSToken*);
+    enum class RawStringsBuildMode { BuildRawStrings, DontBuildRawStrings };
+    JSTokenType scanTrailingTemplateString(JSToken*, RawStringsBuildMode);
 #endif
     bool skipRegExp();
 
@@ -166,6 +167,7 @@ private:
     ALWAYS_INLINE const Identifier* makeLCharIdentifier(const UChar* characters, size_t length);
     ALWAYS_INLINE const Identifier* makeRightSizedIdentifier(const UChar* characters, size_t length, UChar orAllChars);
     ALWAYS_INLINE const Identifier* makeIdentifierLCharFromUChar(const UChar* characters, size_t length);
+    ALWAYS_INLINE const Identifier* makeEmptyIdentifier();
 
     ALWAYS_INLINE bool lastTokenWasRestrKeyword() const;
 
@@ -184,7 +186,7 @@ private:
     enum class EscapeParseMode { Template, String };
     template <bool shouldBuildStrings> ALWAYS_INLINE StringParseResult parseComplexEscape(EscapeParseMode, bool strictMode, T stringQuoteCharacter);
 #if ENABLE(ES6_TEMPLATE_LITERAL_SYNTAX)
-    template <bool shouldBuildStrings> ALWAYS_INLINE StringParseResult parseTemplateLiteral(JSTokenData*);
+    template <bool shouldBuildStrings> ALWAYS_INLINE StringParseResult parseTemplateLiteral(JSTokenData*, RawStringsBuildMode);
 #endif
     ALWAYS_INLINE void parseHex(double& returnValue);
     ALWAYS_INLINE bool parseBinary(double& returnValue);
@@ -201,6 +203,7 @@ private:
 
     Vector<LChar> m_buffer8;
     Vector<UChar> m_buffer16;
+    Vector<UChar> m_bufferForRawTemplateString16;
     bool m_terminator;
     int m_lastToken;
 
@@ -289,6 +292,12 @@ ALWAYS_INLINE const Identifier* Lexer<UChar>::makeRightSizedIdentifier(const UCh
     return &m_arena->makeIdentifier(m_vm, characters, length);
 }
 
+template <typename T>
+ALWAYS_INLINE const Identifier* Lexer<T>::makeEmptyIdentifier()
+{
+    return &m_arena->makeEmptyIdentifier(m_vm);
+}
+
 template <>
 ALWAYS_INLINE void Lexer<LChar>::setCodeStart(const StringImpl* sourceString)
 {
index d4b3355..9546b71 100644 (file)
@@ -143,6 +143,13 @@ namespace JSC {
         , m_templateExpressions(templateExpressions)
     {
     }
+
+    inline TaggedTemplateNode::TaggedTemplateNode(const JSTokenLocation& location, ExpressionNode* tag, TemplateLiteralNode* templateLiteral)
+        : ExpressionNode(location)
+        , m_tag(tag)
+        , m_templateLiteral(templateLiteral)
+    {
+    }
 #endif
 
     inline RegExpNode::RegExpNode(const JSTokenLocation& location, const Identifier& pattern, const Identifier& flags)
index 3a19005..f98fc4c 100644 (file)
@@ -473,12 +473,28 @@ namespace JSC {
         TemplateLiteralNode(const JSTokenLocation&, TemplateStringListNode*);
         TemplateLiteralNode(const JSTokenLocation&, TemplateStringListNode*, TemplateExpressionListNode*);
 
+        TemplateStringListNode* templateStrings() const { return m_templateStrings; }
+        TemplateExpressionListNode* templateExpressions() const { return m_templateExpressions; }
+
     private:
         virtual RegisterID* emitBytecode(BytecodeGenerator&, RegisterID* = 0) override;
 
         TemplateStringListNode* m_templateStrings;
         TemplateExpressionListNode* m_templateExpressions;
     };
+
+    class TaggedTemplateNode : public ExpressionNode, public ThrowableExpressionData {
+    public:
+        TaggedTemplateNode(const JSTokenLocation&, ExpressionNode*, TemplateLiteralNode*);
+
+        TemplateLiteralNode* templateLiteral() const { return m_templateLiteral; }
+
+    private:
+        virtual RegisterID* emitBytecode(BytecodeGenerator&, RegisterID* = 0) override;
+
+        ExpressionNode* m_tag;
+        TemplateLiteralNode* m_templateLiteral;
+    };
 #endif
 
     class RegExpNode : public ExpressionNode, public ThrowableExpressionData {
index 95e988d..10a04bc 100644 (file)
@@ -2282,12 +2282,12 @@ template <class TreeBuilder> TreeExpression Parser<LexerType>::parseArrayLiteral
 
 #if ENABLE(ES6_TEMPLATE_LITERAL_SYNTAX)
 template <typename LexerType>
-template <class TreeBuilder> typename TreeBuilder::TemplateString Parser<LexerType>::parseTemplateString(TreeBuilder& context, bool isTemplateHead, bool& elementIsTail)
+template <class TreeBuilder> typename TreeBuilder::TemplateString Parser<LexerType>::parseTemplateString(TreeBuilder& context, bool isTemplateHead, typename LexerType::RawStringsBuildMode rawStringsBuildMode, bool& elementIsTail)
 {
     if (!isTemplateHead) {
         matchOrFail(CLOSEBRACE, "Expected a closing '}' following an expression in template literal");
         // Re-scan the token to recognize it as Template Element.
-        m_token.m_type = m_lexer->scanTrailingTemplateString(&m_token);
+        m_token.m_type = m_lexer->scanTrailingTemplateString(&m_token, rawStringsBuildMode);
     }
     matchOrFail(TEMPLATE, "Expected an template element");
     const Identifier* cooked = m_token.m_data.cooked;
@@ -2299,12 +2299,12 @@ template <class TreeBuilder> typename TreeBuilder::TemplateString Parser<LexerTy
 }
 
 template <typename LexerType>
-template <class TreeBuilder> typename TreeBuilder::TemplateLiteral Parser<LexerType>::parseTemplateLiteral(TreeBuilder& context)
+template <class TreeBuilder> typename TreeBuilder::TemplateLiteral Parser<LexerType>::parseTemplateLiteral(TreeBuilder& context, typename LexerType::RawStringsBuildMode rawStringsBuildMode)
 {
     JSTokenLocation location(tokenLocation());
     bool elementIsTail = false;
 
-    auto headTemplateString = parseTemplateString(context, true, elementIsTail);
+    auto headTemplateString = parseTemplateString(context, true, rawStringsBuildMode, elementIsTail);
     failIfFalse(headTemplateString, "Cannot parse head template element");
 
     typename TreeBuilder::TemplateStringList templateStringList = context.createTemplateStringList(headTemplateString);
@@ -2320,7 +2320,7 @@ template <class TreeBuilder> typename TreeBuilder::TemplateLiteral Parser<LexerT
     typename TreeBuilder::TemplateExpressionList templateExpressionList = context.createTemplateExpressionList(expression);
     typename TreeBuilder::TemplateExpressionList templateExpressionTail = templateExpressionList;
 
-    auto templateString = parseTemplateString(context, false, elementIsTail);
+    auto templateString = parseTemplateString(context, false, rawStringsBuildMode, elementIsTail);
     failIfFalse(templateString, "Cannot parse template element");
     templateStringTail = context.createTemplateStringList(templateStringTail, templateString);
 
@@ -2331,7 +2331,7 @@ template <class TreeBuilder> typename TreeBuilder::TemplateLiteral Parser<LexerT
 
         templateExpressionTail = context.createTemplateExpressionList(templateExpressionTail, expression);
 
-        auto templateString = parseTemplateString(context, false, elementIsTail);
+        auto templateString = parseTemplateString(context, false, rawStringsBuildMode, elementIsTail);
         failIfFalse(templateString, "Cannot parse template element");
         templateStringTail = context.createTemplateStringList(templateStringTail, templateString);
     }
@@ -2444,7 +2444,7 @@ template <class TreeBuilder> TreeExpression Parser<LexerType>::parsePrimaryExpre
     }
 #if ENABLE(ES6_TEMPLATE_LITERAL_SYNTAX)
     case TEMPLATE:
-        return parseTemplateLiteral(context);
+        return parseTemplateLiteral(context, LexerType::RawStringsBuildMode::DontBuildRawStrings);
 #endif
     default:
         failDueToUnexpectedToken();
@@ -2568,6 +2568,16 @@ template <class TreeBuilder> TreeExpression Parser<LexerType>::parseMemberExpres
             next();
             break;
         }
+        case TEMPLATE: {
+            semanticFailIfTrue(baseIsSuper, "Cannot use super as tag for tagged templates");
+            JSTextPosition expressionEnd = lastTokenEndPosition();
+            int nonLHSCount = m_nonLHSCount;
+            typename TreeBuilder::TemplateLiteral templateLiteral = parseTemplateLiteral(context, LexerType::RawStringsBuildMode::BuildRawStrings);
+            failIfFalse(templateLiteral, "Cannot parse template literal");
+            base = context.createTaggedTemplate(location, base, templateLiteral, expressionStart, expressionEnd, lastTokenEndPosition());
+            m_nonLHSCount = nonLHSCount;
+            break;
+        }
         default:
             goto endMemberExpression;
         }
index 2883daa..dfe0429 100644 (file)
@@ -777,8 +777,8 @@ private:
     template <class TreeBuilder> NEVER_INLINE TreeClassExpression parseClass(TreeBuilder&, FunctionRequirements, ParserClassInfo<TreeBuilder>&);
 #endif
 #if ENABLE(ES6_TEMPLATE_LITERAL_SYNTAX)
-    template <class TreeBuilder> NEVER_INLINE typename TreeBuilder::TemplateString parseTemplateString(TreeBuilder& context, bool isTemplateHead, bool& elementIsTail);
-    template <class TreeBuilder> NEVER_INLINE typename TreeBuilder::TemplateLiteral parseTemplateLiteral(TreeBuilder&);
+    template <class TreeBuilder> NEVER_INLINE typename TreeBuilder::TemplateString parseTemplateString(TreeBuilder& context, bool isTemplateHead, typename LexerType::RawStringsBuildMode, bool& elementIsTail);
+    template <class TreeBuilder> NEVER_INLINE typename TreeBuilder::TemplateLiteral parseTemplateLiteral(TreeBuilder&, typename LexerType::RawStringsBuildMode);
 #endif
 
     ALWAYS_INLINE int isBinaryOperator(JSTokenType);
index 40ece53..2a7d44d 100644 (file)
@@ -45,6 +45,7 @@ namespace JSC {
 
         template <typename T>
         ALWAYS_INLINE const Identifier& makeIdentifier(VM*, const T* characters, size_t length);
+        ALWAYS_INLINE const Identifier& makeEmptyIdentifier(VM*);
         ALWAYS_INLINE const Identifier& makeIdentifierLCharFromUChar(VM*, const UChar* characters, size_t length);
 
         const Identifier& makeNumericIdentifier(VM*, double number);
@@ -91,6 +92,11 @@ namespace JSC {
         return m_identifiers.last();
     }
 
+    ALWAYS_INLINE const Identifier& IdentifierArena::makeEmptyIdentifier(VM* vm)
+    {
+        return vm->propertyNames->emptyIdentifier;
+    }
+
     ALWAYS_INLINE const Identifier& IdentifierArena::makeIdentifierLCharFromUChar(VM* vm, const UChar* characters, size_t length)
     {
         if (!length)
index f045397..31d3fe4 100644 (file)
@@ -82,7 +82,9 @@ public:
         PropertyListResult, ArgumentsListResult, ElementsListResult,
         StatementResult, FormalParameterListResult, ClauseResult,
         ClauseListResult, CommaExpr, DeconstructingAssignment,
-        TemplateStringResult, TemplateStringListResult, TemplateExpressionListResult, TemplateExpr
+        TemplateStringResult, TemplateStringListResult,
+        TemplateExpressionListResult, TemplateExpr,
+        TaggedTemplateExpr
     };
     typedef int ExpressionType;
 
@@ -188,6 +190,7 @@ public:
     TemplateExpressionList createTemplateExpressionList(TemplateExpressionList, Expression) { return TemplateExpressionListResult; }
     TemplateLiteral createTemplateLiteral(const JSTokenLocation&, TemplateStringList) { return TemplateExpr; }
     TemplateLiteral createTemplateLiteral(const JSTokenLocation&, TemplateStringList, TemplateExpressionList) { return TemplateExpr; }
+    ExpressionType createTaggedTemplate(const JSTokenLocation&, ExpressionType, TemplateLiteral, int, int, int) { return TaggedTemplateExpr; }
 #endif
 
     int createArgumentsList(const JSTokenLocation&, int) { return ArgumentsListResult; }
index b0e2838..f7574c6 100644 (file)
     macro(profiledBytecodes) \
     macro(propertyIsEnumerable) \
     macro(prototype) \
+    macro(raw) \
     macro(reload) \
     macro(replace) \
     macro(resolve) \
     macro(TypeError) \
     macro(undefined) \
     macro(BuiltinLog) \
-    macro(homeObject)
+    macro(homeObject) \
+    macro(getTemplateObject)
 
 namespace JSC {
     
index 7d06fa6..da36499 100644 (file)
@@ -83,6 +83,7 @@
 #include "JSSet.h"
 #include "JSSetIterator.h"
 #include "JSStringIterator.h"
+#include "JSTemplateRegistryKey.h"
 #include "JSTypedArrayConstructors.h"
 #include "JSTypedArrayPrototypes.h"
 #include "JSTypedArrays.h"
@@ -169,6 +170,13 @@ const GlobalObjectMethodTable JSGlobalObject::s_globalObjectMethodTable = { &all
 @end
 */
 
+static EncodedJSValue JSC_HOST_CALL getTemplateObject(ExecState* exec)
+{
+    JSValue thisValue = exec->thisValue();
+    ASSERT(thisValue.inherits(JSTemplateRegistryKey::info()));
+    return JSValue::encode(exec->lexicalGlobalObject()->templateRegistry().getTemplateObject(exec, jsCast<JSTemplateRegistryKey*>(thisValue)->templateRegistryKey()));
+}
+
 JSGlobalObject::JSGlobalObject(VM& vm, Structure* structure, const GlobalObjectMethodTable* globalObjectMethodTable)
     : Base(vm, structure, 0)
     , m_vm(vm)
@@ -179,6 +187,7 @@ JSGlobalObject::JSGlobalObject(VM& vm, Structure* structure, const GlobalObjectM
     , m_havingABadTimeWatchpoint(adoptRef(new WatchpointSet(IsWatched)))
     , m_varInjectionWatchpoint(adoptRef(new WatchpointSet(IsWatched)))
     , m_weakRandom(Options::forceWeakRandomSeed() ? Options::forcedWeakRandomSeed() : static_cast<unsigned>(randomNumber() * (std::numeric_limits<unsigned>::max() + 1.0)))
+    , m_templateRegistry(vm)
     , m_evalEnabled(true)
     , m_runtimeFlags()
     , m_consoleClient(nullptr)
@@ -433,6 +442,7 @@ putDirectWithoutTransition(vm, vm.propertyNames-> jsName, lowerName ## Construct
     JSFunction* privateFuncObjectKeys = JSFunction::create(vm, this, 0, String(), objectConstructorKeys);
     JSFunction* privateFuncObjectGetOwnPropertyDescriptor = JSFunction::create(vm, this, 0, String(), objectConstructorGetOwnPropertyDescriptor);
     JSFunction* privateFuncObjectGetOwnPropertySymbols = JSFunction::create(vm, this, 0, String(), objectConstructorGetOwnPropertySymbols);
+    JSFunction* privateFuncGetTemplateObject = JSFunction::create(vm, this, 0, String(), getTemplateObject);
 
     GlobalPropertyInfo staticGlobals[] = {
         GlobalPropertyInfo(vm.propertyNames->NaN, jsNaN(), DontEnum | DontDelete | ReadOnly),
@@ -443,6 +453,7 @@ putDirectWithoutTransition(vm, vm.propertyNames-> jsName, lowerName ## Construct
         GlobalPropertyInfo(vm.propertyNames->objectKeysPrivateName, privateFuncObjectKeys, DontEnum | DontDelete | ReadOnly),
         GlobalPropertyInfo(vm.propertyNames->objectGetOwnPropertyDescriptorPrivateName, privateFuncObjectGetOwnPropertyDescriptor, DontEnum | DontDelete | ReadOnly),
         GlobalPropertyInfo(vm.propertyNames->objectGetOwnPropertySymbolsPrivateName, privateFuncObjectGetOwnPropertySymbols, DontEnum | DontDelete | ReadOnly),
+        GlobalPropertyInfo(vm.propertyNames->getTemplateObjectPrivateName, privateFuncGetTemplateObject, DontEnum | DontDelete | ReadOnly),
         GlobalPropertyInfo(vm.propertyNames->TypeErrorPrivateName, m_typeErrorConstructor.get(), DontEnum | DontDelete | ReadOnly),
         GlobalPropertyInfo(vm.propertyNames->BuiltinLogPrivateName, builtinLog, DontEnum | DontDelete | ReadOnly),
         GlobalPropertyInfo(vm.propertyNames->ArrayPrivateName, arrayConstructor, DontEnum | DontDelete | ReadOnly),
index 47f7c26..ae11387 100644 (file)
@@ -37,6 +37,7 @@
 #include "StructureChain.h"
 #include "StructureRareDataInlines.h"
 #include "SymbolPrototype.h"
+#include "TemplateRegistry.h"
 #include "VM.h"
 #include "Watchpoint.h"
 #include <JavaScriptCore/JSBase.h>
@@ -284,6 +285,8 @@ protected:
 
     WeakRandom m_weakRandom;
 
+    TemplateRegistry m_templateRegistry;
+
     bool m_evalEnabled;
     String m_evalDisabledErrorMessage;
     RuntimeFlags m_runtimeFlags;
@@ -608,6 +611,8 @@ public:
         return m_rareData->opaqueJSClassData;
     }
 
+    TemplateRegistry& templateRegistry() { return m_templateRegistry; }
+
     double weakRandomNumber() { return m_weakRandom.get(); }
     unsigned weakRandomInteger() { return m_weakRandom.getUint32(); }
 
diff --git a/Source/JavaScriptCore/runtime/JSTemplateRegistryKey.cpp b/Source/JavaScriptCore/runtime/JSTemplateRegistryKey.cpp
new file mode 100644 (file)
index 0000000..8014661
--- /dev/null
@@ -0,0 +1,56 @@
+/*
+ * Copyright (C) 2015 Yusuke Suzuki <utatane.tea@gmail.com>.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY APPLE INC. AND ITS CONTRIBUTORS ``AS IS''
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
+ * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR ITS CONTRIBUTORS
+ * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF
+ * THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include "config.h"
+#include "JSTemplateRegistryKey.h"
+
+#include "JSCJSValueInlines.h"
+#include "JSCellInlines.h"
+#include "VM.h"
+
+namespace JSC {
+
+const ClassInfo JSTemplateRegistryKey::s_info = { "TemplateRegistryKey", nullptr, nullptr, CREATE_METHOD_TABLE(JSTemplateRegistryKey) };
+
+
+JSTemplateRegistryKey::JSTemplateRegistryKey(VM& vm, const TemplateRegistryKey& templateRegistryKey)
+    : Base(vm, vm.templateRegistryKeyStructure.get())
+    , m_templateRegistryKey(templateRegistryKey)
+{
+}
+
+JSTemplateRegistryKey* JSTemplateRegistryKey::create(VM& vm, const TemplateRegistryKey& templateRegistryKey)
+{
+    JSTemplateRegistryKey* result = new (NotNull, allocateCell<JSTemplateRegistryKey>(vm.heap)) JSTemplateRegistryKey(vm, templateRegistryKey);
+    result->finishCreation(vm);
+    return result;
+}
+
+void JSTemplateRegistryKey::destroy(JSCell* cell)
+{
+    static_cast<JSTemplateRegistryKey*>(cell)->JSTemplateRegistryKey::~JSTemplateRegistryKey();
+}
+
+} // namespace JSC
diff --git a/Source/JavaScriptCore/runtime/JSTemplateRegistryKey.h b/Source/JavaScriptCore/runtime/JSTemplateRegistryKey.h
new file mode 100644 (file)
index 0000000..fc6e392
--- /dev/null
@@ -0,0 +1,61 @@
+/*
+ * Copyright (C) 2015 Yusuke Suzuki <utatane.tea@gmail.com>.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY APPLE INC. AND ITS CONTRIBUTORS ``AS IS''
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
+ * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR ITS CONTRIBUTORS
+ * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF
+ * THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#ifndef JSTemplateRegistryKey_h
+#define JSTemplateRegistryKey_h
+
+#include "JSDestructibleObject.h"
+#include "Structure.h"
+#include "TemplateRegistryKey.h"
+
+namespace JSC {
+
+class JSTemplateRegistryKey final : public JSDestructibleObject {
+public:
+    typedef JSDestructibleObject Base;
+
+    static JSTemplateRegistryKey* create(VM&, const TemplateRegistryKey&);
+
+    static Structure* createStructure(VM& vm, JSGlobalObject* globalObject, JSValue prototype)
+    {
+        return Structure::create(vm, globalObject, prototype, TypeInfo(ObjectType, StructureFlags), info());
+    }
+
+    DECLARE_INFO;
+
+    const TemplateRegistryKey& templateRegistryKey() const { return m_templateRegistryKey; }
+
+protected:
+    static void destroy(JSCell*);
+
+private:
+    JSTemplateRegistryKey(VM&, const TemplateRegistryKey&);
+
+    TemplateRegistryKey m_templateRegistryKey;
+};
+
+} // namespace JSC
+
+#endif // JSTemplateRegistryKey_h
index d7b516a..3509362 100644 (file)
@@ -462,17 +462,11 @@ EncodedJSValue JSC_HOST_CALL objectConstructorSeal(ExecState* exec)
     return JSValue::encode(obj);
 }
 
-EncodedJSValue JSC_HOST_CALL objectConstructorFreeze(ExecState* exec)
+JSObject* objectConstructorFreeze(ExecState* exec, JSObject* object)
 {
-    // 1. If Type(O) is not Object, return O.
-    JSValue obj = exec->argument(0);
-    if (!obj.isObject())
-        return JSValue::encode(obj);
-    JSObject* object = asObject(obj);
-
     if (isJSFinalObject(object) && !hasIndexedProperties(object->indexingType())) {
         object->freeze(exec->vm());
-        return JSValue::encode(obj);
+        return object;
     }
 
     // 2. For each named own property name P of O,
@@ -496,14 +490,23 @@ EncodedJSValue JSC_HOST_CALL objectConstructorFreeze(ExecState* exec)
         // d. Call the [[DefineOwnProperty]] internal method of O with P, desc, and true as arguments.
         object->methodTable(exec->vm())->defineOwnProperty(object, exec, propertyName, desc, true);
         if (exec->hadException())
-            return JSValue::encode(obj);
+            return object;
     }
 
     // 3. Set the [[Extensible]] internal property of O to false.
     object->preventExtensions(exec->vm());
 
     // 4. Return O.
-    return JSValue::encode(obj);
+    return object;
+}
+
+EncodedJSValue JSC_HOST_CALL objectConstructorFreeze(ExecState* exec)
+{
+    // 1. If Type(O) is not Object, return O.
+    JSValue obj = exec->argument(0);
+    if (!obj.isObject())
+        return JSValue::encode(obj);
+    return JSValue::encode(objectConstructorFreeze(exec, asObject(obj)));
 }
 
 EncodedJSValue JSC_HOST_CALL objectConstructorPreventExtensions(ExecState* exec)
index 53fc0e9..8fc5c73 100644 (file)
@@ -89,6 +89,8 @@ inline JSObject* constructEmptyObject(ExecState* exec)
     return constructEmptyObject(exec, exec->lexicalGlobalObject()->objectPrototype());
 }
 
+JSObject* objectConstructorFreeze(ExecState*, JSObject*);
+
 } // namespace JSC
 
 #endif // ObjectConstructor_h
diff --git a/Source/JavaScriptCore/runtime/TemplateRegistry.cpp b/Source/JavaScriptCore/runtime/TemplateRegistry.cpp
new file mode 100644 (file)
index 0000000..803d204
--- /dev/null
@@ -0,0 +1,70 @@
+/*
+ * Copyright (C) 2015 Yusuke Suzuki <utatane.tea@gmail.com>.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY APPLE INC. AND ITS CONTRIBUTORS ``AS IS''
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
+ * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR ITS CONTRIBUTORS
+ * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF
+ * THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include "config.h"
+#include "TemplateRegistry.h"
+
+#include "JSCJSValueInlines.h"
+#include "JSGlobalObject.h"
+#include "ObjectConstructor.h"
+#include "WeakGCMapInlines.h"
+
+namespace JSC {
+
+TemplateRegistry::TemplateRegistry(VM& vm)
+    : m_templateMap(vm)
+{
+}
+
+JSArray* TemplateRegistry::getTemplateObject(ExecState* exec, const TemplateRegistryKey& templateKey)
+{
+    JSArray* cached = m_templateMap.get(templateKey);
+    if (cached)
+        return cached;
+
+    unsigned count = templateKey.cookedStrings().size();
+    JSArray* templateObject = constructEmptyArray(exec, nullptr, count);
+    JSArray* rawObject = constructEmptyArray(exec, nullptr, count);
+
+    for (unsigned index = 0; index < count; ++index) {
+        templateObject->putDirectIndex(exec, index, jsString(exec, templateKey.cookedStrings()[index]), ReadOnly | DontDelete, PutDirectIndexLikePutDirect);
+        rawObject->putDirectIndex(exec, index, jsString(exec, templateKey.rawStrings()[index]), ReadOnly | DontDelete, PutDirectIndexLikePutDirect);
+    }
+
+    objectConstructorFreeze(exec, rawObject);
+    ASSERT(!exec->hadException());
+
+    templateObject->putDirect(exec->vm(), exec->propertyNames().raw, rawObject, ReadOnly | DontEnum | DontDelete);
+
+    objectConstructorFreeze(exec, templateObject);
+    ASSERT(!exec->hadException());
+
+    m_templateMap.set(templateKey, templateObject);
+
+    return templateObject;
+}
+
+
+} // namespace JSC
diff --git a/Source/JavaScriptCore/runtime/TemplateRegistry.h b/Source/JavaScriptCore/runtime/TemplateRegistry.h
new file mode 100644 (file)
index 0000000..9cb62dd
--- /dev/null
@@ -0,0 +1,48 @@
+/*
+ * Copyright (C) 2015 Yusuke Suzuki <utatane.tea@gmail.com>.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY APPLE INC. AND ITS CONTRIBUTORS ``AS IS''
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
+ * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR ITS CONTRIBUTORS
+ * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF
+ * THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#ifndef TemplateRegistry_h
+#define TemplateRegistry_h
+
+#include "JSArray.h"
+#include "TemplateRegistryKey.h"
+#include "WeakGCMap.h"
+#include <limits>
+
+namespace JSC {
+
+class TemplateRegistry {
+public:
+    TemplateRegistry(VM&);
+
+    JSArray* getTemplateObject(ExecState*, const TemplateRegistryKey&);
+
+private:
+    WeakGCMap<TemplateRegistryKey, JSArray> m_templateMap;
+};
+
+} // namespace JSC
+
+#endif // TemplateRegistry_h
diff --git a/Source/JavaScriptCore/runtime/TemplateRegistryKey.h b/Source/JavaScriptCore/runtime/TemplateRegistryKey.h
new file mode 100644 (file)
index 0000000..06c771a
--- /dev/null
@@ -0,0 +1,103 @@
+/*
+ * Copyright (C) 2015 Yusuke Suzuki <utatane.tea@gmail.com>.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY APPLE INC. AND ITS CONTRIBUTORS ``AS IS''
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
+ * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR ITS CONTRIBUTORS
+ * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF
+ * THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#ifndef TemplateRegistryKey_h
+#define TemplateRegistryKey_h
+
+#include <limits>
+#include <wtf/Vector.h>
+#include <wtf/text/StringHash.h>
+#include <wtf/text/WTFString.h>
+
+namespace JSC {
+
+class TemplateRegistryKey {
+public:
+    typedef Vector<String, 4> StringVector;
+
+    TemplateRegistryKey(const StringVector& rawStrings, const StringVector& cookedStrings);
+    enum DeletedValueTag { DeletedValue };
+    TemplateRegistryKey(DeletedValueTag);
+    enum EmptyValueTag { EmptyValue };
+    TemplateRegistryKey(EmptyValueTag);
+
+    bool isDeletedValue() const { return m_rawStrings.isEmpty() && m_hash == std::numeric_limits<unsigned>::max(); }
+
+    bool isEmptyValue() const { return m_rawStrings.isEmpty() && !m_hash; }
+
+    unsigned hash() const { return m_hash; }
+
+    const StringVector& rawStrings() const { return m_rawStrings; }
+    const StringVector& cookedStrings() const { return m_cookedStrings; }
+
+    bool operator==(const TemplateRegistryKey& other) const { return m_hash == other.m_hash && m_rawStrings == other.m_rawStrings; }
+    bool operator!=(const TemplateRegistryKey& other) const { return m_hash != other.m_hash || m_rawStrings != other.m_rawStrings; }
+
+    struct Hasher {
+        static unsigned hash(const TemplateRegistryKey& key) { return key.hash(); }
+        static bool equal(const TemplateRegistryKey& a, const TemplateRegistryKey& b) { return a == b; }
+        static const bool safeToCompareToEmptyOrDeleted = false;
+    };
+
+private:
+    StringVector m_rawStrings { };
+    StringVector m_cookedStrings { };
+    unsigned m_hash { 0 };
+};
+
+inline TemplateRegistryKey::TemplateRegistryKey(const StringVector& rawStrings, const StringVector& cookedStrings)
+    : m_rawStrings(rawStrings)
+    , m_cookedStrings(cookedStrings)
+{
+    m_hash = 0;
+    for (const String& string : rawStrings)
+        m_hash += WTF::StringHash::hash(string);
+}
+
+inline TemplateRegistryKey::TemplateRegistryKey(DeletedValueTag)
+    : m_hash(std::numeric_limits<unsigned>::max())
+{
+}
+
+inline TemplateRegistryKey::TemplateRegistryKey(EmptyValueTag)
+    : m_hash(0)
+{
+}
+
+} // namespace JSC
+
+namespace WTF {
+template<typename T> struct DefaultHash;
+
+template<> struct DefaultHash<JSC::TemplateRegistryKey> {
+    typedef JSC::TemplateRegistryKey::Hasher Hash;
+};
+
+template<> struct HashTraits<JSC::TemplateRegistryKey> : CustomHashTraits<JSC::TemplateRegistryKey> {
+};
+
+} // namespace WTF
+
+#endif // TemplateRegistryKey_h
index 1a3734d..7126e80 100644 (file)
@@ -65,6 +65,7 @@
 #include "JSPromiseDeferred.h"
 #include "JSPromiseReaction.h"
 #include "JSPropertyNameEnumerator.h"
+#include "JSTemplateRegistryKey.h"
 #include "JSWithScope.h"
 #include "Lexer.h"
 #include "Lookup.h"
@@ -224,6 +225,7 @@ VM::VM(VMType vmType, HeapType heapType)
     symbolTableStructure.set(*this, SymbolTable::createStructure(*this, 0, jsNull()));
     structureChainStructure.set(*this, StructureChain::createStructure(*this, 0, jsNull()));
     sparseArrayValueMapStructure.set(*this, SparseArrayValueMap::createStructure(*this, 0, jsNull()));
+    templateRegistryKeyStructure.set(*this, JSTemplateRegistryKey::createStructure(*this, 0, jsNull()));
     arrayBufferNeuteringWatchpointStructure.set(*this, ArrayBufferNeuteringWatchpoint::createStructure(*this));
     unlinkedFunctionExecutableStructure.set(*this, UnlinkedFunctionExecutable::createStructure(*this, 0, jsNull()));
     unlinkedProgramCodeBlockStructure.set(*this, UnlinkedProgramCodeBlock::createStructure(*this, 0, jsNull()));
index d0d782e..5e57412 100644 (file)
@@ -264,6 +264,7 @@ public:
     Strong<Structure> symbolTableStructure;
     Strong<Structure> structureChainStructure;
     Strong<Structure> sparseArrayValueMapStructure;
+    Strong<Structure> templateRegistryKeyStructure;
     Strong<Structure> arrayBufferNeuteringWatchpointStructure;
     Strong<Structure> unlinkedFunctionExecutableStructure;
     Strong<Structure> unlinkedProgramCodeBlockStructure;
diff --git a/Source/JavaScriptCore/tests/stress/tagged-templates-identity.js b/Source/JavaScriptCore/tests/stress/tagged-templates-identity.js
new file mode 100644 (file)
index 0000000..302d031
--- /dev/null
@@ -0,0 +1,59 @@
+function shouldBe(actual, expected) {
+    if (actual !== expected)
+        throw new Error('bad value: ' + JSON.stringify(actual));
+}
+
+var templates = [];
+function tag(siteObject) {
+    templates.push(siteObject);
+}
+
+tag`Hello`;
+tag`World`;
+tag`Hello`;
+shouldBe(templates.length, 3);
+shouldBe(templates[0] !== templates[1], true);
+shouldBe(templates[0] === templates[2], true);
+
+templates = [];
+tag`Hello\n`;
+tag`Hello\r`;
+tag`Hello\u2028`;
+shouldBe(templates.length, 3);
+shouldBe(templates[0] !== templates[1], true);
+shouldBe(templates[0] !== templates[2], true);
+
+templates = [];
+eval("tag`Hello\n`");
+eval("tag`Hello\r`");
+eval("tag`Hello\u2028`");
+shouldBe(templates.length, 3);
+shouldBe(templates[0] === templates[1], true);
+shouldBe(templates[0] !== templates[2], true);
+
+templates = [];
+eval("tag`Hello\n`");
+eval("tag`Hello\\n`");
+eval("tag`Hello\r`");
+eval("tag`Hello\\r`");
+shouldBe(templates.length, 4);
+shouldBe(templates[0] !== templates[1], true);
+shouldBe(templates[0] === templates[2], true);
+shouldBe(templates[0] !== templates[3], true);
+shouldBe(templates[1] !== templates[2], true);
+shouldBe(templates[1] !== templates[3], true);
+shouldBe(templates[2] !== templates[3], true);
+
+var v = 0;
+templates = [];
+eval("tag`Hello\n${v}world`");
+eval("tag`Hello\n${v}world`");
+shouldBe(templates.length, 2);
+shouldBe(templates[0] === templates[1], true);
+
+var v = 0;
+templates = [];
+eval("tag`Hello${v}\nworld`");
+eval("tag`Hello\n${v}world`");
+shouldBe(templates.length, 2);
+shouldBe(templates[0] !== templates[1], true);
diff --git a/Source/JavaScriptCore/tests/stress/tagged-templates-raw-strings.js b/Source/JavaScriptCore/tests/stress/tagged-templates-raw-strings.js
new file mode 100644 (file)
index 0000000..1d95785
--- /dev/null
@@ -0,0 +1,63 @@
+function shouldBe(actual, expected) {
+    if (actual !== expected)
+        throw new Error('bad value: ' + JSON.stringify(actual));
+}
+
+function tag(results) {
+    return function (siteObject) {
+        shouldBe(siteObject.raw.length, results.length);
+        for (var i = 0; i < siteObject.raw.length; ++i) {
+            shouldBe(siteObject.raw[i], results[i]);
+        }
+    };
+}
+
+tag([''])``;
+tag(['hello'])`hello`;
+tag(['hello', 'world'])`hello${0}world`;
+tag(['hello\\u2028', 'world'])`hello\u2028${0}world`;
+tag(['hello\\u2028\\u2029', 'world'])`hello\u2028\u2029${0}world`;
+tag(['hello\\n\\r', 'world'])`hello\n\r${0}world`;
+
+function testEval(content, results) {
+    var split = 0;
+    var g = tag(results);
+    eval("g`" + content + "`");
+}
+
+for (var ch of [ '\'', '"', '\\', 'b', 'f', 'n', 'r', 't', 'v' ])
+    testEval("\\" + ch, ["\\" + ch]);
+
+var evaluated = [];
+for (var i = 0; i < 0x10000; ++i) {
+    var code = i.toString(16);
+    var input = "\\u" + '0'.repeat(4 - code.length) + code;
+    evaluated.push(input);
+}
+testEval(evaluated.join('${split}'), evaluated)
+
+testEval("Hello\rWorld", [ "Hello\nWorld" ]);
+testEval("Hello\nWorld", [ "Hello\nWorld" ]);
+
+testEval("Hello\r\rWorld", [ "Hello\n\nWorld" ]);
+testEval("Hello\r\nWorld", [ "Hello\nWorld" ]);
+testEval("Hello\n\nWorld", [ "Hello\n\nWorld" ]);
+testEval("Hello\n\rWorld", [ "Hello\n\nWorld" ]);
+
+testEval("Hello\n\r\nWorld", [ "Hello\n\nWorld" ]);
+testEval("Hello\r\n\rWorld", [ "Hello\n\nWorld" ]);
+testEval("Hello\n\n\nWorld", [ "Hello\n\n\nWorld" ]);
+
+testEval("Hello\n\r\n\rWorld", [ "Hello\n\n\nWorld" ]);
+testEval("Hello\n\r\n\nWorld", [ "Hello\n\n\nWorld" ]);
+testEval("Hello\r\n\n\nWorld", [ "Hello\n\n\nWorld" ]);
+
+testEval("Hello\\\n\r\rWorld", [ "Hello\\\n\n\nWorld" ]);
+testEval("Hello\\\r\n\n\nWorld", [ "Hello\\\r\n\n\nWorld" ]);
+testEval("Hello\\\n\r\n\nWorld", [ "Hello\\\n\n\nWorld" ]);
+testEval("Hello\\\n\r\r\nWorld", [ "Hello\\\n\n\nWorld" ]);
+
+testEval("\u2028", [ "\u2028" ]);
+testEval("\u2029", [ "\u2029" ]);
+testEval("\\u2028", [ "\\u2028" ]);
+testEval("\\u2029", [ "\\u2029" ]);
diff --git a/Source/JavaScriptCore/tests/stress/tagged-templates-syntax.js b/Source/JavaScriptCore/tests/stress/tagged-templates-syntax.js
new file mode 100644 (file)
index 0000000..c199bac
--- /dev/null
@@ -0,0 +1,70 @@
+function tag() {
+}
+
+function testSyntax(script) {
+    try {
+        eval(script);
+    } catch (error) {
+        if (error instanceof SyntaxError)
+            throw new Error("Bad error: " + String(error));
+    }
+}
+
+function testSyntaxError(script, message) {
+    var error = null;
+    try {
+        eval(script);
+    } catch (e) {
+        error = e;
+    }
+    if (!error)
+        throw new Error("Expected syntax error not thrown");
+
+    if (String(error) !== message)
+        throw new Error("Bad error: " + String(error));
+}
+
+testSyntax("tag``");
+testSyntax("tag`Hello`");
+testSyntax("tag`Hello${tag}`");
+testSyntax("tag`${tag}`");
+testSyntax("tag`${tag} ${tag}`");
+testSyntax("tag`${tag}${tag}`");
+
+testSyntax("tag.prop``");
+testSyntax("tag.prop`Hello`");
+testSyntax("tag.prop`Hello${tag}`");
+testSyntax("tag.prop`${tag}`");
+testSyntax("tag.prop`${tag} ${tag}`");
+testSyntax("tag.prop`${tag}${tag}`");
+
+testSyntax("tag[prop]``");
+testSyntax("tag[prop]`Hello`");
+testSyntax("tag[prop]`Hello${tag}`");
+testSyntax("tag[prop]`${tag}`");
+testSyntax("tag[prop]`${tag} ${tag}`");
+testSyntax("tag[prop]`${tag}${tag}`");
+
+testSyntax("(tag())``");
+testSyntax("(tag())`Hello`");
+testSyntax("(tag())`Hello${tag}`");
+testSyntax("(tag())`${tag}`");
+testSyntax("(tag())`${tag} ${tag}`");
+testSyntax("(tag())`${tag}${tag}`");
+
+testSyntax("(class { say() { super.tag`` } })");
+testSyntax("(class { say() { super.tag`Hello` } })");
+testSyntax("(class { say() { super.tag`Hello${tag}` } })");
+testSyntax("(class { say() { super.tag`${tag}` } })");
+testSyntax("(class { say() { super.tag`${tag} ${tag}` } })");
+testSyntax("(class { say() { super.tag`${tag}${tag}` } })");
+
+testSyntax("(class extends Hello { constructor() { super()`` } })");
+testSyntax("(class extends Hello { constructor() { super()`Hello` } })");
+testSyntax("(class extends Hello { constructor() { super()`Hello${tag}` } })");
+testSyntax("(class extends Hello { constructor() { super()`${tag}` } })");
+testSyntax("(class extends Hello { constructor() { super()`${tag} ${tag}` } })");
+testSyntax("(class extends Hello { constructor() { super()`${tag}${tag}` } })");
+
+testSyntaxError("super`Hello${tag}`", "SyntaxError: Cannot use super as tag for tagged templates.");
+testSyntaxError("(class { say() { super`Hello${tag}` } })", "SyntaxError: Cannot use super as tag for tagged templates.");
diff --git a/Source/JavaScriptCore/tests/stress/tagged-templates-template-object.js b/Source/JavaScriptCore/tests/stress/tagged-templates-template-object.js
new file mode 100644 (file)
index 0000000..b805de9
--- /dev/null
@@ -0,0 +1,43 @@
+function shouldBe(actual, expected) {
+    if (actual !== expected)
+        throw new Error('bad value: ' + actual);
+}
+
+function tag(elements) {
+    return function (siteObject) {
+        shouldBe(siteObject instanceof Array, true);
+        shouldBe(Object.isFrozen(siteObject), true);
+        shouldBe(siteObject.raw instanceof Array, true);
+        shouldBe(Object.isFrozen(siteObject.raw), true);
+        shouldBe(siteObject.hasOwnProperty("raw"), true);
+        shouldBe(siteObject.propertyIsEnumerable("raw"), false);
+        shouldBe(siteObject.length, arguments.length);
+        shouldBe(siteObject.raw.length, arguments.length);
+        var count = siteObject.length;
+        for (var i = 0; i < count; ++i) {
+            shouldBe(siteObject.hasOwnProperty(i), true);
+            var desc = Object.getOwnPropertyDescriptor(siteObject, i);
+            shouldBe(desc.writable, false);
+            shouldBe(desc.enumerable, true);
+            shouldBe(desc.configurable, false);
+        }
+        shouldBe(siteObject.length, elements.length + 1);
+        for (var i = 0; i < elements.length; ++i)
+            shouldBe(arguments[i + 1], elements[i]);
+    };
+}
+
+var value = {
+    toString() {
+        throw new Error('incorrect');
+    },
+    valueOf() {
+        throw new Error('incorrect');
+    }
+};
+
+tag([])``;
+tag([])`Hello`;
+tag([])`Hello World`;
+tag([value])`Hello ${value} World`;
+tag([value, value])`Hello ${value} OK, ${value}`;
diff --git a/Source/JavaScriptCore/tests/stress/tagged-templates-this.js b/Source/JavaScriptCore/tests/stress/tagged-templates-this.js
new file mode 100644 (file)
index 0000000..24a5992
--- /dev/null
@@ -0,0 +1,26 @@
+function shouldBe(actual, expected) {
+    if (actual !== expected)
+        throw new Error('bad value: ' + actual);
+}
+
+function tag() {
+    "use strict";
+    return this;
+}
+
+var object = {
+    tag() {
+        'use strict';
+        return this;
+    }
+};
+
+shouldBe(tag`Hello`, undefined);
+shouldBe((function () { return tag }())`Hello`, undefined);
+shouldBe(object.tag`Hello`, object);
+shouldBe(object['tag']`Hello`, object);
+shouldBe(object[(function () { return 'tag'; }())]`Hello`, object);
+
+with (object) {
+    shouldBe(tag`Hello`, object);
+}
diff --git a/Source/JavaScriptCore/tests/stress/tagged-templates.js b/Source/JavaScriptCore/tests/stress/tagged-templates.js
new file mode 100644 (file)
index 0000000..eb714bf
--- /dev/null
@@ -0,0 +1,67 @@
+function shouldBe(actual, expected) {
+    if (actual !== expected)
+        throw new Error('bad value: ' + JSON.stringify(actual));
+}
+
+function raw(siteObject) {
+    var result = '';
+    for (var i = 0; i < siteObject.raw.length; ++i) {
+        result += siteObject.raw[i];
+        if ((i + 1) < arguments.length) {
+            result += arguments[i + 1];
+        }
+    }
+    return result;
+}
+
+function cooked(siteObject) {
+    var result = '';
+    for (var i = 0; i < siteObject.raw.length; ++i) {
+        result += siteObject[i];
+        if ((i + 1) < arguments.length) {
+            result += arguments[i + 1];
+        }
+    }
+    return result;
+}
+
+function Counter() {
+    var count = 0;
+    return {
+        toString() {
+            return count++;
+        }
+    };
+}
+
+var c = Counter();
+shouldBe(raw`Hello ${c} World ${c}`, `Hello 0 World 1`);
+var c = Counter();
+shouldBe(raw`${c}${c}${c}`, `012`);
+var c = Counter();
+shouldBe(raw`${c}${ `  ${c}  ` }${c}`, `1  0  2`);
+var c = Counter();
+shouldBe(raw`${c}${ raw`  ${c}  ` }${c}`, `1  0  2`);
+var c = Counter();
+shouldBe(raw`${c}${ `  ${c}${c}  ` }${c}`, `2  01  3`);
+var c = Counter();
+shouldBe(raw`${c}${ raw`  ${c}${c}  ` }${c}`, `2  01  3`);
+
+shouldBe(raw``, ``);
+shouldBe(cooked``, ``);
+shouldBe(raw`\n`, `\\n`);
+shouldBe(cooked`\n`, `\n`);
+shouldBe(raw`\v`, `\\v`);
+shouldBe(cooked`\v`, `\v`);
+shouldBe(raw`
+
+`, `\n\n`);
+shouldBe(cooked`
+
+`, `\n\n`);
+shouldBe(raw`\
+\
+`, `\\\n\\\n`);
+shouldBe(cooked`\
+\
+`, ``);