Bug 1027528 part 15 - Make structured clone work with Latin1 strings. r=jorendorff,bent

This commit is contained in:
Jan de Mooij 2014-06-25 08:05:41 +02:00
parent d36e64fdcd
commit 21cfe56f5f
4 changed files with 90 additions and 18 deletions

View File

@ -35,11 +35,11 @@ namespace {
// If JS_STRUCTURED_CLONE_VERSION changes then we need to update our major
// schema version.
static_assert(JS_STRUCTURED_CLONE_VERSION == 2,
static_assert(JS_STRUCTURED_CLONE_VERSION == 3,
"Need to update the major schema version.");
// Major schema version. Bump for almost everything.
const uint32_t kMajorSchemaVersion = 14;
const uint32_t kMajorSchemaVersion = 15;
// Minor schema version. Should almost always be 0 (maybe bump on release
// branches if we have to).
@ -1460,6 +1460,17 @@ UpgradeSchemaFrom13_0To14_0(mozIStorageConnection* aConnection)
return NS_OK;
}
nsresult
UpgradeSchemaFrom14_0To15_0(mozIStorageConnection* aConnection)
{
// The only change between 14 and 15 was a different structured
// clone format, but it's backwards-compatible.
nsresult rv = aConnection->SetSchemaVersion(MakeSchemaVersion(15, 0));
NS_ENSURE_SUCCESS(rv, rv);
return NS_OK;
}
class VersionChangeEventsRunnable;
class SetVersionHelper : public AsyncConnectionHelper,
@ -2073,7 +2084,7 @@ OpenDatabaseHelper::CreateDatabaseConnection(
}
else {
// This logic needs to change next time we change the schema!
static_assert(kSQLiteSchemaVersion == int32_t((14 << 4) + 0),
static_assert(kSQLiteSchemaVersion == int32_t((15 << 4) + 0),
"Need upgrade code from schema version increase.");
while (schemaVersion != kSQLiteSchemaVersion) {
@ -2108,6 +2119,9 @@ OpenDatabaseHelper::CreateDatabaseConnection(
else if (schemaVersion == MakeSchemaVersion(13, 0)) {
rv = UpgradeSchemaFrom13_0To14_0(connection);
}
else if (schemaVersion == MakeSchemaVersion(14, 0)) {
rv = UpgradeSchemaFrom14_0To15_0(connection);
}
else {
NS_WARNING("Unable to open IndexedDB database, no upgrade path is "
"available!");

View File

@ -121,7 +121,7 @@ typedef void (*FreeTransferStructuredCloneOp)(uint32_t tag, JS::TransferableOwne
// The maximum supported structured-clone serialization format version. Note
// that this does not need to be bumped for Transferable-only changes, since
// they are never saved to persistent storage.
#define JS_STRUCTURED_CLONE_VERSION 2
#define JS_STRUCTURED_CLONE_VERSION 3
struct JSStructuredCloneCallbacks {
ReadStructuredCloneOp read;

View File

@ -0,0 +1,19 @@
// Latin1
var s = deserialize(serialize(toLatin1("foo123\u00EE")));
assertEq(s, "foo123\u00EE");
assertEq(isLatin1(s), true);
var o = deserialize(serialize(new String(toLatin1("foo\u00EE"))));
assertEq(typeof o, "object");
assertEq(o.valueOf(), "foo\u00EE");
assertEq(isLatin1(o.valueOf()), true);
// TwoByte
var s = deserialize(serialize("foo123\u00FF\u1234"));
assertEq(s, "foo123\u00FF\u1234");
assertEq(isLatin1(s), false);
var o = deserialize(serialize(new String("foo\uEEEE")));
assertEq(typeof o, "object");
assertEq(o.valueOf(), "foo\uEEEE");
assertEq(isLatin1(o.valueOf()), false);

View File

@ -135,6 +135,7 @@ struct SCOutput {
bool writePair(uint32_t tag, uint32_t data);
bool writeDouble(double d);
bool writeBytes(const void *p, size_t nbytes);
bool writeChars(const Latin1Char *p, size_t nchars);
bool writeChars(const jschar *p, size_t nchars);
bool writePtr(const void *);
@ -165,6 +166,7 @@ class SCInput {
bool readPair(uint32_t *tagp, uint32_t *datap);
bool readDouble(double *p);
bool readBytes(void *p, size_t nbytes);
bool readChars(Latin1Char *p, size_t nchars);
bool readChars(jschar *p, size_t nchars);
bool readPtr(void **);
@ -212,8 +214,11 @@ struct JSStructuredCloneReader {
bool readTransferMap();
template <typename CharT>
JSString *readStringImpl(uint32_t nchars);
JSString *readString(uint32_t data);
bool checkDouble(double d);
JSString *readString(uint32_t nchars);
bool readTypedArray(uint32_t arrayType, uint32_t nelems, Value *vp, bool v1Read = false);
bool readArrayBuffer(uint32_t nbytes, Value *vp);
bool readV1ArrayBuffer(uint32_t arrayType, uint32_t nelems, Value *vp);
@ -553,6 +558,13 @@ SCInput::readBytes(void *p, size_t nbytes)
return readArray((uint8_t *) p, nbytes);
}
bool
SCInput::readChars(Latin1Char *p, size_t nchars)
{
static_assert(sizeof(Latin1Char) == sizeof(uint8_t), "Latin1Char must fit in 1 byte");
return readBytes(p, nchars);
}
bool
SCInput::readChars(jschar *p, size_t nchars)
{
@ -689,6 +701,13 @@ SCOutput::writeChars(const jschar *p, size_t nchars)
return writeArray((const uint16_t *) p, nchars);
}
bool
SCOutput::writeChars(const Latin1Char *p, size_t nchars)
{
static_assert(sizeof(Latin1Char) == sizeof(uint8_t), "Latin1Char must fit in 1 byte");
return writeBytes(p, nchars);
}
bool
SCOutput::writePtr(const void *p)
{
@ -775,11 +794,21 @@ JSStructuredCloneWriter::reportErrorTransferable()
bool
JSStructuredCloneWriter::writeString(uint32_t tag, JSString *str)
{
size_t length = str->length();
const jschar *chars = str->getChars(context());
if (!chars)
JSLinearString *linear = str->ensureLinear(context());
if (!linear)
return false;
return out.writePair(tag, uint32_t(length)) && out.writeChars(chars, length);
static_assert(JSString::MAX_LENGTH <= INT32_MAX, "String length must fit in 31 bits");
uint32_t length = linear->length();
uint32_t lengthAndEncoding = length | (uint32_t(linear->hasLatin1Chars()) << 31);
if (!out.writePair(tag, lengthAndEncoding))
return false;
JS::AutoCheckCannotGC nogc;
return linear->hasLatin1Chars()
? out.writeChars(linear->latin1Chars(nogc), length)
: out.writeChars(linear->twoByteChars(nogc), length);
}
bool
@ -1134,9 +1163,10 @@ JSStructuredCloneReader::checkDouble(double d)
namespace {
template <typename CharT>
class Chars {
JSContext *cx;
jschar *p;
CharT *p;
public:
explicit Chars(JSContext *cx) : cx(cx), p(nullptr) {}
~Chars() { js_free(p); }
@ -1144,28 +1174,29 @@ class Chars {
bool allocate(size_t len) {
JS_ASSERT(!p);
// We're going to null-terminate!
p = cx->pod_malloc<jschar>(len + 1);
p = cx->pod_malloc<CharT>(len + 1);
if (p) {
p[len] = jschar(0);
p[len] = CharT(0);
return true;
}
return false;
}
jschar *get() { return p; }
CharT *get() { return p; }
void forget() { p = nullptr; }
};
} /* anonymous namespace */
template <typename CharT>
JSString *
JSStructuredCloneReader::readString(uint32_t nchars)
JSStructuredCloneReader::readStringImpl(uint32_t nchars)
{
if (nchars > JSString::MAX_LENGTH) {
JS_ReportErrorNumber(context(), js_GetErrorMessage, nullptr,
JSMSG_SC_BAD_SERIALIZED_DATA, "string length");
return nullptr;
}
Chars chars(context());
Chars<CharT> chars(context());
if (!chars.allocate(nchars) || !in.readChars(chars.get(), nchars))
return nullptr;
JSString *str = NewString<CanGC>(context(), chars.get(), nchars);
@ -1174,6 +1205,14 @@ JSStructuredCloneReader::readString(uint32_t nchars)
return str;
}
JSString *
JSStructuredCloneReader::readString(uint32_t data)
{
uint32_t nchars = data & JS_BITMASK(31);
bool latin1 = data & (1 << 31);
return latin1 ? readStringImpl<Latin1Char>(nchars) : readStringImpl<jschar>(nchars);
}
static uint32_t
TagToV1ArrayType(uint32_t tag)
{
@ -1388,15 +1427,15 @@ JSStructuredCloneReader::startRead(Value *vp)
case SCTAG_REGEXP_OBJECT: {
RegExpFlag flags = RegExpFlag(data);
uint32_t tag2, nchars;
if (!in.readPair(&tag2, &nchars))
uint32_t tag2, stringData;
if (!in.readPair(&tag2, &stringData))
return false;
if (tag2 != SCTAG_STRING) {
JS_ReportErrorNumber(context(), js_GetErrorMessage, nullptr,
JSMSG_SC_BAD_SERIALIZED_DATA, "regexp");
return false;
}
JSString *str = readString(nchars);
JSString *str = readString(stringData);
if (!str)
return false;