SerializedScriptValue should use a compact encoding for 8-bit strings.
authorakling@apple.com <akling@apple.com@268f45cc-cd09-0410-ab3c-d52691b4dbfc>
Sat, 10 Oct 2015 15:27:46 +0000 (15:27 +0000)
committerakling@apple.com <akling@apple.com@268f45cc-cd09-0410-ab3c-d52691b4dbfc>
Sat, 10 Oct 2015 15:27:46 +0000 (15:27 +0000)
<https://webkit.org/b/149934>

Reviewed by Antti Koivisto.

Source/WebCore:

We were encoding known 8-bit strings in a 16-bit format when serializing script values.

Extend the format to support 8-bit strings. The 8-bittiness is encoded in the highest bit
of the string length. This is possible while supporting all older formats due to string
lengths >= 0x7FFFFFFF being disallowed.

This patch knocks ~1 MB off of theverge.com, where some ad or tracker or whatever likes to
do a ton of postMessage() business.

* bindings/js/SerializedScriptValue.cpp:
(WebCore::CurrentVersion): Bump the serialization format version. Also updated the grammar
comment to describe the new format. Artistic license applied in description of bitfield.

(WebCore::writeLittleEndianUInt16): Deleted.

(WebCore::CloneSerializer::serialize):
(WebCore::CloneSerializer::write):
(WebCore::CloneDeserializer::deserializeString):
(WebCore::CloneDeserializer::readString):
(WebCore::CloneDeserializer::readStringData): Support 8-bit strings. I kept the string
length limit at UINT_MAX/sizeof(UChar) since the highest bit of the length is no longer
available. Besides, it seems flimsy to support longer strings if they happen to have all
8-bit characters.

LayoutTests:

Update a test to reflect changes to the serialization format.

* fast/storage/serialized-script-value.html:

git-svn-id: https://svn.webkit.org/repository/webkit/trunk@190838 268f45cc-cd09-0410-ab3c-d52691b4dbfc

LayoutTests/ChangeLog
LayoutTests/fast/storage/serialized-script-value.html
Source/WebCore/ChangeLog
Source/WebCore/bindings/js/SerializedScriptValue.cpp

index 6cdd80e..d02f511 100644 (file)
@@ -1,3 +1,14 @@
+2015-10-10  Andreas Kling  <akling@apple.com>
+
+        SerializedScriptValue should use a compact encoding for 8-bit strings.
+        <https://webkit.org/b/149934>
+
+        Reviewed by Antti Koivisto.
+
+        Update a test to reflect changes to the serialization format.
+
+        * fast/storage/serialized-script-value.html:
+
 2015-10-09  Myles C. Maxfield  <mmaxfield@apple.com>
 
         Unreviewed, rolling out r190689
index cbc524b..5bd89fa 100644 (file)
@@ -5,11 +5,37 @@
     </head>
     <body>
         <script>
