blob: 1614a1c77267b3fdcefc30ed61cf39b61059a89d [file] [log] [blame]
//-------------------------------------------------------------------------------------------------------
// 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
{
typedef struct
{
unsigned long shift;
} Boyer_Moore_Jump;
// Boyer Moore table for only the first character in the search string.
#if defined(_M_IX86) || defined(_M_IX64)
// This table gets memset to 0. The x86 CRT is optimized for doing copies 128 bytes
// at a time, for 16 byte aligned memory. If this table isn't 16 byte aligned, we pay
// an additional cost to pre-align it, dealing with trailing bytes, and the copy
// ends up being twice as slow.
typedef Boyer_Moore_Jump __declspec(align(16)) JmpTable[0x80];
#else
typedef Boyer_Moore_Jump JmpTable[0x80];
#endif
struct PropertyCache;
class SubString;
class StringCopyInfoStack;
bool IsValidCharCount(size_t charCount);
const charcount_t k_InvalidCharCount = static_cast<charcount_t>(-1);
//
// To inspect strings in hybrid debugging, we use vtable lookup to find out concrete string type
// then inspect string content accordingly.
//
// To ensure all known string vtables are listed and exported from chakra.dll and handler class
// exists in chakradiag.dll, declare an abstract method in base JavascriptString class. Any concrete
// subclass that has runtime string instance must DECLARE_CONCRETE_STRING_CLASS, otherwise
// we'll get a compile time error.
//
#if DBG && defined(NTBUILD)
#define DECLARE_CONCRETE_STRING_CLASS_BASE virtual void _declareConcreteStringClass() = 0
#define DECLARE_CONCRETE_STRING_CLASS virtual void _declareConcreteStringClass() override
#else
#define DECLARE_CONCRETE_STRING_CLASS_BASE
#define DECLARE_CONCRETE_STRING_CLASS
#endif
class JavascriptString abstract : public RecyclableObject
{
friend Lowerer;
friend LowererMD;
friend bool IsValidCharCount(size_t);
private:
const char16* m_pszValue; // Flattened, '\0' terminated contents
charcount_t m_charLength; // Length in characters, not including '\0'.
static const charcount_t MaxCharLength = INT_MAX - 1; // Max number of chars not including '\0'.
protected:
static const byte MaxCopyRecursionDepth = 3;
public:
BOOL HasItemAt(charcount_t idxChar);
BOOL GetItemAt(charcount_t idxChar, Var* value);
char16 GetItem(charcount_t index);
_Ret_range_(m_charLength, m_charLength) charcount_t GetLength() const;
virtual size_t GetAllocatedByteCount() const;
virtual bool IsSubstring() const;
int GetLengthAsSignedInt() const;
const char16* UnsafeGetBuffer() const;
LPCWSTR GetSzCopy(ArenaAllocator* alloc); // Copy to an Arena
const char16* GetString(); // Get string, may not be NULL terminated
// NumberUtil::FIntRadStrToDbl and parts of GlobalObject::EntryParseInt were refactored into ToInteger
Var ToInteger(int radix = 0);
double ToDouble();
bool ToDouble(double * result);
static const char16* GetSzHelper(JavascriptString *str) { return str->GetSz(); }
virtual const char16* GetSz(); // Get string, NULL terminated
virtual void const * GetOriginalStringReference(); // Get the original full string (Same as GetString() unless it is a SubString);
public:
template <typename StringType>
void Copy(__out_ecount(bufLen) char16 *const buffer, const charcount_t bufLen);
void Copy(__out_xcount(m_charLength) char16 *const buffer, StringCopyInfoStack &nestedStringTreeCopyInfos, const byte recursionDepth)
{
if (this->IsFinalized())
{
// If we have the buffer already, just copy it
const CharCount copyCharLength = this->GetLength();
CopyHelper(buffer, this->GetString(), copyCharLength);
}
else
{
CopyVirtual(buffer, nestedStringTreeCopyInfos, recursionDepth);
}
}
virtual void CopyVirtual(_Out_writes_(m_charLength) char16 *const buffer, StringCopyInfoStack &nestedStringTreeCopyInfos, const byte recursionDepth);
private:
void FinishCopy(__inout_xcount(m_charLength) char16 *const buffer, StringCopyInfoStack &nestedStringTreeCopyInfos);
public:
virtual int GetRandomAccessItemsFromConcatString(Js::JavascriptString * const *& items) const { return -1; }
virtual bool IsTree() const { return false; }
virtual BOOL SetItem(uint32 index, Var value, PropertyOperationFlags propertyOperationFlags) override;
virtual BOOL DeleteItem(uint32 index, PropertyOperationFlags propertyOperationFlags) override;
virtual BOOL HasItem(uint32 index) override sealed;
virtual BOOL GetItem(Var originalInstance, uint32 index, Var* value, ScriptContext * requestContext) override;
virtual BOOL GetItemReference(Var originalInstance, uint32 index, Var* value, ScriptContext * requestContext) override;
virtual BOOL GetEnumerator(BOOL enumNonEnumerable, Var* enumerator, ScriptContext * requestContext, bool preferSnapshotSemantics = true, bool enumSymbols = false) override;
virtual BOOL HasProperty(PropertyId propertyId) override;
virtual BOOL IsEnumerable(PropertyId propertyId) override;
virtual BOOL DeleteProperty(PropertyId propertyId, PropertyOperationFlags propertyOperationFlags) override;
virtual BOOL GetProperty(Var originalInstance, PropertyId propertyId, Var* value, PropertyValueInfo* info, ScriptContext* requestContext) override;
virtual BOOL GetProperty(Var originalInstance, JavascriptString* propertyNameString, Var* value, PropertyValueInfo* info, ScriptContext* requestContext) override;
virtual BOOL GetPropertyReference(Var originalInstance, PropertyId propertyId, Var* value, PropertyValueInfo* info, ScriptContext* requestContext) override;
virtual BOOL GetDiagValueString(StringBuilder<ArenaAllocator>* stringBuilder, ScriptContext* requestContext) override;
virtual BOOL GetDiagTypeString(StringBuilder<ArenaAllocator>* stringBuilder, ScriptContext* requestContext) override;
virtual RecyclableObject* ToObject(ScriptContext * requestContext) override;
virtual Var GetTypeOfString(ScriptContext * requestContext) override;
// should never be called, JavascriptConversion::ToPrimitive() short-circuits and returns input value
virtual BOOL ToPrimitive(JavascriptHint hint, Var* value, ScriptContext * requestContext) override { AssertMsg(false, "String ToPrimitive should not be called"); *value = this; return true;}
virtual RecyclableObject * CloneToScriptContext(ScriptContext* requestContext) override;
virtual BOOL BufferEquals(__in_ecount(otherLength) LPCWSTR otherBuffer, __in charcount_t otherLength);
virtual char16* GetNormalizedString(NORM_FORM, ArenaAllocator*, charcount_t&);
static bool Is(Var aValue);
static JavascriptString* FromVar(Var aValue);
static bool Equals(Var aLeft, Var aRight);
static bool LessThan(Var aLeft, Var aRight);
static bool IsNegZero(JavascriptString *string);
static uint strstr(JavascriptString *string, JavascriptString *substring, bool useBoyerMoore, uint start=0);
static int strcmp(JavascriptString *string1, JavascriptString *string2);
private:
enum ToCase{
ToLower,
ToUpper
};
char16* GetSzCopy(); // get a copy of the inner string without compacting the chunks
static Var ToCaseCore(JavascriptString* pThis, ToCase toCase);
static int IndexOfUsingJmpTable(JmpTable jmpTable, const char16* inputStr, int len, const char16* searchStr, int searchLen, int position);
static int LastIndexOfUsingJmpTable(JmpTable jmpTable, const char16* inputStr, int len, const char16* searchStr, int searchLen, int position);
static bool BuildLastCharForwardBoyerMooreTable(JmpTable jmpTable, const char16* searchStr, int searchLen);
static bool BuildFirstCharBackwardBoyerMooreTable(JmpTable jmpTable, const char16* searchStr, int searchLen);
static charcount_t ConvertToIndex(Var varIndex, ScriptContext *scriptContext);
template <typename T, bool copyBuffer>
static JavascriptString* NewWithBufferT(const char16 * content, charcount_t charLength, ScriptContext * scriptContext);
bool GetPropertyBuiltIns(PropertyId propertyId, Var* value, ScriptContext* scriptContext);
static const char stringToIntegerMap[128];
static const uint8 maxUintStringLengthTable[37];
protected:
JavascriptString(StaticType * type);
JavascriptString(StaticType * type, charcount_t charLength, const char16* szValue);
DEFINE_VTABLE_CTOR_ABSTRACT(JavascriptString, RecyclableObject);
DECLARE_CONCRETE_STRING_CLASS_BASE;
void SetLength(charcount_t newLength);
void SetBuffer(const char16* buffer);
bool IsValidIndexValue(charcount_t idx) const;
static charcount_t SafeSzSize(charcount_t length); // Throws on overflow
charcount_t SafeSzSize() const; // Throws on overflow
public:
bool IsFinalized() const { return this->UnsafeGetBuffer() != NULL; }
public:
static JavascriptString* NewWithSz(__in_z const char16 * content, ScriptContext* scriptContext);
static JavascriptString* NewWithBuffer(__in_ecount(charLength) const char16 * content, charcount_t charLength, ScriptContext * scriptContext);
static JavascriptString* NewCopySz(__in_z const char16* content, ScriptContext* scriptContext);
static JavascriptString* NewCopyBuffer(__in_ecount(charLength) const char16* content, charcount_t charLength, ScriptContext* scriptContext);
static JavascriptString* NewWithArenaSz(__in_z const char16 * content, ScriptContext* scriptContext);
static JavascriptString* NewWithArenaBuffer(__in_ecount(charLength) const char16 * content, charcount_t charLength, ScriptContext * scriptContext);
static JavascriptString* NewCopySzFromArena(__in_z const char16* content, ScriptContext* scriptContext, ArenaAllocator *arena);
static __ecount(length+1) char16* AllocateLeafAndCopySz(__in Recycler* recycler, __in_ecount(length) const char16* content, charcount_t length);
static __ecount(length+1) char16* AllocateAndCopySz(__in ArenaAllocator* arena, __in_ecount(length) const char16* content, charcount_t length);
static void CopyHelper(__out_ecount(countNeeded) char16 *dst, __in_ecount(countNeeded) const char16 * str, charcount_t countNeeded);
public:
JavascriptString* ConcatDestructive(JavascriptString* pstRight);
private:
JavascriptString* ConcatDestructive_Compound(JavascriptString* pstRight);
JavascriptString* ConcatDestructive_ConcatToCompound(JavascriptString* pstRight);
JavascriptString* ConcatDestructive_OneEmpty(JavascriptString* pstRight);
JavascriptString* ConcatDestructive_CompoundAppendChars(JavascriptString* pstRight);
public:
static JavascriptString* Concat(JavascriptString * pstLeft, JavascriptString * pstRight);
static JavascriptString* Concat3(JavascriptString * pstLeft, JavascriptString * pstCenter, JavascriptString * pstRight);
private:
static JavascriptString* Concat_Compound(JavascriptString * pstLeft, JavascriptString * pstRight);
static JavascriptString* Concat_ConcatToCompound(JavascriptString * pstLeft, JavascriptString * pstRight);
static JavascriptString* Concat_OneEmpty(JavascriptString * pstLeft, JavascriptString * pstRight);
static JavascriptString* Concat_BothOneChar(JavascriptString * pstLeft, JavascriptString * pstRight);
public:
static uint32 GetOffsetOfpszValue()
{
return offsetof(JavascriptString, m_pszValue);
}
static uint32 GetOffsetOfcharLength()
{
return offsetof(JavascriptString, m_charLength);
}
class EntryInfo
{
public:
static FunctionInfo NewInstance;
static FunctionInfo CharAt;
static FunctionInfo CharCodeAt;
static FunctionInfo CodePointAt;
static FunctionInfo Concat;
static FunctionInfo FromCharCode;
static FunctionInfo FromCodePoint;
static FunctionInfo IndexOf;
static FunctionInfo LastIndexOf;
static FunctionInfo LocaleCompare;
static FunctionInfo Match;
static FunctionInfo Normalize;
static FunctionInfo Raw;
static FunctionInfo Replace;
static FunctionInfo Search;
static FunctionInfo Slice;
static FunctionInfo Split;
static FunctionInfo Substring;
static FunctionInfo Substr;
static FunctionInfo ToLocaleLowerCase;
static FunctionInfo ToLocaleUpperCase;
static FunctionInfo ToLowerCase;
static FunctionInfo ToString;
static FunctionInfo ToUpperCase;
static FunctionInfo Trim;
static FunctionInfo TrimLeft;
static FunctionInfo TrimRight;
static FunctionInfo Repeat;
static FunctionInfo StartsWith;
static FunctionInfo EndsWith;
static FunctionInfo Includes;
static FunctionInfo PadStart;
static FunctionInfo PadEnd;
#ifdef TAGENTRY
#undef TAGENTRY
#endif
#define TAGENTRY(name, ...) static FunctionInfo name;
#include "JavascriptStringTagEntries.h"
#undef TAGENTRY
static FunctionInfo ValueOf;
static FunctionInfo SymbolIterator;
};
static Var NewInstance(RecyclableObject* function, CallInfo callInfo, ...);
static Var EntryCharAt(RecyclableObject* function, CallInfo callInfo, ...);
static Var EntryCharCodeAt(RecyclableObject* function, CallInfo callInfo, ...);
static Var EntryCodePointAt(RecyclableObject* function, CallInfo callInfo, ...);
static Var EntryConcat(RecyclableObject* function, CallInfo callInfo, ...);
static Var EntryFromCharCode(RecyclableObject* function, CallInfo callInfo, ...);
static Var EntryFromCodePoint(RecyclableObject* function, CallInfo callInfo, ...);
static Var EntryIndexOf(RecyclableObject* function, CallInfo callInfo, ...);
static Var EntryLastIndexOf(RecyclableObject* function, CallInfo callInfo, ...);
static Var EntryLocaleCompare(RecyclableObject* function, CallInfo callInfo, ...);
static Var EntryMatch(RecyclableObject* function, CallInfo callInfo, ...);
static Var EntryNormalize(RecyclableObject* function, CallInfo callInfo, ...);
static Var EntryRaw(RecyclableObject* function, CallInfo callInfo, ...);
static Var EntryReplace(RecyclableObject* function, CallInfo callInfo, ...);
static Var EntrySearch(RecyclableObject* function, CallInfo callInfo, ...);
static Var EntrySlice(RecyclableObject* function, CallInfo callInfo, ...);
static Var EntrySplit(RecyclableObject* function, CallInfo callInfo, ...);
static Var EntrySubstring(RecyclableObject* function, CallInfo callInfo, ...);
static Var EntrySubstr(RecyclableObject* function, CallInfo callInfo, ...);
static Var EntryToLocaleLowerCase(RecyclableObject* function, CallInfo callInfo, ...);
static Var EntryToLocaleUpperCase(RecyclableObject* function, CallInfo callInfo, ...);
static Var EntryToLowerCase(RecyclableObject* function, CallInfo callInfo, ...);
static Var EntryToString(RecyclableObject* function, CallInfo callInfo, ...);
static Var EntryToUpperCase(RecyclableObject* function, CallInfo callInfo, ...);
static Var EntryTrim(RecyclableObject* function, CallInfo callInfo, ...);
static Var EntryTrimLeft(RecyclableObject* function, CallInfo callInfo, ...);
static Var EntryTrimRight(RecyclableObject* function, CallInfo callInfo, ...);
static Var EntryRepeat(RecyclableObject* function, CallInfo callInfo, ...);
static Var EntryStartsWith(RecyclableObject* function, CallInfo callInfo, ...);
static Var EntryEndsWith(RecyclableObject* function, CallInfo callInfo, ...);
static Var EntryIncludes(RecyclableObject* function, CallInfo callInfo, ...);
static Var EntryAnchor(RecyclableObject* function, CallInfo callInfo, ...);
static Var EntryBig(RecyclableObject* function, CallInfo callInfo, ...);
static Var EntryBlink(RecyclableObject* function, CallInfo callInfo, ...);
static Var EntryBold(RecyclableObject* function, CallInfo callInfo, ...);
static Var EntryFixed(RecyclableObject* function, CallInfo callInfo, ...);
static Var EntryFontColor(RecyclableObject* function, CallInfo callInfo, ...);
static Var EntryFontSize(RecyclableObject* function, CallInfo callInfo, ...);
static Var EntryItalics(RecyclableObject* function, CallInfo callInfo, ...);
static Var EntryLink(RecyclableObject* function, CallInfo callInfo, ...);
static Var EntrySmall(RecyclableObject* function, CallInfo callInfo, ...);
static Var EntryStrike(RecyclableObject* function, CallInfo callInfo, ...);
static Var EntrySub(RecyclableObject* function, CallInfo callInfo, ...);
static Var EntrySup(RecyclableObject* function, CallInfo callInfo, ...);
static Var EntryValueOf(RecyclableObject* function, CallInfo callInfo, ...);
static Var EntrySymbolIterator(RecyclableObject* function, CallInfo callInfo, ...);
static Var EntryPadStart(RecyclableObject* function, CallInfo callInfo, ...);
static Var EntryPadEnd(RecyclableObject* function, CallInfo callInfo, ...);
static JavascriptString* RepeatCore(JavascriptString* currentString, charcount_t count, ScriptContext* scriptContext);
static JavascriptString* PadCore(ArgumentReader& args, JavascriptString *mainString, bool isPadStart, ScriptContext* scriptContext);
static Var SubstringCore(JavascriptString* str, int start, int span, ScriptContext* scriptContext);
static charcount_t GetBufferLength(const char16 *content);
static charcount_t GetBufferLength(const char16 *content, int charLengthOrMinusOne);
static bool IsASCII7BitChar(char16 ch) { return ch < 0x0080; }
static char ToASCII7BitChar(char16 ch) { Assert(IsASCII7BitChar(ch)); return static_cast<char>(ch); }
private:
static int IndexOf(ArgumentReader& args, ScriptContext* scriptContext, const char16* apiNameForErrorMsg, bool isRegExpAnAllowedArg);
static void GetThisStringArgument(ArgumentReader& args, ScriptContext* scriptContext, const char16* apiNameForErrorMsg, JavascriptString** ppThis);
static void GetThisAndSearchStringArguments(ArgumentReader& args, ScriptContext* scriptContext, const char16* apiNameForErrorMsg, JavascriptString** ppThis, JavascriptString** ppSearch, bool isRegExpAnAllowedArg);
static BOOL GetThisValueVar(Var aValue, JavascriptString** pString, ScriptContext* scriptContext);
static Var StringBracketHelper(Arguments args, ScriptContext *scriptContext, __in_ecount(cchTag) char16 const*pszTag, charcount_t cchTag,
__in_ecount_opt(cchProp) char16 const*pszProp, charcount_t cchProp);
template< size_t N >
static Var StringBracketHelper(Arguments args, ScriptContext *scriptContext, const char16 (&tag)[N]);
template< size_t N1, size_t N2 >
static Var StringBracketHelper(Arguments args, ScriptContext *scriptContext, const char16 (&tag)[N1], const char16 (&prop)[N2]);
static void SearchValueHelper(ScriptContext* scriptContext, Var aValue, JavascriptRegExp ** ppSearchRegEx, JavascriptString ** ppSearchString);
static void ReplaceValueHelper(ScriptContext* scriptContext, Var aValue, JavascriptFunction ** ppReplaceFn, JavascriptString ** ppReplaceString);
static Var ToLocaleCaseHelper(Var thisObj, bool toUpper, ScriptContext *scriptContext);
static void InstantiateForceInlinedMembers();
template <bool trimLeft, bool trimRight>
static Var TrimLeftRightHelper(JavascriptString* arg, ScriptContext* scriptContext);
static Var DoStringReplace(Arguments& args, CallInfo& callInfo, JavascriptString* input, ScriptContext* scriptContext);
static Var DoStringSplit(Arguments& args, CallInfo& callInfo, JavascriptString* input, ScriptContext* scriptContext);
template<int argCount, typename FallbackFn>
static Var DelegateToRegExSymbolFunction(ArgumentReader &args, PropertyId symbolPropertyId, FallbackFn fallback, PCWSTR varName, ScriptContext* scriptContext);
static Var GetRegExSymbolFunction(Var regExp, PropertyId propertyId, ScriptContext* scriptContext);
template<int argCount> // The count is excluding 'this'
static Var CallRegExSymbolFunction(Var fn, Var regExp, Arguments& args, PCWSTR const varName, ScriptContext* scriptContext);
template<int argCount> // The count is excluding 'this'
static Var CallRegExFunction(RecyclableObject* fnObj, Var regExp, Arguments& args);
};
template<>
struct PropertyRecordStringHashComparer<JavascriptString *>
{
__inline static bool Equals(JavascriptString * str1, JavascriptString * str2)
{
return (str1->GetLength() == str2->GetLength() &&
JsUtil::CharacterBuffer<WCHAR>::StaticEquals(str1->GetString(), str2->GetString(), str1->GetLength()));
}
__inline static bool Equals(JavascriptString * str1, JsUtil::CharacterBuffer<WCHAR> const & str2)
{
return (str1->GetLength() == str2.GetLength() &&
JsUtil::CharacterBuffer<WCHAR>::StaticEquals(str1->GetString(), str2.GetBuffer(), str1->GetLength()));
}
__inline static bool Equals(JavascriptString * str1, PropertyRecord const * str2)
{
return (str1->GetLength() == str2->GetLength() && !Js::IsInternalPropertyId(str2->GetPropertyId()) &&
JsUtil::CharacterBuffer<WCHAR>::StaticEquals(str1->GetString(), str2->GetBuffer(), str1->GetLength()));
}
__inline static uint GetHashCode(JavascriptString * str)
{
return JsUtil::CharacterBuffer<WCHAR>::StaticGetHashCode(str->GetString(), str->GetLength());
}
};
__inline bool PropertyRecordStringHashComparer<PropertyRecord const *>::Equals(PropertyRecord const * str1, JavascriptString * str2)
{
return (str1->GetLength() == str2->GetLength() && !Js::IsInternalPropertyId(str1->GetPropertyId()) &&
JsUtil::CharacterBuffer<WCHAR>::StaticEquals(str1->GetBuffer(), str2->GetString(), str1->GetLength()));
}
}
template <>
struct DefaultComparer<Js::JavascriptString*>
{
__inline static bool Equals(Js::JavascriptString * x, Js::JavascriptString * y)
{
return Js::JavascriptString::Equals(x, y);
}
__inline static uint GetHashCode(Js::JavascriptString * pStr)
{
return JsUtil::CharacterBuffer<char16>::StaticGetHashCode(pStr->GetString(), pStr->GetLength());
}
};