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

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: Addressed jbroman feedback. 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"
48 #include "core/dom/MessagePort.h" 49 #include "core/dom/MessagePort.h"
49 #include "core/frame/ImageBitmap.h" 50 #include "core/frame/ImageBitmap.h"
50 #include "platform/SharedBuffer.h" 51 #include "platform/SharedBuffer.h"
51 #include "platform/blob/BlobData.h" 52 #include "platform/blob/BlobData.h"
52 #include "platform/heap/Handle.h" 53 #include "platform/heap/Handle.h"
53 #include "platform/wtf/Assertions.h" 54 #include "platform/wtf/Assertions.h"
54 #include "platform/wtf/ByteOrder.h" 55 #include "platform/wtf/ByteOrder.h"
55 #include "platform/wtf/PtrUtil.h" 56 #include "platform/wtf/PtrUtil.h"
56 #include "platform/wtf/Vector.h" 57 #include "platform/wtf/Vector.h"
58 #include "platform/wtf/dtoa/utils.h"
57 #include "platform/wtf/text/StringBuffer.h" 59 #include "platform/wtf/text/StringBuffer.h"
58 #include "platform/wtf/text/StringHash.h" 60 #include "platform/wtf/text/StringHash.h"
59 61
60 namespace blink { 62 namespace blink {
61 63
62 PassRefPtr<SerializedScriptValue> SerializedScriptValue::Serialize( 64 PassRefPtr<SerializedScriptValue> SerializedScriptValue::Serialize(
63 v8::Isolate* isolate, 65 v8::Isolate* isolate,
64 v8::Local<v8::Value> value, 66 v8::Local<v8::Value> value,
65 const SerializeOptions& options, 67 const SerializeOptions& options,
66 ExceptionState& exception) { 68 ExceptionState& exception) {
(...skipping 15 matching lines...) Expand all
82 84
83 PassRefPtr<SerializedScriptValue> SerializedScriptValue::Create() { 85 PassRefPtr<SerializedScriptValue> SerializedScriptValue::Create() {
84 return AdoptRef(new SerializedScriptValue); 86 return AdoptRef(new SerializedScriptValue);
85 } 87 }
86 88
87 PassRefPtr<SerializedScriptValue> SerializedScriptValue::Create( 89 PassRefPtr<SerializedScriptValue> SerializedScriptValue::Create(
88 const String& data) { 90 const String& data) {
89 return AdoptRef(new SerializedScriptValue(data)); 91 return AdoptRef(new SerializedScriptValue(data));
90 } 92 }
91 93
94 // Versions 16 and below (prior to April 2017) used ntohs() to byte-swap SSV
95 // data when converting it to the wire format. This was a historical accient.
96 //
97 // As IndexedDB stores SSVs to disk indefinitely, we still need to keep around
98 // the code needed to deserialize the old format.
99 inline static bool IsByteSwappedWiredData(const char* data, size_t length) {
100 // TODO(pwnall): Return false early if we're on big-endian hardware. Chromium
101 // doesn't currently support big-endian hardware, and there's no header
102 // exposing endianness to Blink yet. ARCH_CPU_LITTLE_ENDIAN seems promising,
103 // but Blink is not currently allowed to include files from build/.
104
105 // The first SSV version without byte-swapping has two envelopes (Blink, V8),
106 // each of which is at least 2 bytes long.
107 if (length < 4)
108 return true;
109
110 // This code handles the following cases:
111 //
112 // v0 (byte-swapped) - [d, t, ...], t = tag byte, d = first data byte
113 // v1-16 (byte-swapped) - [v, 0xFF, ...], v = version (1 <= v <= 16)
114 // v17+ - [0xFF, v, ...], v = first byte of version varint
115
116 if (static_cast<uint8_t>(data[0]) == kVersionTag) {
117 // The only case where byte-swapped data can have 0xFF in byte zero is
118 // version 0. This can only happen if byte one is a tag (supported in
119 // version 0) that takes in extra data, and the first byte of extra data is
120 // 0xFF. There are 13 such tags, listed below. These tags cannot be used as
121 // version numbers in the Blink-side SSV envelope.
122 //
123 // 35 - 0x23 - # - ImageDataTag
124 // 64 - 0x40 - @ - SparseArrayTag
125 // 68 - 0x44 - D - DateTag
126 // 73 - 0x49 - I - Int32Tag
127 // 78 - 0x4E - N - NumberTag
128 // 82 - 0x52 - R - RegExpTag
129 // 83 - 0x53 - S - StringTag
130 // 85 - 0x55 - U - Uint32Tag
131 // 91 - 0x5B - [ - ArrayTag
132 // 98 - 0x62 - b - BlobTag
133 // 102 - 0x66 - f - FileTag
134 // 108 - 0x6C - l - FileListTag
135 // 123 - 0x7B - { - ObjectTag
136 //
137 // Why we care about version 0:
138 //
139 // IndexedDB stores values using the SSV format. Currently, IndexedDB does
140 // not do any sort of migration, so a value written with a SSV version will
141 // be stored with that version until it is removed via an update or delete.
142 //
143 // IndexedDB was shipped in Chrome 11, which was released on April 27, 2011.
144 // SSV version 1 was added in WebKit r91698, which was shipped in Chrome 14,
145 // which was released on September 16, 2011.
146 static_assert(
147 SerializedScriptValue::kWireFormatVersion != 35 &&
148 SerializedScriptValue::kWireFormatVersion != 64 &&
149 SerializedScriptValue::kWireFormatVersion != 68 &&
150 SerializedScriptValue::kWireFormatVersion != 73 &&
151 SerializedScriptValue::kWireFormatVersion != 78 &&
152 SerializedScriptValue::kWireFormatVersion != 82 &&
153 SerializedScriptValue::kWireFormatVersion != 83 &&
154 SerializedScriptValue::kWireFormatVersion != 85 &&
155 SerializedScriptValue::kWireFormatVersion != 91 &&
156 SerializedScriptValue::kWireFormatVersion != 98 &&
157 SerializedScriptValue::kWireFormatVersion != 102 &&
158 SerializedScriptValue::kWireFormatVersion != 108 &&
159 SerializedScriptValue::kWireFormatVersion != 123,
160 "Using a burned version will prevent us from reading SSV version 0");
161
162 // Fast path until the Blink-side SSV envelope reaches version 35.
163 if (SerializedScriptValue::kWireFormatVersion < 35) {
164 if (static_cast<uint8_t>(data[1]) < 35)
165 return false;
166
167 // TODO(pwnall): Add UMA metric here.
168 return true;
169 }
170
171 // Slower path that would kick in after version 35, assuming we don't remove
172 // support for SSV version 0 by then.
173 static constexpr uint8_t version0Tags[] = {35, 64, 68, 73, 78, 82, 83,
174 85, 91, 98, 102, 108, 123};
175 return std::find(std::begin(version0Tags), std::end(version0Tags),
176 data[1]) != std::end(version0Tags);
177 }
178
179 if (static_cast<uint8_t>(data[1]) == kVersionTag) {
180 // The last SSV format that used byte-flipping was version 16. The version
181 // number is stored (before byte-flipping) after a serialization tag, which
182 // is 0xFF.
183 return static_cast<uint8_t>(data[0]) != kVersionTag;
184 }
185
186 // If kVersionTag isn't in any of the first two bytes, this is SSV version 0,
187 // which was byte-flipped.
jbroman 2017/04/12 18:34:04 change this comment and others to say "swapped" ra
pwnall 2017/04/12 21:37:30 Done. Thanks for catching this! I did a find-repla
188 return true;
189 }
190
92 PassRefPtr<SerializedScriptValue> SerializedScriptValue::Create( 191 PassRefPtr<SerializedScriptValue> SerializedScriptValue::Create(
93 const char* data, 192 const char* data,
94 size_t length) { 193 size_t length) {
95 if (!data) 194 if (!data)
96 return Create(); 195 return Create();
97 196
98 // Decode wire data from big endian to host byte order.
99 DCHECK(!(length % sizeof(UChar))); 197 DCHECK(!(length % sizeof(UChar)));
198 const UChar* src = reinterpret_cast<const UChar*>(data);
100 size_t string_length = length / sizeof(UChar); 199 size_t string_length = length / sizeof(UChar);
101 StringBuffer<UChar> buffer(string_length);
102 const UChar* src = reinterpret_cast<const UChar*>(data);
103 UChar* dst = buffer.Characters();
104 for (size_t i = 0; i < string_length; i++)
105 dst[i] = ntohs(src[i]);
106 200
107 return AdoptRef(new SerializedScriptValue(String::Adopt(buffer))); 201 if (IsByteSwappedWiredData(data, length)) {
202 // Decode wire data from big endian to host byte order.
203 StringBuffer<UChar> buffer(string_length);
204 UChar* dst = buffer.Characters();
205 for (size_t i = 0; i < string_length; ++i)
206 dst[i] = ntohs(src[i]);
207
208 return AdoptRef(new SerializedScriptValue(String::Adopt(buffer)));
209 }
210
211 return AdoptRef(new SerializedScriptValue(String(src, string_length)));
108 } 212 }
109 213
110 SerializedScriptValue::SerializedScriptValue() 214 SerializedScriptValue::SerializedScriptValue()
111 : has_registered_external_allocation_(false), 215 : has_registered_external_allocation_(false),
112 transferables_need_external_allocation_registration_(false) {} 216 transferables_need_external_allocation_registration_(false) {}
113 217
114 SerializedScriptValue::SerializedScriptValue(const String& wire_data) 218 SerializedScriptValue::SerializedScriptValue(const String& wire_data)
115 : has_registered_external_allocation_(false), 219 : has_registered_external_allocation_(false),
116 transferables_need_external_allocation_registration_(false) { 220 transferables_need_external_allocation_registration_(false) {
117 size_t byte_length = wire_data.length() * 2; 221 size_t byte_length = wire_data.length() * 2;
118 data_buffer_.reset(static_cast<uint8_t*>(WTF::Partitions::BufferMalloc( 222 data_buffer_.reset(static_cast<uint8_t*>(WTF::Partitions::BufferMalloc(
119 byte_length, "SerializedScriptValue buffer"))); 223 byte_length, "SerializedScriptValue buffer")));
120 data_buffer_size_ = byte_length; 224 data_buffer_size_ = byte_length;
121 wire_data.CopyTo(reinterpret_cast<UChar*>(data_buffer_.get()), 0, 225 wire_data.CopyTo(reinterpret_cast<UChar*>(data_buffer_.get()), 0,
122 wire_data.length()); 226 wire_data.length());
123 } 227 }
124 228
125 SerializedScriptValue::~SerializedScriptValue() { 229 SerializedScriptValue::~SerializedScriptValue() {
126 // If the allocated memory was not registered before, then this class is 230 // 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 231 // 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. 232 // presence of current v8 context is not guaranteed. Avoid calling v8 then.
129 if (has_registered_external_allocation_) { 233 if (has_registered_external_allocation_) {
130 ASSERT(v8::Isolate::GetCurrent()); 234 ASSERT(v8::Isolate::GetCurrent());
131 v8::Isolate::GetCurrent()->AdjustAmountOfExternalAllocatedMemory( 235 v8::Isolate::GetCurrent()->AdjustAmountOfExternalAllocatedMemory(
132 -static_cast<int64_t>(DataLengthInBytes())); 236 -static_cast<int64_t>(DataLengthInBytes()));
133 } 237 }
134 } 238 }
135 239
136 PassRefPtr<SerializedScriptValue> SerializedScriptValue::NullValue() { 240 PassRefPtr<SerializedScriptValue> SerializedScriptValue::NullValue() {
137 // UChar rather than uint8_t here to get host endian behavior. 241 static const uint8_t kNullData[] = {
138 static const UChar kNullData[] = {0xff09, 0x3000}; 242 kVersionTag, kWireFormatVersion, // Blink envelope
jbroman 2017/04/12 18:34:04 I don't strenuously object, but this level of deta
pwnall 2017/04/12 21:37:30 Done. I'll explain my reasoning for the previous
243 // 13 is v8::ValueSerializer::GetCurrentDataFormatVersion(). Having a
244 // hard-coded version here is better than getting V8's version directly.
245 // This approach lets v8 make drastic changes to its serialization format
246 // without a 3-sided patch, as long as it retains the ability to
247 // deserialize the version number listed here.
248 kVersionTag, 13, // V8 envelope
249 '0', // kNull in v8::internal::SerializationTag
250 0x00}; // Padding to an integral number of UChars
139 return Create(reinterpret_cast<const char*>(kNullData), sizeof(kNullData)); 251 return Create(reinterpret_cast<const char*>(kNullData), sizeof(kNullData));
140 } 252 }
141 253
142 String SerializedScriptValue::ToWireString() const { 254 String SerializedScriptValue::ToWireString() const {
143 // Add the padding '\0', but don't put it in |m_dataBuffer|. 255 // Add the padding '\0', but don't put it in |m_dataBuffer|.
144 // This requires direct use of uninitialized strings, though. 256 // This requires direct use of uninitialized strings, though.
145 UChar* destination; 257 UChar* destination;
146 size_t string_size_bytes = (data_buffer_size_ + 1) & ~1; 258 size_t string_size_bytes = (data_buffer_size_ + 1) & ~1;
147 String wire_string = 259 String wire_string =
148 String::CreateUninitialized(string_size_bytes / 2, destination); 260 String::CreateUninitialized(string_size_bytes / 2, destination);
149 memcpy(destination, data_buffer_.get(), data_buffer_size_); 261 memcpy(destination, data_buffer_.get(), data_buffer_size_);
150 if (string_size_bytes > data_buffer_size_) 262 if (string_size_bytes > data_buffer_size_)
151 reinterpret_cast<char*>(destination)[string_size_bytes - 1] = '\0'; 263 reinterpret_cast<char*>(destination)[string_size_bytes - 1] = '\0';
152 return wire_string; 264 return wire_string;
153 } 265 }
154 266
155 // Convert serialized string to big endian wire data.
156 void SerializedScriptValue::ToWireBytes(Vector<char>& result) const { 267 void SerializedScriptValue::ToWireBytes(Vector<char>& result) const {
157 DCHECK(result.IsEmpty()); 268 DCHECK(result.IsEmpty());
158 269
159 size_t wire_size_bytes = (data_buffer_size_ + 1) & ~1; 270 size_t result_size = (data_buffer_size_ + 1) & ~1;
160 result.Resize(wire_size_bytes); 271 result.Resize(result_size);
272 memcpy(result.Data(), data_buffer_.get(), data_buffer_size_);
161 273
162 const UChar* src = reinterpret_cast<UChar*>(data_buffer_.get()); 274 if (result_size > data_buffer_size_) {
163 UChar* dst = reinterpret_cast<UChar*>(result.Data()); 275 DCHECK_EQ(result_size, data_buffer_size_ + 1);
164 for (size_t i = 0; i < data_buffer_size_ / 2; i++) 276 result[data_buffer_size_] = 0;
165 dst[i] = htons(src[i]); 277 }
166
167 // This is equivalent to swapping the byte order of the two bytes (x, 0),
168 // depending on endianness.
169 if (data_buffer_size_ % 2)
170 dst[wire_size_bytes / 2 - 1] = data_buffer_[data_buffer_size_ - 1] << 8;
171 } 278 }
172 279
173 static void AccumulateArrayBuffersForAllWorlds( 280 static void AccumulateArrayBuffersForAllWorlds(
174 v8::Isolate* isolate, 281 v8::Isolate* isolate,
175 DOMArrayBuffer* object, 282 DOMArrayBuffer* object,
176 Vector<v8::Local<v8::ArrayBuffer>, 4>& buffers) { 283 Vector<v8::Local<v8::ArrayBuffer>, 4>& buffers) {
177 Vector<RefPtr<DOMWrapperWorld>> worlds; 284 Vector<RefPtr<DOMWrapperWorld>> worlds;
178 DOMWrapperWorld::AllWorldsInCurrentThread(worlds); 285 DOMWrapperWorld::AllWorldsInCurrentThread(worlds);
179 for (const auto& world : worlds) { 286 for (const auto& world : worlds) {
180 v8::Local<v8::Object> wrapper = world->DomDataStore().Get(object, isolate); 287 v8::Local<v8::Object> wrapper = world->DomDataStore().Get(object, isolate);
(...skipping 281 matching lines...) Expand 10 before | Expand all | Expand 10 after
462 // Only (re)register allocation cost for transferables if this 569 // Only (re)register allocation cost for transferables if this
463 // SerializedScriptValue has explicitly unregistered them before. 570 // SerializedScriptValue has explicitly unregistered them before.
464 if (array_buffer_contents_array_ && 571 if (array_buffer_contents_array_ &&
465 transferables_need_external_allocation_registration_) { 572 transferables_need_external_allocation_registration_) {
466 for (auto& buffer : *array_buffer_contents_array_) 573 for (auto& buffer : *array_buffer_contents_array_)
467 buffer.RegisterExternalAllocationWithCurrentContext(); 574 buffer.RegisterExternalAllocationWithCurrentContext();
468 } 575 }
469 } 576 }
470 577
471 } // namespace blink 578 } // namespace blink
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698