OLD | NEW |
1 // Copyright 2014 The Chromium Authors. All rights reserved. | 1 // Copyright 2014 The Chromium Authors. All rights reserved. |
2 // Use of this source code is governed by a BSD-style license that can be | 2 // Use of this source code is governed by a BSD-style license that can be |
3 // found in the LICENSE file. | 3 // found in the LICENSE file. |
4 | 4 |
5 #include "bindings/core/v8/ScriptValueSerializer.h" | 5 #include "bindings/core/v8/ScriptValueSerializer.h" |
6 | 6 |
7 #include "bindings/core/v8/Transferables.h" | 7 #include "bindings/core/v8/Transferables.h" |
8 #include "bindings/core/v8/V8ArrayBuffer.h" | 8 #include "bindings/core/v8/V8ArrayBuffer.h" |
9 #include "bindings/core/v8/V8ArrayBufferView.h" | 9 #include "bindings/core/v8/V8ArrayBufferView.h" |
10 #include "bindings/core/v8/V8Blob.h" | 10 #include "bindings/core/v8/V8Blob.h" |
(...skipping 58 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
69 const int maxDepth = 20000; | 69 const int maxDepth = 20000; |
70 | 70 |
71 bool shouldCheckForCycles(int depth) { | 71 bool shouldCheckForCycles(int depth) { |
72 ASSERT(depth >= 0); | 72 ASSERT(depth >= 0); |
73 // Since we are not required to spot the cycle as soon as it | 73 // Since we are not required to spot the cycle as soon as it |
74 // happens we can check for cycles only when the current depth | 74 // happens we can check for cycles only when the current depth |
75 // is a power of two. | 75 // is a power of two. |
76 return !(depth & (depth - 1)); | 76 return !(depth & (depth - 1)); |
77 } | 77 } |
78 | 78 |
79 // Returns true if the provided object is to be considered a 'host object', as u
sed in the | 79 // Returns true if the provided object is to be considered a 'host object', as |
80 // HTML5 structured clone algorithm. | 80 // used in the HTML5 structured clone algorithm. |
81 bool isHostObject(v8::Local<v8::Object> object) { | 81 bool isHostObject(v8::Local<v8::Object> object) { |
82 // If the object has any internal fields, then we won't be able to serialize o
r deserialize | 82 // If the object has any internal fields, then we won't be able to serialize |
83 // them; conveniently, this is also a quick way to detect DOM wrapper objects,
because | 83 // or deserialize them; conveniently, this is also a quick way to detect DOM |
84 // the mechanism for these relies on data stored in these fields. We should | 84 // wrapper objects, because the mechanism for these relies on data stored in |
85 // catch external array data as a special case. | 85 // these fields. We should catch external array data as a special case. |
86 return object->InternalFieldCount(); | 86 return object->InternalFieldCount(); |
87 } | 87 } |
88 | 88 |
89 } // namespace | 89 } // namespace |
90 | 90 |
91 void SerializedScriptValueWriter::writeUndefined() { | 91 void SerializedScriptValueWriter::writeUndefined() { |
92 append(UndefinedTag); | 92 append(UndefinedTag); |
93 } | 93 } |
94 | 94 |
95 void SerializedScriptValueWriter::writeNull() { | 95 void SerializedScriptValueWriter::writeNull() { |
(...skipping 472 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
568 hasProperty = v8CallBoolean(composite()->HasRealIndexedProperty( | 568 hasProperty = v8CallBoolean(composite()->HasRealIndexedProperty( |
569 serializer.context(), propertyName.As<v8::Uint32>()->Value())); | 569 serializer.context(), propertyName.As<v8::Uint32>()->Value())); |
570 } | 570 } |
571 if (StateBase* newState = serializer.checkException(this)) | 571 if (StateBase* newState = serializer.checkException(this)) |
572 return newState; | 572 return newState; |
573 if (!hasProperty) { | 573 if (!hasProperty) { |
574 ++m_index; | 574 ++m_index; |
575 continue; | 575 continue; |
576 } | 576 } |
577 | 577 |
578 // |propertyName| is v8::String or v8::Uint32, so its serialization cannot b
e recursive. | 578 // |propertyName| is v8::String or v8::Uint32, so its serialization cannot |
| 579 // be recursive. |
579 serializer.doSerialize(propertyName, nullptr); | 580 serializer.doSerialize(propertyName, nullptr); |
580 | 581 |
581 v8::Local<v8::Value> value; | 582 v8::Local<v8::Value> value; |
582 if (!composite()->Get(serializer.context(), propertyName).ToLocal(&value)) | 583 if (!composite()->Get(serializer.context(), propertyName).ToLocal(&value)) |
583 return serializer.handleError( | 584 return serializer.handleError( |
584 Status::JSException, | 585 Status::JSException, |
585 "Failed to get a property while cloning an object.", this); | 586 "Failed to get a property while cloning an object.", this); |
586 ++m_index; | 587 ++m_index; |
587 ++m_numSerializedProperties; | 588 ++m_numSerializedProperties; |
588 // If we return early here, it's either because we have pushed a new state o
nto the | 589 // If we return early here, it's either because we have pushed a new state |
589 // serialization state stack or because we have encountered an error (and in
both cases | 590 // onto the serialization state stack or because we have encountered an |
590 // we are unwinding the native stack). | 591 // error (and in both cases we are unwinding the native stack). |
591 if (StateBase* newState = serializer.doSerialize(value, this)) | 592 if (StateBase* newState = serializer.doSerialize(value, this)) |
592 return newState; | 593 return newState; |
593 } | 594 } |
594 return objectDone(m_numSerializedProperties, serializer); | 595 return objectDone(m_numSerializedProperties, serializer); |
595 } | 596 } |
596 | 597 |
597 ScriptValueSerializer::StateBase* ScriptValueSerializer::ObjectState::advance( | 598 ScriptValueSerializer::StateBase* ScriptValueSerializer::ObjectState::advance( |
598 ScriptValueSerializer& serializer) { | 599 ScriptValueSerializer& serializer) { |
599 if (m_propertyNames.IsEmpty()) { | 600 if (m_propertyNames.IsEmpty()) { |
600 if (!composite() | 601 if (!composite() |
(...skipping 361 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
962 return nullptr; | 963 return nullptr; |
963 } | 964 } |
964 if (object->IsRegExp()) { | 965 if (object->IsRegExp()) { |
965 writeRegExp(object); | 966 writeRegExp(object); |
966 return nullptr; | 967 return nullptr; |
967 } | 968 } |
968 if (V8CompositorProxy::hasInstance(object, isolate())) { | 969 if (V8CompositorProxy::hasInstance(object, isolate())) { |
969 return writeCompositorProxy(object, next); | 970 return writeCompositorProxy(object, next); |
970 } | 971 } |
971 | 972 |
972 // Since IsNativeError is expensive, this check should always be the last chec
k. | 973 // Since IsNativeError is expensive, this check should always be the last |
| 974 // check. |
973 if (isHostObject(object) || object->IsCallable() || object->IsNativeError()) { | 975 if (isHostObject(object) || object->IsCallable() || object->IsNativeError()) { |
974 return handleError(Status::DataCloneError, "An object could not be cloned.", | 976 return handleError(Status::DataCloneError, "An object could not be cloned.", |
975 next); | 977 next); |
976 } | 978 } |
977 | 979 |
978 return startObjectState(object, next); | 980 return startObjectState(object, next); |
979 } | 981 } |
980 | 982 |
981 ScriptValueSerializer::StateBase* ScriptValueSerializer::doSerializeArrayBuffer( | 983 ScriptValueSerializer::StateBase* ScriptValueSerializer::doSerializeArrayBuffer( |
982 v8::Local<v8::Value> arrayBuffer, | 984 v8::Local<v8::Value> arrayBuffer, |
(...skipping 241 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
1224 v8::Local<v8::Value> underlyingBuffer = | 1226 v8::Local<v8::Value> underlyingBuffer = |
1225 toV8(arrayBufferView->bufferBase(), m_scriptState->context()->Global(), | 1227 toV8(arrayBufferView->bufferBase(), m_scriptState->context()->Global(), |
1226 isolate()); | 1228 isolate()); |
1227 if (underlyingBuffer.IsEmpty()) | 1229 if (underlyingBuffer.IsEmpty()) |
1228 return handleError(Status::DataCloneError, | 1230 return handleError(Status::DataCloneError, |
1229 "An ArrayBuffer could not be cloned.", next); | 1231 "An ArrayBuffer could not be cloned.", next); |
1230 StateBase* stateOut = doSerializeArrayBuffer(underlyingBuffer, next); | 1232 StateBase* stateOut = doSerializeArrayBuffer(underlyingBuffer, next); |
1231 if (stateOut) | 1233 if (stateOut) |
1232 return stateOut; | 1234 return stateOut; |
1233 m_writer.writeArrayBufferView(*arrayBufferView); | 1235 m_writer.writeArrayBufferView(*arrayBufferView); |
1234 // This should be safe: we serialize something that we know to be a wrapper (s
ee | 1236 // This should be safe: we serialize something that we know to be a wrapper |
1235 // the toV8 call above), so the call to doSerializeArrayBuffer should neither | 1237 // (see the toV8 call above), so the call to doSerializeArrayBuffer should |
1236 // cause the system stack to overflow nor should it have potential to reach | 1238 // neither cause the system stack to overflow nor should it have potential to |
1237 // this ArrayBufferView again. | 1239 // reach this ArrayBufferView again. |
1238 // | 1240 // |
1239 // We do need to grey the underlying buffer before we grey its view, however; | 1241 // We do need to grey the underlying buffer before we grey its view, however; |
1240 // ArrayBuffers may be shared, so they need to be given reference IDs, and an | 1242 // ArrayBuffers may be shared, so they need to be given reference IDs, and an |
1241 // ArrayBufferView cannot be constructed without a corresponding ArrayBuffer | 1243 // ArrayBufferView cannot be constructed without a corresponding ArrayBuffer |
1242 // (or without an additional tag that would allow us to do two-stage construct
ion | 1244 // (or without an additional tag that would allow us to do two-stage |
1243 // like we do for Objects and Arrays). | 1245 // construction like we do for Objects and Arrays). |
1244 greyObject(object); | 1246 greyObject(object); |
1245 return nullptr; | 1247 return nullptr; |
1246 } | 1248 } |
1247 | 1249 |
1248 ScriptValueSerializer::StateBase* | 1250 ScriptValueSerializer::StateBase* |
1249 ScriptValueSerializer::writeWasmCompiledModule(v8::Local<v8::Object> object, | 1251 ScriptValueSerializer::writeWasmCompiledModule(v8::Local<v8::Object> object, |
1250 StateBase* next) { | 1252 StateBase* next) { |
1251 CHECK(RuntimeEnabledFeatures::webAssemblySerializationEnabled()); | 1253 CHECK(RuntimeEnabledFeatures::webAssemblySerializationEnabled()); |
1252 // TODO (mtrofin): explore mechanism avoiding data copying / buffer resizing. | 1254 // TODO (mtrofin): explore mechanism avoiding data copying / buffer resizing. |
1253 v8::Local<v8::WasmCompiledModule> wasmModule = | 1255 v8::Local<v8::WasmCompiledModule> wasmModule = |
(...skipping 59 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
1313 V8SharedArrayBuffer::toImpl(value.As<v8::Object>()); | 1315 V8SharedArrayBuffer::toImpl(value.As<v8::Object>()); |
1314 if (!sharedArrayBuffer) | 1316 if (!sharedArrayBuffer) |
1315 return 0; | 1317 return 0; |
1316 m_writer.writeTransferredSharedArrayBuffer(index); | 1318 m_writer.writeTransferredSharedArrayBuffer(index); |
1317 return nullptr; | 1319 return nullptr; |
1318 } | 1320 } |
1319 | 1321 |
1320 bool ScriptValueSerializer::shouldSerializeDensely(uint32_t length, | 1322 bool ScriptValueSerializer::shouldSerializeDensely(uint32_t length, |
1321 uint32_t propertyCount) { | 1323 uint32_t propertyCount) { |
1322 // Let K be the cost of serializing all property values that are there | 1324 // Let K be the cost of serializing all property values that are there |
1323 // Cost of serializing sparsely: 5*propertyCount + K (5 bytes per uint32_t key
) | 1325 // Cost of serializing sparsely: 5*propertyCount + K (5 bytes per uint32_t |
1324 // Cost of serializing densely: K + 1*(length - propertyCount) (1 byte for all
properties that are not there) | 1326 // key) |
| 1327 // Cost of serializing densely: K + 1*(length - propertyCount) (1 byte for all |
| 1328 // properties that are not there) |
1325 // so densely is better than sparsly whenever 6*propertyCount > length | 1329 // so densely is better than sparsly whenever 6*propertyCount > length |
1326 return 6 * propertyCount >= length; | 1330 return 6 * propertyCount >= length; |
1327 } | 1331 } |
1328 | 1332 |
1329 ScriptValueSerializer::StateBase* ScriptValueSerializer::startArrayState( | 1333 ScriptValueSerializer::StateBase* ScriptValueSerializer::startArrayState( |
1330 v8::Local<v8::Array> array, | 1334 v8::Local<v8::Array> array, |
1331 StateBase* next) { | 1335 StateBase* next) { |
1332 v8::Local<v8::Array> propertyNames; | 1336 v8::Local<v8::Array> propertyNames; |
1333 if (!array->GetOwnPropertyNames(context()).ToLocal(&propertyNames)) | 1337 if (!array->GetOwnPropertyNames(context()).ToLocal(&propertyNames)) |
1334 return checkException(next); | 1338 return checkException(next); |
(...skipping 34 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
1369 } | 1373 } |
1370 | 1374 |
1371 ScriptValueSerializer::StateBase* ScriptValueSerializer::startObjectState( | 1375 ScriptValueSerializer::StateBase* ScriptValueSerializer::startObjectState( |
1372 v8::Local<v8::Object> object, | 1376 v8::Local<v8::Object> object, |
1373 StateBase* next) { | 1377 StateBase* next) { |
1374 m_writer.writeGenerateFreshObject(); | 1378 m_writer.writeGenerateFreshObject(); |
1375 // FIXME: check not a wrapper | 1379 // FIXME: check not a wrapper |
1376 return push(new ObjectState(object, next)); | 1380 return push(new ObjectState(object, next)); |
1377 } | 1381 } |
1378 | 1382 |
1379 // Marks object as having been visited by the serializer and assigns it a unique
object reference ID. | 1383 // Marks object as having been visited by the serializer and assigns it a unique |
1380 // An object may only be greyed once. | 1384 // object reference ID. An object may only be greyed once. |
1381 void ScriptValueSerializer::greyObject(const v8::Local<v8::Object>& object) { | 1385 void ScriptValueSerializer::greyObject(const v8::Local<v8::Object>& object) { |
1382 ASSERT(!m_objectPool.contains(object)); | 1386 ASSERT(!m_objectPool.contains(object)); |
1383 uint32_t objectReference = m_nextObjectReference++; | 1387 uint32_t objectReference = m_nextObjectReference++; |
1384 m_objectPool.set(object, objectReference); | 1388 m_objectPool.set(object, objectReference); |
1385 } | 1389 } |
1386 | 1390 |
1387 bool ScriptValueSerializer::appendBlobInfo(const String& uuid, | 1391 bool ScriptValueSerializer::appendBlobInfo(const String& uuid, |
1388 const String& type, | 1392 const String& type, |
1389 unsigned long long size, | 1393 unsigned long long size, |
1390 int* index) { | 1394 int* index) { |
(...skipping 31 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
1422 SerializationTag tag, | 1426 SerializationTag tag, |
1423 v8::Local<v8::Value>* value, | 1427 v8::Local<v8::Value>* value, |
1424 ScriptValueDeserializer& deserializer) { | 1428 ScriptValueDeserializer& deserializer) { |
1425 switch (tag) { | 1429 switch (tag) { |
1426 case ReferenceCountTag: { | 1430 case ReferenceCountTag: { |
1427 if (!m_version) | 1431 if (!m_version) |
1428 return false; | 1432 return false; |
1429 uint32_t referenceTableSize; | 1433 uint32_t referenceTableSize; |
1430 if (!doReadUint32(&referenceTableSize)) | 1434 if (!doReadUint32(&referenceTableSize)) |
1431 return false; | 1435 return false; |
1432 // If this test fails, then the serializer and deserializer disagree about
the assignment | 1436 // If this test fails, then the serializer and deserializer disagree about |
1433 // of object reference IDs. On the deserialization side, this means there
are too many or too few | 1437 // the assignment of object reference IDs. On the deserialization side, |
1434 // calls to pushObjectReference. | 1438 // this means there are too many or too few calls to pushObjectReference. |
1435 if (referenceTableSize != deserializer.objectReferenceCount()) | 1439 if (referenceTableSize != deserializer.objectReferenceCount()) |
1436 return false; | 1440 return false; |
1437 return true; | 1441 return true; |
1438 } | 1442 } |
1439 case InvalidTag: | 1443 case InvalidTag: |
1440 return false; | 1444 return false; |
1441 case PaddingTag: | 1445 case PaddingTag: |
1442 return true; | 1446 return true; |
1443 case UndefinedTag: | 1447 case UndefinedTag: |
1444 *value = v8::Undefined(isolate()); | 1448 *value = v8::Undefined(isolate()); |
(...skipping 813 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
2258 for (unsigned i = 0; i < sizeof(double); ++i) | 2262 for (unsigned i = 0; i < sizeof(double); ++i) |
2259 numberAsByteArray[i] = m_buffer[m_position++]; | 2263 numberAsByteArray[i] = m_buffer[m_position++]; |
2260 return true; | 2264 return true; |
2261 } | 2265 } |
2262 | 2266 |
2263 PassRefPtr<BlobDataHandle> | 2267 PassRefPtr<BlobDataHandle> |
2264 SerializedScriptValueReader::getOrCreateBlobDataHandle(const String& uuid, | 2268 SerializedScriptValueReader::getOrCreateBlobDataHandle(const String& uuid, |
2265 const String& type, | 2269 const String& type, |
2266 long long size) { | 2270 long long size) { |
2267 // The containing ssv may have a BDH for this uuid if this ssv is just being | 2271 // The containing ssv may have a BDH for this uuid if this ssv is just being |
2268 // passed from main to worker thread (for example). We use those values when c
reating | 2272 // passed from main to worker thread (for example). We use those values when |
2269 // the new blob instead of cons'ing up a new BDH. | 2273 // creating the new blob instead of cons'ing up a new BDH. |
2270 // | 2274 // |
2271 // FIXME: Maybe we should require that it work that way where the ssv must hav
e a BDH for any | 2275 // FIXME: Maybe we should require that it work that way where the ssv must |
2272 // blobs it comes across during deserialization. Would require callers to expl
icitly populate | 2276 // have a BDH for any blobs it comes across during deserialization. Would |
2273 // the collection of BDH's for blobs to work, which would encourage lifetimes
to be considered | 2277 // require callers to explicitly populate the collection of BDH's for blobs to |
2274 // when passing ssv's around cross process. At present, we get 'lucky' in some
cases because | 2278 // work, which would encourage lifetimes to be considered when passing ssv's |
2275 // the blob in the src process happens to still exist at the time the dest pro
cess is deserializing. | 2279 // around cross process. At present, we get 'lucky' in some cases because the |
| 2280 // blob in the src process happens to still exist at the time the dest process |
| 2281 // is deserializing. |
2276 // For example in sharedWorker.postMessage(...). | 2282 // For example in sharedWorker.postMessage(...). |
2277 BlobDataHandleMap::const_iterator it = m_blobDataHandles.find(uuid); | 2283 BlobDataHandleMap::const_iterator it = m_blobDataHandles.find(uuid); |
2278 if (it != m_blobDataHandles.end()) { | 2284 if (it != m_blobDataHandles.end()) { |
2279 // make assertions about type and size? | 2285 // make assertions about type and size? |
2280 return it->value; | 2286 return it->value; |
2281 } | 2287 } |
2282 return BlobDataHandle::create(uuid, type, size); | 2288 return BlobDataHandle::create(uuid, type, size); |
2283 } | 2289 } |
2284 | 2290 |
2285 v8::Local<v8::Value> ScriptValueDeserializer::deserialize() { | 2291 v8::Local<v8::Value> ScriptValueDeserializer::deserialize() { |
(...skipping 337 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
2623 m_openCompositeReferenceStack[m_openCompositeReferenceStack.size() - 1]; | 2629 m_openCompositeReferenceStack[m_openCompositeReferenceStack.size() - 1]; |
2624 m_openCompositeReferenceStack.shrink(m_openCompositeReferenceStack.size() - | 2630 m_openCompositeReferenceStack.shrink(m_openCompositeReferenceStack.size() - |
2625 1); | 2631 1); |
2626 if (objectReference >= m_objectPool.size()) | 2632 if (objectReference >= m_objectPool.size()) |
2627 return false; | 2633 return false; |
2628 *object = m_objectPool[objectReference]; | 2634 *object = m_objectPool[objectReference]; |
2629 return true; | 2635 return true; |
2630 } | 2636 } |
2631 | 2637 |
2632 } // namespace blink | 2638 } // namespace blink |
OLD | NEW |