-/*
-    See LayoutTests/platform/chromium/fast/storage/serialized-script-value.js,
-    upon which this test is based, for the corresponding test of the V8
-    serialization format.
-*/
+
+// Here's a little Q&D helper for future adventurers needing to rebaseline this.
+
+function dec2hex(n) {
+    var s = n.toString(16);
+    if (s.length < 2)
+        return "0x0" + s;
+    return "0x" + s;
+}
+
+function dumpSerialization(obj)
+{
+    var serialized = internals.serializeObject(obj);
+    var bufferView = new Uint8Array(serialized);
+
+    var numbers = new Array();
+    for (var i = 0; i < bufferView.length; ++i) {
+        numbers.push(dec2hex(bufferView[i]));
+    }
+    var str = "";
+    for (var i = 0; i < numbers.length; ++i) {
+        if (i % 8 == 0)
+            str += "\n    ";
+        else
+            str += " ";
+        str += numbers[i];
+        if (i != numbers.length - 1)
+            str += ",";
+    }
+    debug(str);
+}
 
 function testSerialization(obj, values, oldFormat, serializeExceptionValue) {
     _testSerialization(1, obj, values, oldFormat, serializeExceptionValue);
@@ -17,16 +43,13 @@ function testSerialization(obj, values, oldFormat, serializeExceptionValue) {
 
 testSerialization({foo: 'zoo', bar: {baz: 'myNewKey'}},
 [
-    0x05, 0x00, 0x00, 0x00, 0x02, 0x03, 0x00, 0x00,
-    0x00, 0x66, 0x00, 0x6f, 0x00, 0x6f, 0x00, 0x10,
-    0x03, 0x00, 0x00, 0x00, 0x7a, 0x00, 0x6f, 0x00,
-    0x6f, 0x00, 0x03, 0x00, 0x00, 0x00, 0x62, 0x00,
-    0x61, 0x00, 0x72, 0x00, 0x02, 0x03, 0x00, 0x00,
-    0x00, 0x62, 0x00, 0x61, 0x00, 0x7a, 0x00, 0x10,
-    0x08, 0x00, 0x00, 0x00, 0x6d, 0x00, 0x79, 0x00,
-    0x4e, 0x00, 0x65, 0x00, 0x77, 0x00, 0x4b, 0x00,
-    0x65, 0x00, 0x79, 0x00, 0xff, 0xff, 0xff, 0xff,
-    0xff, 0xff, 0xff, 0xff
+    0x06, 0x00, 0x00, 0x00, 0x02, 0x03, 0x00, 0x00,
+    0x80, 0x66, 0x6f, 0x6f, 0x10, 0x03, 0x00, 0x00,
+    0x80, 0x7a, 0x6f, 0x6f, 0x03, 0x00, 0x00, 0x80,
+    0x62, 0x61, 0x72, 0x02, 0x03, 0x00, 0x00, 0x80,
+    0x62, 0x61, 0x7a, 0x10, 0x08, 0x00, 0x00, 0x80,
+    0x6d, 0x79, 0x4e, 0x65, 0x77, 0x4b, 0x65, 0x79,
+    0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff
 ],
 [
     0x03, 0x00, 0x00, 0x00, 0x02, 0x03, 0x00, 0x00,
@@ -43,14 +66,12 @@ testSerialization({foo: 'zoo', bar: {baz: 'myNewKey'}},
 
 testSerialization({foo: 'zoo', bar: 'myNewKey'},
 [
-    0x05, 0x00, 0x00, 0x00, 0x02, 0x03, 0x00, 0x00,
-    0x00, 0x66, 0x00, 0x6f, 0x00, 0x6f, 0x00, 0x10,
-    0x03, 0x00, 0x00, 0x00, 0x7a, 0x00, 0x6f, 0x00,
-    0x6f, 0x00, 0x03, 0x00, 0x00, 0x00, 0x62, 0x00,
-    0x61, 0x00, 0x72, 0x00, 0x10, 0x08, 0x00, 0x00,
-    0x00, 0x6d, 0x00, 0x79, 0x00, 0x4e, 0x00, 0x65,
-    0x00, 0x77, 0x00, 0x4b, 0x00, 0x65, 0x00, 0x79,
-    0x00, 0xff, 0xff, 0xff, 0xff
+    0x06, 0x00, 0x00, 0x00, 0x02, 0x03, 0x00, 0x00,
+    0x80, 0x66, 0x6f, 0x6f, 0x10, 0x03, 0x00, 0x00,
+    0x80, 0x7a, 0x6f, 0x6f, 0x03, 0x00, 0x00, 0x80,
+    0x62, 0x61, 0x72, 0x10, 0x08, 0x00, 0x00, 0x80,
+    0x6d, 0x79, 0x4e, 0x65, 0x77, 0x4b, 0x65, 0x79,
+    0xff, 0xff, 0xff, 0xff
 ],
 [
     0x03, 0x00, 0x00, 0x00, 0x02, 0x03, 0x00, 0x00,
@@ -65,7 +86,7 @@ testSerialization({foo: 'zoo', bar: 'myNewKey'},
 
 testSerialization([],
 [
-    0x05, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00,
+    0x06, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00,
     0x00, 0xff, 0xff, 0xff, 0xff
 ],
 [
@@ -74,10 +95,9 @@ testSerialization([],
 ]);
 testSerialization({foo: "zoo"},
 [
-    0x05, 0x00, 0x00, 0x00, 0x02, 0x03, 0x00, 0x00,
-    0x00, 0x66, 0x00, 0x6f, 0x00, 0x6f, 0x00, 0x10,
-    0x03, 0x00, 0x00, 0x00, 0x7a, 0x00, 0x6f, 0x00,
-    0x6f, 0x00, 0xff, 0xff, 0xff, 0xff
+    0x06, 0x00, 0x00, 0x00, 0x02, 0x03, 0x00, 0x00,
+    0x80, 0x66, 0x6f, 0x6f, 0x10, 0x03, 0x00, 0x00,
+    0x80, 0x7a, 0x6f, 0x6f, 0xff, 0xff, 0xff, 0xff
 ],
 [
     0x03, 0x00, 0x00, 0x00, 0x02, 0x03, 0x00, 0x00,
@@ -87,9 +107,9 @@ testSerialization({foo: "zoo"},
 ]);
 testSerialization({foo: null},
 [
-    0x05, 0x00, 0x00, 0x00, 0x02, 0x03, 0x00, 0x00,
-    0x00, 0x66, 0x00, 0x6f, 0x00, 0x6f, 0x00, 0x04,
-    0xff, 0xff, 0xff, 0xff
+    0x06, 0x00, 0x00, 0x00, 0x02, 0x03, 0x00, 0x00,
+    0x80, 0x66, 0x6f, 0x6f, 0x04, 0xff, 0xff, 0xff,
+    0xff
 ],
 [
     0x03, 0x00, 0x00, 0x00, 0x02, 0x03, 0x00, 0x00,
@@ -99,7 +119,7 @@ testSerialization({foo: null},
 
 testSerialization({},
 [
-    0x05, 0x00, 0x00, 0x00, 0x02, 0xff, 0xff, 0xff,
+    0x06, 0x00, 0x00, 0x00, 0x02, 0xff, 0xff, 0xff,
     0xff
 ],
 [
@@ -109,28 +129,28 @@ testSerialization({},
 
 testSerialization(undefined,
 [
-    0x05, 0x00, 0x00, 0x00, 0x03
+    0x06, 0x00, 0x00, 0x00, 0x03
 ],
 [
     0x03, 0x00, 0x00, 0x00, 0x03
 ]);
 testSerialization(true,
 [
-    0x05, 0x00, 0x00, 0x00, 0x09
+    0x06, 0x00, 0x00, 0x00, 0x09
 ],
 [
     0x03, 0x00, 0x00, 0x00, 0x09
 ]);
 testSerialization(false,
 [
-    0x05, 0x00, 0x00, 0x00, 0x08
+    0x06, 0x00, 0x00, 0x00, 0x08
 ],
 [
     0x03, 0x00, 0x00, 0x00, 0x08
 ]);
 testSerialization(new Array(100),
 [
-    0x05, 0x00, 0x00, 0x00, 0x01, 0x64, 0x00, 0x00,
+    0x06, 0x00, 0x00, 0x00, 0x01, 0x64, 0x00, 0x00,
     0x00, 0xff, 0xff, 0xff, 0xff
 ],
 [
@@ -139,7 +159,7 @@ testSerialization(new Array(100),
 ]);
 testSerialization(10,
 [
-    0x05, 0x00, 0x00, 0x00, 0x05, 0x0a, 0x00, 0x00,
+    0x06, 0x00, 0x00, 0x00, 0x05, 0x0a, 0x00, 0x00,
     0x00
 ],
 [
@@ -148,7 +168,7 @@ testSerialization(10,
 ]);
 testSerialization(-10,
 [
-    0x05, 0x00, 0x00, 0x00, 0x05, 0xf6, 0xff, 0xff,
+    0x06, 0x00, 0x00, 0x00, 0x05, 0xf6, 0xff, 0xff,
     0xff
 ],
 [
@@ -157,7 +177,7 @@ testSerialization(-10,
 ]);
 testSerialization(Math.pow(2,30),
 [
-    0x05, 0x00, 0x00, 0x00, 0x05, 0x00, 0x00, 0x00,
+    0x06, 0x00, 0x00, 0x00, 0x05, 0x00, 0x00, 0x00,
     0x40
 ],
 [
@@ -166,7 +186,7 @@ testSerialization(Math.pow(2,30),
 ]);
 testSerialization(Math.pow(2,55),
 [
-    0x05, 0x00, 0x00, 0x00, 0x0a, 0x00, 0x00, 0x00,
+    0x06, 0x00, 0x00, 0x00, 0x0a, 0x00, 0x00, 0x00,
     0x00, 0x00, 0x00, 0x60, 0x43,
 ],
 [
@@ -175,7 +195,7 @@ testSerialization(Math.pow(2,55),
 ]);
 testSerialization(1.23,
 [
-    0x05, 0x00, 0x00, 0x00, 0x0a, 0xae, 0x47, 0xe1,
+    0x06, 0x00, 0x00, 0x00, 0x0a, 0xae, 0x47, 0xe1,
     0x7a, 0x14, 0xae, 0xf3, 0x3f
 ],
 [
@@ -184,15 +204,15 @@ testSerialization(1.23,
 ]);
 testSerialization("",
 [
-    0x05, 0x00, 0x00, 0x00, 0x11
+    0x06, 0x00, 0x00, 0x00, 0x11
 ],
 [
     0x03, 0x00, 0x00, 0x00, 0x11
 ]);
 testSerialization("abc",
 [
-    0x05, 0x00, 0x00, 0x00, 0x10, 0x03, 0x00, 0x00,
-    0x00, 0x61, 0x00, 0x62, 0x00, 0x63, 0x00
+    0x06, 0x00, 0x00, 0x00, 0x10, 0x03, 0x00, 0x00,
+    0x80, 0x61, 0x62, 0x63
 ],
 [
     0x03, 0x00, 0x00, 0x00, 0x10, 0x03, 0x00, 0x00,
@@ -200,10 +220,10 @@ testSerialization("abc",
 ]);
 testSerialization({integer: 123},
 [
-    0x05, 0x00, 0x00, 0x00, 0x02, 0x07, 0x00, 0x00,
-    0x00, 0x69, 0x00, 0x6e, 0x00, 0x74, 0x00, 0x65,
-    0x00, 0x67, 0x00, 0x65, 0x00, 0x72, 0x00, 0x05,
-    0x7b, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff
+    0x06, 0x00, 0x00, 0x00, 0x02, 0x07, 0x00, 0x00,
+    0x80, 0x69, 0x6e, 0x74, 0x65, 0x67, 0x65, 0x72,
+    0x05, 0x7b, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff,
+    0xff
 ],
 [
     0x03, 0x00, 0x00, 0x00, 0x02, 0x07, 0x00, 0x00,
@@ -213,11 +233,10 @@ testSerialization({integer: 123},
 ]);
 testSerialization({string: "str"},
 [
-    0x05, 0x00, 0x00, 0x00, 0x02, 0x06, 0x00, 0x00,
-    0x00, 0x73, 0x00, 0x74, 0x00, 0x72, 0x00, 0x69,
-    0x00, 0x6e, 0x00, 0x67, 0x00, 0x10, 0x03, 0x00,
-    0x00, 0x00, 0x73, 0x00, 0x74, 0x00, 0x72, 0x00,
-    0xff, 0xff, 0xff, 0xff
+    0x06, 0x00, 0x00, 0x00, 0x02, 0x06, 0x00, 0x00,
+    0x80, 0x73, 0x74, 0x72, 0x69, 0x6e, 0x67, 0x10,
+    0x03, 0x00, 0x00, 0x80, 0x73, 0x74, 0x72, 0xff,
+    0xff, 0xff, 0xff
 ],
 [
     0x03, 0x00, 0x00, 0x00, 0x02, 0x06, 0x00, 0x00,
@@ -228,13 +247,13 @@ testSerialization({string: "str"},
 ]);
 testSerialization({list: [1,2,3]},
 [
-    0x05, 0x00, 0x00, 0x00, 0x02, 0x04, 0x00, 0x00,
-    0x00, 0x6c, 0x00, 0x69, 0x00, 0x73, 0x00, 0x74,
-    0x00, 0x01, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00,
-    0x00, 0x00, 0x07, 0x01, 0x00, 0x00, 0x00, 0x05,
-    0x02, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00,
-    0x05, 0x03, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff,
-    0xff, 0xff, 0xff, 0xff, 0xff
+    0x06, 0x00, 0x00, 0x00, 0x02, 0x04, 0x00, 0x00,
+    0x80, 0x6c, 0x69, 0x73, 0x74, 0x01, 0x03, 0x00,
+    0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x07, 0x01,
+    0x00, 0x00, 0x00, 0x05, 0x02, 0x00, 0x00, 0x00,
+    0x02, 0x00, 0x00, 0x00, 0x05, 0x03, 0x00, 0x00,
+    0x00, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+    0xff
 ],
 [
     0x03, 0x00, 0x00, 0x00, 0x02, 0x04, 0x00, 0x00,
@@ -247,16 +266,15 @@ testSerialization({list: [1,2,3]},
 ]);
 testSerialization(null,
 [
-    0x05, 0x00, 0x00, 0x00, 0x04
+    0x06, 0x00, 0x00, 0x00, 0x04
 ],
 [
     0x03, 0x00, 0x00, 0x00, 0x04
 ]);
 testSerialization(/abc/,
 [
-    0x05, 0x00, 0x00, 0x00, 0x12, 0x03, 0x00, 0x00,
-    0x00, 0x61, 0x00, 0x62, 0x00, 0x63, 0x00, 0x00,
-    0x00, 0x00, 0x00
+    0x06, 0x00, 0x00, 0x00, 0x12, 0x03, 0x00, 0x00,
+    0x80, 0x61, 0x62, 0x63, 0x00, 0x00, 0x00, 0x80
 ],
 [
     0x03, 0x00, 0x00, 0x00, 0x12, 0x03, 0x00, 0x00,
@@ -269,16 +287,13 @@ var outerObject = {inner: innerObject};
 outerObject['outer'] = innerObject;
 testSerialization(outerObject,
 [
-    0x05, 0x00, 0x00, 0x00, 0x02, 0x05, 0x00, 0x00,
-    0x00, 0x69, 0x00, 0x6e, 0x00, 0x6e, 0x00, 0x65,
-    0x00, 0x72, 0x00, 0x02, 0x05, 0x00, 0x00, 0x00,
-    0x68, 0x00, 0x65, 0x00, 0x6c, 0x00, 0x6c, 0x00,
-    0x6f, 0x00, 0x10, 0x05, 0x00, 0x00, 0x00, 0x74,
-    0x00, 0x68, 0x00, 0x65, 0x00, 0x72, 0x00, 0x65,
-    0x00, 0xff, 0xff, 0xff, 0xff, 0x05, 0x00, 0x00,
-    0x00, 0x6f, 0x00, 0x75, 0x00, 0x74, 0x00, 0x65,
-    0x00, 0x72, 0x00, 0x13, 0x01, 0xff, 0xff, 0xff,
-    0xff
+    0x06, 0x00, 0x00, 0x00, 0x02, 0x05, 0x00, 0x00,
+    0x80, 0x69, 0x6e, 0x6e, 0x65, 0x72, 0x02, 0x05,
+    0x00, 0x00, 0x80, 0x68, 0x65, 0x6c, 0x6c, 0x6f,
+    0x10, 0x05, 0x00, 0x00, 0x80, 0x74, 0x68, 0x65,
+    0x72, 0x65, 0xff, 0xff, 0xff, 0xff, 0x05, 0x00,
+    0x00, 0x80, 0x6f, 0x75, 0x74, 0x65, 0x72, 0x13,
+    0x01, 0xff, 0xff, 0xff, 0xff
 ],
 [
     0x03, 0x00, 0x00, 0x00, 0x02, 0x05, 0x00, 0x00,
@@ -294,11 +309,10 @@ testSerialization(outerObject,
 ]);
 testSerialization(innerObject,
 [
-    0x05, 0x00, 0x00, 0x00, 0x02, 0x05, 0x00, 0x00,
-    0x00, 0x68, 0x00, 0x65, 0x00, 0x6c, 0x00, 0x6c,
-    0x00, 0x6f, 0x00, 0x10, 0x05, 0x00, 0x00, 0x00,
-    0x74, 0x00, 0x68, 0x00, 0x65, 0x00, 0x72, 0x00,
-    0x65, 0x00, 0xff, 0xff, 0xff, 0xff
+    0x06, 0x00, 0x00, 0x00, 0x02, 0x05, 0x00, 0x00,
+    0x80, 0x68, 0x65, 0x6c, 0x6c, 0x6f, 0x10, 0x05,
+    0x00, 0x00, 0x80, 0x74, 0x68, 0x65, 0x72, 0x65,
+    0xff, 0xff, 0xff, 0xff
 ],
 [
     0x03, 0x00, 0x00, 0x00, 0x02, 0x05, 0x00, 0x00,
@@ -311,12 +325,12 @@ testSerialization(innerObject,
 var unicodeObject = {a: 'a', u: String.fromCharCode(0x03B1,0x03B2), d: 42};
 testSerialization(unicodeObject,
 [
-    0x05, 0x00, 0x00, 0x00, 0x02, 0x01, 0x00, 0x00,
-    0x00, 0x61, 0x00, 0x10, 0xfe, 0xff, 0xff, 0xff,
-    0x00, 0x01, 0x00, 0x00, 0x00, 0x75, 0x00, 0x10,
-    0x02, 0x00, 0x00, 0x00, 0xb1, 0x03, 0xb2, 0x03,
-    0x01, 0x00, 0x00, 0x00, 0x64, 0x00, 0x05, 0x2a,
-    0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff
+    0x06, 0x00, 0x00, 0x00, 0x02, 0x01, 0x00, 0x00,
+    0x80, 0x61, 0x10, 0xfe, 0xff, 0xff, 0xff, 0x00,
+    0x01, 0x00, 0x00, 0x80, 0x75, 0x10, 0x02, 0x00,
+    0x00, 0x00, 0xb1, 0x03, 0xb2, 0x03, 0x01, 0x00,
+    0x00, 0x80, 0x64, 0x05, 0x2a, 0x00, 0x00, 0x00,
+    0xff, 0xff, 0xff, 0xff
 ],
 [
     0x03, 0x00, 0x00, 0x00, 0x02, 0x01, 0x00, 0x00,
@@ -329,13 +343,12 @@ testSerialization(unicodeObject,
 unicodeObject.a = 'ab';
 testSerialization(unicodeObject,
 [
-    0x05, 0x00, 0x00, 0x00, 0x02, 0x01, 0x00, 0x00,
-    0x00, 0x61, 0x00, 0x10, 0x02, 0x00, 0x00, 0x00,
-    0x61, 0x00, 0x62, 0x00, 0x01, 0x00, 0x00, 0x00,
-    0x75, 0x00, 0x10, 0x02, 0x00, 0x00, 0x00, 0xb1,
-    0x03, 0xb2, 0x03, 0x01, 0x00, 0x00, 0x00, 0x64,
-    0x00, 0x05, 0x2a, 0x00, 0x00, 0x00, 0xff, 0xff,
-    0xff, 0xff
+    0x06, 0x00, 0x00, 0x00, 0x02, 0x01, 0x00, 0x00,
+    0x80, 0x61, 0x10, 0x02, 0x00, 0x00, 0x80, 0x61,
+    0x62, 0x01, 0x00, 0x00, 0x80, 0x75, 0x10, 0x02,
+    0x00, 0x00, 0x00, 0xb1, 0x03, 0xb2, 0x03, 0x01,
+    0x00, 0x00, 0x80, 0x64, 0x05, 0x2a, 0x00, 0x00,
+    0x00, 0xff, 0xff, 0xff, 0xff
 ],
 [
     0x03, 0x00, 0x00, 0x00, 0x02, 0x01, 0x00, 0x00,
@@ -355,14 +368,13 @@ arrayObject['bar'] = 456;
 arrayObject[''] = null;
 testSerialization(arrayObject,
 [
-    0x05, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00,
+    0x06, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00,
     0x00, 0xfd, 0xff, 0xff, 0xff, 0x01, 0x00, 0x00,
-    0x00, 0x61, 0x00, 0x09, 0x01, 0x00, 0x00, 0x00,
-    0x62, 0x00, 0x08, 0x03, 0x00, 0x00, 0x00, 0x66,
-    0x00, 0x6f, 0x00, 0x6f, 0x00, 0x05, 0x7b, 0x00,
-    0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x62, 0x00,
-    0x61, 0x00, 0x72, 0x00, 0x05, 0xc8, 0x01, 0x00,
-    0x00, 0x00, 0x00, 0x00, 0x00, 0x04, 0xff, 0xff,
+    0x80, 0x61, 0x09, 0x01, 0x00, 0x00, 0x80, 0x62,
+    0x08, 0x03, 0x00, 0x00, 0x80, 0x66, 0x6f, 0x6f,
+    0x05, 0x7b, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00,
+    0x80, 0x62, 0x61, 0x72, 0x05, 0xc8, 0x01, 0x00,
+    0x00, 0x00, 0x00, 0x00, 0x80, 0x04, 0xff, 0xff,
     0xff, 0xff
 ]);
 
@@ -370,17 +382,16 @@ arrayObject[0] = 'foo';
 arrayObject[1] = 'bar';
 testSerialization(arrayObject,
 [
-    0x05, 0x00, 0x00, 0x00, 0x01, 0x02, 0x00, 0x00,
+    0x06, 0x00, 0x00, 0x00, 0x01, 0x02, 0x00, 0x00,
     0x00, 0x00, 0x00, 0x00, 0x00, 0x10, 0x03, 0x00,
-    0x00, 0x00, 0x66, 0x00, 0x6f, 0x00, 0x6f, 0x00,
-    0x01, 0x00, 0x00, 0x00, 0x10, 0x03, 0x00, 0x00,
-    0x00, 0x62, 0x00, 0x61, 0x00, 0x72, 0x00, 0xfd,
-    0xff, 0xff, 0xff, 0x01, 0x00, 0x00, 0x00, 0x61,
-    0x00, 0x09, 0x01, 0x00, 0x00, 0x00, 0x62, 0x00,
+    0x00, 0x80, 0x66, 0x6f, 0x6f, 0x01, 0x00, 0x00,
+    0x00, 0x10, 0x03, 0x00, 0x00, 0x80, 0x62, 0x61,
+    0x72, 0xfd, 0xff, 0xff, 0xff, 0x01, 0x00, 0x00,
+    0x80, 0x61, 0x09, 0x01, 0x00, 0x00, 0x80, 0x62,
     0x08, 0xfe, 0xff, 0xff, 0xff, 0x00, 0x05, 0x7b,
     0x00, 0x00, 0x00, 0xfe, 0xff, 0xff, 0xff, 0x01,
     0x05, 0xc8, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00,
-    0x00, 0x04, 0xff, 0xff, 0xff, 0xff
+    0x80, 0x04, 0xff, 0xff, 0xff, 0xff
 ]);
 
 var mapObject = new Map;
@@ -392,17 +403,15 @@ mapObject.expando = {};
 
 testSerialization(mapObject,
 [
-    0x05, 0x00, 0x00, 0x00, 0x1e, 0x07, 0x05, 0x02, 
-    0x00, 0x00, 0x00, 0x0a, 0x00, 0x00, 0x00, 0x00, 
-    0x00, 0x00, 0xf8, 0x3f, 0x02, 0xff, 0xff, 0xff, 
-    0xff, 0x13, 0x00, 0x10, 0x03, 0x00, 0x00, 0x00, 
-    0x66, 0x00, 0x6f, 0x00, 0x6f, 0x00, 0x10, 0x03, 
-    0x00, 0x00, 0x00, 0x62, 0x00, 0x61, 0x00, 0x72, 
-    0x00, 0x1a, 0xfe, 0xff, 0xff, 0xff, 0x01, 0x1f, 
-    0x07, 0x00, 0x00, 0x00, 0x65, 0x00, 0x78, 0x00, 
-    0x70, 0x00, 0x61, 0x00, 0x6e, 0x00, 0x64, 0x00, 
-    0x6f, 0x00, 0x02, 0xff, 0xff, 0xff, 0xff, 0xff, 
-    0xff, 0xff, 0xff
+    0x06, 0x00, 0x00, 0x00, 0x1e, 0x07, 0x05, 0x02,
+    0x00, 0x00, 0x00, 0x0a, 0x00, 0x00, 0x00, 0x00,
+    0x00, 0x00, 0xf8, 0x3f, 0x02, 0xff, 0xff, 0xff,
+    0xff, 0x13, 0x00, 0x10, 0x03, 0x00, 0x00, 0x80,
+    0x66, 0x6f, 0x6f, 0x10, 0x03, 0x00, 0x00, 0x80,
+    0x62, 0x61, 0x72, 0x1a, 0xfe, 0xff, 0xff, 0xff,
+    0x01, 0x1f, 0x07, 0x00, 0x00, 0x80, 0x65, 0x78,
+    0x70, 0x61, 0x6e, 0x64, 0x6f, 0x02, 0xff, 0xff,
+    0xff, 0xff, 0xff, 0xff, 0xff, 0xff
 ]);
 
 var setObject = new Set;
@@ -415,14 +424,13 @@ setObject.expando = {};
 
 testSerialization(setObject,
 [
-    0x05, 0x00, 0x00, 0x00, 0x1d, 0x07, 0x0a, 0x00, 0x00,
-    0x00, 0x00, 0x00, 0x00, 0xf8, 0x3f, 0x13, 0x00, 0x10,
-    0x03, 0x00, 0x00, 0x00, 0x62, 0x00, 0x61, 0x00, 0x72,
-    0x00, 0x1a, 0xfe, 0xff, 0xff, 0xff, 0x00, 0x20, 0x07,
-    0x00, 0x00, 0x00, 0x65, 0x00, 0x78, 0x00, 0x70, 0x00,
-    0x61, 0x00, 0x6e, 0x00, 0x64, 0x00, 0x6f, 0x00, 0x02,
-    0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff
-
+    0x06, 0x00, 0x00, 0x00, 0x1d, 0x07, 0x0a, 0x00,
+    0x00, 0x00, 0x00, 0x00, 0x00, 0xf8, 0x3f, 0x13,
+    0x00, 0x10, 0x03, 0x00, 0x00, 0x80, 0x62, 0x61,
+    0x72, 0x1a, 0xfe, 0xff, 0xff, 0xff, 0x00, 0x20,
+    0x07, 0x00, 0x00, 0x80, 0x65, 0x78, 0x70, 0x61,
+    0x6e, 0x64, 0x6f, 0x02, 0xff, 0xff, 0xff, 0xff,
+    0xff, 0xff, 0xff, 0xff
 ]);
 
 testSerialization(function(){}, [], null, DOMException.DATA_CLONE_ERR);
index 71a3f42..66644df 100644 (file)
@@ -1,3 +1,34 @@
+2015-10-10  Andreas Kling  <akling@apple.com>
+
+        SerializedScriptValue should use a compact encoding for 8-bit strings.
+        <https://webkit.org/b/149934>
+
+        Reviewed by Antti Koivisto.
+
+        We were encoding known 8-bit strings in a 16-bit format when serializing script values.
+
+        Extend the format to support 8-bit strings. The 8-bittiness is encoded in the highest bit
+        of the string length. This is possible while supporting all older formats due to string
+        lengths >= 0x7FFFFFFF being disallowed.
+
+        This patch knocks ~1 MB off of theverge.com, where some ad or tracker or whatever likes to
+        do a ton of postMessage() business.
+
+        * bindings/js/SerializedScriptValue.cpp:
+        (WebCore::CurrentVersion): Bump the serialization format version. Also updated the grammar
+        comment to describe the new format. Artistic license applied in description of bitfield.
+
+        (WebCore::writeLittleEndianUInt16): Deleted.
+
+        (WebCore::CloneSerializer::serialize):
+        (WebCore::CloneSerializer::write):
+        (WebCore::CloneDeserializer::deserializeString):
+        (WebCore::CloneDeserializer::readString):
+        (WebCore::CloneDeserializer::readStringData): Support 8-bit strings. I kept the string
+        length limit at UINT_MAX/sizeof(UChar) since the highest bit of the length is no longer
+        available. Besides, it seems flimsy to support longer strings if they happen to have all
+        8-bit characters.
+
 2015-10-10  Dan Bernstein  <mitz@apple.com>
 
         [iOS] Remove project support for iOS 8
index 088ecac..8210762 100644 (file)
@@ -247,12 +247,16 @@ static unsigned countUsages(CryptoKeyUsage usages)
  * and EmptyStringObjectTag for serialization of Boolean, Number and String objects.
  * Version 4. added support for serializing non-index properties of arrays.
  * Version 5. added support for Map and Set types.
+ * Version 6. added support for 8-bit strings.
  */
-static const unsigned CurrentVersion = 5;
+static const unsigned CurrentVersion = 6;
 static const unsigned TerminatorTag = 0xFFFFFFFF;
 static const unsigned StringPoolTag = 0xFFFFFFFE;
 static const unsigned NonIndexPropertiesTag = 0xFFFFFFFD;
 
+// The high bit of a StringData's length determines the character size.
+static const unsigned StringDataIs8BitFlag = 0x80000000;
+
 /*
  * Object serialization is performed according to the following grammar, all tags
  * are recorded as a single uint8_t.
@@ -318,7 +322,7 @@ static const unsigned NonIndexPropertiesTag = 0xFFFFFFFD;
  *
  * StringData :-
  *      StringPoolTag <cpIndex:IndexType>
- *      (not (TerminatorTag | StringPoolTag))<length:uint32_t><characters:UChar{length}> // Added to constant pool when seen, string length 0xFFFFFFFF is disallowed
+ *      (not (TerminatorTag | StringPoolTag))<is8Bit:uint32_t:1><length:uint32_t:31><characters:CharType{length}> // Added to constant pool when seen, string length 0xFFFFFFFF is disallowed
  *
  * File :-
  *    FileTag FileData
@@ -454,19 +458,6 @@ template <typename T> static bool writeLittleEndian(Vector<uint8_t>& buffer, con
     return true;
 }
 
-static bool writeLittleEndianUInt16(Vector<uint8_t>& buffer, const LChar* values, uint32_t length)
-{
-    if (length > std::numeric_limits<uint32_t>::max() / 2)
-        return false;
-
-    for (unsigned i = 0; i < length; ++i) {
-        buffer.append(values[i]);
-        buffer.append(0);
-    }
-
-    return true;
-}
-
 template <> bool writeLittleEndian<uint8_t>(Vector<uint8_t>& buffer, const uint8_t* values, uint32_t length)
 {
     buffer.append(values, length);
@@ -491,9 +482,11 @@ public:
             return true;
         }
         writeLittleEndian<uint8_t>(out, StringTag);
+        if (s.is8Bit()) {
+            writeLittleEndian(out, s.length() | StringDataIs8BitFlag);
+            return writeLittleEndian(out, s.characters8(), s.length());
+        }
         writeLittleEndian(out, s.length());
-        if (s.is8Bit())
-            return writeLittleEndianUInt16(out, s.characters8(), s.length());
         return writeLittleEndian(out, s.characters16(), s.length());
     }
 
@@ -993,22 +986,21 @@ private:
 
         unsigned length = str.length();
 
-        // This condition is unlikely to happen as they would imply an ~8gb
-        // string but we should guard against it anyway
-        if (length >= StringPoolTag) {
-            fail();
-            return;
-        }
-
         // Guard against overflow
         if (length > (std::numeric_limits<uint32_t>::max() - sizeof(uint32_t)) / sizeof(UChar)) {
             fail();
             return;
         }
 
-        writeLittleEndian<uint32_t>(m_buffer, length);
-        if (!length || str.is8Bit()) {
-            if (!writeLittleEndianUInt16(m_buffer, str.characters8(), length))
+        if (str.is8Bit())
+            writeLittleEndian<uint32_t>(m_buffer, length | StringDataIs8BitFlag);
+        else
+            writeLittleEndian<uint32_t>(m_buffer, length);
+
+        if (!length)
+            return;
+        if (str.is8Bit()) {
+            if (!writeLittleEndian(m_buffer, str.characters8(), length))
                 fail();
             return;
         }
@@ -1477,12 +1469,14 @@ public:
         if (!readLittleEndian(ptr, end, tag) || tag != StringTag)
             return String();
         uint32_t length;
-        if (!readLittleEndian(ptr, end, length) || length >= StringPoolTag)
+        if (!readLittleEndian(ptr, end, length))
             return String();
+        bool is8Bit = length & StringDataIs8BitFlag;
+        length &= ~StringDataIs8BitFlag;
         String str;
-        if (!readString(ptr, end, str, length))
+        if (!readString(ptr, end, str, length, is8Bit))
             return String();
-        return String(str.impl());
+        return str;
     }
 
     static DeserializationResult deserialize(ExecState* exec, JSGlobalObject* globalObject,
@@ -1662,11 +1656,19 @@ private:
         return read(i);
     }
 
-    static bool readString(const uint8_t*& ptr, const uint8_t* end, String& str, unsigned length)
+    static bool readString(const uint8_t*& ptr, const uint8_t* end, String& str, unsigned length, bool is8Bit)
     {
         if (length >= std::numeric_limits<int32_t>::max() / sizeof(UChar))
             return false;
 
+        if (is8Bit) {
+            if ((end - ptr) < static_cast<int>(length))
+                return false;
+            str = String(reinterpret_cast<const LChar*>(ptr), length);
+            ptr += length;
+            return true;
+        }
+
         unsigned size = length * sizeof(UChar);
         if ((end - ptr) < static_cast<int>(size))
             return false;
@@ -1717,8 +1719,10 @@ private:
             cachedString = CachedStringRef(&m_constantPool, index);
             return true;
         }
+        bool is8Bit = length & StringDataIs8BitFlag;
+        length &= ~StringDataIs8BitFlag;
         String str;
-        if (!readString(m_ptr, m_end, str, length)) {
+        if (!readString(m_ptr, m_end, str, length, is8Bit)) {
             fail();
             return false;
         }