diff options
Diffstat (limited to 'src/value-serializer.cc')
-rw-r--r-- | src/value-serializer.cc | 331 |
1 files changed, 220 insertions, 111 deletions
diff --git a/src/value-serializer.cc b/src/value-serializer.cc index c6abb8a8..7b17275b 100644 --- a/src/value-serializer.cc +++ b/src/value-serializer.cc @@ -23,7 +23,14 @@ namespace v8 { namespace internal { -static const uint32_t kLatestVersion = 9; +// Version 9: (imported from Blink) +// Version 10: one-byte (Latin-1) strings +// Version 11: properly separate undefined from the hole in arrays +// Version 12: regexp and string objects share normal string encoding +// Version 13: host objects have an explicit tag (rather than handling all +// unknown tags) +static const uint32_t kLatestVersion = 13; + static const int kPretenureThreshold = 100 * KB; template <typename T> @@ -46,6 +53,7 @@ enum class SerializationTag : uint8_t { // refTableSize:uint32_t (previously used for sanity checks; safe to ignore) kVerifyObjectCount = '?', // Oddballs (no data). + kTheHole = '-', kUndefined = '_', kNull = '0', kTrue = 'T', @@ -61,6 +69,7 @@ enum class SerializationTag : uint8_t { kDouble = 'N', // byteLength:uint32_t, then raw data kUtf8String = 'S', + kOneByteString = '"', kTwoByteString = 'c', // Reference to a serialized object. objectID:uint32_t kObjectReference = '^', @@ -110,13 +119,16 @@ enum class SerializationTag : uint8_t { // ObjectReference to one) serialized just before it. This is a quirk arising // from the previous stack-based implementation. kArrayBufferView = 'V', - // Shared array buffer (transferred). transferID:uint32_t - kSharedArrayBufferTransfer = 'u', + // Shared array buffer. transferID:uint32_t + kSharedArrayBuffer = 'u', // Compiled WebAssembly module. encodingType:(one-byte tag). // If encodingType == 'y' (raw bytes): // wasmWireByteLength:uint32_t, then raw data // compiledDataLength:uint32_t, then raw data kWasmModule = 'W', + // The delegate is responsible for processing all following data. + // This "escapes" to whatever wire format the delegate chooses. + kHostObject = '\\', }; namespace { @@ -145,8 +157,9 @@ ValueSerializer::ValueSerializer(Isolate* isolate, : isolate_(isolate), delegate_(delegate), zone_(isolate->allocator(), ZONE_NAME), - id_map_(isolate->heap(), &zone_), - array_buffer_transfer_map_(isolate->heap(), &zone_) {} + id_map_(isolate->heap(), ZoneAllocationPolicy(&zone_)), + array_buffer_transfer_map_(isolate->heap(), + ZoneAllocationPolicy(&zone_)) {} ValueSerializer::~ValueSerializer() { if (buffer_) { @@ -163,6 +176,10 @@ void ValueSerializer::WriteHeader() { WriteVarint(kLatestVersion); } +void ValueSerializer::SetTreatArrayBufferViewsAsHostObjects(bool mode) { + treat_array_buffer_views_as_host_objects_ = mode; +} + void ValueSerializer::WriteTag(SerializationTag tag) { uint8_t raw_tag = static_cast<uint8_t>(tag); WriteRawBytes(&raw_tag, sizeof(raw_tag)); @@ -217,18 +234,26 @@ void ValueSerializer::WriteTwoByteString(Vector<const uc16> chars) { } void ValueSerializer::WriteRawBytes(const void* source, size_t length) { - memcpy(ReserveRawBytes(length), source, length); + uint8_t* dest; + if (ReserveRawBytes(length).To(&dest)) { + memcpy(dest, source, length); + } } -uint8_t* ValueSerializer::ReserveRawBytes(size_t bytes) { +Maybe<uint8_t*> ValueSerializer::ReserveRawBytes(size_t bytes) { size_t old_size = buffer_size_; size_t new_size = old_size + bytes; - if (new_size > buffer_capacity_) ExpandBuffer(new_size); + if (V8_UNLIKELY(new_size > buffer_capacity_)) { + bool ok; + if (!ExpandBuffer(new_size).To(&ok)) { + return Nothing<uint8_t*>(); + } + } buffer_size_ = new_size; - return &buffer_[old_size]; + return Just(&buffer_[old_size]); } -void ValueSerializer::ExpandBuffer(size_t required_capacity) { +Maybe<bool> ValueSerializer::ExpandBuffer(size_t required_capacity) { DCHECK_GT(required_capacity, buffer_capacity_); size_t requested_capacity = std::max(required_capacity, buffer_capacity_ * 2) + 64; @@ -241,9 +266,15 @@ void ValueSerializer::ExpandBuffer(size_t required_capacity) { new_buffer = realloc(buffer_, requested_capacity); provided_capacity = requested_capacity; } - DCHECK_GE(provided_capacity, requested_capacity); - buffer_ = reinterpret_cast<uint8_t*>(new_buffer); - buffer_capacity_ = provided_capacity; + if (new_buffer) { + DCHECK(provided_capacity >= requested_capacity); + buffer_ = reinterpret_cast<uint8_t*>(new_buffer); + buffer_capacity_ = provided_capacity; + return Just(true); + } else { + out_of_memory_ = true; + return Nothing<bool>(); + } } void ValueSerializer::WriteUint32(uint32_t value) { @@ -269,24 +300,26 @@ std::pair<uint8_t*, size_t> ValueSerializer::Release() { void ValueSerializer::TransferArrayBuffer(uint32_t transfer_id, Handle<JSArrayBuffer> array_buffer) { DCHECK(!array_buffer_transfer_map_.Find(array_buffer)); + DCHECK(!array_buffer->is_shared()); array_buffer_transfer_map_.Set(array_buffer, transfer_id); } Maybe<bool> ValueSerializer::WriteObject(Handle<Object> object) { + out_of_memory_ = false; if (object->IsSmi()) { WriteSmi(Smi::cast(*object)); - return Just(true); + return ThrowIfOutOfMemory(); } DCHECK(object->IsHeapObject()); switch (HeapObject::cast(*object)->map()->instance_type()) { case ODDBALL_TYPE: WriteOddball(Oddball::cast(*object)); - return Just(true); + return ThrowIfOutOfMemory(); case HEAP_NUMBER_TYPE: case MUTABLE_HEAP_NUMBER_TYPE: WriteHeapNumber(HeapNumber::cast(*object)); - return Just(true); + return ThrowIfOutOfMemory(); case JS_TYPED_ARRAY_TYPE: case JS_DATA_VIEW_TYPE: { // Despite being JSReceivers, these have their wrapped buffer serialized @@ -295,7 +328,7 @@ Maybe<bool> ValueSerializer::WriteObject(Handle<Object> object) { // TODO(jbroman): It may be possible to avoid materializing a typed // array's buffer here. Handle<JSArrayBufferView> view = Handle<JSArrayBufferView>::cast(object); - if (!id_map_.Find(view)) { + if (!id_map_.Find(view) && !treat_array_buffer_views_as_host_objects_) { Handle<JSArrayBuffer> buffer( view->IsJSTypedArray() ? Handle<JSTypedArray>::cast(view)->GetBuffer() @@ -307,7 +340,7 @@ Maybe<bool> ValueSerializer::WriteObject(Handle<Object> object) { default: if (object->IsString()) { WriteString(Handle<String>::cast(object)); - return Just(true); + return ThrowIfOutOfMemory(); } else if (object->IsJSReceiver()) { return WriteJSReceiver(Handle<JSReceiver>::cast(object)); } else { @@ -356,22 +389,9 @@ void ValueSerializer::WriteString(Handle<String> string) { String::FlatContent flat = string->GetFlatContent(); DCHECK(flat.IsFlat()); if (flat.IsOneByte()) { - // The existing format uses UTF-8, rather than Latin-1. As a result we must - // to do work to encode strings that have characters outside ASCII. - // TODO(jbroman): In a future format version, consider adding a tag for - // Latin-1 strings, so that this can be skipped. - WriteTag(SerializationTag::kUtf8String); Vector<const uint8_t> chars = flat.ToOneByteVector(); - if (String::IsAscii(chars.begin(), chars.length())) { - WriteOneByteString(chars); - } else { - v8::Local<v8::String> api_string = Utils::ToLocal(string); - uint32_t utf8_length = api_string->Utf8Length(); - WriteVarint(utf8_length); - api_string->WriteUtf8( - reinterpret_cast<char*>(ReserveRawBytes(utf8_length)), utf8_length, - nullptr, v8::String::NO_NULL_TERMINATION); - } + WriteTag(SerializationTag::kOneByteString); + WriteOneByteString(chars); } else if (flat.IsTwoByte()) { Vector<const uc16> chars = flat.ToUC16Vector(); uint32_t byte_length = chars.length() * sizeof(uc16); @@ -391,7 +411,7 @@ Maybe<bool> ValueSerializer::WriteJSReceiver(Handle<JSReceiver> receiver) { if (uint32_t id = *id_map_entry) { WriteTag(SerializationTag::kObjectReference); WriteVarint(id - 1); - return Just(true); + return ThrowIfOutOfMemory(); } // Otherwise, allocate an ID for it. @@ -400,7 +420,7 @@ Maybe<bool> ValueSerializer::WriteJSReceiver(Handle<JSReceiver> receiver) { // Eliminate callable and exotic objects, which should not be serialized. InstanceType instance_type = receiver->map()->instance_type(); - if (receiver->IsCallable() || (instance_type <= LAST_SPECIAL_RECEIVER_TYPE && + if (receiver->IsCallable() || (IsSpecialReceiverInstanceType(instance_type) && instance_type != JS_SPECIAL_API_OBJECT_TYPE)) { ThrowDataCloneError(MessageTemplate::kDataCloneError, receiver); return Nothing<bool>(); @@ -417,7 +437,7 @@ Maybe<bool> ValueSerializer::WriteJSReceiver(Handle<JSReceiver> receiver) { case JS_API_OBJECT_TYPE: { Handle<JSObject> js_object = Handle<JSObject>::cast(receiver); Map* map = js_object->map(); - if (FLAG_expose_wasm && + if (!FLAG_wasm_disable_structured_cloning && map->GetConstructor() == isolate_->native_context()->wasm_module_constructor()) { return WriteWasmModule(js_object); @@ -431,18 +451,18 @@ Maybe<bool> ValueSerializer::WriteJSReceiver(Handle<JSReceiver> receiver) { return WriteHostObject(Handle<JSObject>::cast(receiver)); case JS_DATE_TYPE: WriteJSDate(JSDate::cast(*receiver)); - return Just(true); + return ThrowIfOutOfMemory(); case JS_VALUE_TYPE: return WriteJSValue(Handle<JSValue>::cast(receiver)); case JS_REGEXP_TYPE: WriteJSRegExp(JSRegExp::cast(*receiver)); - return Just(true); + return ThrowIfOutOfMemory(); case JS_MAP_TYPE: return WriteJSMap(Handle<JSMap>::cast(receiver)); case JS_SET_TYPE: return WriteJSSet(Handle<JSSet>::cast(receiver)); case JS_ARRAY_BUFFER_TYPE: - return WriteJSArrayBuffer(JSArrayBuffer::cast(*receiver)); + return WriteJSArrayBuffer(Handle<JSArrayBuffer>::cast(receiver)); case JS_TYPED_ARRAY_TYPE: case JS_DATA_VIEW_TYPE: return WriteJSArrayBufferView(JSArrayBufferView::cast(*receiver)); @@ -474,7 +494,8 @@ Maybe<bool> ValueSerializer::WriteJSObject(Handle<JSObject> object) { Handle<Object> value; if (V8_LIKELY(!map_changed)) map_changed = *map == object->map(); - if (V8_LIKELY(!map_changed && details.type() == DATA)) { + if (V8_LIKELY(!map_changed && details.location() == kField)) { + DCHECK_EQ(kData, details.kind()); FieldIndex field_index = FieldIndex::ForDescriptor(*map, i); value = JSObject::FastPropertyAt(object, details.representation(), field_index); @@ -496,7 +517,7 @@ Maybe<bool> ValueSerializer::WriteJSObject(Handle<JSObject> object) { WriteTag(SerializationTag::kEndJSObject); WriteVarint<uint32_t>(properties_written); - return Just(true); + return ThrowIfOutOfMemory(); } Maybe<bool> ValueSerializer::WriteJSObjectSlow(Handle<JSObject> object) { @@ -511,7 +532,7 @@ Maybe<bool> ValueSerializer::WriteJSObjectSlow(Handle<JSObject> object) { } WriteTag(SerializationTag::kEndJSObject); WriteVarint<uint32_t>(properties_written); - return Just(true); + return ThrowIfOutOfMemory(); } Maybe<bool> ValueSerializer::WriteJSArray(Handle<JSArray> array) { @@ -530,10 +551,6 @@ Maybe<bool> ValueSerializer::WriteJSArray(Handle<JSArray> array) { if (should_serialize_densely) { DCHECK_LE(length, static_cast<uint32_t>(FixedArray::kMaxLength)); - - // TODO(jbroman): Distinguish between undefined and a hole (this can happen - // if serializing one of the elements deletes another). This requires wire - // format changes. WriteTag(SerializationTag::kBeginDenseJSArray); WriteVarint<uint32_t>(length); uint32_t i = 0; @@ -548,6 +565,9 @@ Maybe<bool> ValueSerializer::WriteJSArray(Handle<JSArray> array) { break; } case FAST_DOUBLE_ELEMENTS: { + // Elements are empty_fixed_array, not a FixedDoubleArray, if the array + // is empty. No elements to encode in this case anyhow. + if (length == 0) break; Handle<FixedDoubleArray> elements( FixedDoubleArray::cast(array->elements()), isolate_); for (; i < length; i++) { @@ -581,6 +601,13 @@ Maybe<bool> ValueSerializer::WriteJSArray(Handle<JSArray> array) { // with. Handle<Object> element; LookupIterator it(isolate_, array, i, array, LookupIterator::OWN); + if (!it.IsFound()) { + // This can happen in the case where an array that was originally dense + // became sparse during serialization. It's too late to switch to the + // sparse format, but we can mark the elements as absent. + WriteTag(SerializationTag::kTheHole); + continue; + } if (!Object::GetProperty(&it).ToHandle(&element) || !WriteObject(element).FromMaybe(false)) { return Nothing<bool>(); @@ -616,7 +643,7 @@ Maybe<bool> ValueSerializer::WriteJSArray(Handle<JSArray> array) { WriteVarint<uint32_t>(properties_written); WriteVarint<uint32_t>(length); } - return Just(true); + return ThrowIfOutOfMemory(); } void ValueSerializer::WriteJSDate(JSDate* date) { @@ -634,32 +661,19 @@ Maybe<bool> ValueSerializer::WriteJSValue(Handle<JSValue> value) { WriteTag(SerializationTag::kNumberObject); WriteDouble(inner_value->Number()); } else if (inner_value->IsString()) { - // TODO(jbroman): Replace UTF-8 encoding with the same options available for - // ordinary strings. WriteTag(SerializationTag::kStringObject); - v8::Local<v8::String> api_string = - Utils::ToLocal(handle(String::cast(inner_value), isolate_)); - uint32_t utf8_length = api_string->Utf8Length(); - WriteVarint(utf8_length); - api_string->WriteUtf8(reinterpret_cast<char*>(ReserveRawBytes(utf8_length)), - utf8_length, nullptr, - v8::String::NO_NULL_TERMINATION); + WriteString(handle(String::cast(inner_value), isolate_)); } else { DCHECK(inner_value->IsSymbol()); ThrowDataCloneError(MessageTemplate::kDataCloneError, value); return Nothing<bool>(); } - return Just(true); + return ThrowIfOutOfMemory(); } void ValueSerializer::WriteJSRegExp(JSRegExp* regexp) { WriteTag(SerializationTag::kRegExp); - v8::Local<v8::String> api_string = - Utils::ToLocal(handle(regexp->Pattern(), isolate_)); - uint32_t utf8_length = api_string->Utf8Length(); - WriteVarint(utf8_length); - api_string->WriteUtf8(reinterpret_cast<char*>(ReserveRawBytes(utf8_length)), - utf8_length, nullptr, v8::String::NO_NULL_TERMINATION); + WriteString(handle(regexp->Pattern(), isolate_)); WriteVarint(static_cast<uint32_t>(regexp->GetFlags())); } @@ -691,7 +705,7 @@ Maybe<bool> ValueSerializer::WriteJSMap(Handle<JSMap> map) { } WriteTag(SerializationTag::kEndJSMap); WriteVarint<uint32_t>(length); - return Just(true); + return ThrowIfOutOfMemory(); } Maybe<bool> ValueSerializer::WriteJSSet(Handle<JSSet> set) { @@ -721,23 +735,32 @@ Maybe<bool> ValueSerializer::WriteJSSet(Handle<JSSet> set) { } WriteTag(SerializationTag::kEndJSSet); WriteVarint<uint32_t>(length); - return Just(true); + return ThrowIfOutOfMemory(); } -Maybe<bool> ValueSerializer::WriteJSArrayBuffer(JSArrayBuffer* array_buffer) { +Maybe<bool> ValueSerializer::WriteJSArrayBuffer( + Handle<JSArrayBuffer> array_buffer) { + if (array_buffer->is_shared()) { + if (!delegate_) { + ThrowDataCloneError(MessageTemplate::kDataCloneError, array_buffer); + return Nothing<bool>(); + } + + v8::Isolate* v8_isolate = reinterpret_cast<v8::Isolate*>(isolate_); + Maybe<uint32_t> index = delegate_->GetSharedArrayBufferId( + v8_isolate, Utils::ToLocalShared(array_buffer)); + RETURN_VALUE_IF_SCHEDULED_EXCEPTION(isolate_, Nothing<bool>()); + + WriteTag(SerializationTag::kSharedArrayBuffer); + WriteVarint(index.FromJust()); + return ThrowIfOutOfMemory(); + } + uint32_t* transfer_entry = array_buffer_transfer_map_.Find(array_buffer); if (transfer_entry) { - WriteTag(array_buffer->is_shared() - ? SerializationTag::kSharedArrayBufferTransfer - : SerializationTag::kArrayBufferTransfer); + WriteTag(SerializationTag::kArrayBufferTransfer); WriteVarint(*transfer_entry); - return Just(true); - } - - if (array_buffer->is_shared()) { - ThrowDataCloneError( - MessageTemplate::kDataCloneErrorSharedArrayBufferNotTransferred); - return Nothing<bool>(); + return ThrowIfOutOfMemory(); } if (array_buffer->was_neutered()) { ThrowDataCloneError(MessageTemplate::kDataCloneErrorNeuteredArrayBuffer); @@ -745,16 +768,19 @@ Maybe<bool> ValueSerializer::WriteJSArrayBuffer(JSArrayBuffer* array_buffer) { } double byte_length = array_buffer->byte_length()->Number(); if (byte_length > std::numeric_limits<uint32_t>::max()) { - ThrowDataCloneError(MessageTemplate::kDataCloneError, handle(array_buffer)); + ThrowDataCloneError(MessageTemplate::kDataCloneError, array_buffer); return Nothing<bool>(); } WriteTag(SerializationTag::kArrayBuffer); WriteVarint<uint32_t>(byte_length); WriteRawBytes(array_buffer->backing_store(), byte_length); - return Just(true); + return ThrowIfOutOfMemory(); } Maybe<bool> ValueSerializer::WriteJSArrayBufferView(JSArrayBufferView* view) { + if (treat_array_buffer_views_as_host_objects_) { + return WriteHostObject(handle(view, isolate_)); + } WriteTag(SerializationTag::kArrayBufferView); ArrayBufferViewTag tag = ArrayBufferViewTag::kInt8Array; if (view->IsJSTypedArray()) { @@ -773,7 +799,7 @@ Maybe<bool> ValueSerializer::WriteJSArrayBufferView(JSArrayBufferView* view) { WriteVarint(static_cast<uint8_t>(tag)); WriteVarint(NumberToUint32(view->byte_offset())); WriteVarint(NumberToUint32(view->byte_length())); - return Just(true); + return ThrowIfOutOfMemory(); } Maybe<bool> ValueSerializer::WriteWasmModule(Handle<JSObject> object) { @@ -783,11 +809,13 @@ Maybe<bool> ValueSerializer::WriteWasmModule(Handle<JSObject> object) { WriteTag(SerializationTag::kWasmModule); WriteRawBytes(&encoding_tag, sizeof(encoding_tag)); - Handle<String> wire_bytes = compiled_part->module_bytes(); + Handle<String> wire_bytes(compiled_part->module_bytes(), isolate_); int wire_bytes_length = wire_bytes->length(); WriteVarint<uint32_t>(wire_bytes_length); - uint8_t* destination = ReserveRawBytes(wire_bytes_length); - String::WriteToFlat(*wire_bytes, destination, 0, wire_bytes_length); + uint8_t* destination; + if (ReserveRawBytes(wire_bytes_length).To(&destination)) { + String::WriteToFlat(*wire_bytes, destination, 0, wire_bytes_length); + } std::unique_ptr<ScriptData> script_data = WasmCompiledModuleSerializer::SerializeWasmModule(isolate_, @@ -796,10 +824,11 @@ Maybe<bool> ValueSerializer::WriteWasmModule(Handle<JSObject> object) { WriteVarint<uint32_t>(script_data_length); WriteRawBytes(script_data->data(), script_data_length); - return Just(true); + return ThrowIfOutOfMemory(); } Maybe<bool> ValueSerializer::WriteHostObject(Handle<JSObject> object) { + WriteTag(SerializationTag::kHostObject); if (!delegate_) { isolate_->Throw(*isolate_->factory()->NewError( isolate_->error_function(), MessageTemplate::kDataCloneError, object)); @@ -847,6 +876,14 @@ void ValueSerializer::ThrowDataCloneError( isolate_->factory()->empty_string()); } +Maybe<bool> ValueSerializer::ThrowIfOutOfMemory() { + if (out_of_memory_) { + ThrowDataCloneError(MessageTemplate::kDataCloneErrorOutOfMemory); + return Nothing<bool>(); + } + return Just(true); +} + void ValueSerializer::ThrowDataCloneError( MessageTemplate::Template template_index, Handle<Object> arg0) { Handle<String> message = @@ -1006,10 +1043,10 @@ void ValueDeserializer::TransferArrayBuffer( } Handle<SeededNumberDictionary> dictionary = array_buffer_transfer_map_.ToHandleChecked(); - const bool used_as_prototype = false; + Handle<JSObject> not_a_prototype_holder; Handle<SeededNumberDictionary> new_dictionary = SeededNumberDictionary::AtNumberPut(dictionary, transfer_id, array_buffer, - used_as_prototype); + not_a_prototype_holder); if (!new_dictionary.is_identical_to(dictionary)) { GlobalHandles::Destroy(Handle<Object>::cast(dictionary).location()); array_buffer_transfer_map_ = Handle<SeededNumberDictionary>::cast( @@ -1073,6 +1110,8 @@ MaybeHandle<Object> ValueDeserializer::ReadObjectInternal() { } case SerializationTag::kUtf8String: return ReadUtf8String(); + case SerializationTag::kOneByteString: + return ReadOneByteString(); case SerializationTag::kTwoByteString: return ReadTwoByteString(); case SerializationTag::kObjectReference: { @@ -1105,48 +1144,79 @@ MaybeHandle<Object> ValueDeserializer::ReadObjectInternal() { const bool is_shared = false; return ReadTransferredJSArrayBuffer(is_shared); } - case SerializationTag::kSharedArrayBufferTransfer: { + case SerializationTag::kSharedArrayBuffer: { const bool is_shared = true; return ReadTransferredJSArrayBuffer(is_shared); } case SerializationTag::kWasmModule: return ReadWasmModule(); - default: - // TODO(jbroman): Introduce an explicit tag for host objects to avoid - // having to treat every unknown tag as a potential host object. - position_--; + case SerializationTag::kHostObject: return ReadHostObject(); + default: + // Before there was an explicit tag for host objects, all unknown tags + // were delegated to the host. + if (version_ < 13) { + position_--; + return ReadHostObject(); + } + return MaybeHandle<Object>(); } } +MaybeHandle<String> ValueDeserializer::ReadString() { + if (version_ < 12) return ReadUtf8String(); + Handle<Object> object; + if (!ReadObject().ToHandle(&object) || !object->IsString()) { + return MaybeHandle<String>(); + } + return Handle<String>::cast(object); +} + MaybeHandle<String> ValueDeserializer::ReadUtf8String() { uint32_t utf8_length; Vector<const uint8_t> utf8_bytes; if (!ReadVarint<uint32_t>().To(&utf8_length) || utf8_length > static_cast<uint32_t>(std::numeric_limits<int32_t>::max()) || - !ReadRawBytes(utf8_length).To(&utf8_bytes)) + !ReadRawBytes(utf8_length).To(&utf8_bytes)) { return MaybeHandle<String>(); + } return isolate_->factory()->NewStringFromUtf8( Vector<const char>::cast(utf8_bytes), pretenure_); } +MaybeHandle<String> ValueDeserializer::ReadOneByteString() { + uint32_t byte_length; + Vector<const uint8_t> bytes; + if (!ReadVarint<uint32_t>().To(&byte_length) || + byte_length > + static_cast<uint32_t>(std::numeric_limits<int32_t>::max()) || + !ReadRawBytes(byte_length).To(&bytes)) { + return MaybeHandle<String>(); + } + return isolate_->factory()->NewStringFromOneByte(bytes, pretenure_); +} + MaybeHandle<String> ValueDeserializer::ReadTwoByteString() { uint32_t byte_length; Vector<const uint8_t> bytes; if (!ReadVarint<uint32_t>().To(&byte_length) || byte_length > static_cast<uint32_t>(std::numeric_limits<int32_t>::max()) || - byte_length % sizeof(uc16) != 0 || !ReadRawBytes(byte_length).To(&bytes)) + byte_length % sizeof(uc16) != 0 || + !ReadRawBytes(byte_length).To(&bytes)) { return MaybeHandle<String>(); + } // Allocate an uninitialized string so that we can do a raw memcpy into the // string on the heap (regardless of alignment). + if (byte_length == 0) return isolate_->factory()->empty_string(); Handle<SeqTwoByteString> string; if (!isolate_->factory() ->NewRawTwoByteString(byte_length / sizeof(uc16), pretenure_) - .ToHandle(&string)) + .ToHandle(&string)) { return MaybeHandle<String>(); + } // Copy the bytes directly into the new string. // Warning: this uses host endianness. @@ -1269,10 +1339,20 @@ MaybeHandle<JSArray> ValueDeserializer::ReadDenseJSArray() { Handle<FixedArray> elements(FixedArray::cast(array->elements()), isolate_); for (uint32_t i = 0; i < length; i++) { + SerializationTag tag; + if (PeekTag().To(&tag) && tag == SerializationTag::kTheHole) { + ConsumeTag(SerializationTag::kTheHole); + continue; + } + Handle<Object> element; if (!ReadObject().ToHandle(&element)) return MaybeHandle<JSArray>(); - // TODO(jbroman): Distinguish between undefined and a hole. - if (element->IsUndefined(isolate_)) continue; + + // Serialization versions less than 11 encode the hole the same as + // undefined. For consistency with previous behavior, store these as the + // hole. Past version 11, undefined means undefined. + if (version_ < 11 && element->IsUndefined(isolate_)) continue; + elements->set(i, *element); } @@ -1330,7 +1410,7 @@ MaybeHandle<JSValue> ValueDeserializer::ReadJSValue(SerializationTag tag) { } case SerializationTag::kStringObject: { Handle<String> string; - if (!ReadUtf8String().ToHandle(&string)) return MaybeHandle<JSValue>(); + if (!ReadString().ToHandle(&string)) return MaybeHandle<JSValue>(); value = Handle<JSValue>::cast(isolate_->factory()->NewJSObject( isolate_->string_function(), pretenure_)); value->set_value(*string); @@ -1349,7 +1429,7 @@ MaybeHandle<JSRegExp> ValueDeserializer::ReadJSRegExp() { Handle<String> pattern; uint32_t raw_flags; Handle<JSRegExp> regexp; - if (!ReadUtf8String().ToHandle(&pattern) || + if (!ReadString().ToHandle(&pattern) || !ReadVarint<uint32_t>().To(&raw_flags) || !JSRegExp::New(pattern, static_cast<JSRegExp::Flags>(raw_flags)) .ToHandle(®exp)) { @@ -1443,8 +1523,10 @@ MaybeHandle<JSArrayBuffer> ValueDeserializer::ReadJSArrayBuffer() { const bool should_initialize = false; Handle<JSArrayBuffer> array_buffer = isolate_->factory()->NewJSArrayBuffer(SharedFlag::kNotShared, pretenure_); - JSArrayBuffer::SetupAllocatingData(array_buffer, isolate_, byte_length, - should_initialize); + if (!JSArrayBuffer::SetupAllocatingData(array_buffer, isolate_, byte_length, + should_initialize)) { + return MaybeHandle<JSArrayBuffer>(); + } memcpy(array_buffer->backing_store(), position_, byte_length); position_ += byte_length; AddObjectWithID(id, array_buffer); @@ -1514,7 +1596,7 @@ MaybeHandle<JSArrayBufferView> ValueDeserializer::ReadJSArrayBufferView( } MaybeHandle<JSObject> ValueDeserializer::ReadWasmModule() { - if (!FLAG_expose_wasm) return MaybeHandle<JSObject>(); + if (FLAG_wasm_disable_structured_cloning) return MaybeHandle<JSObject>(); Vector<const uint8_t> encoding_tag; if (!ReadRawBytes(sizeof(WasmEncodingTag)).To(&encoding_tag) || @@ -1551,11 +1633,14 @@ MaybeHandle<JSObject> ValueDeserializer::ReadWasmModule() { } // If that fails, recompile. - wasm::ErrorThrower thrower(isolate_, "ValueDeserializer::ReadWasmModule"); - return wasm::CreateModuleObjectFromBytes( - isolate_, wire_bytes.begin(), wire_bytes.end(), &thrower, - wasm::ModuleOrigin::kWasmOrigin, Handle<Script>::null(), nullptr, - nullptr); + MaybeHandle<JSObject> result; + { + wasm::ErrorThrower thrower(isolate_, "ValueDeserializer::ReadWasmModule"); + result = wasm::SyncCompile(isolate_, &thrower, + wasm::ModuleWireBytes(wire_bytes)); + } + RETURN_EXCEPTION_IF_SCHEDULED_EXCEPTION(isolate_, JSObject); + return result; } MaybeHandle<JSObject> ValueDeserializer::ReadHostObject() { @@ -1584,10 +1669,15 @@ static void CommitProperties(Handle<JSObject> object, Handle<Map> map, DisallowHeapAllocation no_gc; DescriptorArray* descriptors = object->map()->instance_descriptors(); for (unsigned i = 0; i < properties.size(); i++) { + // Initializing store. object->WriteToField(i, descriptors->GetDetails(i), *properties[i]); } } +static bool IsValidObjectKey(Handle<Object> value) { + return value->IsName() || value->IsNumber(); +} + Maybe<uint32_t> ValueDeserializer::ReadJSObjectProperties( Handle<JSObject> object, SerializationTag end_tag, bool can_use_transitions) { @@ -1623,7 +1713,9 @@ Maybe<uint32_t> ValueDeserializer::ReadJSObjectProperties( key = expected_key; target = TransitionArray::ExpectedTransitionTarget(map); } else { - if (!ReadObject().ToHandle(&key)) return Nothing<uint32_t>(); + if (!ReadObject().ToHandle(&key) || !IsValidObjectKey(key)) { + return Nothing<uint32_t>(); + } if (key->IsString()) { key = isolate_->factory()->InternalizeString(Handle<String>::cast(key)); @@ -1654,8 +1746,8 @@ Maybe<uint32_t> ValueDeserializer::ReadJSObjectProperties( ->NowContains(value)) { Handle<FieldType> value_type = value->OptimalType(isolate_, expected_representation); - Map::GeneralizeFieldType(target, descriptor, - expected_representation, value_type); + Map::GeneralizeField(target, descriptor, details.constness(), + expected_representation, value_type); } DCHECK(target->instance_descriptors() ->GetFieldType(descriptor) @@ -1703,7 +1795,9 @@ Maybe<uint32_t> ValueDeserializer::ReadJSObjectProperties( } Handle<Object> key; - if (!ReadObject().ToHandle(&key)) return Nothing<uint32_t>(); + if (!ReadObject().ToHandle(&key) || !IsValidObjectKey(key)) { + return Nothing<uint32_t>(); + } Handle<Object> value; if (!ReadObject().ToHandle(&value)) return Nothing<uint32_t>(); @@ -1752,6 +1846,7 @@ static Maybe<bool> SetPropertiesFromKeyValuePairs(Isolate* isolate, uint32_t num_properties) { for (unsigned i = 0; i < 2 * num_properties; i += 2) { Handle<Object> key = data[i]; + if (!IsValidObjectKey(key)) return Nothing<bool>(); Handle<Object> value = data[i + 1]; bool success; LookupIterator it = LookupIterator::PropertyOrElement( @@ -1765,6 +1860,20 @@ static Maybe<bool> SetPropertiesFromKeyValuePairs(Isolate* isolate, return Just(true); } +namespace { + +// Throws a generic "deserialization failed" exception by default, unless a more +// specific exception has already been thrown. +void ThrowDeserializationExceptionIfNonePending(Isolate* isolate) { + if (!isolate->has_pending_exception()) { + isolate->Throw(*isolate->factory()->NewError( + MessageTemplate::kDataCloneDeserializationError)); + } + DCHECK(isolate->has_pending_exception()); +} + +} // namespace + MaybeHandle<Object> ValueDeserializer::ReadObjectUsingEntireBufferForLegacyFormat() { DCHECK_EQ(version_, 0u); @@ -1797,7 +1906,7 @@ ValueDeserializer::ReadObjectUsingEntireBufferForLegacyFormat() { !SetPropertiesFromKeyValuePairs( isolate_, js_object, &stack[begin_properties], num_properties) .FromMaybe(false)) { - DCHECK(isolate_->has_pending_exception()); + ThrowDeserializationExceptionIfNonePending(isolate_); return MaybeHandle<Object>(); } @@ -1828,7 +1937,7 @@ ValueDeserializer::ReadObjectUsingEntireBufferForLegacyFormat() { !SetPropertiesFromKeyValuePairs( isolate_, js_array, &stack[begin_properties], num_properties) .FromMaybe(false)) { - DCHECK(isolate_->has_pending_exception()); + ThrowDeserializationExceptionIfNonePending(isolate_); return MaybeHandle<Object>(); } |