ConcatString.h 13 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291
  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. class LiteralStringWithPropertyStringPtr : public LiteralString
  9. {
  10. private:
  11. Field(PropertyString*) propertyString;
  12. Field(const Js::PropertyRecord*) propertyRecord;
  13. public:
  14. virtual Js::PropertyRecord const * GetPropertyRecord(bool dontLookupFromDictionary = false) override;
  15. PropertyString * GetPropertyString() const;
  16. PropertyString * GetOrAddPropertyString(); // Get if it's there, otherwise bring it in.
  17. void SetPropertyString(PropertyString * propStr);
  18. template <typename StringType> static LiteralStringWithPropertyStringPtr * ConvertString(StringType * originalString);
  19. static uint GetOffsetOfPropertyString() { return offsetof(LiteralStringWithPropertyStringPtr, propertyString); }
  20. static bool Is(Var var);
  21. static bool Is(RecyclableObject* var);
  22. template <typename T> static LiteralStringWithPropertyStringPtr* TryFromVar(T var);
  23. static JavascriptString *
  24. NewFromCString(const char * cString, const CharCount charCount, JavascriptLibrary *const library);
  25. static JavascriptString *
  26. NewFromWideString(const char16 * wString, const CharCount charCount, JavascriptLibrary *const library);
  27. static JavascriptString * CreateEmptyString(JavascriptLibrary *const library);
  28. protected:
  29. LiteralStringWithPropertyStringPtr(StaticType* stringTypeStatic);
  30. LiteralStringWithPropertyStringPtr(const char16 * wString, const CharCount stringLength, JavascriptLibrary *const library);
  31. DEFINE_VTABLE_CTOR(LiteralStringWithPropertyStringPtr, LiteralString);
  32. public:
  33. virtual VTableValue DummyVirtualFunctionToHinderLinkerICF()
  34. {
  35. return VTableValue::VtableLiteralStringWithPropertyStringPtr;
  36. }
  37. };
  38. // Templated so that the Is call dispatchs to different function depending
  39. // on if argument is already a RecyclableObject* or only known to be a Var
  40. //
  41. // In case it is known to be a RecyclableObject*, the Is call skips that check
  42. template <typename T>
  43. inline LiteralStringWithPropertyStringPtr * LiteralStringWithPropertyStringPtr::TryFromVar(T var)
  44. {
  45. return LiteralStringWithPropertyStringPtr::Is(var)
  46. ? reinterpret_cast<LiteralStringWithPropertyStringPtr*>(var)
  47. : nullptr;
  48. }
  49. // Base class for concat strings.
  50. // Concat string is a virtual string, or a non-leaf node in concat string tree.
  51. // It does not hold characters by itself but has one or more child nodes.
  52. // Only leaf nodes (which are not concat strings) hold the actual characters.
  53. // The flattening happens on demand (call GetString() or GetSz()),
  54. // until then we don't create actual big char16* buffer, just keep concat tree as a tree.
  55. // The result of flattening the concat string tree is concat of all leaf nodes from left to right.
  56. // Usage pattern:
  57. // // Create concat tree using one of non-abstract derived classes.
  58. // JavascriptString* result = concatTree->GetString(); // At this time we flatten the tree into 1 actual wchat_t* string.
  59. class ConcatStringBase _ABSTRACT : public LiteralString // vtable will be switched to LiteralString's vtable after flattening
  60. {
  61. friend JavascriptString;
  62. protected:
  63. ConcatStringBase(StaticType* stringTypeStatic);
  64. DEFINE_VTABLE_CTOR_ABSTRACT(ConcatStringBase, LiteralString);
  65. virtual void CopyVirtual(_Out_writes_(m_charLength) char16 *const buffer,
  66. StringCopyInfoStack &nestedStringTreeCopyInfos, const byte recursionDepth) = 0;
  67. void CopyImpl(_Out_writes_(m_charLength) char16 *const buffer,
  68. int itemCount, _In_reads_(itemCount) JavascriptString * const * items,
  69. StringCopyInfoStack &nestedStringTreeCopyInfos, const byte recursionDepth);
  70. // Subclass can call this to implement GetSz and use the actual type to avoid virtual call to Copy.
  71. template <typename ConcatStringType> const char16 * GetSzImpl();
  72. public:
  73. virtual const char16* GetSz() = 0; // Force subclass to call GetSzImpl with the real type to avoid virtual calls
  74. using JavascriptString::Copy;
  75. virtual bool IsTree() const override sealed;
  76. };
  77. // Concat string with N (or less) child nodes.
  78. // Use it when you know the number of children at compile time.
  79. // There could be less nodes than N. When we find 1st NULL node going incrementing the index,
  80. // we see this as indication that the rest on the right are empty and stop iterating.
  81. // Usage pattern:
  82. // ConcatStringN<3>* tree = ConcatStringN<3>::New(scriptContext);
  83. // tree->SetItem(0, javascriptString1);
  84. // tree->SetItem(1, javascriptString2);
  85. // tree->SetItem(2, javascriptString3);
  86. template <int N>
  87. class ConcatStringN : public ConcatStringBase
  88. {
  89. friend JavascriptString;
  90. protected:
  91. ConcatStringN(StaticType* stringTypeStatic, bool doZeroSlotsAndLength = true);
  92. DEFINE_VTABLE_CTOR(ConcatStringN<N>, ConcatStringBase);
  93. virtual void CopyVirtual(_Out_writes_(m_charLength) char16 *const buffer, StringCopyInfoStack &nestedStringTreeCopyInfos, const byte recursionDepth) override
  94. {
  95. __super::CopyImpl(buffer, N, AddressOf(m_slots[0]), nestedStringTreeCopyInfos, recursionDepth);
  96. }
  97. virtual int GetRandomAccessItemsFromConcatString(Js::JavascriptString * const *& items) const
  98. {
  99. items = AddressOf(m_slots[0]);
  100. return N;
  101. }
  102. public:
  103. static ConcatStringN<N>* New(ScriptContext* scriptContext);
  104. const char16 * GetSz() override sealed;
  105. void SetItem(_In_range_(0, N - 1) int index, JavascriptString* value);
  106. protected:
  107. Field(JavascriptString*) m_slots[N]; // These contain the child nodes. 1 slot is per 1 item (JavascriptString*).
  108. };
  109. // Concat string that uses binary tree, each node has 2 children.
  110. // Usage pattern:
  111. // ConcatString* str = ConcatString::New(javascriptString1, javascriptString2);
  112. // 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.
  113. // JavascriptString::Concat(javascriptString1, javascriptString2);
  114. class ConcatString sealed : public ConcatStringN<2>
  115. {
  116. ConcatString(JavascriptString* a, JavascriptString* b);
  117. protected:
  118. DEFINE_VTABLE_CTOR(ConcatString, ConcatStringN<2>);
  119. public:
  120. static ConcatString* New(JavascriptString* a, JavascriptString* b);
  121. static const int MaxDepth = 1000;
  122. JavascriptString *LeftString() const { Assert(!IsFinalized()); return m_slots[0]; }
  123. JavascriptString *RightString() const { Assert(!IsFinalized()); return m_slots[1]; }
  124. };
  125. // Concat string with any number of child nodes, can grow dynamically.
  126. // Usage pattern:
  127. // ConcatStringBuilder* tree = ConcatStringBuider::New(scriptContext, 5);
  128. // tree->Append(javascriptString1);
  129. // tree->Append(javascriptString2);
  130. // ...
  131. // Implementation details:
  132. // - uses chunks, max chunk size is specified by c_maxChunkSlotCount, until we fit into that, we realloc, otherwise create new chunk.
  133. // - We use chunks in order to avoid big allocations, we don't expect lots of reallocs, that why chunk size is relatively big.
  134. // - the chunks are linked using m_prevChunk field. flattening happens from left to right, i.e. first we need to get
  135. // head chunk -- the one that has m_prevChunk == NULL.
  136. class ConcatStringBuilder sealed : public ConcatStringBase
  137. {
  138. friend JavascriptString;
  139. ConcatStringBuilder(ScriptContext* scriptContext, int initialSlotCount);
  140. ConcatStringBuilder(const ConcatStringBuilder& other);
  141. void AllocateSlots(int requestedSlotCount);
  142. ConcatStringBuilder* GetHead() const;
  143. protected:
  144. DEFINE_VTABLE_CTOR(ConcatStringBuilder, ConcatStringBase);
  145. virtual void CopyVirtual(_Out_writes_(m_charLength) char16 *const buffer, StringCopyInfoStack &nestedStringTreeCopyInfos, const byte recursionDepth) override sealed;
  146. public:
  147. static ConcatStringBuilder* New(ScriptContext* scriptContext, int initialSlotCount);
  148. const char16 * GetSz() override sealed;
  149. void Append(JavascriptString* str);
  150. private:
  151. // MAX number of slots in one chunk. Until we fit into this, we realloc, otherwise create new chunk.
  152. static const int c_maxChunkSlotCount = 1024;
  153. int GetItemCount() const;
  154. Field(Field(JavascriptString*)*) m_slots; // Array of child nodes.
  155. Field(int) m_slotCount; // Number of allocated slots (1 slot holds 1 item) in this chunk.
  156. Field(int) m_count; // Actual number of items in this chunk.
  157. Field(ConcatStringBuilder*) m_prevChunk;
  158. };
  159. // Concat string that wraps another string.
  160. // Use it when you need to wrap something with e.g. { and }.
  161. // Usage pattern:
  162. // result = ConcatStringWrapping<_u('{'), _u('}')>::New(result);
  163. template <char16 L, char16 R>
  164. class ConcatStringWrapping sealed : public ConcatStringBase
  165. {
  166. friend JavascriptString;
  167. ConcatStringWrapping(JavascriptString* inner);
  168. JavascriptString* GetFirstItem() const;
  169. JavascriptString* GetLastItem() const;
  170. protected:
  171. DEFINE_VTABLE_CTOR(ConcatStringWrapping, ConcatStringBase);
  172. virtual void CopyVirtual(_Out_writes_(m_charLength) char16 *const buffer, StringCopyInfoStack &nestedStringTreeCopyInfos, const byte recursionDepth) override sealed
  173. {
  174. const_cast<ConcatStringWrapping *>(this)->EnsureAllSlots();
  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. __super::CopyImpl(buffer, slotCount, AddressOf(m_slots[0]), nestedStringTreeCopyInfos, recursionDepth);
  219. }
  220. virtual int GetRandomAccessItemsFromConcatString(Js::JavascriptString * const *& items) const
  221. {
  222. Assert(IsFilled());
  223. items = AddressOf(m_slots[0]);
  224. return slotCount;
  225. }
  226. public:
  227. static ConcatStringMulti * New(uint slotCount, JavascriptString * a1, JavascriptString * a2, ScriptContext* scriptContext);
  228. const char16 * GetSz() override sealed;
  229. static bool Is(Var var);
  230. static ConcatStringMulti * FromVar(Var value);
  231. static ConcatStringMulti * UnsafeFromVar(Var value);
  232. static size_t GetAllocSize(uint slotCount);
  233. void SetItem(_In_range_(0, slotCount - 1) uint index, JavascriptString* value);
  234. static uint32 GetOffsetOfSlotCount() { return offsetof(ConcatStringMulti, slotCount); }
  235. static uint32 GetOffsetOfSlots() { return offsetof(ConcatStringMulti, m_slots); }
  236. protected:
  237. Field(uint) slotCount;
  238. Field(uint) __alignment;
  239. Field(size_t) __alignmentPTR;
  240. Field(JavascriptString*) m_slots[]; // These contain the child nodes.
  241. #if DBG
  242. bool IsFilled() const;
  243. #endif
  244. public:
  245. virtual VTableValue DummyVirtualFunctionToHinderLinkerICF()
  246. {
  247. return VTableValue::VtableConcatStringMulti;
  248. }
  249. };
  250. }