Files
sousa-gecko/xpcom/tests/gtest/TestMoveString.cpp
T
Emilio Cobos Álvarez fe182cca64 Bug 2025271 - Simplify DOMString. r=smaug,nika,extension-reviewers,core-sessionstore-reviewers,dom-worker-reviewers,robwu,asuth
Make DOMString a regular nsAutoTString with some special methods for
performance in some hot getters. For that, we need to change a bit
nsTString:

 * Support for unowned buffers: Owned buffers now are represented by
   STRINGBUFFER|OWNED flags, rather than REFCOUNTED.

 * Inlining of the destructor: It showed up in profiles, it's also just
   a few instructions, so probably worth it. We could OOL the owned path
   I guess, if we wanted to.

 * Inlinling and reordering of NonVoidStringToJsval. Old code had
   special DOMString path inline with [buffer, literal, copy] paths,
   then out of line code with [literal, buffer, copy]. Instead,
   consolidate everything in a [buffer, literal, copy] inline path.
   Do the same for UTF8/Latin1 strings too for consistency.

Differential Revision: https://phabricator.services.mozilla.com/D288148
2026-04-13 09:33:54 +00:00

260 lines
6.3 KiB
C++

/* 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/. */
#include "nsASCIIMask.h"
#include "nsString.h"
#include "nsReadableUtils.h"
#include "nsCRTGlue.h"
#include "gtest/gtest.h"
namespace TestMoveString {
#define NEW_VAL "**new value**"
#define OLD_VAL "old value"
typedef mozilla::detail::StringDataFlags Df;
static void SetAsOwned(nsACString& aStr, const char* aValue) {
size_t len = strlen(aValue);
char* data = new char[len + 1];
memcpy(data, aValue, len + 1);
aStr.Adopt(data, len);
EXPECT_EQ(aStr.GetDataFlags(), Df::OWNED | Df::TERMINATED);
EXPECT_STREQ(PromiseFlatCString(aStr).get(), aValue);
}
static void ExpectTruncated(const nsACString& aStr) {
EXPECT_EQ(aStr.Length(), uint32_t(0));
EXPECT_STREQ(PromiseFlatCString(aStr).get(), "");
EXPECT_EQ(aStr.GetDataFlags(), Df::TERMINATED);
}
static void ExpectNew(const nsACString& aStr) {
EXPECT_EQ(aStr.Length(), strlen(NEW_VAL));
EXPECT_TRUE(aStr.EqualsASCII(NEW_VAL));
}
TEST(MoveString, SharedIntoOwned)
{
nsCString out;
SetAsOwned(out, OLD_VAL);
EXPECT_EQ(out.GetDataFlags(), Df::OWNED | Df::TERMINATED);
nsCString in;
in.Assign(NEW_VAL);
EXPECT_EQ(in.GetDataFlags(), Df::STRINGBUFFER | Df::OWNED | Df::TERMINATED);
const char* data = in.get();
out.Assign(std::move(in));
ExpectTruncated(in);
ExpectNew(out);
EXPECT_EQ(out.GetDataFlags(), Df::STRINGBUFFER | Df::OWNED | Df::TERMINATED);
EXPECT_EQ(out.get(), data);
}
TEST(MoveString, OwnedIntoOwned)
{
nsCString out;
SetAsOwned(out, OLD_VAL);
EXPECT_EQ(out.GetDataFlags(), Df::OWNED | Df::TERMINATED);
nsCString in;
SetAsOwned(in, NEW_VAL);
EXPECT_EQ(in.GetDataFlags(), Df::OWNED | Df::TERMINATED);
const char* data = in.get();
out.Assign(std::move(in));
ExpectTruncated(in);
ExpectNew(out);
EXPECT_EQ(out.GetDataFlags(), Df::OWNED | Df::TERMINATED);
EXPECT_EQ(out.get(), data);
}
TEST(MoveString, LiteralIntoOwned)
{
nsCString out;
SetAsOwned(out, OLD_VAL);
EXPECT_EQ(out.GetDataFlags(), Df::OWNED | Df::TERMINATED);
nsCString in;
in.AssignLiteral(NEW_VAL);
EXPECT_EQ(in.GetDataFlags(), Df::LITERAL | Df::TERMINATED);
const char* data = in.get();
out.Assign(std::move(in));
ExpectTruncated(in);
ExpectNew(out);
EXPECT_EQ(out.GetDataFlags(), Df::LITERAL | Df::TERMINATED);
EXPECT_EQ(out.get(), data);
}
TEST(MoveString, AutoIntoOwned)
{
nsCString out;
SetAsOwned(out, OLD_VAL);
EXPECT_EQ(out.GetDataFlags(), Df::OWNED | Df::TERMINATED);
nsAutoCString in;
in.Assign(NEW_VAL);
EXPECT_EQ(in.GetDataFlags(), Df::INLINE | Df::TERMINATED);
const char* data = in.get();
out.Assign(std::move(in));
ExpectTruncated(in);
ExpectNew(out);
EXPECT_EQ(out.GetDataFlags(), Df::STRINGBUFFER | Df::OWNED | Df::TERMINATED);
EXPECT_NE(out.get(), data);
}
TEST(MoveString, DepIntoOwned)
{
nsCString out;
SetAsOwned(out, OLD_VAL);
EXPECT_EQ(out.GetDataFlags(), Df::OWNED | Df::TERMINATED);
nsDependentCSubstring in(NEW_VAL "garbage after", strlen(NEW_VAL));
EXPECT_EQ(in.GetDataFlags(), Df(0));
out.Assign(std::move(in));
ExpectTruncated(in);
ExpectNew(out);
EXPECT_EQ(out.GetDataFlags(), Df::STRINGBUFFER | Df::OWNED | Df::TERMINATED);
}
TEST(MoveString, VoidIntoOwned)
{
nsCString out;
SetAsOwned(out, OLD_VAL);
EXPECT_EQ(out.GetDataFlags(), Df::OWNED | Df::TERMINATED);
nsCString in = VoidCString();
EXPECT_EQ(in.GetDataFlags(), Df::VOIDED | Df::TERMINATED);
out.Assign(std::move(in));
ExpectTruncated(in);
EXPECT_EQ(out.Length(), 0u);
EXPECT_STREQ(out.get(), "");
EXPECT_EQ(out.GetDataFlags(), Df::VOIDED | Df::TERMINATED);
}
TEST(MoveString, SharedIntoAuto)
{
nsAutoCString out;
out.Assign(OLD_VAL);
EXPECT_EQ(out.GetDataFlags(), Df::INLINE | Df::TERMINATED);
nsCString in;
in.Assign(NEW_VAL);
EXPECT_EQ(in.GetDataFlags(), Df::STRINGBUFFER | Df::OWNED | Df::TERMINATED);
const char* data = in.get();
out.Assign(std::move(in));
ExpectTruncated(in);
ExpectNew(out);
EXPECT_EQ(out.GetDataFlags(), Df::STRINGBUFFER | Df::OWNED | Df::TERMINATED);
EXPECT_EQ(out.get(), data);
}
TEST(MoveString, OwnedIntoAuto)
{
nsAutoCString out;
out.Assign(OLD_VAL);
EXPECT_EQ(out.GetDataFlags(), Df::INLINE | Df::TERMINATED);
nsCString in;
SetAsOwned(in, NEW_VAL);
EXPECT_EQ(in.GetDataFlags(), Df::OWNED | Df::TERMINATED);
const char* data = in.get();
out.Assign(std::move(in));
ExpectTruncated(in);
ExpectNew(out);
EXPECT_EQ(out.GetDataFlags(), Df::OWNED | Df::TERMINATED);
EXPECT_EQ(out.get(), data);
}
TEST(MoveString, LiteralIntoAuto)
{
nsAutoCString out;
out.Assign(OLD_VAL);
EXPECT_EQ(out.GetDataFlags(), Df::INLINE | Df::TERMINATED);
nsCString in;
in.AssignLiteral(NEW_VAL);
EXPECT_EQ(in.GetDataFlags(), Df::LITERAL | Df::TERMINATED);
const char* data = in.get();
out.Assign(std::move(in));
ExpectTruncated(in);
ExpectNew(out);
EXPECT_EQ(out.GetDataFlags(), Df::LITERAL | Df::TERMINATED);
EXPECT_EQ(out.get(), data);
}
TEST(MoveString, AutoIntoAuto)
{
nsAutoCString out;
out.Assign(OLD_VAL);
EXPECT_EQ(out.GetDataFlags(), Df::INLINE | Df::TERMINATED);
nsAutoCString in;
in.Assign(NEW_VAL);
EXPECT_EQ(in.GetDataFlags(), Df::INLINE | Df::TERMINATED);
const char* data = in.get();
out.Assign(std::move(in));
ExpectTruncated(in);
ExpectNew(out);
EXPECT_EQ(out.GetDataFlags(), Df::INLINE | Df::TERMINATED);
EXPECT_NE(out.get(), data);
}
TEST(MoveString, DepIntoAuto)
{
nsAutoCString out;
out.Assign(OLD_VAL);
EXPECT_EQ(out.GetDataFlags(), Df::INLINE | Df::TERMINATED);
nsDependentCSubstring in(NEW_VAL "garbage after", strlen(NEW_VAL));
EXPECT_EQ(in.GetDataFlags(), Df(0));
out.Assign(std::move(in));
ExpectTruncated(in);
ExpectNew(out);
EXPECT_EQ(out.GetDataFlags(), Df::INLINE | Df::TERMINATED);
}
TEST(MoveString, VoidIntoAuto)
{
nsAutoCString out;
out.Assign(OLD_VAL);
EXPECT_EQ(out.GetDataFlags(), Df::INLINE | Df::TERMINATED);
nsCString in = VoidCString();
EXPECT_EQ(in.GetDataFlags(), Df::VOIDED | Df::TERMINATED);
out.Assign(std::move(in));
ExpectTruncated(in);
EXPECT_EQ(out.Length(), 0u);
EXPECT_STREQ(out.get(), "");
EXPECT_EQ(out.GetDataFlags(), Df::VOIDED | Df::TERMINATED);
}
#undef NEW_VAL
#undef OLD_VAL
} // namespace TestMoveString