From f079ff1475fa00082a8c0fde722d79b23cfe38df Mon Sep 17 00:00:00 2001 From: Hitesh Kanwathirtha Date: Thu, 29 Jun 2017 15:50:59 -0700 Subject: [PATCH 1/4] Add string add helper --- lib/Backend/JnHelperMethodList.h | 1 + lib/Backend/Lower.cpp | 5 +++++ lib/Runtime/Library/JavascriptString.cpp | 18 ++++++++++++++++++ lib/Runtime/Library/JavascriptString.h | 1 + 4 files changed, 25 insertions(+) diff --git a/lib/Backend/JnHelperMethodList.h b/lib/Backend/JnHelperMethodList.h index 055d7b551ef..04203e5af3e 100644 --- a/lib/Backend/JnHelperMethodList.h +++ b/lib/Backend/JnHelperMethodList.h @@ -99,6 +99,7 @@ HELPERCALL_FULL_OR_INPLACE_MATH(Op_Negate, Js::JavascriptMath::Negate, Js::SSE2: HELPERCALL_FULL_OR_INPLACE_MATH(Op_Not, Js::JavascriptMath::Not, Js::SSE2::JavascriptMath::Not, AttrCanThrow) HELPERCALL_MATH(Op_AddLeftDead, Js::JavascriptMath::AddLeftDead, Js::SSE2::JavascriptMath::AddLeftDead, AttrCanThrow) +HELPERCALL(Op_AddString, Js::JavascriptString::AddString, AttrCanThrow) HELPERCALL_FULL_OR_INPLACE_MATH(Op_Add, Js::JavascriptMath::Add, Js::SSE2::JavascriptMath::Add, AttrCanThrow) HELPERCALL_FULL_OR_INPLACE_MATH(Op_Divide, Js::JavascriptMath::Divide, Js::SSE2::JavascriptMath::Divide, AttrCanThrow) HELPERCALL_FULL_OR_INPLACE_MATH(Op_Modulus, Js::JavascriptMath::Modulus, Js::SSE2::JavascriptMath::Modulus, AttrCanThrow) diff --git a/lib/Backend/Lower.cpp b/lib/Backend/Lower.cpp index debb30b129a..53869d08be1 100644 --- a/lib/Backend/Lower.cpp +++ b/lib/Backend/Lower.cpp @@ -1269,6 +1269,11 @@ Lowerer::LowerRange(IR::Instr *instrStart, IR::Instr *instrEnd, bool defaultDoFa Assert(instr->GetDst()->GetType() == instr->GetSrc2()->GetType()); m_lowererMD.LowerToFloat(instr); } + else if (instr->GetSrc1()->GetValueType().IsLikelyString() && + instr->GetSrc1()->GetValueType().IsLikelyString()) + { + this->LowerBinaryHelperMem(instr, IR::HelperOp_AddString); + } else if (PHASE_OFF(Js::MathFastPathPhase, this->m_func) || noMathFastPath) { this->LowerBinaryHelperMem(instr, IR::HelperOp_Add); diff --git a/lib/Runtime/Library/JavascriptString.cpp b/lib/Runtime/Library/JavascriptString.cpp index c26d6ef1ded..ca6249ad76d 100644 --- a/lib/Runtime/Library/JavascriptString.cpp +++ b/lib/Runtime/Library/JavascriptString.cpp @@ -12,6 +12,8 @@ #include "../Backend/JITRecyclableObject.h" #endif +#include "JavascriptMath.h" + namespace Js { // White Space characters are defined in ES 2017 Section 11.2 #sec-white-space @@ -2739,6 +2741,22 @@ namespace Js return string->GetLength() == 2 && wmemcmp(string->GetString(), _u("-0"), 2) == 0; } + Var JavascriptString::AddString(Var string1, Var string2, Js::ScriptContext* scriptContext) + { + if (RecyclableObject::Is(string1) && + RecyclableObject::Is(string2)) + { + if (JavascriptString::Is(string1) && + JavascriptString::Is(string2)) + { + wprintf(L"We need to add!"); + return string1; + } + } + + return JavascriptMath::Add(string1, string2, scriptContext); + } + void JavascriptString::FinishCopy(__inout_xcount(m_charLength) char16 *const buffer, StringCopyInfoStack &nestedStringTreeCopyInfos) { while (!nestedStringTreeCopyInfos.IsEmpty()) diff --git a/lib/Runtime/Library/JavascriptString.h b/lib/Runtime/Library/JavascriptString.h index 1a3fddd45ab..02ca05ac334 100644 --- a/lib/Runtime/Library/JavascriptString.h +++ b/lib/Runtime/Library/JavascriptString.h @@ -145,6 +145,7 @@ namespace Js static bool Equals(Var aLeft, Var aRight); static bool LessThan(Var aLeft, Var aRight); static bool IsNegZero(JavascriptString *string); + static Var AddString(Var string1, Var string2, Js::ScriptContext* scriptContext); static uint strstr(JavascriptString *string, JavascriptString *substring, bool useBoyerMoore, uint start=0); static int strcmp(JavascriptString *string1, JavascriptString *string2); From 81aaa2ff515bc79106598178006e298f3f127eb7 Mon Sep 17 00:00:00 2001 From: Hitesh Kanwathirtha Date: Thu, 29 Jun 2017 17:11:05 -0700 Subject: [PATCH 2/4] Add concat string cache support --- lib/Backend/Lower.cpp | 4 +- lib/Common/ConfigFlagsList.h | 4 ++ lib/Runtime/Base/Chakra.Runtime.Base.vcxproj | 3 +- lib/Runtime/Base/ScriptContext.cpp | 39 ++++++++++++++++++++ lib/Runtime/Base/ScriptContext.h | 3 ++ lib/Runtime/Library/JavascriptLibrary.h | 1 + lib/Runtime/Library/JavascriptString.cpp | 18 ++++++++- lib/Runtime/Runtime.h | 1 + 8 files changed, 69 insertions(+), 4 deletions(-) diff --git a/lib/Backend/Lower.cpp b/lib/Backend/Lower.cpp index 53869d08be1..b398ed2676a 100644 --- a/lib/Backend/Lower.cpp +++ b/lib/Backend/Lower.cpp @@ -1269,7 +1269,9 @@ Lowerer::LowerRange(IR::Instr *instrStart, IR::Instr *instrEnd, bool defaultDoFa Assert(instr->GetDst()->GetType() == instr->GetSrc2()->GetType()); m_lowererMD.LowerToFloat(instr); } - else if (instr->GetSrc1()->GetValueType().IsLikelyString() && + else if ( + !PHASE_OFF(Js::ConcatStringCachePhase, this->m_func) && + instr->GetSrc1()->GetValueType().IsLikelyString() && instr->GetSrc1()->GetValueType().IsLikelyString()) { this->LowerBinaryHelperMem(instr, IR::HelperOp_AddString); diff --git a/lib/Common/ConfigFlagsList.h b/lib/Common/ConfigFlagsList.h index d49fb47585e..2bdf0f325d5 100644 --- a/lib/Common/ConfigFlagsList.h +++ b/lib/Common/ConfigFlagsList.h @@ -304,6 +304,7 @@ PHASE(All) PHASE(PropertyStringCache) PHASE(CloneCacheInCollision) PHASE(ConstructorCache) + PHASE(ConcatStringCache) PHASE(InlineCandidate) PHASE(InlineHostCandidate) PHASE(ScriptFunctionWithInlineCache) @@ -404,6 +405,7 @@ PHASE(All) #define DEFAULT_CONFIG_BgJitPendingFuncCap (31) #define DEFAULT_CONFIG_CurrentSourceInfo (true) #define DEFAULT_CONFIG_CreateFunctionProxy (true) +#define DEFAULT_CONFIG_ConcatCacheSize (16) #define DEFAULT_CONFIG_HybridFgJit (false) #define DEFAULT_CONFIG_HybridFgJitBgQueueLengthThreshold (32) #define DEFAULT_CONFIG_Prejit (false) @@ -898,6 +900,8 @@ FLAGNR(Number, BgJitDelayFgBuffer , "When speculatively jitting in the foreg FLAGNR(Number, BgJitPendingFuncCap , "Disable delay if pending function count larger then cap", DEFAULT_CONFIG_BgJitPendingFuncCap) FLAGNR(Boolean, CreateFunctionProxy , "Create function proxies instead of full function bodies", DEFAULT_CONFIG_CreateFunctionProxy) +FLAGNR(Number, ConcatStringCacheSize , "Size of the cache for string concat results", DEFAULT_CONFIG_ConcatCacheSize) + FLAGNR(Boolean, HybridFgJit , "When background JIT is enabled, enable jitting in the foreground based on heuristics. This flag is only effective when OptimizeForManyInstances is disabled (UI threads).", DEFAULT_CONFIG_HybridFgJit) FLAGNR(Number, HybridFgJitBgQueueLengthThreshold, "The background job queue length must exceed this threshold to consider jitting in the foreground", DEFAULT_CONFIG_HybridFgJitBgQueueLengthThreshold) FLAGNR(Boolean, BytecodeHist , "Provide a histogram of the bytecodes run by the script. (NoNative required).", false) diff --git a/lib/Runtime/Base/Chakra.Runtime.Base.vcxproj b/lib/Runtime/Base/Chakra.Runtime.Base.vcxproj index 1f0735255de..1bc72da6291 100644 --- a/lib/Runtime/Base/Chakra.Runtime.Base.vcxproj +++ b/lib/Runtime/Base/Chakra.Runtime.Base.vcxproj @@ -77,6 +77,7 @@ + @@ -130,4 +131,4 @@ - + \ No newline at end of file diff --git a/lib/Runtime/Base/ScriptContext.cpp b/lib/Runtime/Base/ScriptContext.cpp index 0cda9f504d8..ee30df3472e 100644 --- a/lib/Runtime/Base/ScriptContext.cpp +++ b/lib/Runtime/Base/ScriptContext.cpp @@ -1222,6 +1222,8 @@ namespace Js srcInfo->sourceContextInfo = this->Cache()->noContextSourceContextInfo; srcInfo->moduleID = kmodGlobal; this->Cache()->noContextGlobalSourceInfo = srcInfo; + + this->Cache()->concatStringCache = nullptr; } void ScriptContext::InitializePreGlobal() @@ -5064,6 +5066,43 @@ void ScriptContext::RegisterPrototypeChainEnsuredToHaveOnlyWritableDataPropertie return this->charClassifier; } + Js::JavascriptString* ScriptContext::GetConcatCacheString(const Js::ConcatStringCacheKey& key) + { + auto concatCache = Cache()->concatStringCache; + if (concatCache != nullptr) + { + Js::JavascriptString* value; + if (concatCache->TryGetValue(key, &value)) + { + PHASE_PRINT_TRACE1(Js::ConcatStringCachePhase, L"Cache hit [(0x%p, 0x%p), 0x%p]\n", key.left, key.right, value); + return value; + } + } + + return nullptr; + } + + void ScriptContext::AddConcatCacheString(const Js::ConcatStringCacheKey& key, Js::JavascriptString* concat) + { + auto concatCache = Cache()->concatStringCache; + if (concatCache == nullptr) + { +#if ENABLE_DEBUG_CONFIG_OPTIONS + const int maxCacheSize = CONFIG_FLAG(ConcatStringCacheSize); +#else + const int maxCacheSize = 16; +#endif + concatCache = Js::ConcatStringCache::New(this->GeneralAllocator(), + maxCacheSize <= 0 ? 16 : maxCacheSize); + Cache()->concatStringCache = concatCache; + } + + // TODO: confirm that GeneralAllocator::Alloc throws + concatCache->Add(key, concat); + + PHASE_PRINT_TRACE1(Js::ConcatStringCachePhase, L"Cache add [(0x%p, 0x%p), 0x%p]\n", key.left, key.right, concat); + } + void ScriptContext::OnStartupComplete() { JS_ETW(EventWriteJSCRIPT_ON_STARTUP_COMPLETE(this)); diff --git a/lib/Runtime/Base/ScriptContext.h b/lib/Runtime/Base/ScriptContext.h index f9766ac0417..e01cf9d9f79 100644 --- a/lib/Runtime/Base/ScriptContext.h +++ b/lib/Runtime/Base/ScriptContext.h @@ -1044,6 +1044,9 @@ namespace Js ScriptConfiguration const * GetConfig(void) const { return &config; } CharClassifier const * GetCharClassifier(void) const; + Js::JavascriptString* GetConcatCacheString(const Js::ConcatStringCacheKey& key); + void AddConcatCacheString(const Js::ConcatStringCacheKey& key, Js::JavascriptString* concat); + ThreadContext * GetThreadContext() const { return threadContext; } static const int MaxEvalSourceSize = 400; diff --git a/lib/Runtime/Library/JavascriptLibrary.h b/lib/Runtime/Library/JavascriptLibrary.h index e32b509a60f..aa63459893c 100644 --- a/lib/Runtime/Library/JavascriptLibrary.h +++ b/lib/Runtime/Library/JavascriptLibrary.h @@ -84,6 +84,7 @@ namespace Js Field(DynamicProfileInfoList*) profileInfoList; #endif #endif + Field(ConcatStringCache *) concatStringCache; }; class MissingPropertyTypeHandler; diff --git a/lib/Runtime/Library/JavascriptString.cpp b/lib/Runtime/Library/JavascriptString.cpp index ca6249ad76d..5225f35e599 100644 --- a/lib/Runtime/Library/JavascriptString.cpp +++ b/lib/Runtime/Library/JavascriptString.cpp @@ -2749,8 +2749,22 @@ namespace Js if (JavascriptString::Is(string1) && JavascriptString::Is(string2)) { - wprintf(L"We need to add!"); - return string1; + Js::ConcatStringCacheKey key; + key.left = Js::JavascriptString::FromVar(string1); + key.right = Js::JavascriptString::FromVar(string2); + + Js::JavascriptString* concatString = scriptContext->GetConcatCacheString(key); + + if (concatString == nullptr) + { + PHASE_PRINT_TRACE1(Js::ConcatStringCachePhase, L"Cache miss [(0x%p, 0x%p)]\n", string1, string2); + concatString = JavascriptString::Concat(key.left, key.right); + scriptContext->AddConcatCacheString(key, concatString); + } + + Assert(concatString != nullptr); + + return concatString; } } diff --git a/lib/Runtime/Runtime.h b/lib/Runtime/Runtime.h index 59f5060ed08..4b42482fab0 100644 --- a/lib/Runtime/Runtime.h +++ b/lib/Runtime/Runtime.h @@ -437,6 +437,7 @@ enum tagDEBUG_EVENT_INFO_TYPE #include "Library/CustomExternalIterator.h" #include "Base/CharStringCache.h" +#include "Base/ConcatStringCache.h" #include "Library/JavascriptObject.h" #include "Library/BuiltInFlags.h" From 5868b533533502093aa6e07d030565d345960479 Mon Sep 17 00:00:00 2001 From: Hitesh Kanwathirtha Date: Thu, 29 Jun 2017 17:54:58 -0700 Subject: [PATCH 3/4] Clear out the concat string cache on every GC --- lib/Common/DataStructures/MruDictionary.h | 7 ++++++ lib/Runtime/Base/ConcatStringCache.h | 30 +++++++++++++++++++++++ lib/Runtime/Base/ScriptContext.cpp | 8 ++++++ 3 files changed, 45 insertions(+) create mode 100644 lib/Runtime/Base/ConcatStringCache.h diff --git a/lib/Common/DataStructures/MruDictionary.h b/lib/Common/DataStructures/MruDictionary.h index 19fdae527da..c77bb973f3c 100644 --- a/lib/Common/DataStructures/MruDictionary.h +++ b/lib/Common/DataStructures/MruDictionary.h @@ -201,6 +201,13 @@ namespace JsUtil AddToDictionary(entry); } + void Clear() + { + dictionary.Clear(); + entries.Clear(); + mruListCount = 0; + } + PREVENT_COPY(MruDictionary); }; } diff --git a/lib/Runtime/Base/ConcatStringCache.h b/lib/Runtime/Base/ConcatStringCache.h new file mode 100644 index 00000000000..89992c6ec49 --- /dev/null +++ b/lib/Runtime/Base/ConcatStringCache.h @@ -0,0 +1,30 @@ +//------------------------------------------------------------------------------------------------------- +// Copyright (C) Microsoft. All rights reserved. +// Licensed under the MIT license. See LICENSE.txt file in the project root for full license information. +//------------------------------------------------------------------------------------------------------- +#pragma once + +namespace Js +{ + struct ConcatStringCacheKey + { + Js::JavascriptString* left; + Js::JavascriptString* right; + }; + + typedef JsUtil::MruDictionary ConcatStringCache; +}; + +template <> +struct DefaultComparer +{ + inline static bool Equals(Js::ConcatStringCacheKey x, Js::ConcatStringCacheKey y) + { + return x.left == y.left && x.right == y.right; + } + + inline static hash_t GetHashCode(Js::ConcatStringCacheKey d) + { + return (HeapBlockMap32::GetLevel2Id(d.left) << 16 | HeapBlockMap32::GetLevel2Id(d.right)); + } +}; diff --git a/lib/Runtime/Base/ScriptContext.cpp b/lib/Runtime/Base/ScriptContext.cpp index ee30df3472e..92ca2e5b701 100644 --- a/lib/Runtime/Base/ScriptContext.cpp +++ b/lib/Runtime/Base/ScriptContext.cpp @@ -4487,6 +4487,14 @@ namespace Js GetDynamicRegexMap()->RemoveRecentlyUnusedItems(); } + // This list is on the arena so after a GC, it could + // contain invalid items. For simplicity, just clear the list + ConcatStringCache* concatCache = Cache()->concatStringCache; + if (concatCache != nullptr) + { + concatCache->Clear(); + } + CleanSourceListInternal(true); } From 97f4f63bea086aebdcaa67a4aa3666b69aa62e18 Mon Sep 17 00:00:00 2001 From: Hitesh Kanwathirtha Date: Fri, 7 Jul 2017 10:58:29 -0700 Subject: [PATCH 4/4] Checkpoint - updates to concat string cache --- lib/Backend/Lower.cpp | 2 +- lib/Runtime/Base/ConcatStringCache.h | 2 +- lib/Runtime/Base/ScriptContext.cpp | 12 +++++------- lib/Runtime/Library/ConcatString.cpp | 18 +++++++++++++++++- lib/Runtime/Library/ConcatString.h | 12 ++++++++++++ lib/Runtime/Library/ConcatString.inl | 5 +++++ lib/Runtime/Library/JavascriptString.cpp | 6 +++++- 7 files changed, 46 insertions(+), 11 deletions(-) diff --git a/lib/Backend/Lower.cpp b/lib/Backend/Lower.cpp index b398ed2676a..f851902e984 100644 --- a/lib/Backend/Lower.cpp +++ b/lib/Backend/Lower.cpp @@ -1272,7 +1272,7 @@ Lowerer::LowerRange(IR::Instr *instrStart, IR::Instr *instrEnd, bool defaultDoFa else if ( !PHASE_OFF(Js::ConcatStringCachePhase, this->m_func) && instr->GetSrc1()->GetValueType().IsLikelyString() && - instr->GetSrc1()->GetValueType().IsLikelyString()) + instr->GetSrc2()->GetValueType().IsLikelyString()) { this->LowerBinaryHelperMem(instr, IR::HelperOp_AddString); } diff --git a/lib/Runtime/Base/ConcatStringCache.h b/lib/Runtime/Base/ConcatStringCache.h index 89992c6ec49..5b456cc3589 100644 --- a/lib/Runtime/Base/ConcatStringCache.h +++ b/lib/Runtime/Base/ConcatStringCache.h @@ -12,7 +12,7 @@ namespace Js Js::JavascriptString* right; }; - typedef JsUtil::MruDictionary ConcatStringCache; + typedef JsUtil::MruDictionary ConcatStringCache; }; template <> diff --git a/lib/Runtime/Base/ScriptContext.cpp b/lib/Runtime/Base/ScriptContext.cpp index 92ca2e5b701..52a0ae3ac44 100644 --- a/lib/Runtime/Base/ScriptContext.cpp +++ b/lib/Runtime/Base/ScriptContext.cpp @@ -4489,12 +4489,7 @@ namespace Js // This list is on the arena so after a GC, it could // contain invalid items. For simplicity, just clear the list - ConcatStringCache* concatCache = Cache()->concatStringCache; - if (concatCache != nullptr) - { - concatCache->Clear(); - } - + Cache()->concatStringCache = nullptr; CleanSourceListInternal(true); } @@ -5083,6 +5078,8 @@ void ScriptContext::RegisterPrototypeChainEnsuredToHaveOnlyWritableDataPropertie if (concatCache->TryGetValue(key, &value)) { PHASE_PRINT_TRACE1(Js::ConcatStringCachePhase, L"Cache hit [(0x%p, 0x%p), 0x%p]\n", key.left, key.right, value); + PHASE_PRINT_TRACE1(Js::ConcatStringCachePhase, L"\tLeft: %s\n", key.left->GetSz()); + PHASE_PRINT_TRACE1(Js::ConcatStringCachePhase, L"\tRight: %s\n", key.right->GetSz()); return value; } } @@ -5100,13 +5097,14 @@ void ScriptContext::RegisterPrototypeChainEnsuredToHaveOnlyWritableDataPropertie #else const int maxCacheSize = 16; #endif - concatCache = Js::ConcatStringCache::New(this->GeneralAllocator(), + concatCache = Js::ConcatStringCache::New(this->recycler, maxCacheSize <= 0 ? 16 : maxCacheSize); Cache()->concatStringCache = concatCache; } // TODO: confirm that GeneralAllocator::Alloc throws concatCache->Add(key, concat); + concatCache->RemoveRecentlyUnusedItems(); PHASE_PRINT_TRACE1(Js::ConcatStringCachePhase, L"Cache add [(0x%p, 0x%p), 0x%p]\n", key.left, key.right, concat); } diff --git a/lib/Runtime/Library/ConcatString.cpp b/lib/Runtime/Library/ConcatString.cpp index 659023f3995..482ece2f9d6 100644 --- a/lib/Runtime/Library/ConcatString.cpp +++ b/lib/Runtime/Library/ConcatString.cpp @@ -29,7 +29,8 @@ namespace Js /////////////////////// ConcatStringBase ////////////////////////// - ConcatStringBase::ConcatStringBase(StaticType* stringType) : LiteralString(stringType) + ConcatStringBase::ConcatStringBase(StaticType* stringType) : LiteralString(stringType), + hasOnlyLiterals(true) { } @@ -118,6 +119,12 @@ namespace Js m_slots[0] = a; m_slots[1] = b; + if (!VirtualTableInfo::HasVirtualTable(a) || + !VirtualTableInfo::HasVirtualTable(b)) + { + this->SetHasNonLiteral(); + } + this->SetLength(a->GetLength() + b->GetLength()); // does not include null character } @@ -234,6 +241,10 @@ namespace Js len += str->GetLength(); this->SetLength(len); + if (!VirtualTableInfo::HasVirtualTable(str)) + { + this->SetHasNonLiteral(); + } } // Allocate slots, set m_slots and m_slotCount. @@ -376,6 +387,11 @@ namespace Js value = CompoundString::GetImmutableOrScriptUnreferencedString(value); this->SetLength(this->GetLength() + value->GetLength()); m_slots[index] = value; + + if (!VirtualTableInfo::HasVirtualTable(value)) + { + this->SetHasNonLiteral(); + } } #if DBG diff --git a/lib/Runtime/Library/ConcatString.h b/lib/Runtime/Library/ConcatString.h index 0619d7aedb5..adffdd3c4a4 100644 --- a/lib/Runtime/Library/ConcatString.h +++ b/lib/Runtime/Library/ConcatString.h @@ -44,6 +44,8 @@ namespace Js class ConcatStringBase _ABSTRACT : public LiteralString // vtable will be switched to LiteralString's vtable after flattening { friend JavascriptString; + private: + bool hasOnlyLiterals; protected: ConcatStringBase(StaticType* stringTypeStatic); @@ -61,6 +63,16 @@ namespace Js virtual const char16* GetSz() = 0; // Force subclass to call GetSzImpl with the real type to avoid virtual calls using JavascriptString::Copy; virtual bool IsTree() const override sealed; + + bool HasOnlyLiterals() + { + return hasOnlyLiterals; + } + + void SetHasNonLiteral() + { + hasOnlyLiterals = false; + } }; // Concat string with N (or less) child nodes. diff --git a/lib/Runtime/Library/ConcatString.inl b/lib/Runtime/Library/ConcatString.inl index cfd6c363813..c3687f7b781 100644 --- a/lib/Runtime/Library/ConcatString.inl +++ b/lib/Runtime/Library/ConcatString.inl @@ -81,6 +81,11 @@ namespace Js m_slots[index] = value; charcount_t newTotalLen = this->GetLength() - oldItemLen + newItemLen; this->SetLength(newTotalLen); + + if (!VirtualTableInfo::HasVirtualTable(value)) + { + this->SetHasNonLiteral(); + } } template diff --git a/lib/Runtime/Library/JavascriptString.cpp b/lib/Runtime/Library/JavascriptString.cpp index 5225f35e599..327050af1ea 100644 --- a/lib/Runtime/Library/JavascriptString.cpp +++ b/lib/Runtime/Library/JavascriptString.cpp @@ -2746,7 +2746,9 @@ namespace Js if (RecyclableObject::Is(string1) && RecyclableObject::Is(string2)) { - if (JavascriptString::Is(string1) && + if (VirtualTableInfo::HasVirtualTable(string1) && + // VirtualTableInfo::HasVirtualTable(string2) && + JavascriptString::Is(string1) && JavascriptString::Is(string2)) { Js::ConcatStringCacheKey key; @@ -2758,6 +2760,8 @@ namespace Js if (concatString == nullptr) { PHASE_PRINT_TRACE1(Js::ConcatStringCachePhase, L"Cache miss [(0x%p, 0x%p)]\n", string1, string2); + PHASE_PRINT_TRACE1(Js::ConcatStringCachePhase, L"\tLeft: %s\n", key.left->GetSz()); + PHASE_PRINT_TRACE1(Js::ConcatStringCachePhase, L"\tRight: %s\n", key.right->GetSz()); concatString = JavascriptString::Concat(key.left, key.right); scriptContext->AddConcatCacheString(key, concatString); }