Bug 1027528 part 13 - Remove JSString::hasPureChars etc, and refactor callers. r=njn

This commit is contained in:
Jan de Mooij 2014-06-23 16:20:57 +02:00
parent bf09173096
commit 5ced7a4bb6
7 changed files with 240 additions and 162 deletions

View File

@ -161,12 +161,12 @@ AtomHasher::match(const AtomStateEntry &entry, const Lookup &lookup)
const Latin1Char *keyChars = key->latin1Chars(lookup.nogc);
if (lookup.isLatin1)
return mozilla::PodEqual(keyChars, lookup.latin1Chars, lookup.length);
return EqualCharsLatin1TwoByte(keyChars, lookup.twoByteChars, lookup.length);
return EqualChars(keyChars, lookup.twoByteChars, lookup.length);
}
const jschar *keyChars = key->twoByteChars(lookup.nogc);
if (lookup.isLatin1)
return EqualCharsLatin1TwoByte(lookup.latin1Chars, keyChars, lookup.length);
return EqualChars(lookup.latin1Chars, keyChars, lookup.length);
return mozilla::PodEqual(keyChars, lookup.twoByteChars, lookup.length);
}

View File

@ -264,6 +264,53 @@ JSCompartment::putWrapper(JSContext *cx, const CrossCompartmentKey &wrapped, con
return success;
}
static JSString *
CopyStringPure(JSContext *cx, JSString *str)
{
/*
* Directly allocate the copy in the destination compartment, rather than
* first flattening it (and possibly allocating in source compartment),
* because we don't know whether the flattening will pay off later.
*/
size_t len = str->length();
JSString *copy;
if (str->isLinear()) {
/* Only use AutoStableStringChars if the NoGC allocation fails. */
if (str->hasLatin1Chars()) {
JS::AutoCheckCannotGC nogc;
copy = js_NewStringCopyN<NoGC>(cx, str->asLinear().latin1Chars(nogc), len);
} else {
JS::AutoCheckCannotGC nogc;
copy = js_NewStringCopyN<NoGC>(cx, str->asLinear().twoByteChars(nogc), len);
}
if (copy)
return copy;
AutoStableStringChars chars(cx);
if (!chars.init(cx, str))
return nullptr;
return chars.isLatin1()
? js_NewStringCopyN<CanGC>(cx, chars.latin1Range().start().get(), len)
: js_NewStringCopyN<CanGC>(cx, chars.twoByteRange().start().get(), len);
}
if (str->hasLatin1Chars()) {
ScopedJSFreePtr<Latin1Char> copiedChars;
if (!str->asRope().copyLatin1CharsZ(cx, copiedChars))
return nullptr;
return js_NewString<CanGC>(cx, copiedChars.forget(), len);
}
ScopedJSFreePtr<jschar> copiedChars;
if (!str->asRope().copyTwoByteCharsZ(cx, copiedChars))
return nullptr;
return js_NewString<CanGC>(cx, copiedChars.forget(), len);
}
bool
JSCompartment::wrap(JSContext *cx, JSString **strp)
{
@ -289,22 +336,8 @@ JSCompartment::wrap(JSContext *cx, JSString **strp)
return true;
}
/*
* No dice. Make a copy, and cache it. Directly allocate the copy in the
* destination compartment, rather than first flattening it (and possibly
* allocating in source compartment), because we don't know whether the
* flattening will pay off later.
*/
JSString *copy;
if (str->hasPureChars()) {
copy = js_NewStringCopyN<CanGC>(cx, str->pureChars(), str->length());
} else {
ScopedJSFreePtr<jschar> copiedChars;
if (!str->copyNonPureCharsZ(cx, copiedChars))
return false;
copy = js_NewString<CanGC>(cx, copiedChars.forget(), str->length());
}
/* No dice. Make a copy, and cache it. */
JSString *copy = CopyStringPure(cx, str);
if (!copy)
return false;
if (!putWrapper(cx, CrossCompartmentKey(key), StringValue(copy)))

View File

