Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(121)

Side by Side Diff: third_party/WebKit/Source/bindings/core/v8/SerializedScriptValue.cpp

Issue 2803593005: Avoid unnecessary byte-swapping in blink's SerializedScriptValue. (Closed)
Patch Set: Correct handling for SSV version 0. Created 3 years, 8 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch
OLDNEW
1 /* 1 /*
2 * Copyright (C) 2010 Google Inc. All rights reserved. 2 * Copyright (C) 2010 Google Inc. All rights reserved.
3 * 3 *
4 * Redistribution and use in source and binary forms, with or without 4 * Redistribution and use in source and binary forms, with or without
5 * modification, are permitted provided that the following conditions are 5 * modification, are permitted provided that the following conditions are
6 * met: 6 * met:
7 * 7 *
8 * * Redistributions of source code must retain the above copyright 8 * * Redistributions of source code must retain the above copyright
9 * notice, this list of conditions and the following disclaimer. 9 * notice, this list of conditions and the following disclaimer.
10 * * Redistributions in binary form must reproduce the above 10 * * Redistributions in binary form must reproduce the above
(...skipping 17 matching lines...) Expand all
28 * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 28 * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
29 */ 29 */
30 30
31 #include "bindings/core/v8/SerializedScriptValue.h" 31 #include "bindings/core/v8/SerializedScriptValue.h"
32 32
33 #include <memory> 33 #include <memory>
34 #include "bindings/core/v8/DOMDataStore.h" 34 #include "bindings/core/v8/DOMDataStore.h"
35 #include "bindings/core/v8/DOMWrapperWorld.h" 35 #include "bindings/core/v8/DOMWrapperWorld.h"
36 #include "bindings/core/v8/ExceptionState.h" 36 #include "bindings/core/v8/ExceptionState.h"
37 #include "bindings/core/v8/ScriptState.h" 37 #include "bindings/core/v8/ScriptState.h"
38 #include "bindings/core/v8/SerializationTag.h"
38 #include "bindings/core/v8/SerializedScriptValueFactory.h" 39 #include "bindings/core/v8/SerializedScriptValueFactory.h"
39 #include "bindings/core/v8/Transferables.h" 40 #include "bindings/core/v8/Transferables.h"
40 #include "bindings/core/v8/V8ArrayBuffer.h" 41 #include "bindings/core/v8/V8ArrayBuffer.h"
41 #include "bindings/core/v8/V8ImageBitmap.h" 42 #include "bindings/core/v8/V8ImageBitmap.h"
42 #include "bindings/core/v8/V8MessagePort.h" 43 #include "bindings/core/v8/V8MessagePort.h"
43 #include "bindings/core/v8/V8OffscreenCanvas.h" 44 #include "bindings/core/v8/V8OffscreenCanvas.h"
44 #include "bindings/core/v8/V8SharedArrayBuffer.h" 45 #include "bindings/core/v8/V8SharedArrayBuffer.h"
45 #include "core/dom/DOMArrayBuffer.h" 46 #include "core/dom/DOMArrayBuffer.h"
46 #include "core/dom/DOMSharedArrayBuffer.h" 47 #include "core/dom/DOMSharedArrayBuffer.h"
47 #include "core/dom/ExceptionCode.h" 48 #include "core/dom/ExceptionCode.h"
(...skipping 34 matching lines...) Expand 10 before | Expand all | Expand 10 after
82 83
83 PassRefPtr<SerializedScriptValue> SerializedScriptValue::create() { 84 PassRefPtr<SerializedScriptValue> SerializedScriptValue::create() {
84 return adoptRef(new SerializedScriptValue); 85 return adoptRef(new SerializedScriptValue);
85 } 86 }
86 87
87 PassRefPtr<SerializedScriptValue> SerializedScriptValue::create( 88 PassRefPtr<SerializedScriptValue> SerializedScriptValue::create(
88 const String& data) { 89 const String& data) {
89 return adoptRef(new SerializedScriptValue(data)); 90 return adoptRef(new SerializedScriptValue(data));
90 } 91 }
91 92
93 // Versions 16 and below (prior to April 2017) used ntohs() to byte-flip SSV
jsbell 2017/04/07 23:40:34 nit: "byte flip" or "byte swap"? "swap" seems more
pwnall 2017/04/08 01:09:08 Done. After reading "byte-swap", my brain instantl
94 // data when converting it to the wire format. This was a historical accient.
95 //
96 // As IndexedDB stores SSVs to disk indefinitely, we still need to keep around
97 // the code needed to deserialize the old format.
98 inline static bool isByteSwappedWiredData(const char* data, size_t length) {
99 // TODO(pwnall): Bail early if we're on big-endian hardware. Chromium doesn't
100 // currently support big-endian hardware, and there's no header exposing
101 // endianness yet.
jsbell 2017/04/07 23:40:34 ARCH_CPU_LITTLE_ENDIAN seems used in several place
pwnall 2017/04/08 01:09:07 Sadly, it seems like we're not allowed to use it i
102
103 // The first SSV version without byte-flipping has two envelopes (Blink, V8),
104 // each of which is at least 2 bytes long.
105 if (length < 4)
106 return true;
107
108 if (static_cast<uint8_t>(data[0]) == VersionTag) {
jsbell 2017/04/07 23:40:34 Maybe precede this with a list of the different ca
pwnall 2017/04/08 01:09:07 Done.
109 // The only case where byte-flipped data can have 0xFF in byte zero is
110 // serialization version 0. This can only happen if byte one is a tag
111 // (supported in version 0) that takes in extra data, and the first byte of
112 // extra data is 0xFF. There are 13 such tags, listed below. These tags
113 // cannot be used as version numbers in the Blink-side SSV envelope.
jsbell 2017/04/07 23:40:34 nit: remove extra leading space
pwnall 2017/04/08 01:09:07 Done.
114 //
115 // 35 - 0x23 - # - ImageDataTag
116 // 64 - 0x40 - @ - SparseArrayTag
117 // 68 - 0x44 - D - DateTag
118 // 73 - 0x49 - I - Int32Tag
119 // 78 - 0x4E - N - NumberTag
120 // 82 - 0x52 - R - RegExpTag
121 // 83 - 0x53 - S - StringTag
122 // 85 - 0x55 - U - Uint32Tag
123 // 91 - 0x5B - [ - ArrayTag
124 // 98 - 0x62 - b - BlobTag
125 // 102 - 0x66 - f - FileTag
126 // 108 - 0x6C - l - FileListTag
127 // 123 - 0x7B - { - ObjectTag
128 //
129 // Why we care about version 0:
130 //
131 // IndexedDB stores values using the SSV format. Currently, IndexedDB does
132 // not do any sort of migration, so a value written with a SSV version will
133 // be stored with that version until it is removed via an update or delete.
134 //
135 // IndexedDB was shipped in Chrome 11, which was released on April 27, 2011.
136 // SSV version 1 was added in WebKit r91698, which was shipped in Chrome 14,
137 // which was released on September 16, 2011.
138
139 // Fast path until the Blink-side SSV envelope reaches version 35.
140 if (SerializedScriptValue::wireFormatVersion < 35) {
141 if (static_cast<uint8_t>(data[1]) < 35)
142 return false;
143
144 // TODO(pwnall): Add UMA metric here.
145 return true;
146 }
147
148 // Slower path that would kick in after version 35, assuming we don't remove
jbroman 2017/04/11 15:01:47 This is dead code. Is it actually useful to have i
pwnall 2017/04/11 19:10:55 I felt bad leaving a static_assert that'd force a
149 // support for SSV version 0 by then.
150
151 static uint8_t version0Tags[] = {35, 64, 68, 73, 78, 82, 83,
jbroman 2017/04/11 15:01:47 const
pwnall 2017/04/11 19:10:55 Done.
152 85, 91, 98, 102, 108, 123};
153
154 uint8_t maybeVersion0Tag = static_cast<uint8_t>(data[1]);
155 for (size_t i = 0; i < 13; ++i) {
jsbell 2017/04/07 23:40:34 ARRAY_SIZE(version0Tags) (Also, could binary sear
pwnall 2017/04/08 01:09:08 Done.
jbroman 2017/04/11 15:01:47 This loop is equivalent to: return std::count(std
pwnall 2017/04/11 19:10:55 Done.
156 if (maybeVersion0Tag == version0Tags[i])
157 return true;
158 }
159 return false;
160 }
161
162 if (static_cast<uint8_t>(data[1]) == VersionTag) {
163 // The last SSV format that used byte-flipping was version 16. The version
164 // number is stored (before byte-flipping) after a serialization tag, which
165 // is 0xFF.
166 return static_cast<uint8_t>(data[0]) != VersionTag;
167 }
168
169 // If VersionTag isn't in any of the first two bytes, this is SSV version 0,
170 // which was byte-flipped.
171 return true;
172 }
173
92 PassRefPtr<SerializedScriptValue> SerializedScriptValue::create( 174 PassRefPtr<SerializedScriptValue> SerializedScriptValue::create(
93 const char* data, 175 const char* data,
94 size_t length) { 176 size_t length) {
95 if (!data) 177 if (!data)
96 return create(); 178 return create();
97 179
98 // Decode wire data from big endian to host byte order.
99 DCHECK(!(length % sizeof(UChar))); 180 DCHECK(!(length % sizeof(UChar)));
181 const UChar* src = reinterpret_cast<const UChar*>(data);
100 size_t stringLength = length / sizeof(UChar); 182 size_t stringLength = length / sizeof(UChar);
101 StringBuffer<UChar> buffer(stringLength);
102 const UChar* src = reinterpret_cast<const UChar*>(data);
103 UChar* dst = buffer.characters();
104 for (size_t i = 0; i < stringLength; i++)
105 dst[i] = ntohs(src[i]);
106 183
107 return adoptRef(new SerializedScriptValue(String::adopt(buffer))); 184 if (isByteSwappedWiredData(data, length)) {
185 // Decode wire data from big endian to host byte order.
186 StringBuffer<UChar> buffer(stringLength);
187 UChar* dst = buffer.characters();
188 for (size_t i = 0; i < stringLength; ++i)
189 dst[i] = ntohs(src[i]);
190
191 return adoptRef(new SerializedScriptValue(String::adopt(buffer)));
192 }
193
194 return adoptRef(new SerializedScriptValue(String(src, stringLength)));
108 } 195 }
109 196
110 SerializedScriptValue::SerializedScriptValue() 197 SerializedScriptValue::SerializedScriptValue()
111 : m_hasRegisteredExternalAllocation(false), 198 : m_hasRegisteredExternalAllocation(false),
112 m_transferablesNeedExternalAllocationRegistration(false) {} 199 m_transferablesNeedExternalAllocationRegistration(false) {}
113 200
114 SerializedScriptValue::SerializedScriptValue(const String& wireData) 201 SerializedScriptValue::SerializedScriptValue(const String& wireData)
115 : m_hasRegisteredExternalAllocation(false), 202 : m_hasRegisteredExternalAllocation(false),
116 m_transferablesNeedExternalAllocationRegistration(false) { 203 m_transferablesNeedExternalAllocationRegistration(false) {
117 size_t byteLength = wireData.length() * 2; 204 size_t byteLength = wireData.length() * 2;
118 m_dataBuffer.reset(static_cast<uint8_t*>(WTF::Partitions::bufferMalloc( 205 m_dataBuffer.reset(static_cast<uint8_t*>(WTF::Partitions::bufferMalloc(
119 byteLength, "SerializedScriptValue buffer"))); 206 byteLength, "SerializedScriptValue buffer")));
120 m_dataBufferSize = byteLength; 207 m_dataBufferSize = byteLength;
121 wireData.copyTo(reinterpret_cast<UChar*>(m_dataBuffer.get()), 0, 208 wireData.copyTo(reinterpret_cast<UChar*>(m_dataBuffer.get()), 0,
122 wireData.length()); 209 wireData.length());
123 } 210 }
124 211
125 SerializedScriptValue::~SerializedScriptValue() { 212 SerializedScriptValue::~SerializedScriptValue() {
126 // If the allocated memory was not registered before, then this class is 213 // If the allocated memory was not registered before, then this class is
127 // likely used in a context other than Worker's onmessage environment and the 214 // likely used in a context other than Worker's onmessage environment and the
128 // presence of current v8 context is not guaranteed. Avoid calling v8 then. 215 // presence of current v8 context is not guaranteed. Avoid calling v8 then.
129 if (m_hasRegisteredExternalAllocation) { 216 if (m_hasRegisteredExternalAllocation) {
130 ASSERT(v8::Isolate::GetCurrent()); 217 ASSERT(v8::Isolate::GetCurrent());
131 v8::Isolate::GetCurrent()->AdjustAmountOfExternalAllocatedMemory( 218 v8::Isolate::GetCurrent()->AdjustAmountOfExternalAllocatedMemory(
132 -static_cast<int64_t>(dataLengthInBytes())); 219 -static_cast<int64_t>(dataLengthInBytes()));
133 } 220 }
134 } 221 }
135 222
136 PassRefPtr<SerializedScriptValue> SerializedScriptValue::nullValue() { 223 PassRefPtr<SerializedScriptValue> SerializedScriptValue::nullValue() {
137 // UChar rather than uint8_t here to get host endian behavior. 224 static const uint8_t kNullData[] = {0xff, 17, 0xff, 13, '0', 0x00};
jsbell 2017/04/07 23:40:34 Maybe document what this is, while we're here? Is
pwnall 2017/04/08 01:09:07 Done. kNull is not exported from V8, so I couldn'
138 static const UChar kNullData[] = {0xff09, 0x3000};
139 return create(reinterpret_cast<const char*>(kNullData), sizeof(kNullData)); 225 return create(reinterpret_cast<const char*>(kNullData), sizeof(kNullData));
140 } 226 }
141 227
142 String SerializedScriptValue::toWireString() const { 228 String SerializedScriptValue::toWireString() const {
143 // Add the padding '\0', but don't put it in |m_dataBuffer|. 229 // Add the padding '\0', but don't put it in |m_dataBuffer|.
144 // This requires direct use of uninitialized strings, though. 230 // This requires direct use of uninitialized strings, though.
145 UChar* destination; 231 UChar* destination;
146 size_t stringSizeBytes = (m_dataBufferSize + 1) & ~1; 232 size_t stringSizeBytes = (m_dataBufferSize + 1) & ~1;
147 String wireString = 233 String wireString =
148 String::createUninitialized(stringSizeBytes / 2, destination); 234 String::createUninitialized(stringSizeBytes / 2, destination);
149 memcpy(destination, m_dataBuffer.get(), m_dataBufferSize); 235 memcpy(destination, m_dataBuffer.get(), m_dataBufferSize);
150 if (stringSizeBytes > m_dataBufferSize) 236 if (stringSizeBytes > m_dataBufferSize)
151 reinterpret_cast<char*>(destination)[stringSizeBytes - 1] = '\0'; 237 reinterpret_cast<char*>(destination)[stringSizeBytes - 1] = '\0';
152 return wireString; 238 return wireString;
153 } 239 }
154 240
155 // Convert serialized string to big endian wire data.
156 void SerializedScriptValue::toWireBytes(Vector<char>& result) const { 241 void SerializedScriptValue::toWireBytes(Vector<char>& result) const {
157 DCHECK(result.isEmpty()); 242 DCHECK(result.isEmpty());
158 243
159 size_t wireSizeBytes = (m_dataBufferSize + 1) & ~1; 244 size_t resultSize = (m_dataBufferSize + 1) & ~1;
jsbell 2017/04/07 23:40:34 How feasible is follow-on work to not expose the r
pwnall 2017/04/08 01:09:08 Seems fairly feasible to me. There aren't a lot of
160 result.resize(wireSizeBytes); 245 result.resize(resultSize);
246 memcpy(result.data(), m_dataBuffer.get(), m_dataBufferSize);
161 247
162 const UChar* src = reinterpret_cast<UChar*>(m_dataBuffer.get()); 248 if (resultSize > m_dataBufferSize) {
163 UChar* dst = reinterpret_cast<UChar*>(result.data()); 249 DCHECK_EQ(resultSize, m_dataBufferSize + 1);
164 for (size_t i = 0; i < m_dataBufferSize / 2; i++) 250 result[m_dataBufferSize] = 0;
165 dst[i] = htons(src[i]); 251 }
166
167 // This is equivalent to swapping the byte order of the two bytes (x, 0),
168 // depending on endianness.
169 if (m_dataBufferSize % 2)
170 dst[wireSizeBytes / 2 - 1] = m_dataBuffer[m_dataBufferSize - 1] << 8;
171 } 252 }
172 253
173 static void accumulateArrayBuffersForAllWorlds( 254 static void accumulateArrayBuffersForAllWorlds(
174 v8::Isolate* isolate, 255 v8::Isolate* isolate,
175 DOMArrayBuffer* object, 256 DOMArrayBuffer* object,
176 Vector<v8::Local<v8::ArrayBuffer>, 4>& buffers) { 257 Vector<v8::Local<v8::ArrayBuffer>, 4>& buffers) {
177 Vector<RefPtr<DOMWrapperWorld>> worlds; 258 Vector<RefPtr<DOMWrapperWorld>> worlds;
178 DOMWrapperWorld::allWorldsInCurrentThread(worlds); 259 DOMWrapperWorld::allWorldsInCurrentThread(worlds);
179 for (const auto& world : worlds) { 260 for (const auto& world : worlds) {
180 v8::Local<v8::Object> wrapper = world->domDataStore().get(object, isolate); 261 v8::Local<v8::Object> wrapper = world->domDataStore().get(object, isolate);
(...skipping 280 matching lines...) Expand 10 before | Expand all | Expand 10 after
461 // Only (re)register allocation cost for transferables if this 542 // Only (re)register allocation cost for transferables if this
462 // SerializedScriptValue has explicitly unregistered them before. 543 // SerializedScriptValue has explicitly unregistered them before.
463 if (m_arrayBufferContentsArray && 544 if (m_arrayBufferContentsArray &&
464 m_transferablesNeedExternalAllocationRegistration) { 545 m_transferablesNeedExternalAllocationRegistration) {
465 for (auto& buffer : *m_arrayBufferContentsArray) 546 for (auto& buffer : *m_arrayBufferContentsArray)
466 buffer.registerExternalAllocationWithCurrentContext(); 547 buffer.registerExternalAllocationWithCurrentContext();
467 } 548 }
468 } 549 }
469 550
470 } // namespace blink 551 } // namespace blink
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698