Bug 966911 Part 1: Improve DOMString and AString conversion code paths r=bholley

This commit is contained in:
Neil Rashbrook 2014-02-12 21:22:07 +00:00
parent e3e24713ec
commit dace60c6bb
5 changed files with 93 additions and 140 deletions

View File

@ -379,8 +379,6 @@ XPCConvert::JSData2Native(void* d, HandleValue s,
{
NS_PRECONDITION(d, "bad param");
bool isDOMString = true;
AutoJSContext cx;
if (pErr)
*pErr = NS_ERROR_XPC_BAD_CONVERT_JS;
@ -473,81 +471,62 @@ XPCConvert::JSData2Native(void* d, HandleValue s,
case nsXPTType::T_ASTRING:
{
isDOMString = false;
if (JSVAL_IS_VOID(s)) {
if (useAllocator)
*((const nsAString**)d) = &EmptyString();
else
(**((nsAString**)d)).SetIsVoid(true);
return true;
}
// Fall through to T_DOMSTRING case.
}
case nsXPTType::T_DOMSTRING:
{
static const char16_t EMPTY_STRING[] = { '\0' };
static const char16_t VOID_STRING[] = { 'u', 'n', 'd', 'e', 'f', 'i', 'n', 'e', 'd', '\0' };
if (JSVAL_IS_NULL(s)) {
if (useAllocator)
*((const nsAString**)d) = &NullString();
else
(**((nsAString**)d)).SetIsVoid(true);
return true;
}
size_t length = 0;
const char16_t* chars = nullptr;
JSString* str = nullptr;
bool isNewString = false;
uint32_t length = 0;
if (JSVAL_IS_VOID(s)) {
if (isDOMString) {
chars = VOID_STRING;
length = ArrayLength(VOID_STRING) - 1;
} else {
chars = EMPTY_STRING;
length = 0;
}
} else if (!JSVAL_IS_NULL(s)) {
if (!JSVAL_IS_VOID(s)) {
str = ToString(cx, s);
if (!str)
return false;
length = (uint32_t) JS_GetStringLength(str);
if (length) {
chars = JS_GetStringCharsZ(cx, str);
if (!chars)
return false;
if (STRING_TO_JSVAL(str) != s)
isNewString = true;
} else {
str = nullptr;
chars = EMPTY_STRING;
chars = useAllocator ? JS_GetStringCharsZAndLength(cx, str, &length)
: JS_GetStringCharsAndLength(cx, str, &length);
if (!chars)
return false;
if (!length) {
if (useAllocator)
*((const nsAString**)d) = &EmptyString();
else
(**((nsAString**)d)).Truncate();
return true;
}
}
nsString* ws;
if (useAllocator) {
// XXX extra string copy when isNewString
if (str && !isNewString) {
size_t strLength;
const jschar *strChars = JS_GetStringCharsZAndLength(cx, str, &strLength);
if (!strChars)
return false;
XPCReadableJSStringWrapper *wrapper =
nsXPConnect::GetRuntimeInstance()->NewStringWrapper(strChars, strLength);
if (!wrapper)
return false;
*((const nsAString**)d) = wrapper;
} else if (JSVAL_IS_NULL(s)) {
XPCReadableJSStringWrapper *wrapper =
new XPCReadableJSStringWrapper();
if (!wrapper)
return false;
*((const nsAString**)d) = wrapper;
} else {
// use nsString to encourage sharing
const nsAString *rs = new nsString(chars, length);
if (!rs)
return false;
*((const nsAString**)d) = rs;
}
ws = nsXPConnect::GetRuntimeInstance()->NewShortLivedString();
*((const nsString**)d) = ws;
} else {
nsAString* ws = *((nsAString**)d);
ws = *((nsString**)d);
}
if (JSVAL_IS_NULL(s) || (!isDOMString && JSVAL_IS_VOID(s))) {
ws->Truncate();
ws->SetIsVoid(true);
} else
ws->Assign(chars, length);
if (!str) {
ws->AssignLiteral(MOZ_UTF16("undefined"));
} else if (useAllocator && STRING_TO_JSVAL(str) == s) {
// The JS string will exist over the function call.
// We don't need to copy the characters in this case.
ws->Rebind(chars, length);
} else {
ws->Assign(chars, length);
}
return true;
}
@ -622,20 +601,14 @@ XPCConvert::JSData2Native(void* d, HandleValue s,
case nsXPTType::T_UTF8STRING:
{
const jschar* chars;
uint32_t length;
size_t length;
JSString* str;
if (JSVAL_IS_NULL(s) || JSVAL_IS_VOID(s)) {
if (useAllocator) {
nsACString *rs = new nsCString();
if (!rs)
return false;
rs->SetIsVoid(true);
*((nsACString**)d) = rs;
*((const nsACString**)d) = &NullCString();
} else {
nsCString* rs = *((nsCString**)d);
rs->Truncate();
rs->SetIsVoid(true);
}
return true;
@ -644,11 +617,19 @@ XPCConvert::JSData2Native(void* d, HandleValue s,
// The JS val is neither null nor void...
if (!(str = ToString(cx, s))||
!(chars = JS_GetStringCharsZ(cx, str))) {
!(chars = JS_GetStringCharsAndLength(cx, str, &length))) {
return false;
}
length = JS_GetStringLength(str);
if (!length) {
if (useAllocator) {
*((const nsACString**)d) = &EmptyCString();
} else {
nsCString* rs = *((nsCString**)d);
rs->Truncate();
}
return true;
}
nsCString *rs;
if (useAllocator) {
@ -661,9 +642,7 @@ XPCConvert::JSData2Native(void* d, HandleValue s,
} else {
rs = *((nsCString**)d);
}
const char16_t* start = (const char16_t*)chars;
const char16_t* end = start + length;
CopyUTF16toUTF8(nsDependentSubstring(start, end), *rs);
CopyUTF16toUTF8(Substring(chars, length), *rs);
return true;
}
@ -696,6 +675,16 @@ XPCConvert::JSData2Native(void* d, HandleValue s,
return false;
}
if (!length) {
if (useAllocator) {
*((const nsACString**)d) = &EmptyCString();
} else {
nsCString* rs = *((nsCString**)d);
rs->Truncate();
}
return true;
}
nsACString *rs;
if (useAllocator) {
rs = new nsCString();

View File

@ -1415,38 +1415,32 @@ XPCJSRuntime::SizeOfIncludingThis(MallocSizeOf mallocSizeOf)
return n;
}
XPCReadableJSStringWrapper *
XPCJSRuntime::NewStringWrapper(const char16_t *str, uint32_t len)
nsString*
XPCJSRuntime::NewShortLivedString()
{
for (uint32_t i = 0; i < XPCCCX_STRING_CACHE_SIZE; ++i) {
StringWrapperEntry& ent = mScratchStrings[i];
if (!ent.mInUse) {
ent.mInUse = true;
// Construct the string using placement new.
return new (ent.mString.addr()) XPCReadableJSStringWrapper(str, len);
if (mScratchStrings[i].empty()) {
mScratchStrings[i].construct();
return mScratchStrings[i].addr();
}
}
// All our internal string wrappers are used, allocate a new string.
return new XPCReadableJSStringWrapper(str, len);
return new nsString();
}
void
XPCJSRuntime::DeleteString(nsAString *string)
XPCJSRuntime::DeleteShortLivedString(nsString *string)
{
if (string == &EmptyString() || string == &NullString())
return;
for (uint32_t i = 0; i < XPCCCX_STRING_CACHE_SIZE; ++i) {
StringWrapperEntry& ent = mScratchStrings[i];
if (string == ent.mString.addr()) {
if (!mScratchStrings[i].empty() &&
mScratchStrings[i].addr() == string) {
// One of our internal strings is no longer in use, mark
// it as such and destroy the string.
ent.mInUse = false;
ent.mString.addr()->~XPCReadableJSStringWrapper();
// it as such and free its data.
mScratchStrings[i].destroy();
return;
}
}
@ -1566,7 +1560,7 @@ XPCJSRuntime::~XPCJSRuntime()
#ifdef DEBUG
for (uint32_t i = 0; i < XPCCCX_STRING_CACHE_SIZE; ++i) {
MOZ_ASSERT(!mScratchStrings[i].mInUse, "Uh, string wrapper still in use!");
MOZ_ASSERT(mScratchStrings[i].empty(), "Short lived string still in use");
}
#endif
}

View File

@ -2311,11 +2311,15 @@ CallMethodHelper::CleanupParam(nsXPTCMiniVariant& param, nsXPTType& type)
break;
case nsXPTType::T_ASTRING:
case nsXPTType::T_DOMSTRING:
nsXPConnect::GetRuntimeInstance()->DeleteString((nsAString*)param.val.p);
nsXPConnect::GetRuntimeInstance()->DeleteShortLivedString((nsString*)param.val.p);
break;
case nsXPTType::T_UTF8STRING:
case nsXPTType::T_CSTRING:
delete (nsCString*) param.val.p;
{
nsCString* rs = (nsCString*)param.val.p;
if (rs != &EmptyCString() && rs != &NullCString())
delete rs;
}
break;
default:
MOZ_ASSERT(!type.IsArithmetic(), "Cleanup requested on unexpected type.");

View File

@ -80,6 +80,7 @@
#include "mozilla/Attributes.h"
#include "mozilla/CycleCollectedJSRuntime.h"
#include "mozilla/GuardObjects.h"
#include "mozilla/Maybe.h"
#include "mozilla/MemoryReporting.h"
#include <math.h>
@ -393,33 +394,6 @@ private:
/***************************************************************************/
// class to export a JSString as an const nsAString, no refcounting :(
class XPCReadableJSStringWrapper : public nsDependentString
{
public:
typedef nsDependentString::char_traits char_traits;
XPCReadableJSStringWrapper(const char16_t *chars, size_t length) :
nsDependentString(chars, length)
{ }
XPCReadableJSStringWrapper() :
nsDependentString(char_traits::sEmptyBuffer, char_traits::sEmptyBuffer)
{ SetIsVoid(true); }
bool init(JSContext* aContext, JSString* str)
{
size_t length;
const jschar* chars = JS_GetStringCharsZAndLength(aContext, str, &length);
if (!chars)
return false;
MOZ_ASSERT(IsEmpty(), "init() on initialized string");
new(static_cast<nsDependentString *>(this)) nsDependentString(chars, length);
return true;
}
};
// In the current xpconnect system there can only be one XPCJSRuntime.
// So, xpconnect can only be used on one JSRuntime within the process.
@ -580,8 +554,8 @@ public:
~XPCJSRuntime();
XPCReadableJSStringWrapper *NewStringWrapper(const char16_t *str, uint32_t len);
void DeleteString(nsAString *string);
nsString* NewShortLivedString();
void DeleteShortLivedString(nsString *string);
void AddGCCallback(xpcGCCallback cb);
void RemoveGCCallback(xpcGCCallback cb);
@ -646,20 +620,7 @@ private:
#define XPCCCX_STRING_CACHE_SIZE 2
// String wrapper entry, holds a string, and a boolean that tells
// whether the string is in use or not.
//
// NB: The string is not stored by value so that we avoid the cost of
// construction/destruction.
struct StringWrapperEntry
{
StringWrapperEntry() : mInUse(false) { }
mozilla::AlignedStorage2<XPCReadableJSStringWrapper> mString;
bool mInUse;
};
StringWrapperEntry mScratchStrings[XPCCCX_STRING_CACHE_SIZE];
mozilla::Maybe<nsString> mScratchStrings[XPCCCX_STRING_CACHE_SIZE];
friend class Watchdog;
friend class AutoLockWatchdog;

View File

@ -380,6 +380,11 @@ class nsTString_CharT : public nsTSubstring_CharT
#endif // !MOZ_STRING_WITH_OBSOLETE_API
/**
* Allow this string to be bound to a character buffer
* until the string is rebound or mutated; the caller
* must ensure that the buffer outlives the string.
*/
void Rebind( const char_type* data, size_type length );
/**