@ -1727,14 +1727,14 @@ HasSubstringAt(JSLinearString *text, JSLinearString *pat, size_t start)
if (pat->hasLatin1Chars())
return PodEqual(textChars, pat->latin1Chars(nogc), patLen);
return EqualCharsLatin1TwoByte(textChars, pat->twoByteChars(nogc), patLen);
return EqualChars(textChars, pat->twoByteChars(nogc), patLen);
}
const jschar *textChars = text->twoByteChars(nogc) + start;
if (pat->hasTwoByteChars())
return PodEqual(textChars, pat->twoByteChars(nogc), patLen);
return EqualCharsLatin1TwoByte(pat->latin1Chars(nogc), textChars, patLen);
return EqualChars(pat->latin1Chars(nogc), textChars, patLen);
}
/* ES6 20131108 draft 21.1.3.18. */
@ -4565,13 +4565,13 @@ js::EqualChars(JSLinearString *str1, JSLinearString *str2)
if (str2->hasTwoByteChars())
return PodEqual(str1->twoByteChars(nogc), str2->twoByteChars(nogc), len);
return EqualCharsLatin1TwoByte(str2->latin1Chars(nogc), str1->twoByteChars(nogc), len);
return EqualChars(str2->latin1Chars(nogc), str1->twoByteChars(nogc), len);
}
if (str2->hasLatin1Chars())
return PodEqual(str1->latin1Chars(nogc), str2->latin1Chars(nogc), len);
return EqualCharsLatin1TwoByte(str1->latin1Chars(nogc), str2->twoByteChars(nogc), len);
return EqualChars(str1->latin1Chars(nogc), str2->twoByteChars(nogc), len);
}
bool
@ -4686,7 +4686,7 @@ js::StringEqualsAscii(JSLinearString *str, const char *asciiBytes)
AutoCheckCannotGC nogc;
return str->hasLatin1Chars()
? PodEqual(latin1, str->latin1Chars(nogc), length)
: EqualCharsLatin1TwoByte(latin1, str->twoByteChars(nogc), length);
: EqualChars(latin1, str->twoByteChars(nogc), length);
}
size_t
@ -5338,3 +5338,11 @@ js::PutEscapedStringImpl(char *buffer, size_t bufferSize, FILE *fp, const Latin1
template size_t
js::PutEscapedStringImpl(char *buffer, size_t bufferSize, FILE *fp, const jschar *chars,
size_t length, uint32_t quote);
template size_t
js::PutEscapedString(char *buffer, size_t bufferSize, const Latin1Char *chars, size_t length,
uint32_t quote);
template size_t
js::PutEscapedString(char *buffer, size_t bufferSize, const jschar *chars, size_t length,
uint32_t quote);

View File

@ -249,11 +249,23 @@ js_strdup(js::ThreadSafeContext *cx, const jschar *s);
namespace js {
template <typename Char1, typename Char2>
inline bool
EqualCharsLatin1TwoByte(const Latin1Char *s1, const jschar *s2, size_t len)
EqualChars(const Char1 *s1, const Char2 *s2, size_t len);
template <typename Char1>
inline bool
EqualChars(const Char1 *s1, const Char1 *s2, size_t len)
{
for (const Latin1Char *s1end = s1 + len; s1 < s1end; s1++, s2++) {
if (jschar(*s1) != *s2)
return mozilla::PodEqual(s1, s2, len);
}
template <typename Char1, typename Char2>
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;
@ -356,8 +368,9 @@ PutEscapedString(char *buffer, size_t size, JSLinearString *str, uint32_t quote)
return n;
}
template <typename CharT>
inline size_t
PutEscapedString(char *buffer, size_t bufferSize, const jschar *chars, size_t length, uint32_t quote)
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);

View File

@ -43,51 +43,78 @@ MemoryReportingSundriesThreshold()
return 8 * 1024;
}
/* static */ HashNumber
InefficientNonFlatteningStringHashPolicy::hash(const Lookup &l)
template <typename CharT>
static uint32_t
HashStringChars(JSString *s)
{
ScopedJSFreePtr<jschar> ownedChars;
const jschar *chars;
if (l->hasPureChars()) {
chars = l->pureChars();
ScopedJSFreePtr<CharT> ownedChars;
const CharT *chars;
JS::AutoCheckCannotGC nogc;
if (s->isLinear()) {
chars = s->asLinear().chars<CharT>(nogc);
} else {
// Slowest hash function evar!
if (!l->copyNonPureChars(/* tcx */ nullptr, ownedChars))
if (!s->asRope().copyChars<CharT>(/* tcx */ nullptr, ownedChars))
MOZ_CRASH("oom");
chars = ownedChars;
}
return mozilla::HashString(chars, l->length());
return mozilla::HashString(chars, s->length());
}
/* static */ HashNumber
InefficientNonFlatteningStringHashPolicy::hash(const Lookup &l)
{
return l->hasLatin1Chars()
? HashStringChars<Latin1Char>(l)
: HashStringChars<jschar>(l);
}
template <typename Char1, typename Char2>
static bool
EqualStringsPure(JSString *s1, JSString *s2)
{
if (s1->length() != s2->length())
return false;
const Char1 *c1;
ScopedJSFreePtr<Char1> ownedChars1;
JS::AutoCheckCannotGC nogc;
if (s1->isLinear()) {
c1 = s1->asLinear().chars<Char1>(nogc);
} else {
if (!s1->asRope().copyChars<Char1>(/* tcx */ nullptr, ownedChars1))
MOZ_CRASH("oom");
c1 = ownedChars1;
}
const Char2 *c2;
ScopedJSFreePtr<Char2> ownedChars2;
if (s2->isLinear()) {
c2 = s2->asLinear().chars<Char2>(nogc);
} else {
if (!s2->asRope().copyChars<Char2>(/* tcx */ nullptr, ownedChars2))
MOZ_CRASH("oom");
c2 = ownedChars2;
}
return EqualChars(c1, c2, s1->length());
}
/* static */ bool
InefficientNonFlatteningStringHashPolicy::match(const JSString *const &k, const Lookup &l)
{
// We can't use js::EqualStrings, because that flattens our strings.
if (k->length() != l->length())
return false;
const jschar *c1;
ScopedJSFreePtr<jschar> ownedChars1;
if (k->hasPureChars()) {
c1 = k->pureChars();
} else {
if (!k->copyNonPureChars(/* tcx */ nullptr, ownedChars1))
MOZ_CRASH("oom");
c1 = ownedChars1;
JSString *s1 = const_cast<JSString *>(k);
if (k->hasLatin1Chars()) {
return l->hasLatin1Chars()
? EqualStringsPure<Latin1Char, Latin1Char>(s1, l)
: EqualStringsPure<Latin1Char, jschar>(s1, l);
}
const jschar *c2;
ScopedJSFreePtr<jschar> ownedChars2;
if (l->hasPureChars()) {
c2 = l->pureChars();
} else {
if (!l->copyNonPureChars(/* tcx */ nullptr, ownedChars2))
MOZ_CRASH("oom");
c2 = ownedChars2;
}
return PodEqual(c1, c2, k->length());
return l->hasLatin1Chars()
? EqualStringsPure<jschar, Latin1Char>(s1, l)
: EqualStringsPure<jschar, jschar>(s1, l);
}
/* static */ HashNumber
@ -113,6 +140,27 @@ NotableStringInfo::NotableStringInfo()
{
}
template <typename CharT>
static void
StoreStringChars(char *buffer, size_t bufferSize, JSString *str)
{
const CharT* chars;
ScopedJSFreePtr<CharT> ownedChars;
JS::AutoCheckCannotGC nogc;
if (str->isLinear()) {
chars = str->asLinear().chars<CharT>(nogc);
} else {
if (!str->asRope().copyChars<CharT>(/* tcx */ nullptr, ownedChars))
MOZ_CRASH("oom");
chars = ownedChars;
}
// We might truncate |str| even if it's much shorter than 1024 chars, if
// |str| contains unicode chars. Since this is just for a memory reporter,
// we don't care.
PutEscapedString(buffer, bufferSize, chars, str->length(), /* quote */ 0);
}
NotableStringInfo::NotableStringInfo(JSString *str, const StringInfo &info)
: StringInfo(info),
length(str->length())
@ -123,20 +171,10 @@ NotableStringInfo::NotableStringInfo(JSString *str, const StringInfo &info)
MOZ_CRASH("oom");
}
const jschar* chars;
ScopedJSFreePtr<jschar> ownedChars;
if (str->hasPureChars()) {
chars = str->pureChars();
} else {
if (!str->copyNonPureChars(/* tcx */ nullptr, ownedChars))
MOZ_CRASH("oom");
chars = ownedChars;
}
// We might truncate |str| even if it's much shorter than 1024 chars, if
// |str| contains unicode chars. Since this is just for a memory reporter,
// we don't care.
PutEscapedString(buffer, bufferSize, chars, str->length(), /* quote */ 0);
if (str->hasLatin1Chars())
StoreStringChars<Latin1Char>(buffer, bufferSize, str);
else
StoreStringChars<jschar>(buffer, bufferSize, str);
}
NotableStringInfo::NotableStringInfo(NotableStringInfo &&info)

