ConcatString.h 14 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293
  1. //-------------------------------------------------------------------------------------------------------
  2. // Copyright (C) Microsoft. All rights reserved.
  3. // Licensed under the MIT license. See LICENSE.txt file in the project root for full license information.
  4. //-------------------------------------------------------------------------------------------------------
  5. #pragma once
  6. namespace Js
  7. {
  8. // Upon construction, LiteralStringWithPropertyStringPtr is guaranteed to point to a recycler-allocated
  9. // buffer, so it passes the LiteralString assertions. However, upon receiving a property record pointer,
  10. // this object will update to point to the copy of its data in that property record, meaning that using
  11. // this class is just like using PropertyString: you can't take a reference to the underlying buffer via
  12. // GetSz or GetString, drop the reference to the owning string, and expect the buffer to stay alive.
  13. class LiteralStringWithPropertyStringPtr : public LiteralString
  14. {
  15. private:
  16. Field(PropertyString*) propertyString;
  17. Field(const Js::PropertyRecord*) propertyRecord;
  18. public:
  19. virtual void GetPropertyRecord(_Out_ PropertyRecord const** propRecord, bool dontLookupFromDictionary = false) override;
  20. void GetPropertyRecordImpl(_Out_ PropertyRecord const** propRecord, bool dontLookupFromDictionary = false);
  21. virtual void CachePropertyRecord(_In_ PropertyRecord const* propertyRecord) override;
  22. void CachePropertyRecordImpl(_In_ PropertyRecord const* propertyRecord);
  23. virtual void const * GetOriginalStringReference() override;
  24. virtual RecyclableObject* CloneToScriptContext(ScriptContext* requestContext) override;
  25. bool HasPropertyRecord() const { return propertyRecord != nullptr; }
  26. PropertyString * GetPropertyString() const;
  27. PropertyString * GetOrAddPropertyString(); // Get if it's there, otherwise bring it in.
  28. void SetPropertyString(PropertyString * propStr);
  29. template <typename StringType> static LiteralStringWithPropertyStringPtr * ConvertString(StringType * originalString);
  30. static uint GetOffsetOfPropertyString() { return offsetof(LiteralStringWithPropertyStringPtr, propertyString); }
  31. static JavascriptString *
  32. NewFromCString(const char * cString, const CharCount charCount, JavascriptLibrary *const library);
  33. static JavascriptString *
  34. NewFromWideString(const char16 * wString, const CharCount charCount, JavascriptLibrary *const library);
  35. static JavascriptString * CreateEmptyString(JavascriptLibrary *const library);
  36. protected:
  37. LiteralStringWithPropertyStringPtr(StaticType* stringTypeStatic);
  38. LiteralStringWithPropertyStringPtr(const char16 * wString, const CharCount stringLength, JavascriptLibrary *const library);
  39. DEFINE_VTABLE_CTOR(LiteralStringWithPropertyStringPtr, LiteralString);
  40. public:
  41. virtual VTableValue DummyVirtualFunctionToHinderLinkerICF()
  42. {
  43. return VTableValue::VtableLiteralStringWithPropertyStringPtr;
  44. }
  45. };
  46. template <> bool VarIsImpl<LiteralStringWithPropertyStringPtr>(RecyclableObject * obj);
  47. // Base class for concat strings.
  48. // Concat string is a virtual string, or a non-leaf node in concat string tree.
  49. // It does not hold characters by itself but has one or more child nodes.
  50. // Only leaf nodes (which are not concat strings) hold the actual characters.
  51. // The flattening happens on demand (call GetString() or GetSz()),
  52. // until then we don't create actual big char16* buffer, just keep concat tree as a tree.
  53. // The result of flattening the concat string tree is concat of all leaf nodes from left to right.
  54. // Usage pattern:
  55. // // Create concat tree using one of non-abstract derived classes.
  56. // JavascriptString* result = concatTree->GetString(); // At this time we flatten the tree into 1 actual wchat_t* string.
  57. class ConcatStringBase : public LiteralString // vtable will be switched to LiteralString's vtable after flattening
  58. {
  59. friend JavascriptString;
  60. protected:
  61. ConcatStringBase(StaticType* stringTypeStatic);
  62. DEFINE_VTABLE_CTOR_ABSTRACT(ConcatStringBase, LiteralString);
  63. virtual void CopyVirtual(_Out_writes_(m_charLength) char16 *const buffer,
  64. StringCopyInfoStack &nestedStringTreeCopyInfos, const byte recursionDepth) = 0;
  65. void CopyImpl(_Out_writes_(m_charLength) char16 *const buffer,
  66. int itemCount, _In_reads_(itemCount) JavascriptString * const * items,
  67. StringCopyInfoStack &nestedStringTreeCopyInfos, const byte recursionDepth);
  68. // Subclass can call this to implement GetSz and use the actual type to avoid virtual call to Copy.
  69. template <typename ConcatStringType> const char16 * GetSzImpl();
  70. public:
  71. virtual const char16* GetSz() = 0; // Force subclass to call GetSzImpl with the real type to avoid virtual calls
  72. using JavascriptString::Copy;
  73. virtual bool IsTree() const override sealed;
  74. };
  75. // Concat string with N (or less) child nodes.
  76. // Use it when you know the number of children at compile time.
  77. // There could be less nodes than N. When we find 1st NULL node going incrementing the index,
  78. // we see this as indication that the rest on the right are empty and stop iterating.
  79. // Usage pattern:
  80. // ConcatStringN<3>* tree = ConcatStringN<3>::New(scriptContext);
  81. // tree->SetItem(0, javascriptString1);
  82. // tree->SetItem(1, javascriptString2);
  83. // tree->SetItem(2, javascriptString3);
  84. template <int N>
  85. class ConcatStringN : public ConcatStringBase
  86. {
  87. friend JavascriptString;
  88. protected:
  89. ConcatStringN(StaticType* stringTypeStatic, bool doZeroSlotsAndLength = true);
  90. DEFINE_VTABLE_CTOR(ConcatStringN<N>, ConcatStringBase);
  91. virtual void CopyVirtual(_Out_writes_(m_charLength) char16 *const buffer, StringCopyInfoStack &nestedStringTreeCopyInfos, const byte recursionDepth) override
  92. {
  93. #pragma prefast(suppress: __WARNING_POTENTIAL_BUFFER_OVERFLOW_HIGH_PRIORITY, "WDGVSO:14980704 The CopyImpl method uses GetLength() to ensure we only access m_charLength elements of buffer.")
  94. __super::CopyImpl(buffer, N, AddressOf(m_slots[0]), nestedStringTreeCopyInfos, recursionDepth);
  95. }
  96. virtual int GetRandomAccessItemsFromConcatString(Js::JavascriptString * const *& items) const
  97. {
  98. items = AddressOf(m_slots[0]);
  99. return N;
  100. }
  101. public:
  102. static ConcatStringN<N>* New(ScriptContext* scriptContext);
  103. const char16 * GetSz() override sealed;
  104. void SetItem(_In_range_(0, N - 1) int index, JavascriptString* value);
  105. protected:
  106. Field(JavascriptString*) m_slots[N]; // These contain the child nodes. 1 slot is per 1 item (JavascriptString*).
  107. };
  108. // Concat string that uses binary tree, each node has 2 children.
  109. // Usage pattern:
  110. // ConcatString* str = ConcatString::New(javascriptString1, javascriptString2);
  111. // Note: it's preferred you would use the following for concats, that would figure out whether concat string is optimal or create a new string is better.
  112. // JavascriptString::Concat(javascriptString1, javascriptString2);
  113. class ConcatString sealed : public ConcatStringN<2>
  114. {
  115. ConcatString(JavascriptString* a, JavascriptString* b);
  116. protected:
  117. DEFINE_VTABLE_CTOR(ConcatString, ConcatStringN<2>);
  118. public:
  119. static ConcatString* New(JavascriptString* a, JavascriptString* b);
  120. static const int MaxDepth = 1000;
  121. JavascriptString *LeftString() const { Assert(!IsFinalized()); return m_slots[0]; }
  122. JavascriptString *RightString() const { Assert(!IsFinalized()); return m_slots[1]; }
  123. };
  124. // Concat string with any number of child nodes, can grow dynamically.
  125. // Usage pattern:
  126. // ConcatStringBuilder* tree = ConcatStringBuider::New(scriptContext, 5);
  127. // tree->Append(javascriptString1);
  128. // tree->Append(javascriptString2);
  129. // ...
  130. // Implementation details:
  131. // - uses chunks, max chunk size is specified by c_maxChunkSlotCount, until we fit into that, we realloc, otherwise create new chunk.
  132. // - We use chunks in order to avoid big allocations, we don't expect lots of reallocs, that why chunk size is relatively big.
  133. // - the chunks are linked using m_prevChunk field. flattening happens from left to right, i.e. first we need to get
  134. // head chunk -- the one that has m_prevChunk == NULL.
  135. class ConcatStringBuilder sealed : public ConcatStringBase
  136. {
  137. friend JavascriptString;
  138. ConcatStringBuilder(ScriptContext* scriptContext, int initialSlotCount);
  139. ConcatStringBuilder(const ConcatStringBuilder& other);
  140. void AllocateSlots(int requestedSlotCount);
  141. ConcatStringBuilder* GetHead() const;
  142. protected:
  143. DEFINE_VTABLE_CTOR(ConcatStringBuilder, ConcatStringBase);
  144. virtual void CopyVirtual(_Out_writes_(m_charLength) char16 *const buffer, StringCopyInfoStack &nestedStringTreeCopyInfos, const byte recursionDepth) override sealed;
  145. public:
  146. static ConcatStringBuilder* New(ScriptContext* scriptContext, int initialSlotCount);
  147. const char16 * GetSz() override sealed;
  148. void Append(JavascriptString* str);
  149. private:
  150. // MAX number of slots in one chunk. Until we fit into this, we realloc, otherwise create new chunk.
  151. static const int c_maxChunkSlotCount = 1024;
  152. int GetItemCount() const;
  153. Field(Field(JavascriptString*)*) m_slots; // Array of child nodes.
  154. Field(int) m_slotCount; // Number of allocated slots (1 slot holds 1 item) in this chunk.
  155. Field(int) m_count; // Actual number of items in this chunk.
  156. Field(ConcatStringBuilder*) m_prevChunk;
  157. };
  158. // Concat string that wraps another string.
  159. // Use it when you need to wrap something with e.g. { and }.
  160. // Usage pattern:
  161. // result = ConcatStringWrapping<_u('{'), _u('}')>::New(result);
  162. template <char16 L, char16 R>
  163. class ConcatStringWrapping sealed : public ConcatStringBase
  164. {
  165. friend JavascriptString;
  166. ConcatStringWrapping(JavascriptString* inner);
  167. JavascriptString* GetFirstItem() const;
  168. JavascriptString* GetLastItem() const;
  169. protected:
  170. DEFINE_VTABLE_CTOR(ConcatStringWrapping, ConcatStringBase);
  171. virtual void CopyVirtual(_Out_writes_(m_charLength) char16 *const buffer, StringCopyInfoStack &nestedStringTreeCopyInfos, const byte recursionDepth) override sealed
  172. {
  173. const_cast<ConcatStringWrapping *>(this)->EnsureAllSlots();
  174. #pragma prefast(suppress: __WARNING_POTENTIAL_BUFFER_OVERFLOW_HIGH_PRIORITY, "WDGVSO:14980704 The CopyImpl method uses GetLength() to ensure we only access m_charLength elements of buffer.")
  175. __super::CopyImpl(buffer, _countof(m_slots), AddressOf(m_slots[0]), nestedStringTreeCopyInfos, recursionDepth);
  176. }
  177. virtual int GetRandomAccessItemsFromConcatString(Js::JavascriptString * const *& items) const override sealed
  178. {
  179. const_cast<ConcatStringWrapping *>(this)->EnsureAllSlots();
  180. items = AddressOf(m_slots[0]);
  181. return _countof(m_slots);
  182. }
  183. public:
  184. static ConcatStringWrapping<L, R>* New(JavascriptString* inner);
  185. const char16 * GetSz() override sealed;
  186. private:
  187. void EnsureAllSlots()
  188. {
  189. m_slots[0] = this->GetFirstItem();
  190. m_slots[1] = m_inner;
  191. m_slots[2] = this->GetLastItem();
  192. }
  193. Field(JavascriptString *) m_inner;
  194. // Use the padding space for the concat
  195. Field(JavascriptString *) m_slots[3];
  196. };
  197. // Make sure the padding doesn't add tot he size of ConcatStringWrapping
  198. #if defined(TARGET_64)
  199. CompileAssert(sizeof(ConcatStringWrapping<_u('"'), _u('"')>) == 64);
  200. #else
  201. CompileAssert(sizeof(ConcatStringWrapping<_u('"'), _u('"')>) == 32);
  202. #endif
  203. // Concat string with N child nodes. Use it when you don't know the number of children at compile time.
  204. // Usage pattern:
  205. // ConcatStringMulti* tree = ConcatStringMulti::New(3, scriptContext);
  206. // tree->SetItem(0, javascriptString1);
  207. // tree->SetItem(1, javascriptString2);
  208. // tree->SetItem(2, javascriptString3);
  209. class ConcatStringMulti sealed : public ConcatStringBase
  210. {
  211. friend JavascriptString;
  212. protected:
  213. ConcatStringMulti(uint slotCount, JavascriptString * a1, JavascriptString * a2, StaticType* stringTypeStatic);
  214. DEFINE_VTABLE_CTOR(ConcatStringMulti, ConcatStringBase);
  215. virtual void CopyVirtual(_Out_writes_(m_charLength) char16 *const buffer, StringCopyInfoStack &nestedStringTreeCopyInfos, const byte recursionDepth) override
  216. {
  217. Assert(IsFilled());
  218. #pragma prefast(suppress: __WARNING_POTENTIAL_BUFFER_OVERFLOW_HIGH_PRIORITY, "WDGVSO:14980704 The CopyImpl method uses GetLength() to ensure we only access m_charLength elements of buffer.")
  219. __super::CopyImpl(buffer, slotCount, AddressOf(m_slots[0]), nestedStringTreeCopyInfos, recursionDepth);
  220. }
  221. virtual int GetRandomAccessItemsFromConcatString(Js::JavascriptString * const *& items) const
  222. {
  223. Assert(IsFilled());
  224. items = AddressOf(m_slots[0]);
  225. return slotCount;
  226. }
  227. public:
  228. static ConcatStringMulti * New(uint slotCount, JavascriptString * a1, JavascriptString * a2, ScriptContext* scriptContext);
  229. const char16 * GetSz() override sealed;
  230. static size_t GetAllocSize(uint slotCount);
  231. void SetItem(_In_range_(0, slotCount - 1) uint index, JavascriptString* value);
  232. static uint32 GetOffsetOfSlotCount() { return offsetof(ConcatStringMulti, slotCount); }
  233. static uint32 GetOffsetOfSlots() { return offsetof(ConcatStringMulti, m_slots); }
  234. protected:
  235. Field(uint) slotCount;
  236. Field(uint) __alignment;
  237. Field(size_t) __alignmentPTR;
  238. Field(JavascriptString*) m_slots[]; // These contain the child nodes.
  239. #if DBG
  240. bool IsFilled() const;
  241. #endif
  242. public:
  243. virtual VTableValue DummyVirtualFunctionToHinderLinkerICF()
  244. {
  245. return VTableValue::VtableConcatStringMulti;
  246. }
  247. };
  248. template <> bool VarIsImpl<ConcatStringMulti>(RecyclableObject* obj);
  249. }