diff options
-rw-r--r-- | Source/JavaScriptCore/API/JSStringRef.cpp | 2 | ||||
-rw-r--r-- | Source/WTF/wtf/text/StringImpl.cpp | 38 | ||||
-rw-r--r-- | Source/WTF/wtf/text/StringImpl.h | 42 | ||||
-rw-r--r-- | Source/WTF/wtf/text/WTFString.cpp | 10 | ||||
-rw-r--r-- | Source/WTF/wtf/text/WTFString.h | 1 | ||||
-rw-r--r-- | Source/WebCore/platform/sql/SQLiteFileSystem.cpp | 6 | ||||
-rw-r--r-- | Source/WebCore/platform/sql/SQLiteFileSystem.h | 4 | ||||
-rw-r--r-- | Source/WebCore/platform/sql/SQLiteStatement.cpp | 46 | ||||
-rw-r--r-- | Tools/TestWebKitAPI/Tests/WTF/AtomicString.cpp | 2 | ||||
-rw-r--r-- | Tools/TestWebKitAPI/Tests/WTF/StringImpl.cpp | 3 | ||||
-rw-r--r-- | Tools/TestWebKitAPI/Tests/WTF/WTFString.cpp | 2 |
11 files changed, 111 insertions, 45 deletions
diff --git a/Source/JavaScriptCore/API/JSStringRef.cpp b/Source/JavaScriptCore/API/JSStringRef.cpp index a03afed5b..812f3d413 100644 --- a/Source/JavaScriptCore/API/JSStringRef.cpp +++ b/Source/JavaScriptCore/API/JSStringRef.cpp @@ -62,7 +62,7 @@ JSStringRef JSStringCreateWithUTF8CString(const char* string) JSStringRef JSStringCreateWithCharactersNoCopy(const JSChar* chars, size_t numChars) { initializeThreading(); - return OpaqueJSString::create(StringImpl::createWithoutCopying(chars, numChars)).leakRef(); + return OpaqueJSString::create(StringImpl::createWithoutCopying(chars, numChars, WTF::DoesNotHaveTerminatingNullCharacter)).leakRef(); } JSStringRef JSStringRetain(JSStringRef string) diff --git a/Source/WTF/wtf/text/StringImpl.cpp b/Source/WTF/wtf/text/StringImpl.cpp index d1a3b56e8..5e6533e64 100644 --- a/Source/WTF/wtf/text/StringImpl.cpp +++ b/Source/WTF/wtf/text/StringImpl.cpp @@ -158,7 +158,7 @@ PassRefPtr<StringImpl> StringImpl::createFromLiteral(const char* characters, uns { ASSERT_WITH_MESSAGE(length, "Use StringImpl::empty() to create an empty string"); ASSERT(charactersAreAllASCII<LChar>(reinterpret_cast<const LChar*>(characters), length)); - return adoptRef(new StringImpl(reinterpret_cast<const LChar*>(characters), length, ConstructWithoutCopying)); + return adoptRef(new StringImpl(reinterpret_cast<const LChar*>(characters), length, DoesHaveTerminatingNullCharacter, ConstructWithoutCopying)); } PassRefPtr<StringImpl> StringImpl::createFromLiteral(const char* characters) @@ -166,20 +166,20 @@ PassRefPtr<StringImpl> StringImpl::createFromLiteral(const char* characters) return createFromLiteral(characters, strlen(characters)); } -PassRefPtr<StringImpl> StringImpl::createWithoutCopying(const UChar* characters, unsigned length) +PassRefPtr<StringImpl> StringImpl::createWithoutCopying(const UChar* characters, unsigned length, HasTerminatingNullCharacter hasTerminatingNullCharacter) { if (!length) return empty(); - return adoptRef(new StringImpl(characters, length, ConstructWithoutCopying)); + return adoptRef(new StringImpl(characters, length, hasTerminatingNullCharacter, ConstructWithoutCopying)); } -PassRefPtr<StringImpl> StringImpl::createWithoutCopying(const LChar* characters, unsigned length) +PassRefPtr<StringImpl> StringImpl::createWithoutCopying(const LChar* characters, unsigned length, HasTerminatingNullCharacter hasTerminatingNullCharacter) { if (!length) return empty(); - return adoptRef(new StringImpl(characters, length, ConstructWithoutCopying)); + return adoptRef(new StringImpl(characters, length, hasTerminatingNullCharacter, ConstructWithoutCopying)); } template <typename CharType> @@ -315,6 +315,8 @@ const UChar* StringImpl::getData16SlowCase() const STRING_STATS_ADD_UPCONVERTED_STRING(m_length); unsigned len = length(); + if (hasTerminatingNullCharacter()) + ++len; m_copyData16 = static_cast<UChar*>(fastMalloc(len * sizeof(UChar))); @@ -1935,6 +1937,30 @@ PassRefPtr<StringImpl> StringImpl::adopt(QStringData* qStringData) } #endif +PassRefPtr<StringImpl> StringImpl::createWithTerminatingNullCharacter(const StringImpl& string) +{ + // Use createUninitialized instead of 'new StringImpl' so that the string and its buffer + // get allocated in a single memory block. + unsigned length = string.m_length; + if (length >= numeric_limits<unsigned>::max()) + CRASH(); + RefPtr<StringImpl> terminatedString; + if (string.is8Bit()) { + LChar* data; + terminatedString = createUninitialized(length + 1, data); + memcpy(data, string.m_data8, length * sizeof(LChar)); + data[length] = 0; + } else { + UChar* data; + terminatedString = createUninitialized(length + 1, data); + memcpy(data, string.m_data16, length * sizeof(UChar)); + data[length] = 0; + } + --(terminatedString->m_length); + terminatedString->m_hashAndFlags = (string.m_hashAndFlags & (~s_flagMask | s_hashFlag8BitBuffer)) | s_hashFlagHasTerminatingNullCharacter; + return terminatedString.release(); +} + size_t StringImpl::sizeInBytes() const { // FIXME: support substrings @@ -1942,6 +1968,8 @@ size_t StringImpl::sizeInBytes() const if (is8Bit()) { if (has16BitShadow()) { size += 2 * size; + if (hasTerminatingNullCharacter()) + size += 2; } } else size *= 2; diff --git a/Source/WTF/wtf/text/StringImpl.h b/Source/WTF/wtf/text/StringImpl.h index 0d6c358a4..a0dd3021c 100644 --- a/Source/WTF/wtf/text/StringImpl.h +++ b/Source/WTF/wtf/text/StringImpl.h @@ -73,6 +73,12 @@ enum TextCaseSensitivity { TextCaseInsensitive }; +enum HasTerminatingNullCharacter { + DoesNotHaveTerminatingNullCharacter, + DoesHaveTerminatingNullCharacter, +}; + + typedef bool (*CharacterMatchFunctionPtr)(UChar); typedef bool (*IsWhiteSpaceFunctionPtr)(UChar); @@ -243,28 +249,30 @@ private: } enum ConstructWithoutCopyingTag { ConstructWithoutCopying }; - StringImpl(const UChar* characters, unsigned length, ConstructWithoutCopyingTag) + StringImpl(const UChar* characters, unsigned length, HasTerminatingNullCharacter hasTerminatingNullCharacter, ConstructWithoutCopyingTag) : m_refCount(s_refCountIncrement) , m_length(length) , m_data16(characters) , m_buffer(0) - , m_hashAndFlags(BufferInternal) + , m_hashAndFlags(BufferInternal | (hasTerminatingNullCharacter ? s_hashFlagHasTerminatingNullCharacter : 0)) { ASSERT(m_data16); ASSERT(m_length); + ASSERT(!(m_hashAndFlags & s_hashFlagHasTerminatingNullCharacter) || !characters[length]); STRING_STATS_ADD_16BIT_STRING(0); } - StringImpl(const LChar* characters, unsigned length, ConstructWithoutCopyingTag) + StringImpl(const LChar* characters, unsigned length, HasTerminatingNullCharacter hasTerminatingNullCharacter, ConstructWithoutCopyingTag) : m_refCount(s_refCountIncrement) , m_length(length) , m_data8(characters) , m_buffer(0) - , m_hashAndFlags(s_hashFlag8BitBuffer | BufferInternal) + , m_hashAndFlags(s_hashFlag8BitBuffer | BufferInternal | (hasTerminatingNullCharacter ? s_hashFlagHasTerminatingNullCharacter : 0)) { ASSERT(m_data8); ASSERT(m_length); + ASSERT(!(m_hashAndFlags & s_hashFlagHasTerminatingNullCharacter) || !characters[length]); STRING_STATS_ADD_8BIT_STRING(0); } @@ -413,15 +421,15 @@ public: COMPILE_ASSERT(charactersCount > 1, StringImplFromLiteralNotEmpty); COMPILE_ASSERT((charactersCount - 1 <= ((unsigned(~0) - sizeof(StringImpl)) / sizeof(LChar))), StringImplFromLiteralCannotOverflow); - return createWithoutCopying(reinterpret_cast<const LChar*>(characters), charactersCount - 1); + return createWithoutCopying(reinterpret_cast<const LChar*>(characters), charactersCount - 1, DoesHaveTerminatingNullCharacter); } // FIXME: Transition off of these functions to createWithoutCopying instead. WTF_EXPORT_STRING_API static PassRefPtr<StringImpl> createFromLiteral(const char* characters, unsigned length); WTF_EXPORT_STRING_API static PassRefPtr<StringImpl> createFromLiteral(const char* characters); - WTF_EXPORT_STRING_API static PassRefPtr<StringImpl> createWithoutCopying(const UChar* characters, unsigned length); - WTF_EXPORT_STRING_API static PassRefPtr<StringImpl> createWithoutCopying(const LChar* characters, unsigned length); + WTF_EXPORT_STRING_API static PassRefPtr<StringImpl> createWithoutCopying(const UChar* characters, unsigned length, HasTerminatingNullCharacter); + WTF_EXPORT_STRING_API static PassRefPtr<StringImpl> createWithoutCopying(const LChar* characters, unsigned length, HasTerminatingNullCharacter); WTF_EXPORT_STRING_API static PassRefPtr<StringImpl> createUninitialized(unsigned length, LChar*& data); WTF_EXPORT_STRING_API static PassRefPtr<StringImpl> createUninitialized(unsigned length, UChar*& data); @@ -460,6 +468,7 @@ public: static unsigned flagsOffset() { return OBJECT_OFFSETOF(StringImpl, m_hashAndFlags); } static unsigned flagIs8Bit() { return s_hashFlag8BitBuffer; } static unsigned dataOffset() { return OBJECT_OFFSETOF(StringImpl, m_data8); } + static PassRefPtr<StringImpl> createWithTerminatingNullCharacter(const StringImpl&); template<typename CharType, size_t inlineCapacity, typename OverflowHandler> static PassRefPtr<StringImpl> adopt(Vector<CharType, inlineCapacity, OverflowHandler>& vector) @@ -529,6 +538,8 @@ public: return !length() && !isStatic(); } + bool hasTerminatingNullCharacter() const { return m_hashAndFlags & s_hashFlagHasTerminatingNullCharacter; } + bool isAtomic() const { return m_hashAndFlags & s_hashFlagIsAtomic; } void setIsAtomic(bool isAtomic) { @@ -778,13 +789,14 @@ private: static const unsigned s_refCountFlagIsStaticString = 0x1; static const unsigned s_refCountIncrement = 0x2; // This allows us to ref / deref without disturbing the static string flag. - // The bottom 7 bits in the hash are flags. - static const unsigned s_flagCount = 7; + // The bottom 8 bits in the hash are flags. + static const unsigned s_flagCount = 8; static const unsigned s_flagMask = (1u << s_flagCount) - 1; - COMPILE_ASSERT(s_flagCount <= StringHasher::flagCount, StringHasher_reserves_enough_bits_for_StringImpl_flags); + COMPILE_ASSERT(s_flagCount == StringHasher::flagCount, StringHasher_reserves_enough_bits_for_StringImpl_flags); - static const unsigned s_hashFlagHas16BitShadow = 1u << 6; - static const unsigned s_hashFlag8BitBuffer = 1u << 5; + static const unsigned s_hashFlagHas16BitShadow = 1u << 7; + static const unsigned s_hashFlag8BitBuffer = 1u << 6; + static const unsigned s_hashFlagHasTerminatingNullCharacter = 1u << 5; static const unsigned s_hashFlagIsAtomic = 1u << 4; static const unsigned s_hashFlagDidReportCost = 1u << 3; static const unsigned s_hashFlagIsIdentifier = 1u << 2; @@ -805,7 +817,7 @@ public: // These values mimic ConstructFromLiteral. static const unsigned s_initialRefCount = s_refCountIncrement; - static const unsigned s_initialFlags = s_hashFlag8BitBuffer | BufferInternal; + static const unsigned s_initialFlags = s_hashFlag8BitBuffer | BufferInternal | s_hashFlagHasTerminatingNullCharacter; static const unsigned s_hashShift = s_flagCount; }; @@ -1330,8 +1342,8 @@ inline PassRefPtr<StringImpl> StringImpl::isolatedCopy() const { if (!requiresCopy()) { if (is8Bit()) - return StringImpl::createWithoutCopying(m_data8, m_length); - return StringImpl::createWithoutCopying(m_data16, m_length); + return StringImpl::createWithoutCopying(m_data8, m_length, hasTerminatingNullCharacter() ? DoesHaveTerminatingNullCharacter : DoesNotHaveTerminatingNullCharacter); + return StringImpl::createWithoutCopying(m_data16, m_length, hasTerminatingNullCharacter() ? DoesHaveTerminatingNullCharacter : DoesNotHaveTerminatingNullCharacter); } if (is8Bit()) diff --git a/Source/WTF/wtf/text/WTFString.cpp b/Source/WTF/wtf/text/WTFString.cpp index 29c13c10d..a61fe103f 100644 --- a/Source/WTF/wtf/text/WTFString.cpp +++ b/Source/WTF/wtf/text/WTFString.cpp @@ -409,6 +409,16 @@ Vector<UChar> String::charactersWithNullTermination() const return result; } +const UChar* String::deprecatedCharactersWithNullTermination() +{ + if (!m_impl) + return 0; + if (m_impl->hasTerminatingNullCharacter()) + return m_impl->characters(); + m_impl = StringImpl::createWithTerminatingNullCharacter(*m_impl); + return m_impl->characters(); +} + String String::format(const char *format, ...) { #if PLATFORM(QT) diff --git a/Source/WTF/wtf/text/WTFString.h b/Source/WTF/wtf/text/WTFString.h index 1c8e9097e..63feb0fa1 100644 --- a/Source/WTF/wtf/text/WTFString.h +++ b/Source/WTF/wtf/text/WTFString.h @@ -282,6 +282,7 @@ public: { return caseSensitive ? reverseFind(str, start) : reverseFindIgnoringCase(str, start); } WTF_EXPORT_STRING_API Vector<UChar> charactersWithNullTermination() const; + WTF_EXPORT_STRING_API const UChar* deprecatedCharactersWithNullTermination(); WTF_EXPORT_STRING_API UChar32 characterStartingAt(unsigned) const; // Ditto. diff --git a/Source/WebCore/platform/sql/SQLiteFileSystem.cpp b/Source/WebCore/platform/sql/SQLiteFileSystem.cpp index 735f0b41f..cbf8883ed 100644 --- a/Source/WebCore/platform/sql/SQLiteFileSystem.cpp +++ b/Source/WebCore/platform/sql/SQLiteFileSystem.cpp @@ -44,9 +44,11 @@ SQLiteFileSystem::SQLiteFileSystem() { } -int SQLiteFileSystem::openDatabase(const String& filename, sqlite3** database, bool) +int SQLiteFileSystem::openDatabase(const String& fileName, sqlite3** database, bool) { - return sqlite3_open(filename.utf8().data(), database); + // SQLite expects a null terminator on its UTF-16 strings. + String path = fileName; + return sqlite3_open16(path.deprecatedCharactersWithNullTermination(), database); } String SQLiteFileSystem::getFileNameForNewDatabase(const String& dbDir, const String&, diff --git a/Source/WebCore/platform/sql/SQLiteFileSystem.h b/Source/WebCore/platform/sql/SQLiteFileSystem.h index 14085b929..9800f7e54 100644 --- a/Source/WebCore/platform/sql/SQLiteFileSystem.h +++ b/Source/WebCore/platform/sql/SQLiteFileSystem.h @@ -46,13 +46,13 @@ class SQLiteFileSystem { public: // Opens a database file. // - // filemame - The name of the database file. + // fileName - The name of the database file. // database - The SQLite structure that represents the database stored // in the given file. // forWebSQLDatabase - True, if and only if we're opening a Web SQL Database file. // Used by Chromium to determine if the DB file needs to be opened // using a custom VFS. - static int openDatabase(const String& filename, sqlite3** database, bool forWebSQLDatabase); + static int openDatabase(const String& fileName, sqlite3** database, bool forWebSQLDatabase); // Returns the file name for a database. // diff --git a/Source/WebCore/platform/sql/SQLiteStatement.cpp b/Source/WebCore/platform/sql/SQLiteStatement.cpp index 1203e84ee..fd2af3286 100644 --- a/Source/WebCore/platform/sql/SQLiteStatement.cpp +++ b/Source/WebCore/platform/sql/SQLiteStatement.cpp @@ -32,14 +32,18 @@ #include <wtf/Assertions.h> #include <wtf/text/StringImpl.h> -// SQLite 3.6.16 makes sqlite3_prepare_v2 automatically retry preparing the statement -// once if the database scheme has changed. We rely on this behavior. -#if SQLITE_VERSION_NUMBER < 3006016 -#error SQLite version 3.6.16 or newer is required -#endif - namespace WebCore { +#if SQLITE_VERSION_NUMBER < 3003009 + +// FIXME: This overload helps us compile with older versions of SQLite 3, but things like quotas will not work. +static inline int sqlite3_prepare16_v2(sqlite3* db, const void* zSql, int nBytes, sqlite3_stmt** ppStmt, const void** pzTail) +{ + return sqlite3_prepare16(db, zSql, nBytes, ppStmt, pzTail); +} + +#endif + SQLiteStatement::SQLiteStatement(SQLiteDatabase& db, const String& sql) : m_database(db) , m_query(sql) @@ -63,23 +67,25 @@ int SQLiteStatement::prepare() if (m_database.isInterrupted()) return SQLITE_INTERRUPT; - CString query = m_query.stripWhiteSpace().utf8(); - - LOG(SQLDatabase, "SQL - prepare - %s", query.data()); - - // Pass the length of the string including the null character to sqlite3_prepare_v2; - // this lets SQLite avoid an extra string copy. - size_t lengthIncludingNullCharacter = query.length() + 1; - - const char* tail; - int error = sqlite3_prepare_v2(m_database.sqlite3Handle(), query.data(), lengthIncludingNullCharacter, &m_statement, &tail); + const void* tail = 0; + LOG(SQLDatabase, "SQL - prepare - %s", m_query.ascii().data()); + String strippedQuery = m_query.stripWhiteSpace(); + const UChar* nullTermed = strippedQuery.deprecatedCharactersWithNullTermination(); + int error = sqlite3_prepare16_v2(m_database.sqlite3Handle(), nullTermed, -1, &m_statement, &tail); + + // Starting with version 3.6.16, sqlite has a patch (http://www.sqlite.org/src/ci/256ec3c6af) + // that should make sure sqlite3_prepare16_v2 doesn't return a SQLITE_SCHEMA error. + // If we're using an older sqlite version, try to emulate the patch. + if (error == SQLITE_SCHEMA) { + sqlite3_finalize(m_statement); + error = sqlite3_prepare16_v2(m_database.sqlite3Handle(), m_query.deprecatedCharactersWithNullTermination(), -1, &m_statement, &tail); + } if (error != SQLITE_OK) - LOG(SQLDatabase, "sqlite3_prepare16 failed (%i)\n%s\n%s", error, query.data(), sqlite3_errmsg(m_database.sqlite3Handle())); - - if (tail && *tail) + LOG(SQLDatabase, "sqlite3_prepare16 failed (%i)\n%s\n%s", error, m_query.ascii().data(), sqlite3_errmsg(m_database.sqlite3Handle())); + const UChar* ch = static_cast<const UChar*>(tail); + if (ch && *ch) error = SQLITE_ERROR; - #ifndef NDEBUG m_isPrepared = error == SQLITE_OK; #endif diff --git a/Tools/TestWebKitAPI/Tests/WTF/AtomicString.cpp b/Tools/TestWebKitAPI/Tests/WTF/AtomicString.cpp index 9df71ce44..bddf22c23 100644 --- a/Tools/TestWebKitAPI/Tests/WTF/AtomicString.cpp +++ b/Tools/TestWebKitAPI/Tests/WTF/AtomicString.cpp @@ -35,12 +35,14 @@ TEST(WTF, AtomicStringCreationFromLiteral) ASSERT_EQ(strlen("Template Literal"), stringWithTemplate.length()); ASSERT_TRUE(stringWithTemplate == "Template Literal"); ASSERT_TRUE(stringWithTemplate.string().is8Bit()); + ASSERT_TRUE(stringWithTemplate.impl()->hasTerminatingNullCharacter()); const char* programmaticStringData = "Explicit Size Literal"; AtomicString programmaticString(programmaticStringData, strlen(programmaticStringData), AtomicString::ConstructFromLiteral); ASSERT_EQ(strlen(programmaticStringData), programmaticString.length()); ASSERT_TRUE(programmaticStringData == programmaticStringData); ASSERT_TRUE(programmaticString.string().is8Bit()); + ASSERT_TRUE(programmaticString.impl()->hasTerminatingNullCharacter()); ASSERT_EQ(programmaticStringData, reinterpret_cast<const char*>(programmaticString.string().characters8())); } diff --git a/Tools/TestWebKitAPI/Tests/WTF/StringImpl.cpp b/Tools/TestWebKitAPI/Tests/WTF/StringImpl.cpp index 10ab9e2cb..35d64844c 100644 --- a/Tools/TestWebKitAPI/Tests/WTF/StringImpl.cpp +++ b/Tools/TestWebKitAPI/Tests/WTF/StringImpl.cpp @@ -37,6 +37,7 @@ TEST(WTF, StringImplCreationFromLiteral) ASSERT_EQ(strlen("Template Literal"), stringWithTemplate->length()); ASSERT_TRUE(equal(stringWithTemplate.get(), "Template Literal")); ASSERT_TRUE(stringWithTemplate->is8Bit()); + ASSERT_TRUE(stringWithTemplate->hasTerminatingNullCharacter()); // Constructor taking the size explicitely. const char* programmaticStringData = "Explicit Size Literal"; @@ -45,6 +46,7 @@ TEST(WTF, StringImplCreationFromLiteral) ASSERT_TRUE(equal(programmaticString.get(), programmaticStringData)); ASSERT_EQ(programmaticStringData, reinterpret_cast<const char*>(programmaticString->characters8())); ASSERT_TRUE(programmaticString->is8Bit()); + ASSERT_TRUE(programmaticString->hasTerminatingNullCharacter()); // Constructor without explicit size. const char* stringWithoutLengthLiteral = "No Size Literal"; @@ -53,6 +55,7 @@ TEST(WTF, StringImplCreationFromLiteral) ASSERT_TRUE(equal(programmaticStringNoLength.get(), stringWithoutLengthLiteral)); ASSERT_EQ(stringWithoutLengthLiteral, reinterpret_cast<const char*>(programmaticStringNoLength->characters8())); ASSERT_TRUE(programmaticStringNoLength->is8Bit()); + ASSERT_TRUE(programmaticStringNoLength->hasTerminatingNullCharacter()); } TEST(WTF, StringImplFromLiteralLoop16BitConversion) diff --git a/Tools/TestWebKitAPI/Tests/WTF/WTFString.cpp b/Tools/TestWebKitAPI/Tests/WTF/WTFString.cpp index 3da40ff83..4a92cd4e8 100644 --- a/Tools/TestWebKitAPI/Tests/WTF/WTFString.cpp +++ b/Tools/TestWebKitAPI/Tests/WTF/WTFString.cpp @@ -38,12 +38,14 @@ TEST(WTF, StringCreationFromLiteral) ASSERT_EQ(strlen("Explicit construction syntax"), stringFromLiteral.length()); ASSERT_TRUE(stringFromLiteral == "Explicit construction syntax"); ASSERT_TRUE(stringFromLiteral.is8Bit()); + ASSERT_TRUE(stringFromLiteral.impl()->hasTerminatingNullCharacter()); ASSERT_TRUE(String("Explicit construction syntax") == stringFromLiteral); String stringWithTemplate("Template Literal", String::ConstructFromLiteral); ASSERT_EQ(strlen("Template Literal"), stringWithTemplate.length()); ASSERT_TRUE(stringWithTemplate == "Template Literal"); ASSERT_TRUE(stringWithTemplate.is8Bit()); + ASSERT_TRUE(stringWithTemplate.impl()->hasTerminatingNullCharacter()); ASSERT_TRUE(String("Template Literal") == stringWithTemplate); } |