View File

@ -170,20 +170,33 @@ AllocChars(ThreadSafeContext *maybecx, size_t length, CharT **chars, size_t *cap
}
bool
JSRope::copyNonPureChars(ThreadSafeContext *cx, ScopedJSFreePtr<jschar> &out) const
JSRope::copyLatin1CharsZ(ThreadSafeContext *cx, ScopedJSFreePtr<Latin1Char> &out) const
{
return copyNonPureCharsInternal(cx, out, false);
return copyCharsInternal<Latin1Char>(cx, out, true);
}
bool
JSRope::copyNonPureCharsZ(ThreadSafeContext *cx, ScopedJSFreePtr<jschar> &out) const
JSRope::copyTwoByteCharsZ(ThreadSafeContext *cx, ScopedJSFreePtr<jschar> &out) const
{
return copyNonPureCharsInternal(cx, out, true);
return copyCharsInternal<jschar>(cx, out, true);
}
bool
JSRope::copyNonPureCharsInternal(ThreadSafeContext *cx, ScopedJSFreePtr<jschar> &out,
bool nullTerminate) const
JSRope::copyLatin1Chars(ThreadSafeContext *cx, ScopedJSFreePtr<Latin1Char> &out) const
{
return copyCharsInternal<Latin1Char>(cx, out, false);
}
bool
JSRope::copyTwoByteChars(ThreadSafeContext *cx, ScopedJSFreePtr<jschar> &out) const
{
return copyCharsInternal<jschar>(cx, out, false);
}
template <typename CharT>
bool
JSRope::copyCharsInternal(ThreadSafeContext *cx, ScopedJSFreePtr<CharT> &out,
bool nullTerminate) const
{
/*
* Perform non-destructive post-order traversal of the rope, splatting
@ -192,25 +205,24 @@ JSRope::copyNonPureCharsInternal(ThreadSafeContext *cx, ScopedJSFreePtr<jschar>
size_t n = length();
if (cx)
out.reset(cx->pod_malloc<jschar>(n + 1));
out.reset(cx->pod_malloc<CharT>(n + 1));
else
out.reset(js_pod_malloc<jschar>(n + 1));
out.reset(js_pod_malloc<CharT>(n + 1));
if (!out)
return false;
Vector<const JSString *, 8, SystemAllocPolicy> nodeStack;
const JSString *str = this;
jschar *pos = out;
CharT *pos = out;
while (true) {
if (str->isRope()) {
if (!nodeStack.append(str->asRope().rightChild()))
return false;
str = str->asRope().leftChild();
} else {
size_t len = str->length();
PodCopy(pos, str->asLinear().chars(), len);
pos += len;
CopyChars(pos, str->asLinear());
pos += str->length();
if (nodeStack.empty())
break;
str = nodeStack.popCopy();
@ -490,23 +502,6 @@ js::ConcatStrings<CanGC>(ThreadSafeContext *cx, HandleString left, HandleString
template JSString *
js::ConcatStrings<NoGC>(ThreadSafeContext *cx, JSString *left, JSString *right);
bool
JSDependentString::copyNonPureCharsZ(ThreadSafeContext *cx, ScopedJSFreePtr<jschar> &out) const
{
JS_ASSERT(JSString::isDependent());
size_t n = length();
jschar *s = cx->pod_malloc<jschar>(n + 1);
if (!s)
return false;
PodCopy(s, nonInlineChars(), n);
s[n] = 0;
out.reset(s);
return true;
}
template <typename CharT>
JSFlatString *
JSDependentString::undependInternal(ExclusiveContext *cx)
@ -621,14 +616,30 @@ ScopedThreadSafeStringInspector::ensureChars(ThreadSafeContext *cx, const AutoCh
latin1Chars_ = linear->latin1Chars(nogc);
}
} else {
if (str_->hasPureChars()) {
state_ = TwoByte;
twoByteChars_ = str_->pureChars();
if (str_->isLinear()) {
if (str_->hasLatin1Chars()) {
state_ = Latin1;
latin1Chars_ = str_->asLinear().latin1Chars(nogc);
} else {
state_ = TwoByte;
twoByteChars_ = str_->asLinear().twoByteChars(nogc);
}
} else {
if (!str_->copyNonPureChars(cx, scopedChars_))
return false;
state_ = TwoByte;
twoByteChars_ = scopedChars_;
if (str_->hasLatin1Chars()) {
ScopedJSFreePtr<Latin1Char> chars;
if (!str_->asRope().copyLatin1Chars(cx, chars))
return false;
state_ = Latin1;
latin1Chars_ = chars;
scopedChars_ = chars.forget();
} else {
ScopedJSFreePtr<jschar> chars;
if (!str_->asRope().copyTwoByteChars(cx, chars))
return false;
state_ = TwoByte;
twoByteChars_ = chars;
scopedChars_ = chars.forget();
}
}
}

View File

@ -328,23 +328,6 @@ class JSString : public js::gc::BarrieredCell<JSString>
inline const jschar *getCharsZ(js::ExclusiveContext *cx);
inline bool getChar(js::ExclusiveContext *cx, size_t index, jschar *code);
/*
* A string has "pure" chars if it can return a pointer to its chars
* infallibly without mutating anything so they are safe to be from off the
* main thread. If a string does not have pure chars, the caller can call
* copyNonPureChars to allocate a copy of the chars which is also a
* non-mutating threadsafe operation. Beware, this is an O(n) operation
* (involving a DAG traversal for ropes).
*/
bool hasPureChars() const { return isLinear(); }
bool hasPureCharsZ() const { return isFlat(); }
inline const jschar *pureChars() const;
inline const jschar *pureCharsZ() const;
inline bool copyNonPureChars(js::ThreadSafeContext *cx,
js::ScopedJSFreePtr<jschar> &out) const;
inline bool copyNonPureCharsZ(js::ThreadSafeContext *cx,
js::ScopedJSFreePtr<jschar> &out) const;
/* Strings have either Latin1 or TwoByte chars. */
bool hasLatin1Chars() const {
return d.u1.flags & LATIN1_CHARS_BIT;
@ -537,11 +520,9 @@ static const bool EnableLatin1Strings = false;
class JSRope : public JSString
{
bool copyNonPureCharsInternal(js::ThreadSafeContext *cx,
js::ScopedJSFreePtr<jschar> &out,
bool nullTerminate) const;
bool copyNonPureChars(js::ThreadSafeContext *cx, js::ScopedJSFreePtr<jschar> &out) const;
bool copyNonPureCharsZ(js::ThreadSafeContext *cx, js::ScopedJSFreePtr<jschar> &out) const;
template <typename CharT>
bool copyCharsInternal(js::ThreadSafeContext *cx, js::ScopedJSFreePtr<CharT> &out,
bool nullTerminate) const;
enum UsingBarrier { WithIncrementalBarrier, NoBarrier };
@ -563,6 +544,17 @@ class JSRope : public JSString
typename js::MaybeRooted<JSString*, allowGC>::HandleType right,
size_t length);
bool copyLatin1Chars(js::ThreadSafeContext *cx,
js::ScopedJSFreePtr<JS::Latin1Char> &out) const;
bool copyTwoByteChars(js::ThreadSafeContext *cx, js::ScopedJSFreePtr<jschar> &out) const;
bool copyLatin1CharsZ(js::ThreadSafeContext *cx,
js::ScopedJSFreePtr<JS::Latin1Char> &out) const;
bool copyTwoByteCharsZ(js::ThreadSafeContext *cx, js::ScopedJSFreePtr<jschar> &out) const;
template <typename CharT>
bool copyChars(js::ThreadSafeContext *cx, js::ScopedJSFreePtr<CharT> &out) const;
inline JSString *leftChild() const {
JS_ASSERT(isRope());
return d.s.u2.left;
@ -683,8 +675,6 @@ JS_STATIC_ASSERT(sizeof(JSLinearString) == sizeof(JSString));
class JSDependentString : public JSLinearString
{
bool copyNonPureCharsZ(js::ThreadSafeContext *cx, js::ScopedJSFreePtr<jschar> &out) const;
friend class JSString;
JSFlatString *undepend(js::ExclusiveContext *cx);
@ -1023,7 +1013,7 @@ class ScopedThreadSafeStringInspector
{
private:
JSString *str_;
ScopedJSFreePtr<jschar> scopedChars_;
ScopedJSFreePtr<void> scopedChars_;
union {
const jschar *twoByteChars_;
const JS::Latin1Char *latin1Chars_;
@ -1335,36 +1325,6 @@ JSString::getCharsZ(js::ExclusiveContext *cx)
return nullptr;
}
MOZ_ALWAYS_INLINE const jschar *
JSString::pureChars() const
{
JS_ASSERT(hasPureChars());
return asLinear().chars();
}
MOZ_ALWAYS_INLINE const jschar *
JSString::pureCharsZ() const
{
JS_ASSERT(hasPureCharsZ());
return asFlat().charsZ();
}
MOZ_ALWAYS_INLINE bool
JSString::copyNonPureChars(js::ThreadSafeContext *cx, js::ScopedJSFreePtr<jschar> &out) const
{
JS_ASSERT(!hasPureChars());
return asRope().copyNonPureChars(cx, out);
}
MOZ_ALWAYS_INLINE bool
JSString::copyNonPureCharsZ(js::ThreadSafeContext *cx, js::ScopedJSFreePtr<jschar> &out) const
{
JS_ASSERT(!hasPureChars());
if (isDependent())
return asDependent().copyNonPureCharsZ(cx, out);
return asRope().copyNonPureCharsZ(cx, out);
}
MOZ_ALWAYS_INLINE JSLinearString *
JSString::ensureLinear(js::ExclusiveContext *cx)
{
@ -1419,6 +1379,21 @@ JSLinearString::chars(const JS::AutoCheckCannotGC &nogc) const
return rawLatin1Chars();
}
template <>
MOZ_ALWAYS_INLINE bool
JSRope::copyChars<JS::Latin1Char>(js::ThreadSafeContext *cx,
js::ScopedJSFreePtr<JS::Latin1Char> &out) const
{
return copyLatin1Chars(cx, out);
}
template <>
MOZ_ALWAYS_INLINE bool
JSRope::copyChars<jschar>(js::ThreadSafeContext *cx, js::ScopedJSFreePtr<jschar> &out) const
{
return copyTwoByteChars(cx, out);
}
template<>
MOZ_ALWAYS_INLINE bool
JSInlineString::lengthFits<JS::Latin1Char>(size_t length)