| //------------------------------------------------------------------------------------------------------- |
| // 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 |
| { |
| class LiteralStringWithPropertyStringPtr : public LiteralString |
| { |
| private: |
| PropertyString * propertyString; |
| |
| public: |
| PropertyString * GetPropertyString() const; |
| void SetPropertyString(PropertyString * propStr); |
| |
| template <typename StringType> static LiteralStringWithPropertyStringPtr * ConvertString(StringType * originalString); |
| |
| static uint GetOffsetOfPropertyString() { return offsetof(LiteralStringWithPropertyStringPtr, propertyString); } |
| |
| protected: |
| LiteralStringWithPropertyStringPtr(StaticType* stringTypeStatic); |
| DEFINE_VTABLE_CTOR(LiteralStringWithPropertyStringPtr, LiteralString); |
| DECLARE_CONCRETE_STRING_CLASS; |
| |
| public: |
| virtual VTableValue DummyVirtualFunctionToHinderLinkerICF() |
| { |
| return VTableValue::VtableLiteralStringWithPropertyStringPtr; |
| } |
| }; |
| |
| // Base class for concat strings. |
| // Concat string is a virtual string, or a non-leaf node in concat string tree. |
| // It does not hold characters by itself but has one or more child nodes. |
| // Only leaf nodes (which are not concat strings) hold the actual characters. |
| // The flattening happens on demand (call GetString() or GetSz()), |
| // until then we don't create actual big char16* buffer, just keep concat tree as a tree. |
| // The result of flattening the concat string tree is concat of all leaf nodes from left to right. |
| // Usage pattern: |
| // // Create concat tree using one of non-abstract derived classes. |
| // JavascriptString* result = concatTree->GetString(); // At this time we flatten the tree into 1 actual wchat_t* string. |
| class ConcatStringBase _ABSTRACT : public LiteralString // vtable will be switched to LiteralString's vtable after flattening |
| { |
| friend JavascriptString; |
| |
| protected: |
| ConcatStringBase(StaticType* stringTypeStatic); |
| DEFINE_VTABLE_CTOR_ABSTRACT(ConcatStringBase, LiteralString); |
| |
| virtual void CopyVirtual(_Out_writes_(m_charLength) char16 *const buffer, |
| StringCopyInfoStack &nestedStringTreeCopyInfos, const byte recursionDepth) = 0; |
| void CopyImpl(_Out_writes_(m_charLength) char16 *const buffer, |
| int itemCount, _In_reads_(itemCount) JavascriptString * const * items, |
| StringCopyInfoStack &nestedStringTreeCopyInfos, const byte recursionDepth); |
| |
| // Subclass can call this to implement GetSz and use the actual type to avoid virtual call to Copy. |
| template <typename ConcatStringType> const char16 * GetSzImpl(); |
| public: |
| 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; |
| }; |
| |
| // Concat string with N (or less) child nodes. |
| // Use it when you know the number of children at compile time. |
| // There could be less nodes than N. When we find 1st NULL node going incrementing the index, |
| // we see this as indication that the rest on the right are empty and stop iterating. |
| // Usage pattern: |
| // ConcatStringN<3>* tree = ConcatStringN<3>::New(scriptContext); |
| // tree->SetItem(0, javascriptString1); |
| // tree->SetItem(1, javascriptString2); |
| // tree->SetItem(2, javascriptString3); |
| template <int N> |
| class ConcatStringN : public ConcatStringBase |
| { |
| friend JavascriptString; |
| |
| protected: |
| ConcatStringN(StaticType* stringTypeStatic, bool doZeroSlotsAndLength = true); |
| DEFINE_VTABLE_CTOR(ConcatStringN<N>, ConcatStringBase); |
| DECLARE_CONCRETE_STRING_CLASS; |
| |
| virtual void CopyVirtual(_Out_writes_(m_charLength) char16 *const buffer, StringCopyInfoStack &nestedStringTreeCopyInfos, const byte recursionDepth) override |
| { |
| __super::CopyImpl(buffer, N, AddressOf(m_slots[0]), nestedStringTreeCopyInfos, recursionDepth); |
| } |
| virtual int GetRandomAccessItemsFromConcatString(Js::JavascriptString * const *& items) const |
| { |
| items = AddressOf(m_slots[0]); |
| return N; |
| } |
| |
| public: |
| static ConcatStringN<N>* New(ScriptContext* scriptContext); |
| const char16 * GetSz() override sealed; |
| void SetItem(_In_range_(0, N - 1) int index, JavascriptString* value); |
| |
| protected: |
| Field(JavascriptString*) m_slots[N]; // These contain the child nodes. 1 slot is per 1 item (JavascriptString*). |
| }; |
| |
| // Concat string that uses binary tree, each node has 2 children. |
| // Usage pattern: |
| // ConcatString* str = ConcatString::New(javascriptString1, javascriptString2); |
| // 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. |
| // JavascriptString::Concat(javascriptString1, javascriptString2); |
| class ConcatString sealed : public ConcatStringN<2> |
| { |
| ConcatString(JavascriptString* a, JavascriptString* b); |
| protected: |
| DEFINE_VTABLE_CTOR(ConcatString, ConcatStringN<2>); |
| DECLARE_CONCRETE_STRING_CLASS; |
| public: |
| static ConcatString* New(JavascriptString* a, JavascriptString* b); |
| static const int MaxDepth = 1000; |
| |
| JavascriptString *LeftString() const { Assert(!IsFinalized()); return m_slots[0]; } |
| JavascriptString *RightString() const { Assert(!IsFinalized()); return m_slots[1]; } |
| }; |
| |
| // Concat string with any number of child nodes, can grow dynamically. |
| // Usage pattern: |
| // ConcatStringBuilder* tree = ConcatStringBuider::New(scriptContext, 5); |
| // tree->Append(javascriptString1); |
| // tree->Append(javascriptString2); |
| // ... |
| // Implementation details: |
| // - uses chunks, max chunk size is specified by c_maxChunkSlotCount, until we fit into that, we realloc, otherwise create new chunk. |
| // - We use chunks in order to avoid big allocations, we don't expect lots of reallocs, that why chunk size is relatively big. |
| // - the chunks are linked using m_prevChunk field. flattening happens from left to right, i.e. first we need to get |
| // head chunk -- the one that has m_prevChunk == NULL. |
| class ConcatStringBuilder sealed : public ConcatStringBase |
| { |
| friend JavascriptString; |
| ConcatStringBuilder(ScriptContext* scriptContext, int initialSlotCount); |
| ConcatStringBuilder(const ConcatStringBuilder& other); |
| void AllocateSlots(int requestedSlotCount); |
| ConcatStringBuilder* GetHead() const; |
| |
| protected: |
| DEFINE_VTABLE_CTOR(ConcatStringBuilder, ConcatStringBase); |
| DECLARE_CONCRETE_STRING_CLASS; |
| virtual void CopyVirtual(_Out_writes_(m_charLength) char16 *const buffer, StringCopyInfoStack &nestedStringTreeCopyInfos, const byte recursionDepth) override sealed; |
| |
| public: |
| static ConcatStringBuilder* New(ScriptContext* scriptContext, int initialSlotCount); |
| const char16 * GetSz() override sealed; |
| void Append(JavascriptString* str); |
| |
| private: |
| // MAX number of slots in one chunk. Until we fit into this, we realloc, otherwise create new chunk. |
| static const int c_maxChunkSlotCount = 1024; |
| int GetItemCount() const; |
| |
| Field(Field(JavascriptString*)*) m_slots; // Array of child nodes. |
| Field(int) m_slotCount; // Number of allocated slots (1 slot holds 1 item) in this chunk. |
| Field(int) m_count; // Actual number of items in this chunk. |
| Field(ConcatStringBuilder*) m_prevChunk; |
| }; |
| |
| // Concat string that wraps another string. |
| // Use it when you need to wrap something with e.g. { and }. |
| // Usage pattern: |
| // result = ConcatStringWrapping<_u('{'), _u('}')>::New(result); |
| template <char16 L, char16 R> |
| class ConcatStringWrapping sealed : public ConcatStringBase |
| { |
| friend JavascriptString; |
| ConcatStringWrapping(JavascriptString* inner); |
| JavascriptString* GetFirstItem() const; |
| JavascriptString* GetLastItem() const; |
| |
| protected: |
| DEFINE_VTABLE_CTOR(ConcatStringWrapping, ConcatStringBase); |
| DECLARE_CONCRETE_STRING_CLASS; |
| virtual void CopyVirtual(_Out_writes_(m_charLength) char16 *const buffer, StringCopyInfoStack &nestedStringTreeCopyInfos, const byte recursionDepth) override sealed |
| { |
| const_cast<ConcatStringWrapping *>(this)->EnsureAllSlots(); |
| __super::CopyImpl(buffer, _countof(m_slots), AddressOf(m_slots[0]), nestedStringTreeCopyInfos, recursionDepth); |
| } |
| virtual int GetRandomAccessItemsFromConcatString(Js::JavascriptString * const *& items) const override sealed |
| { |
| const_cast<ConcatStringWrapping *>(this)->EnsureAllSlots(); |
| items = AddressOf(m_slots[0]); |
| return _countof(m_slots); |
| } |
| public: |
| static ConcatStringWrapping<L, R>* New(JavascriptString* inner); |
| const char16 * GetSz() override sealed; |
| private: |
| void EnsureAllSlots() |
| { |
| m_slots[0] = this->GetFirstItem(); |
| m_slots[1] = m_inner; |
| m_slots[2] = this->GetLastItem(); |
| } |
| |
| Field(JavascriptString *) m_inner; |
| |
| // Use the padding space for the concat |
| Field(JavascriptString *) m_slots[3]; |
| }; |
| |
| // Make sure the padding doesn't add tot he size of ConcatStringWrapping |
| #if defined(_M_X64_OR_ARM64) |
| CompileAssert(sizeof(ConcatStringWrapping<_u('"'), _u('"')>) == 64); |
| #else |
| CompileAssert(sizeof(ConcatStringWrapping<_u('"'), _u('"')>) == 32); |
| #endif |
| |
| // Concat string with N child nodes. Use it when you don't know the number of children at compile time. |
| // Usage pattern: |
| // ConcatStringMulti* tree = ConcatStringMulti::New(3, scriptContext); |
| // tree->SetItem(0, javascriptString1); |
| // tree->SetItem(1, javascriptString2); |
| // tree->SetItem(2, javascriptString3); |
| class ConcatStringMulti sealed : public ConcatStringBase |
| { |
| friend JavascriptString; |
| |
| protected: |
| ConcatStringMulti(uint slotCount, JavascriptString * a1, JavascriptString * a2, StaticType* stringTypeStatic); |
| DEFINE_VTABLE_CTOR(ConcatStringMulti, ConcatStringBase); |
| DECLARE_CONCRETE_STRING_CLASS; |
| |
| virtual void CopyVirtual(_Out_writes_(m_charLength) char16 *const buffer, StringCopyInfoStack &nestedStringTreeCopyInfos, const byte recursionDepth) override |
| { |
| Assert(IsFilled()); |
| __super::CopyImpl(buffer, slotCount, AddressOf(m_slots[0]), nestedStringTreeCopyInfos, recursionDepth); |
| } |
| virtual int GetRandomAccessItemsFromConcatString(Js::JavascriptString * const *& items) const |
| { |
| Assert(IsFilled()); |
| items = AddressOf(m_slots[0]); |
| return slotCount; |
| } |
| |
| public: |
| static ConcatStringMulti * New(uint slotCount, JavascriptString * a1, JavascriptString * a2, ScriptContext* scriptContext); |
| const char16 * GetSz() override sealed; |
| static bool Is(Var var); |
| static ConcatStringMulti * FromVar(Var value); |
| static size_t GetAllocSize(uint slotCount); |
| void SetItem(_In_range_(0, slotCount - 1) uint index, JavascriptString* value); |
| |
| static uint32 GetOffsetOfSlotCount() { return offsetof(ConcatStringMulti, slotCount); } |
| static uint32 GetOffsetOfSlots() { return offsetof(ConcatStringMulti, m_slots); } |
| protected: |
| Field(uint) slotCount; |
| Field(uint) __alignment; |
| Field(JavascriptString*) m_slots[]; // These contain the child nodes. |
| |
| #if DBG |
| bool IsFilled() const; |
| #endif |
| |
| |
| public: |
| virtual VTableValue DummyVirtualFunctionToHinderLinkerICF() |
| { |
| return VTableValue::VtableConcatStringMulti; |
| } |
| }; |
| } |
| |
| |