// Copyright (c) 2012, the Dart project authors. Please see the AUTHORS file // for details. All rights reserved. Use of this source code is governed by a // BSD-style license that can be found in the LICENSE file. #include #include "vm/dart_api_message.h" #include "platform/undefined_behavior_sanitizer.h" #include "platform/unicode.h" #include "vm/object.h" #include "vm/snapshot_ids.h" #include "vm/symbols.h" namespace dart { static const int kNumInitialReferences = 4; ApiMessageReader::ApiMessageReader(Message* msg) : BaseReader(msg->IsRaw() ? reinterpret_cast( static_cast(msg->raw_obj())) : msg->snapshot(), msg->snapshot_length()), zone_(NULL), backward_references_(kNumInitialReferences), vm_isolate_references_(kNumInitialReferences), vm_symbol_references_(NULL), finalizable_data_(msg->finalizable_data()) {} ApiMessageReader::~ApiMessageReader() {} void ApiMessageReader::Init() { // We need to have an enclosing ApiNativeScope. ASSERT(ApiNativeScope::Current() != NULL); zone_ = ApiNativeScope::Current()->zone(); ASSERT(zone_ != NULL); // Initialize marker objects used to handle Lists. // TODO(sjesse): Remove this when message serialization format is // updated. memset(&type_arguments_marker, 0, sizeof(type_arguments_marker)); memset(&dynamic_type_marker, 0, sizeof(dynamic_type_marker)); type_arguments_marker.type = static_cast(Dart_CObject_Internal::kTypeArguments); dynamic_type_marker.type = static_cast(Dart_CObject_Internal::kDynamicType); } Dart_CObject* ApiMessageReader::ReadMessage() { Init(); if (PendingBytes() > 0) { // Read the object out of the message. return ReadObject(); } else { const ObjectPtr raw_obj = static_cast( reinterpret_cast(CurrentBufferAddress())); ASSERT(ApiObjectConverter::CanConvert(raw_obj)); Dart_CObject* cobj = reinterpret_cast(allocator(sizeof(Dart_CObject))); ApiObjectConverter::Convert(raw_obj, cobj); return cobj; } } intptr_t ApiMessageReader::LookupInternalClass(intptr_t class_header) { if (IsVMIsolateObject(class_header)) { return GetVMIsolateObjectId(class_header); } ASSERT(SerializedHeaderTag::decode(class_header) == kObjectId); return SerializedHeaderData::decode(class_header); } Dart_CObject* ApiMessageReader::AllocateDartCObject(Dart_CObject_Type type) { Dart_CObject* value = reinterpret_cast(allocator(sizeof(Dart_CObject))); ASSERT(value != NULL); value->type = type; return value; } Dart_CObject* ApiMessageReader::AllocateDartCObjectUnsupported() { return AllocateDartCObject(Dart_CObject_kUnsupported); } Dart_CObject* ApiMessageReader::AllocateDartCObjectNull() { return AllocateDartCObject(Dart_CObject_kNull); } Dart_CObject* ApiMessageReader::AllocateDartCObjectBool(bool val) { Dart_CObject* value = AllocateDartCObject(Dart_CObject_kBool); value->value.as_bool = val; return value; } Dart_CObject* ApiMessageReader::AllocateDartCObjectInt32(int32_t val) { Dart_CObject* value = AllocateDartCObject(Dart_CObject_kInt32); value->value.as_int32 = val; return value; } Dart_CObject* ApiMessageReader::AllocateDartCObjectInt64(int64_t val) { Dart_CObject* value = AllocateDartCObject(Dart_CObject_kInt64); value->value.as_int64 = val; return value; } Dart_CObject* ApiMessageReader::AllocateDartCObjectDouble(double val) { Dart_CObject* value = AllocateDartCObject(Dart_CObject_kDouble); value->value.as_double = val; return value; } Dart_CObject* ApiMessageReader::AllocateDartCObjectString(intptr_t length) { // Allocate a Dart_CObject structure followed by an array of chars // for the string content. The pointer to the string content is set // up to this area. Dart_CObject* value = reinterpret_cast( allocator(sizeof(Dart_CObject) + length + 1)); ASSERT(value != NULL); value->value.as_string = reinterpret_cast(value) + sizeof(*value); value->type = Dart_CObject_kString; return value; } static int GetTypedDataSizeInBytes(Dart_TypedData_Type type) { switch (type) { case Dart_TypedData_kInt8: case Dart_TypedData_kUint8: case Dart_TypedData_kUint8Clamped: return 1; case Dart_TypedData_kInt16: case Dart_TypedData_kUint16: return 2; case Dart_TypedData_kInt32: case Dart_TypedData_kUint32: case Dart_TypedData_kFloat32: return 4; case Dart_TypedData_kInt64: case Dart_TypedData_kUint64: case Dart_TypedData_kFloat64: return 8; case Dart_TypedData_kInt32x4: case Dart_TypedData_kFloat32x4: case Dart_TypedData_kFloat64x2: return 16; default: break; } UNREACHABLE(); return -1; } Dart_CObject* ApiMessageReader::AllocateDartCObjectTypedData( Dart_TypedData_Type type, intptr_t length) { // Allocate a Dart_CObject structure followed by an array of bytes // for the byte array content. The pointer to the byte array content // is set up to this area. intptr_t length_in_bytes = GetTypedDataSizeInBytes(type) * length; Dart_CObject* value = reinterpret_cast( allocator(sizeof(Dart_CObject) + length_in_bytes)); ASSERT(value != NULL); value->type = Dart_CObject_kTypedData; value->value.as_typed_data.type = type; value->value.as_typed_data.length = length_in_bytes; if (length > 0) { value->value.as_typed_data.values = reinterpret_cast(value) + sizeof(*value); } else { value->value.as_typed_data.values = NULL; } return value; } Dart_CObject* ApiMessageReader::AllocateDartCObjectArray(intptr_t length) { // Allocate a Dart_CObject structure followed by an array of // pointers to Dart_CObject structures. The pointer to the array // content is set up to this area. Dart_CObject* value = reinterpret_cast( allocator(sizeof(Dart_CObject) + length * sizeof(value))); ASSERT(value != NULL); value->type = Dart_CObject_kArray; value->value.as_array.length = length; if (length > 0) { value->value.as_array.values = reinterpret_cast(value + 1); } else { value->value.as_array.values = NULL; } return value; } Dart_CObject* ApiMessageReader::AllocateDartCObjectVmIsolateObj(intptr_t id) { ObjectPtr raw = VmIsolateSnapshotObject(id); intptr_t cid = raw->GetClassId(); switch (cid) { case kOneByteStringCid: { OneByteStringPtr raw_str = static_cast(raw); const char* str = reinterpret_cast(raw_str->untag()->data()); ASSERT(str != NULL); Dart_CObject* object = NULL; for (intptr_t i = 0; i < vm_isolate_references_.length(); i++) { object = vm_isolate_references_.At(i); if (object->type == Dart_CObject_kString) { if (strcmp(str, object->value.as_string) == 0) { return object; } } } object = CreateDartCObjectString(raw); vm_isolate_references_.Add(object); return object; } case kMintCid: { const Mint& obj = Mint::Handle(static_cast(raw)); int64_t value64 = obj.value(); if ((kMinInt32 <= value64) && (value64 <= kMaxInt32)) { return GetCanonicalMintObject(Dart_CObject_kInt32, value64); } else { return GetCanonicalMintObject(Dart_CObject_kInt64, value64); } } default: UNREACHABLE(); return NULL; } } Dart_CObject_Internal* ApiMessageReader::AllocateDartCObjectInternal( Dart_CObject_Internal::Type type) { Dart_CObject_Internal* value = reinterpret_cast( allocator(sizeof(Dart_CObject_Internal))); ASSERT(value != NULL); value->type = static_cast(type); return value; } Dart_CObject_Internal* ApiMessageReader::AllocateDartCObjectClass() { return AllocateDartCObjectInternal(Dart_CObject_Internal::kClass); } ApiMessageReader::BackRefNode* ApiMessageReader::AllocateBackRefNode( Dart_CObject* reference, DeserializeState state) { BackRefNode* value = reinterpret_cast(allocator(sizeof(BackRefNode))); value->set_reference(reference); value->set_state(state); return value; } Dart_CObject* ApiMessageReader::ReadInlinedObject(intptr_t object_id) { // Read the class header information and lookup the class. intptr_t class_header = Read(); intptr_t tags = ReadTags(); USE(tags); intptr_t class_id; // There is limited support for reading regular dart instances. Only // typed data views are currently handled. if (SerializedHeaderData::decode(class_header) == kInstanceObjectId) { Dart_CObject_Internal* object = reinterpret_cast(GetBackRef(object_id)); if (object == NULL) { object = AllocateDartCObjectInternal(Dart_CObject_Internal::kUninitialized); AddBackRef(object_id, object, kIsDeserialized); // Read class of object. object->cls = reinterpret_cast(ReadObjectImpl()); ASSERT(object->cls->type == static_cast(Dart_CObject_Internal::kClass)); } ASSERT(object->type == static_cast( Dart_CObject_Internal::kUninitialized)); return object; } ASSERT((class_header & kSmiTagMask) != 0); class_id = LookupInternalClass(class_header); if ((class_id == kArrayCid) || (class_id == kImmutableArrayCid)) { intptr_t len = ReadSmiValue(); Dart_CObject* value = GetBackRef(object_id); if (value == NULL) { value = AllocateDartCObjectArray(len); AddBackRef(object_id, value, kIsDeserialized); } // Skip type arguments. // TODO(sjesse): Remove this when message serialization format is // updated (currently type_arguments is leaked). Dart_CObject* type_arguments = ReadObjectImpl(); if (type_arguments != &type_arguments_marker && type_arguments->type != Dart_CObject_kNull) { return AllocateDartCObjectUnsupported(); } for (int i = 0; i < len; i++) { value->value.as_array.values[i] = ReadObjectRef(); } return value; } return ReadInternalVMObject(class_id, object_id); } Dart_CObject* ApiMessageReader::ReadPredefinedSymbol(intptr_t object_id) { ASSERT(Symbols::IsPredefinedSymbolId(object_id)); intptr_t symbol_id = object_id - kMaxPredefinedObjectIds; Dart_CObject* object; if (vm_symbol_references_ != NULL && (object = vm_symbol_references_[symbol_id]) != NULL) { return object; } if (vm_symbol_references_ == NULL) { intptr_t size = (sizeof(*vm_symbol_references_) * Symbols::kMaxPredefinedId); vm_symbol_references_ = reinterpret_cast(allocator(size)); memset(vm_symbol_references_, 0, size); } object = CreateDartCObjectString(Symbols::GetPredefinedSymbol(object_id)); ASSERT(vm_symbol_references_[symbol_id] == NULL); vm_symbol_references_[symbol_id] = object; return object; } intptr_t ApiMessageReader::NextAvailableObjectId() const { return backward_references_.length() + kMaxPredefinedObjectIds; } Dart_CObject* ApiMessageReader::CreateDartCObjectString(ObjectPtr raw) { ASSERT(IsOneByteStringClassId(raw->GetClassId())); OneByteStringPtr raw_str = static_cast(raw); intptr_t len = Smi::Value(raw_str->untag()->length()); Dart_CObject* object = AllocateDartCObjectString(len); char* p = object->value.as_string; memmove(p, raw_str->untag()->data(), len); p[len] = '\0'; return object; } Dart_CObject* ApiMessageReader::GetCanonicalMintObject(Dart_CObject_Type type, int64_t value64) { Dart_CObject* object = NULL; for (intptr_t i = 0; i < vm_isolate_references_.length(); i++) { object = vm_isolate_references_.At(i); if (object->type == type) { if (value64 == object->value.as_int64) { return object; } } } if (type == Dart_CObject_kInt32) { object = AllocateDartCObjectInt32(static_cast(value64)); } else { object = AllocateDartCObjectInt64(value64); } vm_isolate_references_.Add(object); return object; } Dart_CObject* ApiMessageReader::ReadObjectRef() { int64_t value64 = Read(); if ((value64 & kSmiTagMask) == 0) { int64_t untagged_value = value64 >> kSmiTagShift; if ((kMinInt32 <= untagged_value) && (untagged_value <= kMaxInt32)) { return AllocateDartCObjectInt32(static_cast(untagged_value)); } else { return AllocateDartCObjectInt64(untagged_value); } } ASSERT((value64 <= kIntptrMax) && (value64 >= kIntptrMin)); intptr_t value = static_cast(value64); if (IsVMIsolateObject(value)) { return ReadVMIsolateObject(value); } if (SerializedHeaderTag::decode(value) == kObjectId) { return ReadIndexedObject(SerializedHeaderData::decode(value)); } ASSERT(SerializedHeaderTag::decode(value) == kInlined); // Read the class header information and lookup the class. intptr_t class_header = Read(); intptr_t object_id = SerializedHeaderData::decode(value); if (object_id == kOmittedObjectId) { object_id = NextAvailableObjectId(); } intptr_t tags = ReadTags(); USE(tags); // Reading of regular dart instances has limited support in order to // read typed data views. if (SerializedHeaderData::decode(class_header) == kInstanceObjectId) { Dart_CObject_Internal* object = AllocateDartCObjectInternal(Dart_CObject_Internal::kUninitialized); AddBackRef(object_id, object, kIsNotDeserialized); // Read class of object. object->cls = reinterpret_cast(ReadObjectImpl()); ASSERT(object->cls->type == static_cast(Dart_CObject_Internal::kClass)); return object; } ASSERT((class_header & kSmiTagMask) != 0); intptr_t class_id = LookupInternalClass(class_header); if ((class_id == kArrayCid) || (class_id == kImmutableArrayCid)) { ASSERT(GetBackRef(object_id) == NULL); intptr_t len = ReadSmiValue(); Dart_CObject* value = AllocateDartCObjectArray(len); AddBackRef(object_id, value, kIsNotDeserialized); return value; } return ReadInternalVMObject(class_id, object_id); } Dart_CObject* ApiMessageReader::ReadVMIsolateObject(intptr_t value) { intptr_t object_id = GetVMIsolateObjectId(value); if (object_id == kNullObject) { return AllocateDartCObjectNull(); } if (object_id == kTrueValue) { return AllocateDartCObjectBool(true); } if (object_id == kFalseValue) { return AllocateDartCObjectBool(false); } if (object_id == kDoubleObject) { return AllocateDartCObjectDouble(ReadDouble()); } if (Symbols::IsPredefinedSymbolId(object_id)) { return ReadPredefinedSymbol(object_id); } // No other VM isolate objects are supported. return AllocateDartCObjectNull(); } Dart_CObject* ApiMessageReader::ReadInternalVMObject(intptr_t class_id, intptr_t object_id) { switch (class_id) { case kClassCid: { Dart_CObject_Internal* object = AllocateDartCObjectClass(); AddBackRef(object_id, object, kIsDeserialized); object->internal.as_class.library_url = ReadObjectImpl(); ASSERT(object->internal.as_class.library_url->type == Dart_CObject_kString); object->internal.as_class.class_name = ReadObjectImpl(); ASSERT(object->internal.as_class.class_name->type == Dart_CObject_kString); return object; } case kTypeArgumentsCid: { // TODO(sjesse): Remove this when message serialization format is // updated (currently length is leaked). Dart_CObject* value = &type_arguments_marker; AddBackRef(object_id, value, kIsDeserialized); Dart_CObject* length = ReadObjectImpl(); ASSERT(length->type == Dart_CObject_kInt32); // The instantiations_ field is only written to a full snapshot. for (int i = 0; i < length->value.as_int32; i++) { Dart_CObject* type = ReadObjectImpl(); if (type != &dynamic_type_marker) { return AllocateDartCObjectUnsupported(); } } return value; } case kTypeParameterCid: { Dart_CObject* value = &dynamic_type_marker; AddBackRef(object_id, value, kIsDeserialized); intptr_t index = Read(); USE(index); intptr_t token_index = Read(); USE(token_index); int8_t type_state = Read(); USE(type_state); Dart_CObject* parameterized_class = ReadObjectImpl(); // The type parameter is finalized, therefore parameterized_class is null. ASSERT(parameterized_class->type == Dart_CObject_kNull); Dart_CObject* name = ReadObjectImpl(); ASSERT(name->type == Dart_CObject_kString); return value; } case kMintCid: { int64_t value64 = Read(); Dart_CObject* object; if ((kMinInt32 <= value64) && (value64 <= kMaxInt32)) { object = AllocateDartCObjectInt32(static_cast(value64)); } else { object = AllocateDartCObjectInt64(value64); } AddBackRef(object_id, object, kIsDeserialized); return object; } case kDoubleCid: { // Doubles are handled specially when being sent as part of message // snapshots. UNREACHABLE(); } case kOneByteStringCid: { intptr_t len = ReadSmiValue(); uint8_t* latin1 = reinterpret_cast(allocator(len * sizeof(uint8_t))); intptr_t utf8_len = 0; for (intptr_t i = 0; i < len; i++) { latin1[i] = Read(); utf8_len += Utf8::Length(latin1[i]); } Dart_CObject* object = AllocateDartCObjectString(utf8_len); AddBackRef(object_id, object, kIsDeserialized); char* p = object->value.as_string; for (intptr_t i = 0; i < len; i++) { p += Utf8::Encode(latin1[i], p); } *p = '\0'; ASSERT(p == (object->value.as_string + utf8_len)); return object; } case kTwoByteStringCid: { intptr_t len = ReadSmiValue(); uint16_t* utf16 = reinterpret_cast(allocator(len * sizeof(uint16_t))); intptr_t utf8_len = 0; // Read all the UTF-16 code units. for (intptr_t i = 0; i < len; i++) { utf16[i] = Read(); } // Calculate the UTF-8 length and check if the string can be // UTF-8 encoded. bool valid = true; intptr_t i = 0; while (i < len && valid) { int32_t ch = Utf16::Next(utf16, &i, len); utf8_len += Utf8::Length(ch); valid = !Utf16::IsSurrogate(ch); } if (!valid) { return AllocateDartCObjectUnsupported(); } Dart_CObject* object = AllocateDartCObjectString(utf8_len); AddBackRef(object_id, object, kIsDeserialized); char* p = object->value.as_string; i = 0; while (i < len) { p += Utf8::Encode(Utf16::Next(utf16, &i, len), p); } *p = '\0'; ASSERT(p == (object->value.as_string + utf8_len)); return object; } case kSendPortCid: { int64_t value64 = Read(); int64_t originId = Read(); Dart_CObject* object = AllocateDartCObject(Dart_CObject_kSendPort); object->value.as_send_port.id = value64; object->value.as_send_port.origin_id = originId; AddBackRef(object_id, object, kIsDeserialized); return object; } case kCapabilityCid: { int64_t id = Read(); Dart_CObject* object = AllocateDartCObject(Dart_CObject_kCapability); object->value.as_capability.id = id; AddBackRef(object_id, object, kIsDeserialized); return object; } #define READ_TYPED_DATA(tname, ctype) \ { \ intptr_t len = ReadSmiValue(); \ auto type = Dart_TypedData_k##tname; \ intptr_t length_in_bytes = GetTypedDataSizeInBytes(type) * len; \ Dart_CObject* object = \ reinterpret_cast(allocator(sizeof(Dart_CObject))); \ ASSERT(object != NULL); \ object->type = Dart_CObject_kTypedData; \ object->value.as_typed_data.type = type; \ object->value.as_typed_data.length = length_in_bytes; \ if (len > 0) { \ Align(Zone::kAlignment); \ object->value.as_typed_data.values = \ const_cast(CurrentBufferAddress()); \ Advance(length_in_bytes); \ } else { \ object->value.as_typed_data.values = NULL; \ } \ AddBackRef(object_id, object, kIsDeserialized); \ return object; \ } #define READ_EXTERNAL_TYPED_DATA(tname, ctype) \ { \ intptr_t len = ReadSmiValue(); \ auto type = Dart_TypedData_k##tname; \ intptr_t length_in_bytes = GetTypedDataSizeInBytes(type) * len; \ Dart_CObject* object = \ reinterpret_cast(allocator(sizeof(Dart_CObject))); \ ASSERT(object != NULL); \ object->type = Dart_CObject_kTypedData; \ object->value.as_typed_data.type = type; \ object->value.as_typed_data.length = length_in_bytes; \ object->value.as_typed_data.values = \ reinterpret_cast(finalizable_data_->Get().data); \ AddBackRef(object_id, object, kIsDeserialized); \ return object; \ } #define READ_TYPED_DATA_VIEW(tname, ctype) \ { \ Dart_CObject_Internal* object = \ AllocateDartCObjectInternal(Dart_CObject_Internal::kUninitialized); \ AddBackRef(object_id, object, kIsDeserialized); \ object->type = \ static_cast(Dart_CObject_Internal::kView); \ object->internal.as_view.offset_in_bytes = ReadSmiValue(); \ object->internal.as_view.length = ReadSmiValue(); \ object->internal.as_view.buffer = ReadObjectImpl(); \ Dart_CObject* buffer = object->internal.as_view.buffer; \ RELEASE_ASSERT(buffer->type == Dart_CObject_kTypedData); \ \ /* Now turn the view into a byte array.*/ \ const Dart_TypedData_Type type = Dart_TypedData_k##tname; \ object->type = Dart_CObject_kTypedData; \ object->value.as_typed_data.type = type; \ object->value.as_typed_data.length = \ object->internal.as_view.length * GetTypedDataSizeInBytes(type); \ object->value.as_typed_data.values = \ buffer->value.as_typed_data.values + \ object->internal.as_view.offset_in_bytes; \ return object; \ } #define TYPED_DATA_LIST(V) \ V(Int8, int8_t) \ V(Uint8, uint8_t) \ V(Uint8Clamped, uint8_t) \ V(Int16, int16_t) \ V(Uint16, uint16_t) \ V(Int32, int32_t) \ V(Uint32, uint32_t) \ V(Int64, int64_t) \ V(Uint64, uint64_t) \ V(Float32, float) \ V(Float64, double) \ V(Int32x4, simd128_value_t) \ V(Float32x4, simd128_value_t) \ V(Float64x2, simd128_value_t) #define EMIT_TYPED_DATA_CASES(type, c_type) \ case kTypedData##type##ArrayCid: \ READ_TYPED_DATA(type, c_type); \ case kExternalTypedData##type##ArrayCid: \ READ_EXTERNAL_TYPED_DATA(type, c_type); \ case kTypedData##type##ArrayViewCid: \ READ_TYPED_DATA_VIEW(type, c_type); TYPED_DATA_LIST(EMIT_TYPED_DATA_CASES) #undef EMIT_TYPED_DATA_CASES #undef TYPED_DATA_LIST #undef READ_TYPED_DATA #undef READ_EXTERNAL_TYPED_DATA #undef READ_TYPED_DATA_VIEW case kGrowableObjectArrayCid: { // A GrowableObjectArray is serialized as its type arguments and // length followed by its backing store. The backing store is an // array with a length which might be longer than the length of // the GrowableObjectArray. Dart_CObject* value = GetBackRef(object_id); ASSERT(value == NULL); // Allocate an empty array for the GrowableObjectArray which // will be updated to point to the content when the backing // store has been deserialized. value = AllocateDartCObjectArray(0); AddBackRef(object_id, value, kIsDeserialized); // Read and skip the type arguments field. // TODO(sjesse): Remove this when message serialization format is // updated (currently type_arguments is leaked). Dart_CObject* type_arguments = ReadObjectImpl(); if (type_arguments != &type_arguments_marker && type_arguments->type != Dart_CObject_kNull) { return AllocateDartCObjectUnsupported(); } // Read the length field. intptr_t len = ReadSmiValue(); // Read the content of the GrowableObjectArray. Dart_CObject* content = ReadObjectRef(); ASSERT(content->type == Dart_CObject_kArray); // Make the empty array allocated point to the backing store content. value->value.as_array.length = len; value->value.as_array.values = content->value.as_array.values; return value; } default: // Everything else not supported. Dart_CObject* value = AllocateDartCObjectUnsupported(); AddBackRef(object_id, value, kIsDeserialized); return value; } } Dart_CObject* ApiMessageReader::ReadIndexedObject(intptr_t object_id) { if (object_id >= kFirstTypeSnapshotId && object_id <= kLastTypeSnapshotId) { // Always return dynamic type (this is only a marker). return &dynamic_type_marker; } if (object_id >= kFirstTypeArgumentsSnapshotId && object_id <= kLastTypeArgumentsSnapshotId) { return &type_arguments_marker; } intptr_t index = object_id - kMaxPredefinedObjectIds; ASSERT((0 <= index) && (index < backward_references_.length())); ASSERT(backward_references_[index]->reference() != NULL); return backward_references_[index]->reference(); } Dart_CObject* ApiMessageReader::ReadObject() { Dart_CObject* value = ReadObjectImpl(); for (intptr_t i = 0; i < backward_references_.length(); i++) { if (!backward_references_[i]->is_deserialized()) { ReadObjectImpl(); backward_references_[i]->set_state(kIsDeserialized); } } return value; } Dart_CObject* ApiMessageReader::ReadObjectImpl() { int64_t value64 = Read(); if ((value64 & kSmiTagMask) == 0) { int64_t untagged_value = value64 >> kSmiTagShift; if ((kMinInt32 <= untagged_value) && (untagged_value <= kMaxInt32)) { return AllocateDartCObjectInt32(static_cast(untagged_value)); } else { return AllocateDartCObjectInt64(untagged_value); } } ASSERT((value64 <= kIntptrMax) && (value64 >= kIntptrMin)); intptr_t value = static_cast(value64); if (IsVMIsolateObject(value)) { return ReadVMIsolateObject(value); } if (SerializedHeaderTag::decode(value) == kObjectId) { return ReadIndexedObject(SerializedHeaderData::decode(value)); } ASSERT(SerializedHeaderTag::decode(value) == kInlined); intptr_t object_id = SerializedHeaderData::decode(value); if (object_id == kOmittedObjectId) { object_id = NextAvailableObjectId(); } return ReadInlinedObject(object_id); } void ApiMessageReader::AddBackRef(intptr_t id, Dart_CObject* obj, DeserializeState state) { intptr_t index = (id - kMaxPredefinedObjectIds); ASSERT(index == backward_references_.length()); BackRefNode* node = AllocateBackRefNode(obj, state); ASSERT(node != NULL); backward_references_.Add(node); } Dart_CObject* ApiMessageReader::GetBackRef(intptr_t id) { ASSERT(id >= kMaxPredefinedObjectIds); intptr_t index = (id - kMaxPredefinedObjectIds); if (index < backward_references_.length()) { return backward_references_[index]->reference(); } return NULL; } ApiMessageWriter::ApiMessageWriter() : BaseWriter(kInitialSize), object_id_(0), forward_list_(NULL), forward_list_length_(0), forward_id_(0), finalizable_data_(new MessageFinalizableData()) { ASSERT(kDartCObjectTypeMask >= Dart_CObject_kNumberOfTypes - 1); } ApiMessageWriter::~ApiMessageWriter() { ::free(forward_list_); delete finalizable_data_; } NO_SANITIZE_UNDEFINED( "enum") // TODO(https://github.com/dart-lang/sdk/issues/39427) void ApiMessageWriter::MarkCObject(Dart_CObject* object, intptr_t object_id) { // Mark the object as serialized by adding the object id to the // upper bits of the type field in the Dart_CObject structure. Add // an offset for making marking of object id 0 possible. ASSERT(!IsCObjectMarked(object)); intptr_t mark_value = object_id + kDartCObjectMarkOffset; object->type = static_cast( ((mark_value) << kDartCObjectTypeBits) | object->type); } NO_SANITIZE_UNDEFINED( "enum") // TODO(https://github.com/dart-lang/sdk/issues/39427) void ApiMessageWriter::UnmarkCObject(Dart_CObject* object) { ASSERT(IsCObjectMarked(object)); object->type = static_cast(object->type & kDartCObjectTypeMask); } NO_SANITIZE_UNDEFINED( "enum") // TODO(https://github.com/dart-lang/sdk/issues/39427) bool ApiMessageWriter::IsCObjectMarked(Dart_CObject* object) { return (object->type & kDartCObjectMarkMask) != 0; } NO_SANITIZE_UNDEFINED( "enum") // TODO(https://github.com/dart-lang/sdk/issues/39427) intptr_t ApiMessageWriter::GetMarkedCObjectMark(Dart_CObject* object) { ASSERT(IsCObjectMarked(object)); intptr_t mark_value = ((object->type & kDartCObjectMarkMask) >> kDartCObjectTypeBits); // An offset was added to object id for making marking object id 0 possible. return mark_value - kDartCObjectMarkOffset; } void ApiMessageWriter::UnmarkAllCObjects(Dart_CObject* object) { if (!IsCObjectMarked(object)) return; UnmarkCObject(object); if (object->type == Dart_CObject_kArray) { for (int i = 0; i < object->value.as_array.length; i++) { Dart_CObject* element = object->value.as_array.values[i]; UnmarkAllCObjects(element); } } } void ApiMessageWriter::AddToForwardList(Dart_CObject* object) { if (forward_id_ >= forward_list_length_) { void* new_list = NULL; if (forward_list_length_ == 0) { forward_list_length_ = 4; intptr_t new_size = forward_list_length_ * sizeof(object); new_list = dart::malloc(new_size); } else { forward_list_length_ *= 2; intptr_t new_size = (forward_list_length_ * sizeof(object)); new_list = dart::realloc(forward_list_, new_size); } ASSERT(new_list != NULL); forward_list_ = reinterpret_cast(new_list); } forward_list_[forward_id_] = object; forward_id_ += 1; } void ApiMessageWriter::WriteSmi(int64_t value) { ASSERT(Smi::IsValid(value)); Write(Smi::New(static_cast(value))); } void ApiMessageWriter::WriteNullObject() { WriteVMIsolateObject(kNullObject); } void ApiMessageWriter::WriteMint(Dart_CObject* object, int64_t value) { ASSERT(!Smi::IsValid(value)); // Write out the serialization header value for mint object. WriteInlinedHeader(object); // Write out the class and tags information. WriteIndexedObject(kMintCid); WriteTags(0); // Write the 64-bit value. Write(value); } void ApiMessageWriter::WriteInt32(Dart_CObject* object) { int64_t value = object->value.as_int32; if (Smi::IsValid(value)) { WriteSmi(value); } else { WriteMint(object, value); } } void ApiMessageWriter::WriteInt64(Dart_CObject* object) { int64_t value = object->value.as_int64; if (Smi::IsValid(value)) { WriteSmi(value); } else { WriteMint(object, value); } } void ApiMessageWriter::WriteInlinedHeader(Dart_CObject* object) { // Write out the serialization header value for this object. WriteInlinedObjectHeader(kMaxPredefinedObjectIds + object_id_); // Mark object with its object id. MarkCObject(object, object_id_); // Advance object id. object_id_++; } bool ApiMessageWriter::WriteCObject(Dart_CObject* object) { if (IsCObjectMarked(object)) { intptr_t object_id = GetMarkedCObjectMark(object); WriteIndexedObject(kMaxPredefinedObjectIds + object_id); return true; } Dart_CObject_Type type = object->type; if (type == Dart_CObject_kArray) { const intptr_t array_length = object->value.as_array.length; if (!Array::IsValidLength(array_length)) { return false; } // Write out the serialization header value for this object. WriteInlinedHeader(object); // Write out the class and tags information. WriteIndexedObject(kArrayCid); WriteTags(0); // Write out the length information. WriteSmi(array_length); // Write out the type arguments. WriteNullObject(); // Write out array elements. for (int i = 0; i < array_length; i++) { bool success = WriteCObjectRef(object->value.as_array.values[i]); if (!success) return false; } return true; } return WriteCObjectInlined(object, type); } bool ApiMessageWriter::WriteCObjectRef(Dart_CObject* object) { if (IsCObjectMarked(object)) { intptr_t object_id = GetMarkedCObjectMark(object); WriteIndexedObject(kMaxPredefinedObjectIds + object_id); return true; } Dart_CObject_Type type = object->type; if (type == Dart_CObject_kArray) { const intptr_t array_length = object->value.as_array.length; if (!Array::IsValidLength(array_length)) { return false; } // Write out the serialization header value for this object. WriteInlinedHeader(object); // Write out the class information. WriteIndexedObject(kArrayCid); WriteTags(0); // Write out the length information. WriteSmi(array_length); // Add object to forward list so that this object is serialized later. AddToForwardList(object); return true; } return WriteCObjectInlined(object, type); } NO_SANITIZE_UNDEFINED( "enum") // TODO(https://github.com/dart-lang/sdk/issues/39427) bool ApiMessageWriter::WriteForwardedCObject(Dart_CObject* object) { ASSERT(IsCObjectMarked(object)); Dart_CObject_Type type = static_cast(object->type & kDartCObjectTypeMask); ASSERT(type == Dart_CObject_kArray); const intptr_t array_length = object->value.as_array.length; if (!Array::IsValidLength(array_length)) { return false; } // Write out the serialization header value for this object. intptr_t object_id = GetMarkedCObjectMark(object); WriteInlinedObjectHeader(kMaxPredefinedObjectIds + object_id); // Write out the class and tags information. WriteIndexedObject(kArrayCid); WriteTags(0); // Write out the length information. WriteSmi(array_length); // Write out the type arguments. WriteNullObject(); // Write out array elements. for (int i = 0; i < array_length; i++) { bool success = WriteCObjectRef(object->value.as_array.values[i]); if (!success) return false; } return true; } bool ApiMessageWriter::WriteCObjectInlined(Dart_CObject* object, Dart_CObject_Type type) { switch (type) { case Dart_CObject_kNull: WriteNullObject(); break; case Dart_CObject_kBool: if (object->value.as_bool) { WriteVMIsolateObject(kTrueValue); } else { WriteVMIsolateObject(kFalseValue); } break; case Dart_CObject_kInt32: WriteInt32(object); break; case Dart_CObject_kInt64: WriteInt64(object); break; case Dart_CObject_kDouble: WriteVMIsolateObject(kDoubleObject); WriteDouble(object->value.as_double); break; case Dart_CObject_kString: { const uint8_t* utf8_str = reinterpret_cast(object->value.as_string); intptr_t utf8_len = strlen(object->value.as_string); if (!Utf8::IsValid(utf8_str, utf8_len)) { return false; } Utf8::Type type = Utf8::kLatin1; intptr_t len = Utf8::CodeUnitCount(utf8_str, utf8_len, &type); if (len > String::kMaxElements) { return false; } // Write out the serialization header value for this object. WriteInlinedHeader(object); // Write out the class and tags information. WriteIndexedObject(type == Utf8::kLatin1 ? kOneByteStringCid : kTwoByteStringCid); WriteTags(0); // Write string length and content. WriteSmi(len); if (type == Utf8::kLatin1) { uint8_t* latin1_str = reinterpret_cast(dart::malloc(len * sizeof(uint8_t))); bool success = Utf8::DecodeToLatin1(utf8_str, utf8_len, latin1_str, len); ASSERT(success); for (intptr_t i = 0; i < len; i++) { Write(latin1_str[i]); } ::free(latin1_str); } else { uint16_t* utf16_str = reinterpret_cast(dart::malloc(len * sizeof(uint16_t))); bool success = Utf8::DecodeToUTF16(utf8_str, utf8_len, utf16_str, len); ASSERT(success); for (intptr_t i = 0; i < len; i++) { Write(utf16_str[i]); } ::free(utf16_str); } break; } case Dart_CObject_kTypedData: { // Write out the serialization header value for this object. WriteInlinedHeader(object); // Write out the class and tags information. intptr_t class_id; switch (object->value.as_typed_data.type) { case Dart_TypedData_kInt8: class_id = kTypedDataInt8ArrayCid; break; case Dart_TypedData_kUint8: class_id = kTypedDataUint8ArrayCid; break; case Dart_TypedData_kUint32: class_id = kTypedDataUint32ArrayCid; break; default: class_id = kTypedDataUint8ArrayCid; UNIMPLEMENTED(); } intptr_t len = object->value.as_typed_data.length; if (len < 0 || len > TypedData::MaxElements(class_id)) { return false; } WriteIndexedObject(class_id); WriteTags(0); WriteSmi(len); switch (class_id) { case kTypedDataInt8ArrayCid: case kTypedDataUint8ArrayCid: { uint8_t* bytes = object->value.as_typed_data.values; Align(Zone::kAlignment); WriteBytes(bytes, len); break; } case kTypedDataUint32ArrayCid: { uint8_t* bytes = object->value.as_typed_data.values; Align(Zone::kAlignment); WriteBytes(bytes, len * sizeof(uint32_t)); break; } default: UNIMPLEMENTED(); } break; } case Dart_CObject_kExternalTypedData: { // TODO(ager): we are writing C pointers into the message in // order to post external arrays through ports. We need to make // sure that messages containing pointers can never be posted // to other processes. // Write out serialization header value for this object. WriteInlinedHeader(object); // Write out the class and tag information. WriteIndexedObject(kExternalTypedDataUint8ArrayCid); WriteTags(0); intptr_t length = object->value.as_external_typed_data.length; if (length < 0 || length > ExternalTypedData::MaxElements( kExternalTypedDataUint8ArrayCid)) { return false; } uint8_t* data = object->value.as_external_typed_data.data; void* peer = object->value.as_external_typed_data.peer; Dart_HandleFinalizer callback = object->value.as_external_typed_data.callback; if (callback == NULL) { return false; } WriteSmi(length); finalizable_data_->Put(length, reinterpret_cast(data), peer, callback); break; } case Dart_CObject_kSendPort: { WriteInlinedHeader(object); WriteIndexedObject(kSendPortCid); WriteTags(0); Write(object->value.as_send_port.id); Write(object->value.as_send_port.origin_id); break; } case Dart_CObject_kCapability: { WriteInlinedHeader(object); WriteIndexedObject(kCapabilityCid); WriteTags(0); Write(object->value.as_capability.id); break; } default: FATAL1("Unexpected Dart_CObject_Type %d\n", type); } return true; } std::unique_ptr ApiMessageWriter::WriteCMessage( Dart_CObject* object, Dart_Port dest_port, Message::Priority priority) { bool success = WriteCObject(object); if (!success) { UnmarkAllCObjects(object); intptr_t unused; free(Steal(&unused)); return nullptr; } // Write out all objects that were added to the forward list and have // not been serialized yet. These would typically be fields of arrays. // NOTE: The forward list might grow as we process the list. for (intptr_t i = 0; i < forward_id_; i++) { success = WriteForwardedCObject(forward_list_[i]); if (!success) { UnmarkAllCObjects(object); intptr_t unused; free(Steal(&unused)); return nullptr; } } UnmarkAllCObjects(object); MessageFinalizableData* finalizable_data = finalizable_data_; finalizable_data_ = nullptr; intptr_t size; uint8_t* buffer = Steal(&size); return Message::New(dest_port, buffer, size, finalizable_data, priority); } } // namespace dart