blob: 0619d7aedb5dd9ee998e413e677d215432ba9b12 [file]
//-------------------------------------------------------------------------------------------------------
// 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;
}
};
}