blob: 4b69daec43c1b544402d1ee3cabf9bb14af80f31 [file] [log] [blame]
// Copyright 2012 The Chromium Authors
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include "dbus/values_util.h"
#include <memory>
#include <utility>
#include "base/json/json_writer.h"
#include "base/logging.h"
#include "base/memory/raw_ptr.h"
#include "base/notreached.h"
#include "base/values.h"
#include "dbus/message.h"
namespace dbus {
namespace {
// Returns whether |value| is exactly representable by double or not.
template <typename T>
bool IsExactlyRepresentableByDouble(T value) {
return value == static_cast<T>(static_cast<double>(value));
}
// Pops values from |reader| and appends them to |list_value|.
bool PopListElements(MessageReader* reader, base::Value::List& list_value) {
while (reader->HasMoreData()) {
base::Value element_value = PopDataAsValue(reader);
if (element_value.is_none())
return false;
list_value.Append(std::move(element_value));
}
return true;
}
// Pops dict-entries from |reader| and sets them to |dictionary_value|
bool PopDictionaryEntries(MessageReader* reader,
base::Value::Dict& dictionary_value) {
while (reader->HasMoreData()) {
DCHECK_EQ(Message::DICT_ENTRY, reader->GetDataType());
MessageReader entry_reader(nullptr);
if (!reader->PopDictEntry(&entry_reader))
return false;
// Get key as a string.
std::string key_string;
if (entry_reader.GetDataType() == Message::STRING) {
// If the type of keys is STRING, pop it directly.
if (!entry_reader.PopString(&key_string))
return false;
} else {
// If the type of keys is not STRING, convert it to string.
base::Value key = PopDataAsValue(&entry_reader);
if (key.is_none())
return false;
// Use JSONWriter to convert an arbitrary value to a string.
base::JSONWriter::Write(key, &key_string);
}
// Get the value and set the key-value pair.
base::Value value = PopDataAsValue(&entry_reader);
if (value.is_none())
return false;
dictionary_value.Set(key_string, std::move(value));
}
return true;
}
// Gets the D-Bus type signature for the value.
std::string GetTypeSignature(base::ValueView value) {
struct Visitor {
std::string operator()(absl::monostate) {
DLOG(ERROR) << "Unexpected type " << base::Value::Type::NONE;
return std::string();
}
std::string operator()(bool) { return "b"; }
std::string operator()(int) { return "i"; }
std::string operator()(double) { return "d"; }
std::string operator()(std::string_view) { return "s"; }
std::string operator()(const base::Value::BlobStorage&) { return "ay"; }
std::string operator()(const base::Value::Dict&) { return "a{sv}"; }
std::string operator()(const base::Value::List&) { return "av"; }
};
return value.Visit(Visitor());
}
} // namespace
base::Value PopDataAsValue(MessageReader* reader) {
base::Value result;
switch (reader->GetDataType()) {
case Message::INVALID_DATA:
// Do nothing.
break;
case Message::BYTE: {
uint8_t value = 0;
if (reader->PopByte(&value))
result = base::Value(value);
break;
}
case Message::BOOL: {
bool value = false;
if (reader->PopBool(&value))
result = base::Value(value);
break;
}
case Message::INT16: {
int16_t value = 0;
if (reader->PopInt16(&value))
result = base::Value(value);
break;
}
case Message::UINT16: {
uint16_t value = 0;
if (reader->PopUint16(&value))
result = base::Value(value);
break;
}
case Message::INT32: {
int32_t value = 0;
if (reader->PopInt32(&value))
result = base::Value(value);
break;
}
case Message::UINT32: {
uint32_t value = 0;
if (reader->PopUint32(&value)) {
result = base::Value(static_cast<double>(value));
}
break;
}
case Message::INT64: {
int64_t value = 0;
if (reader->PopInt64(&value)) {
DLOG_IF(WARNING, !IsExactlyRepresentableByDouble(value))
<< value << " is not exactly representable by double";
result = base::Value(static_cast<double>(value));
}
break;
}
case Message::UINT64: {
uint64_t value = 0;
if (reader->PopUint64(&value)) {
DLOG_IF(WARNING, !IsExactlyRepresentableByDouble(value))
<< value << " is not exactly representable by double";
result = base::Value(static_cast<double>(value));
}
break;
}
case Message::DOUBLE: {
double value = 0;
if (reader->PopDouble(&value))
result = base::Value(value);
break;
}
case Message::STRING: {
std::string value;
if (reader->PopString(&value))
result = base::Value(value);
break;
}
case Message::OBJECT_PATH: {
ObjectPath value;
if (reader->PopObjectPath(&value))
result = base::Value(value.value());
break;
}
case Message::UNIX_FD: {
// Cannot distinguish a file descriptor from an int
NOTREACHED_IN_MIGRATION();
break;
}
case Message::ARRAY: {
MessageReader sub_reader(nullptr);
if (reader->PopArray(&sub_reader)) {
// If the type of the array's element is DICT_ENTRY, create a
// Value with type base::Value::Dict, otherwise create a
// Value with type base::Value::List.
if (sub_reader.GetDataType() == Message::DICT_ENTRY) {
base::Value::Dict dictionary_value;
if (PopDictionaryEntries(&sub_reader, dictionary_value))
result = base::Value(std::move(dictionary_value));
} else {
base::Value::List list_value;
if (PopListElements(&sub_reader, list_value))
result = base::Value(std::move(list_value));
}
}
break;
}
case Message::STRUCT: {
MessageReader sub_reader(nullptr);
if (reader->PopStruct(&sub_reader)) {
base::Value::List list_value;
if (PopListElements(&sub_reader, list_value))
result = base::Value(std::move(list_value));
}
break;
}
case Message::DICT_ENTRY:
// DICT_ENTRY must be popped as an element of an array.
NOTREACHED_IN_MIGRATION();
break;
case Message::VARIANT: {
MessageReader sub_reader(nullptr);
if (reader->PopVariant(&sub_reader))
result = PopDataAsValue(&sub_reader);
break;
}
}
return result;
}
void AppendBasicTypeValueData(MessageWriter* writer, base::ValueView value) {
struct Visitor {
raw_ptr<MessageWriter> writer;
void operator()(absl::monostate) {
DLOG(ERROR) << "Unexpected type: " << base::Value::Type::NONE;
}
void operator()(bool value) { writer->AppendBool(value); }
void operator()(int value) { writer->AppendInt32(value); }
void operator()(double value) { writer->AppendDouble(value); }
void operator()(std::string_view value) { writer->AppendString(value); }
void operator()(const base::Value::BlobStorage&) {
DLOG(ERROR) << "Unexpected type: " << base::Value::Type::BINARY;
}
void operator()(const base::Value::Dict&) {
DLOG(ERROR) << "Unexpected type: " << base::Value::Type::DICT;
}
void operator()(const base::Value::List&) {
DLOG(ERROR) << "Unexpected type: " << base::Value::Type::LIST;
}
};
value.Visit(Visitor{.writer = writer});
}
void AppendBasicTypeValueDataAsVariant(MessageWriter* writer,
base::ValueView value) {
MessageWriter sub_writer(nullptr);
writer->OpenVariant(GetTypeSignature(value), &sub_writer);
AppendBasicTypeValueData(&sub_writer, value);
writer->CloseContainer(&sub_writer);
}
void AppendValueData(MessageWriter* writer, base::ValueView value) {
struct Visitor {
raw_ptr<MessageWriter> writer;
void operator()(absl::monostate) {
DLOG(ERROR) << "Unexpected type: " << base::Value::Type::NONE;
}
void operator()(bool value) {
return AppendBasicTypeValueData(writer, value);
}
void operator()(int value) {
return AppendBasicTypeValueData(writer, value);
}
void operator()(double value) {
return AppendBasicTypeValueData(writer, value);
}
void operator()(std::string_view value) {
return AppendBasicTypeValueData(writer, value);
}
void operator()(const base::Value::BlobStorage& value) {
DLOG(ERROR) << "Unexpected type: " << base::Value::Type::BINARY;
}
void operator()(const base::Value::Dict& value) {
dbus::MessageWriter array_writer(nullptr);
writer->OpenArray("{sv}", &array_writer);
for (auto item : value) {
dbus::MessageWriter dict_entry_writer(nullptr);
array_writer.OpenDictEntry(&dict_entry_writer);
dict_entry_writer.AppendString(item.first);
AppendValueDataAsVariant(&dict_entry_writer, item.second);
array_writer.CloseContainer(&dict_entry_writer);
}
writer->CloseContainer(&array_writer);
}
void operator()(const base::Value::List& value) {
dbus::MessageWriter array_writer(nullptr);
writer->OpenArray("v", &array_writer);
for (const auto& value_in_list : value) {
AppendValueDataAsVariant(&array_writer, value_in_list);
}
writer->CloseContainer(&array_writer);
}
};
value.Visit(Visitor{.writer = writer});
}
void AppendValueDataAsVariant(MessageWriter* writer, base::ValueView value) {
MessageWriter variant_writer(nullptr);
writer->OpenVariant(GetTypeSignature(value), &variant_writer);
AppendValueData(&variant_writer, value);
writer->CloseContainer(&variant_writer);
}
} // namespace dbus