| // Copyright 2025 The Fuchsia Authors. All rights reserved. | |
| // Use of this source code is governed by a BSD-style license that can be | |
| // found in the LICENSE file. | |
| #include "python_dict_visitor.h" | |
| #include <Python.h> | |
| #include <string> | |
| #include <fuchsia_controller_abi/utils.h> | |
| #include "src/lib/fidl_codec/printer.h" | |
| #include "src/lib/fidl_codec/wire_object.h" | |
| #include "src/lib/fidl_codec/wire_types.h" | |
| #include "utils.h" | |
| namespace fuchsia_controller::fidl_codec::python_dict_visitor { | |
| using FuchsiaControllerObject = ::fuchsia_controller::abi::utils::Object; | |
| using ::fuchsia_controller::fidl_codec::utils::NormalizeMemberName; | |
| void PythonDictVisitor::VisitValue(const ::fidl_codec::Value* node, | |
| const ::fidl_codec::Type* for_type) { | |
| std::stringstream ss; | |
| ::fidl_codec::PrettyPrinter printer(ss, ::fidl_codec::WithoutColors, false, "", 0, false); | |
| node->PrettyPrint(for_type, printer); | |
| result_ = PyUnicode_FromString(ss.str().c_str()); | |
| } | |
| void PythonDictVisitor::VisitInvalidValue(const ::fidl_codec::InvalidValue* node, | |
| const ::fidl_codec::Type* for_type) { | |
| PyErr_Format(PyExc_TypeError, "invalid value for type: %s", | |
| for_type ? for_type->Name().c_str() : "[unknown]"); | |
| } | |
| void PythonDictVisitor::VisitNullValue(const ::fidl_codec::NullValue* node, | |
| const ::fidl_codec::Type* for_type) { | |
| Py_IncRef(Py_None); | |
| result_ = Py_None; | |
| } | |
| void PythonDictVisitor::VisitBoolValue(const ::fidl_codec::BoolValue* node, | |
| const ::fidl_codec::Type* for_type) { | |
| result_ = PyBool_FromLong(node->value()); | |
| } | |
| void PythonDictVisitor::VisitStringValue(const ::fidl_codec::StringValue* node, | |
| const ::fidl_codec::Type* for_type) { | |
| result_ = PyUnicode_FromStringAndSize(node->string().data(), | |
| static_cast<Py_ssize_t>(node->string().length())); | |
| } | |
| void PythonDictVisitor::VisitUnionValue(const ::fidl_codec::UnionValue* node, | |
| const ::fidl_codec::Type* type) { | |
| auto res = FuchsiaControllerObject(PyDict_New()); | |
| PythonDictVisitor visitor; | |
| node->value()->Visit(&visitor, node->member().type()); | |
| if (visitor.result() == nullptr) { | |
| return; | |
| } | |
| PyDict_SetItemString(res.get(), NormalizeMemberName(node->member().name()).c_str(), | |
| visitor.result()); | |
| result_ = res.take(); | |
| } | |
| void PythonDictVisitor::VisitStructValue(const ::fidl_codec::StructValue* node, | |
| const ::fidl_codec::Type* for_type) { | |
| auto res = FuchsiaControllerObject(PyDict_New()); | |
| for (const auto& member : node->struct_definition().members()) { | |
| auto it = node->fields().find(member.get()); | |
| if (it == node->fields().end()) { | |
| continue; | |
| } | |
| PythonDictVisitor visitor; | |
| it->second->Visit(&visitor, member->type()); | |
| if (visitor.result() == nullptr) { | |
| return; | |
| } | |
| PyDict_SetItemString(res.get(), NormalizeMemberName(member->name()).c_str(), visitor.result()); | |
| } | |
| result_ = res.take(); | |
| } | |
| void PythonDictVisitor::VisitVectorValue(const ::fidl_codec::VectorValue* node, | |
| const ::fidl_codec::Type* for_type) { | |
| if (for_type == nullptr) { | |
| PyErr_SetString(PyExc_TypeError, | |
| "expected vector type in during decoding. Received null value"); | |
| return; | |
| } | |
| const auto component_type = for_type->GetComponentType(); | |
| if (component_type == nullptr) { | |
| PyErr_SetString(PyExc_TypeError, "vector value's type does not contain a component type"); | |
| return; | |
| } | |
| auto res = FuchsiaControllerObject(PyList_New(static_cast<Py_ssize_t>(node->values().size()))); | |
| Py_ssize_t values_size = static_cast<Py_ssize_t>(node->values().size()); | |
| const auto& values = node->values(); | |
| for (Py_ssize_t i = 0; i < values_size; ++i) { | |
| PythonDictVisitor visitor; | |
| values[i]->Visit(&visitor, component_type); | |
| if (visitor.result() == nullptr) { | |
| return; | |
| } | |
| PyList_SetItem(res.get(), i, visitor.result()); | |
| } | |
| result_ = res.take(); | |
| } | |
| void PythonDictVisitor::VisitTableValue(const ::fidl_codec::TableValue* node, | |
| const ::fidl_codec::Type* for_type) { | |
| auto res = FuchsiaControllerObject(PyDict_New()); | |
| for (const auto& member : node->table_definition().members()) { | |
| if (member != nullptr) { | |
| auto it = node->members().find(member.get()); | |
| if (it == node->members().end()) { | |
| Py_IncRef(Py_None); | |
| PyDict_SetItemString(res.get(), NormalizeMemberName(member->name()).c_str(), Py_None); | |
| continue; | |
| } | |
| if (it->second == nullptr || it->second->IsNull()) { | |
| Py_IncRef(Py_None); | |
| PyDict_SetItemString(res.get(), NormalizeMemberName(member->name()).c_str(), Py_None); | |
| continue; | |
| } | |
| PythonDictVisitor visitor; | |
| it->second->Visit(&visitor, member->type()); | |
| if (visitor.result() == nullptr) { | |
| return; | |
| } | |
| PyDict_SetItemString(res.get(), NormalizeMemberName(member->name()).c_str(), | |
| visitor.result()); | |
| } | |
| } | |
| result_ = res.take(); | |
| } | |
| void PythonDictVisitor::VisitDoubleValue(const ::fidl_codec::DoubleValue* node, | |
| const ::fidl_codec::Type* for_type) { | |
| double value; | |
| node->GetDoubleValue(&value); | |
| result_ = PyFloat_FromDouble(value); | |
| } | |
| void PythonDictVisitor::VisitIntegerValue(const ::fidl_codec::IntegerValue* node, | |
| const ::fidl_codec::Type* for_type) { | |
| uint64_t value; | |
| bool negative; | |
| node->GetIntegerValue(&value, &negative); | |
| if (negative) { | |
| // Max possible absolute value for a signed integer (2^63). | |
| if (value > 1ul << 63) { | |
| PyErr_SetString(PyExc_OverflowError, "Integer overflow"); | |
| return; | |
| } | |
| int64_t res = static_cast<int64_t>(value); | |
| res *= -1; | |
| result_ = PyLong_FromLongLong(res); | |
| } else { | |
| result_ = PyLong_FromUnsignedLongLong(value); | |
| } | |
| } | |
| void PythonDictVisitor::VisitHandleValue(const ::fidl_codec::HandleValue* handle, | |
| const ::fidl_codec::Type* for_type) { | |
| if (handle->handle().handle == 0) { | |
| Py_INCREF(Py_None); | |
| result_ = Py_None; | |
| } else { | |
| result_ = PyLong_FromUnsignedLongLong(handle->handle().handle); | |
| } | |
| } | |
| } // namespace fuchsia_controller::fidl_codec::python_dict_visitor |