/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*- * This Source Code Form is subject to the terms of the Mozilla Public * License, v. 2.0. If a copy of the MPL was not distributed with this * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ #ifndef jsstr_h #define jsstr_h #include "mozilla/HashFunctions.h" #include "mozilla/PodOperations.h" #include "mozilla/TextUtils.h" #include #include "jsutil.h" #include "NamespaceImports.h" #include "gc/Rooting.h" #include "js/RootingAPI.h" #include "js/UniquePtr.h" #include "vm/Printer.h" #include "vm/Unicode.h" class JSAutoByteString; class JSLinearString; namespace js { class StringBuffer; template extern JSString* ConcatStrings(ExclusiveContext* cx, typename MaybeRooted::HandleType left, typename MaybeRooted::HandleType right); // Return s advanced past any Unicode white space characters. template static inline const CharT* SkipSpace(const CharT* s, const CharT* end) { MOZ_ASSERT(s <= end); while (s < end && unicode::IsSpace(*s)) s++; return s; } // Return less than, equal to, or greater than zero depending on whether // s1 is less than, equal to, or greater than s2. template inline int32_t CompareChars(const Char1* s1, size_t len1, const Char2* s2, size_t len2) { size_t n = Min(len1, len2); for (size_t i = 0; i < n; i++) { if (int32_t cmp = s1[i] - s2[i]) return cmp; } return int32_t(len1 - len2); } extern int32_t CompareChars(const char16_t* s1, size_t len1, JSLinearString* s2); } /* namespace js */ struct JSSubString { JSLinearString* base; size_t offset; size_t length; JSSubString() { mozilla::PodZero(this); } void initEmpty(JSLinearString* base) { this->base = base; offset = length = 0; } void init(JSLinearString* base, size_t offset, size_t length) { this->base = base; this->offset = offset; this->length = length; } }; /* * Shorthands for ASCII (7-bit) decimal and hex conversion. * Manually inline isdigit for performance; MSVC doesn't do this for us. */ #define JS7_ISDEC(c) ((((unsigned)(c)) - '0') <= 9) #define JS7_UNDEC(c) ((c) - '0') #define JS7_ISOCT(c) ((((unsigned)(c)) - '0') <= 7) #define JS7_UNOCT(c) (JS7_UNDEC(c)) #define JS7_ISHEX(c) ((c) < 128 && isxdigit(c)) #define JS7_UNHEX(c) (unsigned)(JS7_ISDEC(c) ? (c) - '0' : 10 + tolower(c) - 'a') #define JS7_ISLET(c) (mozilla::IsAsciiAlpha(c)) extern size_t js_strlen(const char16_t* s); extern int32_t js_strcmp(const char16_t* lhs, const char16_t* rhs); template extern const CharT* js_strchr_limit(const CharT* s, char16_t c, const CharT* limit); static MOZ_ALWAYS_INLINE void js_strncpy(char16_t* dst, const char16_t* src, size_t nelem) { return mozilla::PodCopy(dst, src, nelem); } extern int32_t js_fputs(const char16_t* s, FILE* f); namespace js { /* Initialize the String class, returning its prototype object. */ extern JSObject* InitStringClass(JSContext* cx, HandleObject obj); /* * Convert a value to a printable C string. */ extern const char* ValueToPrintable(JSContext* cx, const Value&, JSAutoByteString* bytes, bool asSource = false); extern UniqueChars DuplicateString(ExclusiveContext* cx, const char* s); extern UniqueTwoByteChars DuplicateString(ExclusiveContext* cx, const char16_t* s); /* * These variants do not report OOMs, you must arrange for OOMs to be reported * yourself. */ extern UniqueChars DuplicateString(const char* s); extern UniqueChars DuplicateString(const char* s, size_t n); extern UniqueTwoByteChars DuplicateString(const char16_t* s); extern UniqueTwoByteChars DuplicateString(const char16_t* s, size_t n); /* * Convert a non-string value to a string, returning null after reporting an * error, otherwise returning a new string reference. */ template extern JSString* ToStringSlow(ExclusiveContext* cx, typename MaybeRooted::HandleType arg); /* * Convert the given value to a string. This method includes an inline * fast-path for the case where the value is already a string; if the value is * known not to be a string, use ToStringSlow instead. */ template static MOZ_ALWAYS_INLINE JSString* ToString(JSContext* cx, JS::HandleValue v) { if (v.isString()) return v.toString(); return ToStringSlow(cx, v); } /* * This function implements E-262-3 section 9.8, toString. Convert the given * value to a string of characters appended to the given buffer. On error, the * passed buffer may have partial results appended. */ inline bool ValueToStringBuffer(JSContext* cx, const Value& v, StringBuffer& sb); /* * Convert a value to its source expression, returning null after reporting * an error, otherwise returning a new string reference. */ extern JSString* ValueToSource(JSContext* cx, HandleValue v); /* * Convert a JSString to its source expression; returns null after reporting an * error, otherwise returns a new string reference. No Handle needed since the * input is dead after the GC. */ extern JSString* StringToSource(JSContext* cx, JSString* str); /* * Test if strings are equal. The caller can call the function even if str1 * or str2 are not GC-allocated things. */ extern bool EqualStrings(JSContext* cx, JSString* str1, JSString* str2, bool* result); /* Use the infallible method instead! */ extern bool EqualStrings(JSContext* cx, JSLinearString* str1, JSLinearString* str2, bool* result) = delete; /* EqualStrings is infallible on linear strings. */ extern bool EqualStrings(JSLinearString* str1, JSLinearString* str2); extern bool EqualChars(JSLinearString* str1, JSLinearString* str2); /* * Return less than, equal to, or greater than zero depending on whether * str1 is less than, equal to, or greater than str2. */ extern bool CompareStrings(JSContext* cx, JSString* str1, JSString* str2, int32_t* result); /* * Same as CompareStrings but for atoms. Don't use this to just test * for equality; use this when you need an ordering on atoms. */ extern int32_t CompareAtoms(JSAtom* atom1, JSAtom* atom2); /* * Return true if the string matches the given sequence of ASCII bytes. */ extern bool StringEqualsAscii(JSLinearString* str, const char* asciiBytes); /* Return true if the string contains a pattern anywhere inside it. */ extern bool StringHasPattern(JSLinearString* text, const char16_t* pat, uint32_t patlen); extern int StringFindPattern(JSLinearString* text, JSLinearString* pat, size_t start); /* Return true if the string contains a pattern at |start|. */ extern bool HasSubstringAt(JSLinearString* text, JSLinearString* pat, size_t start); template inline bool EqualChars(const Char1* s1, const Char2* s2, size_t len); template inline bool EqualChars(const Char1* s1, const Char1* s2, size_t len) { return mozilla::PodEqual(s1, s2, len); } template inline bool EqualChars(const Char1* s1, const Char2* s2, size_t len) { for (const Char1* s1end = s1 + len; s1 < s1end; s1++, s2++) { if (*s1 != *s2) return false; } return true; } /* * Computes |str|'s substring for the range [beginInt, beginInt + lengthInt). * Negative, overlarge, swapped, etc. |beginInt| and |lengthInt| are forbidden * and constitute API misuse. */ JSString* SubstringKernel(JSContext* cx, HandleString str, int32_t beginInt, int32_t lengthInt); /* * Inflate bytes in ASCII encoding to char16_t code units. Return null on error, * otherwise return the char16_t buffer that was malloc'ed. length is updated to * the length of the new string (in char16_t code units). A null char is * appended, but it is not included in the length. */ extern char16_t* InflateString(ExclusiveContext* cx, const char* bytes, size_t* length); /* * Inflate bytes to JS chars in an existing buffer. 'dst' must be large * enough for 'srclen' char16_t code units. The buffer is NOT null-terminated. */ inline void CopyAndInflateChars(char16_t* dst, const char* src, size_t srclen) { for (size_t i = 0; i < srclen; i++) dst[i] = (unsigned char) src[i]; } inline void CopyAndInflateChars(char16_t* dst, const JS::Latin1Char* src, size_t srclen) { for (size_t i = 0; i < srclen; i++) dst[i] = src[i]; } /* * Deflate JS chars to bytes into a buffer. 'bytes' must be large enough for * 'length chars. The buffer is NOT null-terminated. The destination length * must to be initialized with the buffer size and will contain on return the * number of copied bytes. */ template extern bool DeflateStringToBuffer(JSContext* maybecx, const CharT* chars, size_t charsLength, char* bytes, size_t* length); extern bool str_fromCharCode(JSContext* cx, unsigned argc, Value* vp); extern bool str_fromCharCode_one_arg(JSContext* cx, HandleValue code, MutableHandleValue rval); extern bool str_fromCodePoint(JSContext* cx, unsigned argc, Value* vp); extern bool str_fromCodePoint_one_arg(JSContext* cx, HandleValue code, MutableHandleValue rval); /* String methods exposed so they can be installed in the self-hosting global. */ extern bool str_includes(JSContext* cx, unsigned argc, Value* vp); extern bool str_indexOf(JSContext* cx, unsigned argc, Value* vp); extern bool str_lastIndexOf(JSContext* cx, unsigned argc, Value* vp); extern bool str_startsWith(JSContext* cx, unsigned argc, Value* vp); extern bool str_toLowerCase(JSContext* cx, unsigned argc, Value* vp); extern bool str_toUpperCase(JSContext* cx, unsigned argc, Value* vp); extern bool str_toString(JSContext* cx, unsigned argc, Value* vp); extern bool str_charAt(JSContext* cx, unsigned argc, Value* vp); extern bool str_charCodeAt_impl(JSContext* cx, HandleString string, HandleValue index, MutableHandleValue res); extern bool str_charCodeAt(JSContext* cx, unsigned argc, Value* vp); extern bool str_contains(JSContext *cx, unsigned argc, Value *vp); extern bool str_endsWith(JSContext* cx, unsigned argc, Value* vp); extern bool str_trim(JSContext* cx, unsigned argc, Value* vp); extern bool str_trimStart(JSContext* cx, unsigned argc, Value* vp); extern bool str_trimEnd(JSContext* cx, unsigned argc, Value* vp); /** * Returns the input string converted to lower case based on the language * specific case mappings for the input locale. * * Usage: lowerCase = intl_toLocaleLowerCase(string, locale) */ [[nodiscard]] extern bool intl_toLocaleLowerCase(JSContext* cx, unsigned argc, Value* vp); /** * Returns the input string converted to upper case based on the language * specific case mappings for the input locale. * * Usage: upperCase = intl_toLocaleUpperCase(string, locale) */ [[nodiscard]] extern bool intl_toLocaleUpperCase(JSContext* cx, unsigned argc, Value* vp); extern bool str_normalize(JSContext* cx, unsigned argc, Value* vp); extern bool str_concat(JSContext* cx, unsigned argc, Value* vp); /* * Convert one UCS-4 char and write it into a UTF-8 buffer, which must be at * least 4 bytes long. Return the number of UTF-8 bytes of data written. */ extern uint32_t OneUcs4ToUtf8Char(uint8_t* utf8Buffer, uint32_t ucs4Char); extern size_t PutEscapedStringImpl(char* buffer, size_t size, GenericPrinter* out, JSLinearString* str, uint32_t quote); template extern size_t PutEscapedStringImpl(char* buffer, size_t bufferSize, GenericPrinter* out, const CharT* chars, size_t length, uint32_t quote); /* * Write str into buffer escaping any non-printable or non-ASCII character * using \escapes for JS string literals. * Guarantees that a NUL is at the end of the buffer unless size is 0. Returns * the length of the written output, NOT including the NUL. Thus, a return * value of size or more means that the output was truncated. If buffer * is null, just returns the length of the output. If quote is not 0, it must * be a single or double quote character that will quote the output. */ inline size_t PutEscapedString(char* buffer, size_t size, JSLinearString* str, uint32_t quote) { size_t n = PutEscapedStringImpl(buffer, size, nullptr, str, quote); /* PutEscapedStringImpl can only fail with a file. */ MOZ_ASSERT(n != size_t(-1)); return n; } template inline size_t PutEscapedString(char* buffer, size_t bufferSize, const CharT* chars, size_t length, uint32_t quote) { size_t n = PutEscapedStringImpl(buffer, bufferSize, nullptr, chars, length, quote); /* PutEscapedStringImpl can only fail with a file. */ MOZ_ASSERT(n != size_t(-1)); return n; } inline bool EscapedStringPrinter(GenericPrinter& out, JSLinearString* str, uint32_t quote) { return PutEscapedStringImpl(nullptr, 0, &out, str, quote) != size_t(-1); } inline bool EscapedStringPrinter(GenericPrinter& out, const char* chars, size_t length, uint32_t quote) { return PutEscapedStringImpl(nullptr, 0, &out, chars, length, quote) != size_t(-1); } /* * Write str into file escaping any non-printable or non-ASCII character. * If quote is not 0, it must be a single or double quote character that * will quote the output. */ inline bool FileEscapedString(FILE* fp, JSLinearString* str, uint32_t quote) { Fprinter out(fp); bool res = EscapedStringPrinter(out, str, quote); out.finish(); return res; } inline bool FileEscapedString(FILE* fp, const char* chars, size_t length, uint32_t quote) { Fprinter out(fp); bool res = EscapedStringPrinter(out, chars, length, quote); out.finish(); return res; } JSObject* str_split_string(JSContext* cx, HandleObjectGroup group, HandleString str, HandleString sep, uint32_t limit); JSString * str_flat_replace_string(JSContext *cx, HandleString string, HandleString pattern, HandleString replacement); JSString* str_replace_string_raw(JSContext* cx, HandleString string, HandleString pattern, HandleString replacement); JSString* str_replaceAll_string_raw(JSContext* cx, HandleString string, HandleString pattern, HandleString replacement); extern JSString* StringToLowerCase(JSContext* cx, HandleLinearString string); extern JSString* StringToUpperCase(JSContext* cx, HandleLinearString string); extern bool StringConstructor(JSContext* cx, unsigned argc, Value* vp); extern bool FlatStringMatch(JSContext* cx, unsigned argc, Value* vp); extern bool FlatStringSearch(JSContext* cx, unsigned argc, Value* vp); } /* namespace js */ #endif /* jsstr_h */