mirror of
https://github.com/mozilla/gecko-dev.git
synced 2024-10-09 03:15:11 +00:00
Bug 1015917 part 1 - Support string concatenation for Latin1 strings. r=luke
This commit is contained in:
parent
35aee7a28a
commit
b90f317a8c
@ -137,7 +137,7 @@ class gcstats::StatisticsSerializer
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
InflateStringToBuffer(buf, nchars, out);
|
||||
CopyAndInflateChars(out, buf, nchars);
|
||||
js_free(buf);
|
||||
|
||||
out[nchars] = 0;
|
||||
|
@ -5154,7 +5154,8 @@ CodeGenerator::visitConcatPar(LConcatPar *lir)
|
||||
}
|
||||
|
||||
static void
|
||||
CopyStringChars(MacroAssembler &masm, Register to, Register from, Register len, Register scratch)
|
||||
CopyStringChars(MacroAssembler &masm, Register to, Register from, Register len, Register scratch,
|
||||
size_t fromWidth, size_t toWidth)
|
||||
{
|
||||
// Copy |len| jschars from |from| to |to|. Assumes len > 0 (checked below in
|
||||
// debug builds), and when done |to| must point to the next available char.
|
||||
@ -5166,17 +5167,125 @@ CopyStringChars(MacroAssembler &masm, Register to, Register from, Register len,
|
||||
masm.bind(&ok);
|
||||
#endif
|
||||
|
||||
JS_STATIC_ASSERT(sizeof(jschar) == 2);
|
||||
MOZ_ASSERT(fromWidth == 1 || fromWidth == 2);
|
||||
MOZ_ASSERT(toWidth == 1 || toWidth == 2);
|
||||
MOZ_ASSERT_IF(toWidth == 1, fromWidth == 1);
|
||||
|
||||
Label start;
|
||||
masm.bind(&start);
|
||||
masm.load16ZeroExtend(Address(from, 0), scratch);
|
||||
masm.store16(scratch, Address(to, 0));
|
||||
masm.addPtr(Imm32(2), from);
|
||||
masm.addPtr(Imm32(2), to);
|
||||
if (fromWidth == 2)
|
||||
masm.load16ZeroExtend(Address(from, 0), scratch);
|
||||
else
|
||||
masm.load8ZeroExtend(Address(from, 0), scratch);
|
||||
if (toWidth == 2)
|
||||
masm.store16(scratch, Address(to, 0));
|
||||
else
|
||||
masm.store8(scratch, Address(to, 0));
|
||||
masm.addPtr(Imm32(fromWidth), from);
|
||||
masm.addPtr(Imm32(toWidth), to);
|
||||
masm.branchSub32(Assembler::NonZero, Imm32(1), len, &start);
|
||||
}
|
||||
|
||||
static void
|
||||
CopyStringCharsMaybeInflate(MacroAssembler &masm, Register input, Register destChars,
|
||||
Register temp1, Register temp2)
|
||||
{
|
||||
// destChars is TwoByte and input is a Latin1 or TwoByte string, so we may
|
||||
// have to inflate.
|
||||
|
||||
Label isLatin1, done;
|
||||
masm.loadStringLength(input, temp1);
|
||||
masm.branchTest32(Assembler::NonZero, Address(input, JSString::offsetOfFlags()),
|
||||
Imm32(JSString::LATIN1_CHARS_BIT), &isLatin1);
|
||||
{
|
||||
masm.loadStringChars(input, input);
|
||||
CopyStringChars(masm, destChars, input, temp1, temp2, sizeof(jschar), sizeof(jschar));
|
||||
masm.jump(&done);
|
||||
}
|
||||
masm.bind(&isLatin1);
|
||||
{
|
||||
masm.loadStringChars(input, input);
|
||||
CopyStringChars(masm, destChars, input, temp1, temp2, sizeof(char), sizeof(jschar));
|
||||
}
|
||||
masm.bind(&done);
|
||||
}
|
||||
|
||||
static void
|
||||
ConcatFatInlineString(MacroAssembler &masm, Register lhs, Register rhs, Register output,
|
||||
Register temp1, Register temp2, Register temp3, Register forkJoinContext,
|
||||
ExecutionMode mode, Label *failure, Label *failurePopTemps, bool isTwoByte)
|
||||
{
|
||||
// State: result length in temp2.
|
||||
|
||||
// Ensure both strings are linear.
|
||||
masm.branchIfRope(lhs, failure);
|
||||
masm.branchIfRope(rhs, failure);
|
||||
|
||||
// Allocate a JSFatInlineString.
|
||||
switch (mode) {
|
||||
case SequentialExecution:
|
||||
masm.newGCFatInlineString(output, temp1, failure);
|
||||
break;
|
||||
case ParallelExecution:
|
||||
masm.push(temp1);
|
||||
masm.push(temp2);
|
||||
masm.newGCFatInlineStringPar(output, forkJoinContext, temp1, temp2, failurePopTemps);
|
||||
masm.pop(temp2);
|
||||
masm.pop(temp1);
|
||||
break;
|
||||
default:
|
||||
MOZ_ASSUME_UNREACHABLE("No such execution mode");
|
||||
}
|
||||
|
||||
// Store length and flags.
|
||||
uint32_t flags = JSString::INIT_FAT_INLINE_FLAGS;
|
||||
if (!isTwoByte)
|
||||
flags |= JSString::LATIN1_CHARS_BIT;
|
||||
masm.store32(Imm32(flags), Address(output, JSString::offsetOfFlags()));
|
||||
masm.store32(temp2, Address(output, JSString::offsetOfLength()));
|
||||
|
||||
// Load chars pointer in temp2.
|
||||
masm.computeEffectiveAddress(Address(output, JSInlineString::offsetOfInlineStorage()), temp2);
|
||||
|
||||
{
|
||||
// We use temp3 in this block, which in parallel execution also holds
|
||||
// a live ForkJoinContext pointer. If we are compiling for parallel
|
||||
// execution, be sure to save and restore the ForkJoinContext.
|
||||
if (mode == ParallelExecution)
|
||||
masm.push(temp3);
|
||||
|
||||
// Copy lhs chars. Note that this advances temp2 to point to the next
|
||||
// char. This also clobbers the lhs register.
|
||||
if (isTwoByte) {
|
||||
CopyStringCharsMaybeInflate(masm, lhs, temp2, temp1, temp3);
|
||||
} else {
|
||||
masm.loadStringLength(lhs, temp3);
|
||||
masm.loadStringChars(lhs, lhs);
|
||||
CopyStringChars(masm, temp2, lhs, temp3, temp1, sizeof(char), sizeof(char));
|
||||
}
|
||||
|
||||
// Copy rhs chars. Clobbers the rhs register.
|
||||
if (isTwoByte) {
|
||||
CopyStringCharsMaybeInflate(masm, rhs, temp2, temp1, temp3);
|
||||
} else {
|
||||
masm.loadStringLength(rhs, temp3);
|
||||
masm.loadStringChars(rhs, rhs);
|
||||
CopyStringChars(masm, temp2, rhs, temp3, temp1, sizeof(char), sizeof(char));
|
||||
}
|
||||
|
||||
// Null-terminate.
|
||||
if (isTwoByte)
|
||||
masm.store16(Imm32(0), Address(temp2, 0));
|
||||
else
|
||||
masm.store8(Imm32(0), Address(temp2, 0));
|
||||
|
||||
if (mode == ParallelExecution)
|
||||
masm.pop(temp3);
|
||||
}
|
||||
|
||||
masm.ret();
|
||||
}
|
||||
|
||||
JitCode *
|
||||
JitCompartment::generateStringConcatStub(JSContext *cx, ExecutionMode mode)
|
||||
{
|
||||
@ -5208,10 +5317,27 @@ JitCompartment::generateStringConcatStub(JSContext *cx, ExecutionMode mode)
|
||||
|
||||
masm.add32(temp1, temp2);
|
||||
|
||||
// Check if we can use a JSFatInlineString.
|
||||
Label isFatInline;
|
||||
masm.branch32(Assembler::BelowOrEqual, temp2, Imm32(JSFatInlineString::MAX_LENGTH_TWO_BYTE),
|
||||
&isFatInline);
|
||||
// Check if we can use a JSFatInlineString. The result is a Latin1 string if
|
||||
// lhs and rhs are both Latin1, so we AND the flags.
|
||||
Label isFatInlineTwoByte, isFatInlineLatin1;
|
||||
masm.load32(Address(lhs, JSString::offsetOfFlags()), temp1);
|
||||
masm.and32(Address(rhs, JSString::offsetOfFlags()), temp1);
|
||||
|
||||
Label isLatin1, notInline;
|
||||
masm.branchTest32(Assembler::NonZero, temp1, Imm32(JSString::LATIN1_CHARS_BIT), &isLatin1);
|
||||
{
|
||||
masm.branch32(Assembler::BelowOrEqual, temp2, Imm32(JSFatInlineString::MAX_LENGTH_TWO_BYTE),
|
||||
&isFatInlineTwoByte);
|
||||
masm.jump(¬Inline);
|
||||
}
|
||||
masm.bind(&isLatin1);
|
||||
{
|
||||
masm.branch32(Assembler::BelowOrEqual, temp2, Imm32(JSFatInlineString::MAX_LENGTH_LATIN1),
|
||||
&isFatInlineLatin1);
|
||||
}
|
||||
masm.bind(¬Inline);
|
||||
|
||||
// Keep AND'ed flags in temp1.
|
||||
|
||||
// Ensure result length <= JSString::MAX_LENGTH.
|
||||
masm.branch32(Assembler::Above, temp2, Imm32(JSString::MAX_LENGTH), &failure);
|
||||
@ -5232,8 +5358,12 @@ JitCompartment::generateStringConcatStub(JSContext *cx, ExecutionMode mode)
|
||||
MOZ_ASSUME_UNREACHABLE("No such execution mode");
|
||||
}
|
||||
|
||||
// Store lengthAndFlags.
|
||||
masm.store32(Imm32(JSString::ROPE_FLAGS), Address(output, JSString::offsetOfFlags()));
|
||||
// Store rope length and flags. temp1 still holds the result of AND'ing the
|
||||
// lhs and rhs flags, so we just have to clear the other flags to get our
|
||||
// rope flags (Latin1 if both lhs and rhs are Latin1).
|
||||
static_assert(JSString::ROPE_FLAGS == 0, "Rope flags must be 0");
|
||||
masm.and32(Imm32(JSString::LATIN1_CHARS_BIT), temp1);
|
||||
masm.store32(temp1, Address(output, JSString::offsetOfFlags()));
|
||||
masm.store32(temp2, Address(output, JSString::offsetOfLength()));
|
||||
|
||||
// Store left and right nodes.
|
||||
@ -5249,62 +5379,13 @@ JitCompartment::generateStringConcatStub(JSContext *cx, ExecutionMode mode)
|
||||
masm.mov(lhs, output);
|
||||
masm.ret();
|
||||
|
||||
masm.bind(&isFatInline);
|
||||
masm.bind(&isFatInlineTwoByte);
|
||||
ConcatFatInlineString(masm, lhs, rhs, output, temp1, temp2, temp3, forkJoinContext,
|
||||
mode, &failure, &failurePopTemps, true);
|
||||
|
||||
// State: lhs length in temp1, result length in temp2.
|
||||
|
||||
// Ensure both strings are linear.
|
||||
masm.branchIfRope(lhs, &failure);
|
||||
masm.branchIfRope(rhs, &failure);
|
||||
|
||||
// Allocate a JSFatInlineString.
|
||||
switch (mode) {
|
||||
case SequentialExecution:
|
||||
masm.newGCFatInlineString(output, temp3, &failure);
|
||||
break;
|
||||
case ParallelExecution:
|
||||
masm.push(temp1);
|
||||
masm.push(temp2);
|
||||
masm.newGCFatInlineStringPar(output, forkJoinContext, temp1, temp2, &failurePopTemps);
|
||||
masm.pop(temp2);
|
||||
masm.pop(temp1);
|
||||
break;
|
||||
default:
|
||||
MOZ_ASSUME_UNREACHABLE("No such execution mode");
|
||||
}
|
||||
|
||||
// Set length and flags.
|
||||
masm.store32(Imm32(JSString::INIT_FAT_INLINE_FLAGS), Address(output, JSString::offsetOfFlags()));
|
||||
masm.store32(temp2, Address(output, JSString::offsetOfLength()));
|
||||
|
||||
// Load chars pointer in temp2.
|
||||
masm.computeEffectiveAddress(Address(output, JSInlineString::offsetOfInlineStorage()), temp2);
|
||||
|
||||
{
|
||||
// We use temp3 in this block, which in parallel execution also holds
|
||||
// a live ForkJoinContext pointer. If we are compiling for parallel
|
||||
// execution, be sure to save and restore the ForkJoinContext.
|
||||
if (mode == ParallelExecution)
|
||||
masm.push(temp3);
|
||||
|
||||
// Copy lhs chars. Temp1 still holds the lhs length. Note that this
|
||||
// advances temp2 to point to the next char. Note that this also
|
||||
// repurposes the lhs register.
|
||||
masm.loadStringChars(lhs, lhs);
|
||||
CopyStringChars(masm, temp2, lhs, temp1, temp3);
|
||||
|
||||
// Copy rhs chars.
|
||||
masm.loadStringLength(rhs, temp1);
|
||||
masm.loadStringChars(rhs, rhs);
|
||||
CopyStringChars(masm, temp2, rhs, temp1, temp3);
|
||||
|
||||
if (mode == ParallelExecution)
|
||||
masm.pop(temp3);
|
||||
}
|
||||
|
||||
// Null-terminate.
|
||||
masm.store16(Imm32(0), Address(temp2, 0));
|
||||
masm.ret();
|
||||
masm.bind(&isFatInlineLatin1);
|
||||
ConcatFatInlineString(masm, lhs, rhs, output, temp1, temp2, temp3, forkJoinContext,
|
||||
mode, &failure, &failurePopTemps, false);
|
||||
|
||||
masm.bind(&failurePopTemps);
|
||||
masm.pop(temp2);
|
||||
|
@ -357,11 +357,12 @@ CompareStringsPar(ForkJoinContext *cx, JSString *left, JSString *right, int32_t
|
||||
{
|
||||
ScopedThreadSafeStringInspector leftInspector(left);
|
||||
ScopedThreadSafeStringInspector rightInspector(right);
|
||||
if (!leftInspector.ensureChars(cx) || !rightInspector.ensureChars(cx))
|
||||
AutoCheckCannotGC nogc;
|
||||
if (!leftInspector.ensureChars(cx, nogc) || !rightInspector.ensureChars(cx, nogc))
|
||||
return false;
|
||||
|
||||
*res = CompareChars(leftInspector.chars(), left->length(),
|
||||
rightInspector.chars(), right->length());
|
||||
*res = CompareChars(leftInspector.twoByteChars(), left->length(),
|
||||
rightInspector.twoByteChars(), right->length());
|
||||
return true;
|
||||
}
|
||||
|
||||
|
@ -1936,6 +1936,13 @@ MacroAssemblerARMCompat::and32(Imm32 imm, Register dest)
|
||||
ma_and(imm, dest, SetCond);
|
||||
}
|
||||
|
||||
void
|
||||
MacroAssemblerARMCompat::and32(const Address &src, Register dest)
|
||||
{
|
||||
load32(src, ScratchRegister);
|
||||
ma_and(ScratchRegister, dest, SetCond);
|
||||
}
|
||||
|
||||
void
|
||||
MacroAssemblerARMCompat::addPtr(Register src, Register dest)
|
||||
{
|
||||
|
@ -1299,6 +1299,7 @@ class MacroAssemblerARMCompat : public MacroAssemblerARM
|
||||
void and32(Register src, Register dest);
|
||||
void and32(Imm32 imm, Register dest);
|
||||
void and32(Imm32 imm, const Address &dest);
|
||||
void and32(const Address &src, Register dest);
|
||||
void or32(Imm32 imm, const Address &dest);
|
||||
void xorPtr(Imm32 imm, Register dest);
|
||||
void xorPtr(Register src, Register dest);
|
||||
|
@ -111,6 +111,9 @@ class MacroAssemblerX86Shared : public Assembler
|
||||
void and32(Register src, Register dest) {
|
||||
andl(src, dest);
|
||||
}
|
||||
void and32(const Address &src, Register dest) {
|
||||
andl(Operand(src), dest);
|
||||
}
|
||||
void and32(Imm32 imm, Register dest) {
|
||||
andl(imm, dest);
|
||||
}
|
||||
|
@ -5597,14 +5597,14 @@ JS_DecodeBytes(JSContext *cx, const char *src, size_t srclen, jschar *dst, size_
|
||||
size_t dstlen = *dstlenp;
|
||||
|
||||
if (srclen > dstlen) {
|
||||
InflateStringToBuffer(src, dstlen, dst);
|
||||
CopyAndInflateChars(dst, src, dstlen);
|
||||
|
||||
AutoSuppressGC suppress(cx);
|
||||
JS_ReportErrorNumber(cx, js_GetErrorMessage, nullptr, JSMSG_BUFFER_TOO_SMALL);
|
||||
return false;
|
||||
}
|
||||
|
||||
InflateStringToBuffer(src, srclen, dst);
|
||||
CopyAndInflateChars(dst, src, srclen);
|
||||
*dstlenp = srclen;
|
||||
return true;
|
||||
}
|
||||
|
@ -452,7 +452,7 @@ js::Atomize(ExclusiveContext *cx, const char *bytes, size_t length, InternBehavi
|
||||
* js::AtomizeString rarely has to copy the temp string we make.
|
||||
*/
|
||||
jschar inflated[ATOMIZE_BUF_MAX];
|
||||
InflateStringToBuffer(bytes, length, inflated);
|
||||
CopyAndInflateChars(inflated, bytes, length);
|
||||
return AtomizeAndCopyChars(cx, inflated, length, ib);
|
||||
}
|
||||
|
||||
|
@ -605,7 +605,7 @@ js::Int32ToString(ThreadSafeContext *cx, int32_t si)
|
||||
jschar *start = BackfillInt32InBuffer(si, buffer,
|
||||
JSFatInlineString::MAX_LENGTH_TWO_BYTE + 1, &length);
|
||||
|
||||
PodCopy(str->init(length), start, length + 1);
|
||||
PodCopy(str->initTwoByte(length), start, length + 1);
|
||||
|
||||
CacheNumber(cx, si, str);
|
||||
return str;
|
||||
@ -1437,7 +1437,7 @@ js::IndexToString(JSContext *cx, uint32_t index)
|
||||
*end = '\0';
|
||||
RangedPtr<jschar> start = BackfillIndexInCharBuffer(index, end);
|
||||
|
||||
jschar *dst = str->init(end - start);
|
||||
jschar *dst = str->initTwoByte(end - start);
|
||||
PodCopy(dst, start.get(), end - start + 1);
|
||||
|
||||
c->dtoaCache.cache(10, index, str);
|
||||
@ -1533,11 +1533,12 @@ CharsToNumber(ThreadSafeContext *cx, const jschar *chars, size_t length, double
|
||||
bool
|
||||
js::StringToNumber(ThreadSafeContext *cx, JSString *str, double *result)
|
||||
{
|
||||
AutoCheckCannotGC nogc;
|
||||
ScopedThreadSafeStringInspector inspector(str);
|
||||
if (!inspector.ensureChars(cx))
|
||||
if (!inspector.ensureChars(cx, nogc))
|
||||
return false;
|
||||
|
||||
return CharsToNumber(cx, inspector.chars(), str->length(), result);
|
||||
return CharsToNumber(cx, inspector.twoByteChars(), str->length(), result);
|
||||
}
|
||||
|
||||
bool
|
||||
|
@ -2780,8 +2780,8 @@ FlattenSubstrings(JSContext *cx, const jschar *chars,
|
||||
JSFatInlineString *str = js_NewGCFatInlineString<CanGC>(cx);
|
||||
if (!str)
|
||||
return nullptr;
|
||||
jschar *buf = str->init(outputLen);
|
||||
|
||||
jschar *buf = str->initTwoByte(outputLen);
|
||||
size_t pos = 0;
|
||||
for (size_t i = 0; i < rangesLen; i++) {
|
||||
PodCopy(buf + pos, chars + ranges[i].start, ranges[i].length);
|
||||
|
@ -256,7 +256,7 @@ InflateString(ThreadSafeContext *cx, const char *bytes, size_t *length);
|
||||
* enough for 'srclen' jschars. The buffer is NOT null-terminated.
|
||||
*/
|
||||
inline void
|
||||
InflateStringToBuffer(const char *src, size_t srclen, jschar *dst)
|
||||
CopyAndInflateChars(jschar *dst, const char *src, size_t srclen)
|
||||
{
|
||||
for (size_t i = 0; i < srclen; i++)
|
||||
dst[i] = (unsigned char) src[i];
|
||||
|
@ -132,9 +132,11 @@ DeflateStringToUTF8Buffer(js::ThreadSafeContext *cx, const jschar *src, size_t s
|
||||
|
||||
bufferTooSmall:
|
||||
*dstlenp = (origDstlen - dstlen);
|
||||
if (cx->isJSContext())
|
||||
if (cx->isJSContext()) {
|
||||
js::gc::AutoSuppressGC suppress(cx->asJSContext());
|
||||
JS_ReportErrorNumber(cx->asJSContext(), js_GetErrorMessage, nullptr,
|
||||
JSMSG_BUFFER_TOO_SMALL);
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
|
@ -279,11 +279,12 @@ intrinsic_ParallelSpew(ThreadSafeContext *cx, unsigned argc, Value *vp)
|
||||
JS_ASSERT(args.length() == 1);
|
||||
JS_ASSERT(args[0].isString());
|
||||
|
||||
AutoCheckCannotGC nogc;
|
||||
ScopedThreadSafeStringInspector inspector(args[0].toString());
|
||||
if (!inspector.ensureChars(cx))
|
||||
if (!inspector.ensureChars(cx, nogc))
|
||||
return false;
|
||||
|
||||
ScopedJSFreePtr<char> bytes(TwoByteCharsToNewUTF8CharsZ(cx, inspector.range()).c_str());
|
||||
ScopedJSFreePtr<char> bytes(TwoByteCharsToNewUTF8CharsZ(cx, inspector.twoByteRange()).c_str());
|
||||
parallel::Spew(parallel::SpewOps, bytes);
|
||||
|
||||
args.rval().setUndefined();
|
||||
|
@ -32,12 +32,12 @@ NewFatInlineString(ThreadSafeContext *cx, JS::Latin1Chars chars)
|
||||
str = JSInlineString::new_<allowGC>(cx);
|
||||
if (!str)
|
||||
return nullptr;
|
||||
p = str->init(len);
|
||||
p = str->initTwoByte(len);
|
||||
} else {
|
||||
JSFatInlineString *fatstr = JSFatInlineString::new_<allowGC>(cx);
|
||||
if (!fatstr)
|
||||
return nullptr;
|
||||
p = fatstr->init(len);
|
||||
p = fatstr->initTwoByte(len);
|
||||
str = fatstr;
|
||||
}
|
||||
|
||||
@ -65,12 +65,12 @@ NewFatInlineString(ExclusiveContext *cx, JS::TwoByteChars chars)
|
||||
str = JSInlineString::new_<allowGC>(cx);
|
||||
if (!str)
|
||||
return nullptr;
|
||||
storage = str->init(len);
|
||||
storage = str->initTwoByte(len);
|
||||
} else {
|
||||
JSFatInlineString *fatstr = JSFatInlineString::new_<allowGC>(cx);
|
||||
if (!fatstr)
|
||||
return nullptr;
|
||||
storage = fatstr->init(len);
|
||||
storage = fatstr->initTwoByte(len);
|
||||
str = fatstr;
|
||||
}
|
||||
|
||||
@ -107,6 +107,8 @@ JSRope::init(js::ThreadSafeContext *cx, JSString *left, JSString *right, size_t
|
||||
{
|
||||
d.u1.length = length;
|
||||
d.u1.flags = ROPE_FLAGS;
|
||||
if (left->hasLatin1Chars() && right->hasLatin1Chars())
|
||||
d.u1.flags |= LATIN1_CHARS_BIT;
|
||||
d.s.u2.left = left;
|
||||
d.s.u3.right = right;
|
||||
js::StringWriteBarrierPost(cx, &d.s.u2.left);
|
||||
@ -248,7 +250,7 @@ JSInlineString::new_(js::ThreadSafeContext *cx)
|
||||
}
|
||||
|
||||
MOZ_ALWAYS_INLINE jschar *
|
||||
JSInlineString::init(size_t length)
|
||||
JSInlineString::initTwoByte(size_t length)
|
||||
{
|
||||
JS_ASSERT(twoByteLengthFits(length));
|
||||
d.u1.length = length;
|
||||
@ -256,8 +258,17 @@ JSInlineString::init(size_t length)
|
||||
return d.inlineStorageTwoByte;
|
||||
}
|
||||
|
||||
MOZ_ALWAYS_INLINE char *
|
||||
JSInlineString::initLatin1(size_t length)
|
||||
{
|
||||
JS_ASSERT(latin1LengthFits(length));
|
||||
d.u1.length = length;
|
||||
d.u1.flags = INIT_INLINE_FLAGS | LATIN1_CHARS_BIT;
|
||||
return d.inlineStorageLatin1;
|
||||
}
|
||||
|
||||
MOZ_ALWAYS_INLINE jschar *
|
||||
JSFatInlineString::init(size_t length)
|
||||
JSFatInlineString::initTwoByte(size_t length)
|
||||
{
|
||||
JS_ASSERT(twoByteLengthFits(length));
|
||||
d.u1.length = length;
|
||||
@ -265,6 +276,15 @@ JSFatInlineString::init(size_t length)
|
||||
return d.inlineStorageTwoByte;
|
||||
}
|
||||
|
||||
MOZ_ALWAYS_INLINE char *
|
||||
JSFatInlineString::initLatin1(size_t length)
|
||||
{
|
||||
JS_ASSERT(latin1LengthFits(length));
|
||||
d.u1.length = length;
|
||||
d.u1.flags = INIT_FAT_INLINE_FLAGS | LATIN1_CHARS_BIT;
|
||||
return d.inlineStorageLatin1;
|
||||
}
|
||||
|
||||
template <js::AllowGC allowGC>
|
||||
MOZ_ALWAYS_INLINE JSFatInlineString *
|
||||
JSFatInlineString::new_(js::ThreadSafeContext *cx)
|
||||
@ -339,7 +359,7 @@ JSFlatString::finalize(js::FreeOp *fop)
|
||||
JS_ASSERT(getAllocKind() != js::gc::FINALIZE_FAT_INLINE_STRING);
|
||||
|
||||
if (!isInline())
|
||||
fop->free_(const_cast<jschar *>(nonInlineChars()));
|
||||
fop->free_(nonInlineCharsRaw());
|
||||
}
|
||||
|
||||
inline void
|
||||
@ -348,7 +368,7 @@ JSFatInlineString::finalize(js::FreeOp *fop)
|
||||
JS_ASSERT(getAllocKind() == js::gc::FINALIZE_FAT_INLINE_STRING);
|
||||
|
||||
if (!isInline())
|
||||
fop->free_(const_cast<jschar *>(nonInlineChars()));
|
||||
fop->free_(nonInlineCharsRaw());
|
||||
}
|
||||
|
||||
inline void
|
||||
@ -358,7 +378,7 @@ JSAtom::finalize(js::FreeOp *fop)
|
||||
JS_ASSERT(JSString::isFlat());
|
||||
|
||||
if (!isInline())
|
||||
fop->free_(const_cast<jschar *>(nonInlineChars()));
|
||||
fop->free_(nonInlineCharsRaw());
|
||||
}
|
||||
|
||||
inline void
|
||||
|
@ -405,21 +405,39 @@ js::ConcatStrings(ThreadSafeContext *cx,
|
||||
if (!JSString::validateLength(cx, wholeLength))
|
||||
return nullptr;
|
||||
|
||||
if (JSFatInlineString::twoByteLengthFits(wholeLength) && cx->isJSContext()) {
|
||||
bool isLatin1 = left->hasLatin1Chars() && right->hasLatin1Chars();
|
||||
bool canUseFatInline = isLatin1
|
||||
? JSFatInlineString::latin1LengthFits(wholeLength)
|
||||
: JSFatInlineString::twoByteLengthFits(wholeLength);
|
||||
if (canUseFatInline && cx->isJSContext()) {
|
||||
JSFatInlineString *str = js_NewGCFatInlineString<allowGC>(cx);
|
||||
if (!str)
|
||||
return nullptr;
|
||||
|
||||
AutoCheckCannotGC nogc;
|
||||
ScopedThreadSafeStringInspector leftInspector(left);
|
||||
ScopedThreadSafeStringInspector rightInspector(right);
|
||||
if (!leftInspector.ensureChars(cx) || !rightInspector.ensureChars(cx))
|
||||
if (!leftInspector.ensureChars(cx, nogc) || !rightInspector.ensureChars(cx, nogc))
|
||||
return nullptr;
|
||||
|
||||
jschar *buf = str->init(wholeLength);
|
||||
PodCopy(buf, leftInspector.chars(), leftLen);
|
||||
PodCopy(buf + leftLen, rightInspector.chars(), rightLen);
|
||||
if (isLatin1) {
|
||||
char *buf = str->initLatin1(wholeLength);
|
||||
PodCopy(buf, leftInspector.latin1Chars(), leftLen);
|
||||
PodCopy(buf + leftLen, rightInspector.latin1Chars(), rightLen);
|
||||
buf[wholeLength] = 0;
|
||||
} else {
|
||||
jschar *buf = str->initTwoByte(wholeLength);
|
||||
if (leftInspector.hasTwoByteChars())
|
||||
PodCopy(buf, leftInspector.twoByteChars(), leftLen);
|
||||
else
|
||||
CopyAndInflateChars(buf, leftInspector.latin1Chars(), leftLen);
|
||||
if (rightInspector.hasTwoByteChars())
|
||||
PodCopy(buf + leftLen, rightInspector.twoByteChars(), rightLen);
|
||||
else
|
||||
CopyAndInflateChars(buf + leftLen, rightInspector.latin1Chars(), rightLen);
|
||||
buf[wholeLength] = 0;
|
||||
}
|
||||
|
||||
buf[wholeLength] = 0;
|
||||
return str;
|
||||
}
|
||||
|
||||
@ -530,27 +548,35 @@ JSFlatString::isIndexSlow(uint32_t *indexp) const
|
||||
}
|
||||
|
||||
bool
|
||||
ScopedThreadSafeStringInspector::ensureChars(ThreadSafeContext *cx)
|
||||
ScopedThreadSafeStringInspector::ensureChars(ThreadSafeContext *cx, const AutoCheckCannotGC &nogc)
|
||||
{
|
||||
if (chars_)
|
||||
if (state_ != Uninitialized)
|
||||
return true;
|
||||
|
||||
if (cx->isExclusiveContext()) {
|
||||
JSLinearString *linear = str_->ensureLinear(cx->asExclusiveContext());
|
||||
if (!linear)
|
||||
return false;
|
||||
chars_ = linear->chars();
|
||||
if (linear->hasTwoByteChars()) {
|
||||
state_ = TwoByte;
|
||||
twoByteChars_ = linear->twoByteChars(nogc);
|
||||
} else {
|
||||
state_ = Latin1;
|
||||
latin1Chars_ = linear->latin1Chars(nogc);
|
||||
}
|
||||
} else {
|
||||
if (str_->hasPureChars()) {
|
||||
chars_ = str_->pureChars();
|
||||
state_ = TwoByte;
|
||||
twoByteChars_ = str_->pureChars();
|
||||
} else {
|
||||
if (!str_->copyNonPureChars(cx, scopedChars_))
|
||||
return false;
|
||||
chars_ = scopedChars_;
|
||||
state_ = TwoByte;
|
||||
twoByteChars_ = scopedChars_;
|
||||
}
|
||||
}
|
||||
|
||||
JS_ASSERT(chars_);
|
||||
MOZ_ASSERT(state_ != Uninitialized);
|
||||
return true;
|
||||
}
|
||||
|
||||
|
@ -577,6 +577,17 @@ class JSLinearString : public JSString
|
||||
bool isLinear() const MOZ_DELETE;
|
||||
JSLinearString &asLinear() const MOZ_DELETE;
|
||||
|
||||
protected:
|
||||
/* Returns void pointer to latin1/twoByte chars, for finalizers. */
|
||||
MOZ_ALWAYS_INLINE
|
||||
void *nonInlineCharsRaw() const {
|
||||
JS_ASSERT(!isInline());
|
||||
static_assert(offsetof(JSLinearString, d.s.u2.nonInlineCharsTwoByte) ==
|
||||
offsetof(JSLinearString, d.s.u2.nonInlineCharsLatin1),
|
||||
"nonInlineCharsTwoByte and nonInlineCharsLatin1 must have same offset");
|
||||
return (void *)d.s.u2.nonInlineCharsTwoByte;
|
||||
}
|
||||
|
||||
public:
|
||||
MOZ_ALWAYS_INLINE
|
||||
const jschar *nonInlineChars() const {
|
||||
@ -734,7 +745,8 @@ class JSInlineString : public JSFlatString
|
||||
template <js::AllowGC allowGC>
|
||||
static inline JSInlineString *new_(js::ThreadSafeContext *cx);
|
||||
|
||||
inline jschar *init(size_t length);
|
||||
inline jschar *initTwoByte(size_t length);
|
||||
inline char *initLatin1(size_t length);
|
||||
|
||||
inline void resetLength(size_t length);
|
||||
|
||||
@ -822,7 +834,8 @@ class JSFatInlineString : public JSInlineString
|
||||
INLINE_EXTENSION_CHARS_TWO_BYTE
|
||||
-1 /* null terminator */;
|
||||
|
||||
inline jschar *init(size_t length);
|
||||
inline jschar *initTwoByte(size_t length);
|
||||
inline char *initLatin1(size_t length);
|
||||
|
||||
static bool latin1LengthFits(size_t length) {
|
||||
return length <= MAX_LENGTH_LATIN1;
|
||||
@ -924,24 +937,36 @@ class ScopedThreadSafeStringInspector
|
||||
private:
|
||||
JSString *str_;
|
||||
ScopedJSFreePtr<jschar> scopedChars_;
|
||||
const jschar *chars_;
|
||||
union {
|
||||
const jschar *twoByteChars_;
|
||||
const char *latin1Chars_;
|
||||
};
|
||||
enum State { Uninitialized, Latin1, TwoByte };
|
||||
State state_;
|
||||
|
||||
public:
|
||||
explicit ScopedThreadSafeStringInspector(JSString *str)
|
||||
: str_(str),
|
||||
chars_(nullptr)
|
||||
state_(Uninitialized)
|
||||
{ }
|
||||
|
||||
bool ensureChars(ThreadSafeContext *cx);
|
||||
bool ensureChars(ThreadSafeContext *cx, const JS::AutoCheckCannotGC &nogc);
|
||||
|
||||
const jschar *chars() {
|
||||
JS_ASSERT(chars_);
|
||||
return chars_;
|
||||
bool hasTwoByteChars() const { return state_ == TwoByte; }
|
||||
bool hasLatin1Chars() const { return state_ == Latin1; }
|
||||
|
||||
const jschar *twoByteChars() const {
|
||||
MOZ_ASSERT(state_ == TwoByte);
|
||||
return twoByteChars_;
|
||||
}
|
||||
const char *latin1Chars() const {
|
||||
MOZ_ASSERT(state_ == Latin1);
|
||||
return latin1Chars_;
|
||||
}
|
||||
|
||||
JS::TwoByteChars range() {
|
||||
JS_ASSERT(chars_);
|
||||
return JS::TwoByteChars(chars_, str_->length());
|
||||
JS::TwoByteChars twoByteRange() const {
|
||||
MOZ_ASSERT(state_ == TwoByte);
|
||||
return JS::TwoByteChars(twoByteChars_, str_->length());
|
||||
}
|
||||
};
|
||||
|
||||
|
@ -120,7 +120,7 @@ StringBuffer::appendInflated(const char *cstr, size_t cstrlen)
|
||||
size_t lengthBefore = length();
|
||||
if (!cb.growByUninitialized(cstrlen))
|
||||
return false;
|
||||
InflateStringToBuffer(cstr, cstrlen, begin() + lengthBefore);
|
||||
CopyAndInflateChars(begin() + lengthBefore, cstr, cstrlen);
|
||||
return true;
|
||||
}
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user