| 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 |