Bug 1576076 - Part 2: Support Latin1Char in JSExternalString. r=sfink

Adds:
  * JS_NewExternalStringLatin1
  * JS_NewMaybeExternalStringLatin1
  * IsExternalStringLatin1
  * latin1 methods for JSExternalStringCallbacks

Differential Revision: https://phabricator.services.mozilla.com/D194673
This commit is contained in:
Tooru Fujisawa 2023-12-07 15:17:04 +00:00
parent 7cc22c161b
commit 8101d8a476
11 changed files with 313 additions and 40 deletions

View file

@ -556,9 +556,11 @@ using JSHostCleanupFinalizationRegistryCallback =
*/
struct JSExternalStringCallbacks {
/**
* Finalizes external strings created by JS_NewExternalUCString. The finalizer
* can be called off the main thread.
* Finalizes external strings created by JS_NewExternalStringLatin1 or
* JS_NewExternalUCString. The finalizer can be called off the main
* thread.
*/
virtual void finalize(JS::Latin1Char* chars) const = 0;
virtual void finalize(char16_t* chars) const = 0;
/**
@ -569,6 +571,8 @@ struct JSExternalStringCallbacks {
*
* Implementations of this callback MUST NOT do anything that can cause GC.
*/
virtual size_t sizeOfBuffer(const JS::Latin1Char* chars,
mozilla::MallocSizeOf mallocSizeOf) const = 0;
virtual size_t sizeOfBuffer(const char16_t* chars,
mozilla::MallocSizeOf mallocSizeOf) const = 0;
};
@ -1269,6 +1273,9 @@ extern JS_PUBLIC_API void JS_SetGCParametersBasedOnAvailableMemory(
* Create a new JSString whose chars member refers to external memory, i.e.,
* memory requiring application-specific finalization.
*/
extern JS_PUBLIC_API JSString* JS_NewExternalStringLatin1(
JSContext* cx, const JS::Latin1Char* chars, size_t length,
const JSExternalStringCallbacks* callbacks);
extern JS_PUBLIC_API JSString* JS_NewExternalUCString(
JSContext* cx, const char16_t* chars, size_t length,
const JSExternalStringCallbacks* callbacks);
@ -1280,13 +1287,17 @@ extern JS_PUBLIC_API JSString* JS_NewExternalUCString(
* external string allocated by a previous call and |*allocatedExternal| is set
* to false. If |*allocatedExternal| is false, |fin| won't be called.
*/
extern JS_PUBLIC_API JSString* JS_NewMaybeExternalStringLatin1(
JSContext* cx, const JS::Latin1Char* chars, size_t length,
const JSExternalStringCallbacks* callbacks, bool* allocatedExternal);
extern JS_PUBLIC_API JSString* JS_NewMaybeExternalUCString(
JSContext* cx, const char16_t* chars, size_t length,
const JSExternalStringCallbacks* callbacks, bool* allocatedExternal);
/**
* Return the 'callbacks' arg passed to JS_NewExternalUCString or
* JS_NewMaybeExternalUCString.
* Return the 'callbacks' arg passed to JS_NewExternalStringLatin1,
* JS_NewExternalUCString, JS_NewMaybeExternalStringLatin1,
* or JS_NewMaybeExternalUCString.
*/
extern JS_PUBLIC_API const JSExternalStringCallbacks*
JS_GetExternalStringCallbacks(JSString* str);

View file

@ -392,17 +392,37 @@ MOZ_ALWAYS_INLINE JSLinearString* AtomToLinearString(JSAtom* atom) {
}
/**
* If the provided string uses externally-managed storage, return true and set
* |*callbacks| to the external-string callbacks used to create it and |*chars|
* to a pointer to its two-byte storage. (These pointers remain valid as long
* as the provided string is kept alive.)
* If the provided string uses externally-managed latin-1 storage, return true
* and set |*callbacks| to the external-string callbacks used to create it and
* |*chars| to a pointer to its latin1 storage. (These pointers remain valid
* as long as the provided string is kept alive.)
*/
MOZ_ALWAYS_INLINE bool IsExternalStringLatin1(
JSString* str, const JSExternalStringCallbacks** callbacks,
const JS::Latin1Char** chars) {
shadow::String* s = shadow::AsShadowString(str);
if (!s->isExternal() || !s->hasLatin1Chars()) {
return false;
}
*callbacks = s->externalCallbacks;
*chars = s->nonInlineCharsLatin1;
return true;
}
/**
* If the provided string uses externally-managed two-byte storage, return true
* and set |*callbacks| to the external-string callbacks used to create it and
* |*chars| to a pointer to its two-byte storage. (These pointers remain valid
* as long as the provided string is kept alive.)
*/
MOZ_ALWAYS_INLINE bool IsExternalUCString(
JSString* str, const JSExternalStringCallbacks** callbacks,
const char16_t** chars) {
shadow::String* s = shadow::AsShadowString(str);
if (!s->isExternal()) {
if (!s->isExternal() || s->hasLatin1Chars()) {
return false;
}

View file

@ -3419,7 +3419,12 @@ static bool AddWatchtowerTarget(JSContext* cx, unsigned argc, Value* vp) {
}
struct TestExternalString : public JSExternalStringCallbacks {
void finalize(JS::Latin1Char* chars) const override { js_free(chars); }
void finalize(char16_t* chars) const override { js_free(chars); }
size_t sizeOfBuffer(const JS::Latin1Char* chars,
mozilla::MallocSizeOf mallocSizeOf) const override {
return mallocSizeOf(chars);
}
size_t sizeOfBuffer(const char16_t* chars,
mozilla::MallocSizeOf mallocSizeOf) const override {
return mallocSizeOf(chars);

View file

@ -92,13 +92,25 @@ class MOZ_NON_TEMPORARY_CLASS ExternalStringCache {
inlineEntries_ = {};
}
MOZ_ALWAYS_INLINE JSExternalString* lookupExternal(
const JS::Latin1Char* chars, size_t len) const;
MOZ_ALWAYS_INLINE JSExternalString* lookupExternal(const char16_t* chars,
size_t len) const;
MOZ_ALWAYS_INLINE void putExternal(JSExternalString* s);
MOZ_ALWAYS_INLINE JSInlineString* lookupInline(const JS::Latin1Char* chars,
size_t len) const;
MOZ_ALWAYS_INLINE JSInlineString* lookupInline(const char16_t* chars,
size_t len) const;
MOZ_ALWAYS_INLINE void putInline(JSInlineString* s);
private:
template <typename CharT>
MOZ_ALWAYS_INLINE JSExternalString* lookupExternalImpl(const CharT* chars,
size_t len) const;
template <typename CharT>
MOZ_ALWAYS_INLINE JSInlineString* lookupInlineImpl(const CharT* chars,
size_t len) const;
};
class MOZ_NON_TEMPORARY_CLASS FunctionToStringCache {

View file

@ -8,28 +8,48 @@
static const char16_t arr[] = u"hi, don't delete me";
static const size_t arrlen = js_strlen(arr);
static const char arr2[] = "hi, don't delete me";
static const size_t arrlen2 = js_strlen(arr2);
static int finalized1 = 0;
static int finalized2 = 0;
static int finalized3 = 0;
static int finalized4 = 0;
struct ExternalStringCallbacks : public JSExternalStringCallbacks {
int* finalizedCount = nullptr;
bool isTwoBytes = false;
explicit ExternalStringCallbacks(int* finalizedCount)
: finalizedCount(finalizedCount) {}
explicit ExternalStringCallbacks(int* finalizedCount, bool isTwoBytes)
: finalizedCount(finalizedCount), isTwoBytes(isTwoBytes) {}
void finalize(JS::Latin1Char* chars) const override {
MOZ_ASSERT(!isTwoBytes);
MOZ_ASSERT(chars == (JS::Latin1Char*)arr2);
(*finalizedCount)++;
}
void finalize(char16_t* chars) const override {
MOZ_ASSERT(isTwoBytes);
MOZ_ASSERT(chars == arr);
(*finalizedCount)++;
}
size_t sizeOfBuffer(const JS::Latin1Char* chars,
mozilla::MallocSizeOf mallocSizeOf) const override {
MOZ_CRASH("Unexpected call");
}
size_t sizeOfBuffer(const char16_t* chars,
mozilla::MallocSizeOf mallocSizeOf) const override {
MOZ_CRASH("Unexpected call");
}
};
static const ExternalStringCallbacks callbacks1(&finalized1);
static const ExternalStringCallbacks callbacks2(&finalized2);
static const ExternalStringCallbacks callbacks1(&finalized1, true);
static const ExternalStringCallbacks callbacks2(&finalized2, true);
static const ExternalStringCallbacks callbacks3(&finalized3, false);
static const ExternalStringCallbacks callbacks4(&finalized4, false);
BEGIN_TEST(testExternalStrings) {
const unsigned N = 1000;
@ -37,15 +57,18 @@ BEGIN_TEST(testExternalStrings) {
for (unsigned i = 0; i < N; ++i) {
CHECK(JS_NewExternalUCString(cx, arr, arrlen, &callbacks1));
CHECK(JS_NewExternalUCString(cx, arr, arrlen, &callbacks2));
CHECK(JS_NewExternalStringLatin1(cx, (JS::Latin1Char*)arr2, arrlen2,
&callbacks3));
CHECK(JS_NewExternalStringLatin1(cx, (JS::Latin1Char*)arr2, arrlen2,
&callbacks4));
}
JS_GC(cx);
// a generous fudge factor to account for strings rooted by conservative gc
const unsigned epsilon = 10;
CHECK((N - finalized1) < epsilon);
CHECK((N - finalized2) < epsilon);
CHECK((N - finalized1) == 0);
CHECK((N - finalized2) == 0);
CHECK((N - finalized3) == 0);
CHECK((N - finalized4) == 0);
return true;
}

View file

@ -1463,6 +1463,14 @@ JS_PUBLIC_API void JS_SetGCParametersBasedOnAvailableMemory(
}
}
JS_PUBLIC_API JSString* JS_NewExternalStringLatin1(
JSContext* cx, const Latin1Char* chars, size_t length,
const JSExternalStringCallbacks* callbacks) {
AssertHeapIsIdle();
CHECK_THREAD(cx);
return JSExternalString::new_(cx, chars, length, callbacks);
}
JS_PUBLIC_API JSString* JS_NewExternalUCString(
JSContext* cx, const char16_t* chars, size_t length,
const JSExternalStringCallbacks* callbacks) {
@ -1471,6 +1479,15 @@ JS_PUBLIC_API JSString* JS_NewExternalUCString(
return JSExternalString::new_(cx, chars, length, callbacks);
}
JS_PUBLIC_API JSString* JS_NewMaybeExternalStringLatin1(
JSContext* cx, const JS::Latin1Char* chars, size_t length,
const JSExternalStringCallbacks* callbacks, bool* allocatedExternal) {
AssertHeapIsIdle();
CHECK_THREAD(cx);
return NewMaybeExternalString(cx, chars, length, callbacks,
allocatedExternal);
}
JS_PUBLIC_API JSString* JS_NewMaybeExternalUCString(
JSContext* cx, const char16_t* chars, size_t length,
const JSExternalStringCallbacks* callbacks, bool* allocatedExternal) {

View file

@ -589,17 +589,29 @@ inline JSExternalString::JSExternalString(
d.s.u3.externalCallbacks = callbacks;
}
MOZ_ALWAYS_INLINE JSExternalString* JSExternalString::new_(
JSContext* cx, const char16_t* chars, size_t length,
inline JSExternalString::JSExternalString(
const JS::Latin1Char* chars, size_t length,
const JSExternalStringCallbacks* callbacks) {
MOZ_ASSERT(callbacks);
setLengthAndFlags(length, EXTERNAL_FLAGS | LATIN1_CHARS_BIT);
d.s.u2.nonInlineCharsLatin1 = chars;
d.s.u3.externalCallbacks = callbacks;
}
template <typename CharT>
/* static */
MOZ_ALWAYS_INLINE JSExternalString* JSExternalString::newImpl(
JSContext* cx, const CharT* chars, size_t length,
const JSExternalStringCallbacks* callbacks) {
if (MOZ_UNLIKELY(!validateLength(cx, length))) {
return nullptr;
}
auto* str = cx->newCell<JSExternalString>(chars, length, callbacks);
if (!str) {
return nullptr;
}
size_t nbytes = length * sizeof(char16_t);
size_t nbytes = length * sizeof(CharT);
MOZ_ASSERT(str->isTenured());
js::AddCellMemory(str, nbytes, js::MemoryUse::StringContents);
@ -607,6 +619,20 @@ MOZ_ALWAYS_INLINE JSExternalString* JSExternalString::new_(
return str;
}
/* static */
MOZ_ALWAYS_INLINE JSExternalString* JSExternalString::new_(
JSContext* cx, const JS::Latin1Char* chars, size_t length,
const JSExternalStringCallbacks* callbacks) {
return newImpl(cx, chars, length, callbacks);
}
/* static */
MOZ_ALWAYS_INLINE JSExternalString* JSExternalString::new_(
JSContext* cx, const char16_t* chars, size_t length,
const JSExternalStringCallbacks* callbacks) {
return newImpl(cx, chars, length, callbacks);
}
inline js::NormalAtom::NormalAtom(size_t length, JS::Latin1Char** chars,
js::HashNumber hash)
: hash_(hash) {
@ -725,10 +751,17 @@ inline void js::FatInlineAtom::finalize(JS::GCContext* gcx) {
inline void JSExternalString::finalize(JS::GCContext* gcx) {
MOZ_ASSERT(JSString::isExternal());
size_t nbytes = length() * sizeof(char16_t);
gcx->removeCellMemory(this, nbytes, js::MemoryUse::StringContents);
if (hasLatin1Chars()) {
size_t nbytes = length() * sizeof(JS::Latin1Char);
gcx->removeCellMemory(this, nbytes, js::MemoryUse::StringContents);
callbacks()->finalize(const_cast<char16_t*>(rawTwoByteChars()));
callbacks()->finalize(const_cast<JS::Latin1Char*>(rawLatin1Chars()));
} else {
size_t nbytes = length() * sizeof(char16_t);
gcx->removeCellMemory(this, nbytes, js::MemoryUse::StringContents);
callbacks()->finalize(const_cast<char16_t*>(rawTwoByteChars()));
}
}
#endif /* vm_StringType_inl_h */

View file

@ -86,8 +86,14 @@ size_t JSString::sizeOfExcludingThis(mozilla::MallocSizeOf mallocSizeOf) {
if (isExternal()) {
// Our callback isn't supposed to cause GC.
JS::AutoSuppressGCAnalysis nogc;
return asExternal().callbacks()->sizeOfBuffer(asExternal().twoByteChars(),
mallocSizeOf);
JSExternalString& external = asExternal();
if (external.hasLatin1Chars()) {
return asExternal().callbacks()->sizeOfBuffer(external.latin1Chars(),
mallocSizeOf);
} else {
return asExternal().callbacks()->sizeOfBuffer(external.twoByteChars(),
mallocSizeOf);
}
}
// JSExtensibleString: count the full capacity, not just the used space.
@ -1550,6 +1556,11 @@ JSLinearString* js::NewDependentString(JSContext* cx, JSString* baseArg,
return JSDependentString::new_(cx, base, start, length, heap);
}
static constexpr bool CanStoreCharsAsLatin1(const JS::Latin1Char* s,
size_t length) {
return true;
}
static inline bool CanStoreCharsAsLatin1(const char16_t* s, size_t length) {
return IsUtf16Latin1(Span(s, length));
}
@ -1890,8 +1901,9 @@ JSLinearString* NewStringCopyUTF8N(JSContext* cx, const JS::UTF8Chars utf8,
return NewString<js::CanGC>(cx, std::move(utf16), length, heap);
}
MOZ_ALWAYS_INLINE JSExternalString* ExternalStringCache::lookupExternal(
const char16_t* chars, size_t len) const {
template <typename CharT>
MOZ_ALWAYS_INLINE JSExternalString* ExternalStringCache::lookupExternalImpl(
const CharT* chars, size_t len) const {
AutoCheckCannotGC nogc;
for (size_t i = 0; i < NumEntries; i++) {
@ -1900,7 +1912,17 @@ MOZ_ALWAYS_INLINE JSExternalString* ExternalStringCache::lookupExternal(
continue;
}
const char16_t* strChars = str->nonInlineTwoByteChars(nogc);
if constexpr (std::is_same_v<CharT, JS::Latin1Char>) {
if (!str->hasLatin1Chars()) {
continue;
}
} else {
if (!str->hasTwoByteChars()) {
continue;
}
}
const CharT* strChars = str->nonInlineChars<CharT>(nogc);
if (chars == strChars) {
// Note that we don't need an incremental barrier here or below.
// The cache is purged on GC so any string we get from the cache
@ -1919,16 +1941,25 @@ MOZ_ALWAYS_INLINE JSExternalString* ExternalStringCache::lookupExternal(
return nullptr;
}
MOZ_ALWAYS_INLINE JSExternalString* ExternalStringCache::lookupExternal(
const JS::Latin1Char* chars, size_t len) const {
return lookupExternalImpl(chars, len);
}
MOZ_ALWAYS_INLINE JSExternalString* ExternalStringCache::lookupExternal(
const char16_t* chars, size_t len) const {
return lookupExternalImpl(chars, len);
}
MOZ_ALWAYS_INLINE void ExternalStringCache::putExternal(JSExternalString* str) {
MOZ_ASSERT(str->hasTwoByteChars());
for (size_t i = NumEntries - 1; i > 0; i--) {
externalEntries_[i] = externalEntries_[i - 1];
}
externalEntries_[0] = str;
}
MOZ_ALWAYS_INLINE JSInlineString* ExternalStringCache::lookupInline(
const char16_t* chars, size_t len) const {
template <typename CharT>
MOZ_ALWAYS_INLINE JSInlineString* ExternalStringCache::lookupInlineImpl(
const CharT* chars, size_t len) const {
MOZ_ASSERT(CanStoreCharsAsLatin1(chars, len));
MOZ_ASSERT(JSThinInlineString::lengthFits<Latin1Char>(len));
@ -1949,6 +1980,15 @@ MOZ_ALWAYS_INLINE JSInlineString* ExternalStringCache::lookupInline(
return nullptr;
}
MOZ_ALWAYS_INLINE JSInlineString* ExternalStringCache::lookupInline(
const JS::Latin1Char* chars, size_t len) const {
return lookupInlineImpl(chars, len);
}
MOZ_ALWAYS_INLINE JSInlineString* ExternalStringCache::lookupInline(
const char16_t* chars, size_t len) const {
return lookupInlineImpl(chars, len);
}
MOZ_ALWAYS_INLINE void ExternalStringCache::putInline(JSInlineString* str) {
MOZ_ASSERT(str->hasLatin1Chars());
@ -1958,7 +1998,26 @@ MOZ_ALWAYS_INLINE void ExternalStringCache::putInline(JSInlineString* str) {
inlineEntries_[0] = str;
}
JSString* NewMaybeExternalString(JSContext* cx, const char16_t* s, size_t n,
} /* namespace js */
template <AllowGC allowGC>
static MOZ_ALWAYS_INLINE JSInlineString* NewInlineStringMaybeDeflated(
JSContext* cx, const mozilla::Range<const JS::Latin1Char>& chars,
gc::Heap heap = gc::Heap::Default) {
return NewInlineString<allowGC>(cx, chars, heap);
}
template <AllowGC allowGC>
static MOZ_ALWAYS_INLINE JSInlineString* NewInlineStringMaybeDeflated(
JSContext* cx, const mozilla::Range<const char16_t>& chars,
gc::Heap heap = gc::Heap::Default) {
return NewInlineStringDeflated<allowGC>(cx, chars, heap);
}
namespace js {
template <typename CharT>
JSString* NewMaybeExternalString(JSContext* cx, const CharT* s, size_t n,
const JSExternalStringCallbacks* callbacks,
bool* allocatedExternal, gc::Heap heap) {
if (JSString* str = TryEmptyOrStaticString(cx, s, n)) {
@ -1974,8 +2033,8 @@ JSString* NewMaybeExternalString(JSContext* cx, const char16_t* s, size_t n,
if (JSInlineString* str = cache.lookupInline(s, n)) {
return str;
}
JSInlineString* str = NewInlineStringDeflated<AllowGC::CanGC>(
cx, mozilla::Range<const char16_t>(s, n), heap);
JSInlineString* str = NewInlineStringMaybeDeflated<AllowGC::CanGC>(
cx, mozilla::Range<const CharT>(s, n), heap);
if (!str) {
return nullptr;
}
@ -1998,6 +2057,16 @@ JSString* NewMaybeExternalString(JSContext* cx, const char16_t* s, size_t n,
return str;
}
template JSString* NewMaybeExternalString(
JSContext* cx, const JS::Latin1Char* s, size_t n,
const JSExternalStringCallbacks* callbacks, bool* allocatedExternal,
gc::Heap heap);
template JSString* NewMaybeExternalString(
JSContext* cx, const char16_t* s, size_t n,
const JSExternalStringCallbacks* callbacks, bool* allocatedExternal,
gc::Heap heap);
} /* namespace js */
#if defined(DEBUG) || defined(JS_JITSPEW) || defined(JS_CACHEIR_SPEW)
@ -2029,9 +2098,17 @@ void JSLinearString::dumpRepresentation(js::GenericPrinter& out,
#endif
struct RepresentativeExternalString : public JSExternalStringCallbacks {
void finalize(JS::Latin1Char* chars) const override {
// Constant chars, nothing to do.
}
void finalize(char16_t* chars) const override {
// Constant chars, nothing to do.
}
size_t sizeOfBuffer(const JS::Latin1Char* chars,
mozilla::MallocSizeOf mallocSizeOf) const override {
// This string's buffer is not heap-allocated, so its malloc size is 0.
return 0;
}
size_t sizeOfBuffer(const char16_t* chars,
mozilla::MallocSizeOf mallocSizeOf) const override {
// This string's buffer is not heap-allocated, so its malloc size is 0.
@ -2131,7 +2208,6 @@ static bool FillWithRepresentatives(JSContext* cx, Handle<ArrayObject*> array,
}
MOZ_ASSERT(extensible->isExtensible());
// External. Note that we currently only support TwoByte external strings.
RootedString external1(cx), external2(cx);
if constexpr (std::is_same_v<CharT, char16_t>) {
external1 = JS_NewExternalUCString(cx, (const char16_t*)chars, len,
@ -2147,6 +2223,22 @@ static bool FillWithRepresentatives(JSContext* cx, Handle<ArrayObject*> array,
return false;
}
MOZ_ASSERT(external2->isExternal());
} else {
external1 =
JS_NewExternalStringLatin1(cx, (const Latin1Char*)chars, len,
&RepresentativeExternalStringCallbacks);
if (!external1 || !AppendString(cx, array, index, external1)) {
return false;
}
MOZ_ASSERT(external1->isExternal());
external2 =
JS_NewExternalStringLatin1(cx, (const Latin1Char*)chars, 2,
&RepresentativeExternalStringCallbacks);
if (!external2 || !AppendString(cx, array, index, external2)) {
return false;
}
MOZ_ASSERT(external2->isExternal());
}
// Assert the strings still have the types we expect after creating the
@ -2167,8 +2259,8 @@ static bool FillWithRepresentatives(JSContext* cx, Handle<ArrayObject*> array,
MOZ_ASSERT(rope->isRope());
MOZ_ASSERT(dep->isDependent());
MOZ_ASSERT(extensible->isExtensible());
MOZ_ASSERT_IF(external1, external1->isExternal());
MOZ_ASSERT_IF(external2, external2->isExternal());
MOZ_ASSERT(external1->isExternal());
MOZ_ASSERT(external2->isExternal());
return true;
}
@ -2215,7 +2307,27 @@ bool JSString::fillWithRepresentatives(JSContext* cx,
return false;
}
MOZ_ASSERT(index == 40);
#ifdef DEBUG
// * Normal atom
// * Inline atom.
// * Fat inline atom.
// * Normal linear string
// * Inline string
// * Fat inline string
// * Rope; maybe nursery.
// * Dependent
// * Extensible
// * External with original len
// * External with len==2
static constexpr uint32_t StringTypes = 11;
// * Latin1
// * TwoByte
static constexpr uint32_t CharTypes = 2;
// * Tenured
// * Default
static constexpr uint32_t HeapType = 2;
MOZ_ASSERT(index == StringTypes * CharTypes * HeapType);
#endif
return true;
}

View file

@ -1258,6 +1258,8 @@ static_assert(sizeof(JSFatInlineString) % js::gc::CellAlignBytes == 0,
class JSExternalString : public JSLinearString {
friend class js::gc::CellAllocator;
JSExternalString(const JS::Latin1Char* chars, size_t length,
const JSExternalStringCallbacks* callbacks);
JSExternalString(const char16_t* chars, size_t length,
const JSExternalStringCallbacks* callbacks);
@ -1265,7 +1267,15 @@ class JSExternalString : public JSLinearString {
bool isExternal() const = delete;
JSExternalString& asExternal() const = delete;
template <typename CharT>
static inline JSExternalString* newImpl(
JSContext* cx, const CharT* chars, size_t length,
const JSExternalStringCallbacks* callbacks);
public:
static inline JSExternalString* new_(
JSContext* cx, const JS::Latin1Char* chars, size_t length,
const JSExternalStringCallbacks* callbacks);
static inline JSExternalString* new_(
JSContext* cx, const char16_t* chars, size_t length,
const JSExternalStringCallbacks* callbacks);
@ -1277,6 +1287,7 @@ class JSExternalString : public JSLinearString {
// External chars are never allocated inline or in the nursery, so we can
// safely expose this without requiring an AutoCheckCannotGC argument.
const JS::Latin1Char* latin1Chars() const { return rawLatin1Chars(); }
const char16_t* twoByteChars() const { return rawTwoByteChars(); }
// Only called by the GC for strings with the AllocKind::EXTERNAL_STRING
@ -1627,7 +1638,8 @@ inline JSLinearString* NewStringCopyUTF8Z(
cx, JS::UTF8Chars(utf8.c_str(), strlen(utf8.c_str())), heap);
}
JSString* NewMaybeExternalString(JSContext* cx, const char16_t* s, size_t n,
template <typename CharT>
JSString* NewMaybeExternalString(JSContext* cx, const CharT* s, size_t n,
const JSExternalStringCallbacks* callbacks,
bool* allocatedExternal,
js::gc::Heap heap = js::gc::Heap::Default);

View file

@ -32,22 +32,44 @@ const XPCStringConvert::LiteralExternalString
const XPCStringConvert::DOMStringExternalString
XPCStringConvert::sDOMStringExternalString;
void XPCStringConvert::LiteralExternalString::finalize(
JS::Latin1Char* aChars) const {
MOZ_CRASH("Unexpected");
}
void XPCStringConvert::LiteralExternalString::finalize(char16_t* aChars) const {
// Nothing to do.
}
size_t XPCStringConvert::LiteralExternalString::sizeOfBuffer(
const JS::Latin1Char* aChars, mozilla::MallocSizeOf aMallocSizeOf) const {
MOZ_CRASH("Unexpected");
return 0;
}
size_t XPCStringConvert::LiteralExternalString::sizeOfBuffer(
const char16_t* aChars, mozilla::MallocSizeOf aMallocSizeOf) const {
// This string's buffer is not heap-allocated, so its malloc size is 0.
return 0;
}
void XPCStringConvert::DOMStringExternalString::finalize(
JS::Latin1Char* aChars) const {
MOZ_CRASH("Unexpected");
}
void XPCStringConvert::DOMStringExternalString::finalize(
char16_t* aChars) const {
nsStringBuffer* buf = nsStringBuffer::FromData(aChars);
buf->Release();
}
size_t XPCStringConvert::DOMStringExternalString::sizeOfBuffer(
const JS::Latin1Char* aChars, mozilla::MallocSizeOf aMallocSizeOf) const {
MOZ_CRASH("Unexpected");
return 0;
}
size_t XPCStringConvert::DOMStringExternalString::sizeOfBuffer(
const char16_t* aChars, mozilla::MallocSizeOf aMallocSizeOf) const {
// We promised the JS engine we would not GC. Enforce that:

View file

@ -309,12 +309,18 @@ class XPCStringConvert {
private:
struct LiteralExternalString : public JSExternalStringCallbacks {
void finalize(JS::Latin1Char* aChars) const override;
void finalize(char16_t* aChars) const override;
size_t sizeOfBuffer(const JS::Latin1Char* aChars,
mozilla::MallocSizeOf aMallocSizeOf) const override;
size_t sizeOfBuffer(const char16_t* aChars,
mozilla::MallocSizeOf aMallocSizeOf) const override;
};
struct DOMStringExternalString : public JSExternalStringCallbacks {
void finalize(JS::Latin1Char* aChars) const override;
void finalize(char16_t* aChars) const override;
size_t sizeOfBuffer(const JS::Latin1Char* aChars,
mozilla::MallocSizeOf aMallocSizeOf) const override;
size_t sizeOfBuffer(const char16_t* aChars,
mozilla::MallocSizeOf aMallocSizeOf) const override;
};