mirror of
				https://github.com/RGBCube/serenity
				synced 2025-10-31 16:12:44 +00:00 
			
		
		
		
	 57dc179b1f
			
		
	
	
		57dc179b1f
		
	
	
	
	
		
			
			This will make it easier to support both string types at the same time while we convert code, and tracking down remaining uses. One big exception is Value::to_string() in LibJS, where the name is dictated by the ToString AO.
		
			
				
	
	
		
			216 lines
		
	
	
	
		
			6.3 KiB
		
	
	
	
		
			C++
		
	
	
	
	
	
			
		
		
	
	
			216 lines
		
	
	
	
		
			6.3 KiB
		
	
	
	
		
			C++
		
	
	
	
	
	
| /*
 | |
|  * Copyright (c) 2018-2020, Andreas Kling <kling@serenityos.org>
 | |
|  *
 | |
|  * SPDX-License-Identifier: BSD-2-Clause
 | |
|  */
 | |
| 
 | |
| #include "RemoteObjectPropertyModel.h"
 | |
| #include "RemoteObject.h"
 | |
| #include "RemoteProcess.h"
 | |
| 
 | |
| namespace Inspector {
 | |
| 
 | |
| RemoteObjectPropertyModel::RemoteObjectPropertyModel(RemoteObject& object)
 | |
|     : m_object(object)
 | |
| {
 | |
| }
 | |
| 
 | |
| int RemoteObjectPropertyModel::row_count(const GUI::ModelIndex& index) const
 | |
| {
 | |
|     Function<int(JsonValue const&)> do_count = [&](JsonValue const& value) {
 | |
|         if (value.is_array())
 | |
|             return value.as_array().size();
 | |
|         else if (value.is_object())
 | |
|             return value.as_object().size();
 | |
|         return (size_t)0;
 | |
|     };
 | |
| 
 | |
|     if (index.is_valid()) {
 | |
|         auto* path = static_cast<JsonPath const*>(index.internal_data());
 | |
|         return do_count(path->resolve(m_object.json));
 | |
|     } else {
 | |
|         return do_count(m_object.json);
 | |
|     }
 | |
| }
 | |
| 
 | |
| DeprecatedString RemoteObjectPropertyModel::column_name(int column) const
 | |
| {
 | |
|     switch (column) {
 | |
|     case Column::Name:
 | |
|         return "Name";
 | |
|     case Column::Value:
 | |
|         return "Value";
 | |
|     }
 | |
|     VERIFY_NOT_REACHED();
 | |
| }
 | |
| 
 | |
| GUI::Variant RemoteObjectPropertyModel::data(const GUI::ModelIndex& index, GUI::ModelRole role) const
 | |
| {
 | |
|     auto* path = static_cast<JsonPath const*>(index.internal_data());
 | |
|     if (!path)
 | |
|         return {};
 | |
| 
 | |
|     if (role == GUI::ModelRole::Display) {
 | |
|         switch (index.column()) {
 | |
|         case Column::Name:
 | |
|             return path->last().to_deprecated_string();
 | |
|         case Column::Value: {
 | |
|             auto data = path->resolve(m_object.json);
 | |
|             if (data.is_array())
 | |
|                 return DeprecatedString::formatted("<Array with {} element{}", data.as_array().size(), data.as_array().size() == 1 ? ">" : "s>");
 | |
|             if (data.is_object())
 | |
|                 return DeprecatedString::formatted("<Object with {} entr{}", data.as_object().size(), data.as_object().size() == 1 ? "y>" : "ies>");
 | |
|             return data;
 | |
|         }
 | |
|         }
 | |
|     }
 | |
|     return {};
 | |
| }
 | |
| 
 | |
| void RemoteObjectPropertyModel::set_data(const GUI::ModelIndex& index, const GUI::Variant& new_value)
 | |
| {
 | |
|     if (!index.is_valid())
 | |
|         return;
 | |
| 
 | |
|     auto* path = static_cast<JsonPath const*>(index.internal_data());
 | |
|     if (path->size() != 1)
 | |
|         return;
 | |
| 
 | |
|     FlatPtr address = m_object.address;
 | |
|     RemoteProcess::the().set_property(address, path->first().to_deprecated_string(), new_value.to_deprecated_string());
 | |
|     did_update();
 | |
| }
 | |
| 
 | |
| GUI::ModelIndex RemoteObjectPropertyModel::index(int row, int column, const GUI::ModelIndex& parent) const
 | |
| {
 | |
|     auto const& parent_path = parent.is_valid() ? *static_cast<JsonPath const*>(parent.internal_data()) : JsonPath {};
 | |
| 
 | |
|     auto nth_child = [&](int n, JsonValue const& value) -> JsonPath const* {
 | |
|         auto path = make<JsonPath>();
 | |
|         path->extend(parent_path);
 | |
|         int row_index = n;
 | |
|         if (value.is_object()) {
 | |
|             DeprecatedString property_name;
 | |
|             auto& object = value.as_object();
 | |
|             object.for_each_member([&](auto& name, auto&) {
 | |
|                 if (row_index > 0) {
 | |
|                     --row_index;
 | |
|                 } else if (row_index == 0) {
 | |
|                     property_name = name;
 | |
|                     --row_index;
 | |
|                 }
 | |
|             });
 | |
|             if (property_name.is_null())
 | |
|                 return nullptr;
 | |
| 
 | |
|             path->append({ property_name });
 | |
|             m_paths.append(move(path));
 | |
|         } else if (value.is_array()) {
 | |
|             path->append(JsonPathElement { (size_t)n });
 | |
|             m_paths.append(move(path));
 | |
|         } else {
 | |
|             return nullptr;
 | |
|         }
 | |
|         return &m_paths.last();
 | |
|     };
 | |
| 
 | |
|     if (!parent.is_valid()) {
 | |
|         if (m_object.json.is_empty())
 | |
|             return {};
 | |
|     }
 | |
| 
 | |
|     auto index_path = cached_path_at(row, parent_path);
 | |
| 
 | |
|     if (!index_path)
 | |
|         index_path = nth_child(row, parent_path.resolve(m_object.json));
 | |
| 
 | |
|     if (!index_path)
 | |
|         return {};
 | |
| 
 | |
|     return create_index(row, column, index_path);
 | |
| }
 | |
| 
 | |
| GUI::ModelIndex RemoteObjectPropertyModel::parent_index(const GUI::ModelIndex& index) const
 | |
| {
 | |
|     if (!index.is_valid())
 | |
|         return index;
 | |
| 
 | |
|     auto path = *static_cast<JsonPath const*>(index.internal_data());
 | |
|     if (path.is_empty())
 | |
|         return {};
 | |
| 
 | |
|     path.take_last();
 | |
|     if (path.is_empty())
 | |
|         return {};
 | |
| 
 | |
|     auto* cpath = find_cached_path(path);
 | |
|     if (cpath) {
 | |
|         int index_in_parent = 0;
 | |
|         if (cpath->last().kind() == JsonPathElement::Kind::Index)
 | |
|             index_in_parent = cpath->last().index();
 | |
|         else if (cpath->last().kind() == JsonPathElement::Kind::Key) {
 | |
|             auto path_copy = path;
 | |
|             auto last = path_copy.take_last();
 | |
|             bool found = false;
 | |
|             path_copy.resolve(m_object.json).as_object().for_each_member([&](auto& name, auto&) {
 | |
|                 if (!found) {
 | |
|                     if (last.key() == name)
 | |
|                         found = true;
 | |
|                     else
 | |
|                         index_in_parent++;
 | |
|                 }
 | |
|             });
 | |
|         }
 | |
|         return create_index(index_in_parent, 0, cpath);
 | |
|     }
 | |
| 
 | |
|     dbgln("No cached path found for path {}", path.to_deprecated_string());
 | |
|     return {};
 | |
| }
 | |
| 
 | |
| JsonPath const* RemoteObjectPropertyModel::cached_path_at(int n, Vector<JsonPathElement> const& prefix) const
 | |
| {
 | |
|     // FIXME: ModelIndex wants a void*, so we have to keep these
 | |
|     //        indices alive, but allocating a new path every time
 | |
|     //        we're asked for an index is silly, so we have to look for existing ones first.
 | |
|     JsonPath const* index_path = nullptr;
 | |
|     int row_index = n;
 | |
|     for (auto& path : m_paths) {
 | |
|         if (path.size() != prefix.size() + 1)
 | |
|             continue;
 | |
| 
 | |
|         for (size_t i = 0; i < prefix.size(); ++i) {
 | |
|             if (path[i] != prefix[i])
 | |
|                 goto do_continue;
 | |
|         }
 | |
| 
 | |
|         if (row_index == 0) {
 | |
|             index_path = &path;
 | |
|             break;
 | |
|         }
 | |
|         --row_index;
 | |
|     do_continue:;
 | |
|     }
 | |
| 
 | |
|     return index_path;
 | |
| };
 | |
| 
 | |
| JsonPath const* RemoteObjectPropertyModel::find_cached_path(Vector<JsonPathElement> const& path) const
 | |
| {
 | |
|     for (auto& cpath : m_paths) {
 | |
|         if (cpath.size() != path.size())
 | |
|             continue;
 | |
| 
 | |
|         for (size_t i = 0; i < cpath.size(); ++i) {
 | |
|             if (cpath[i] != path[i])
 | |
|                 goto do_continue;
 | |
|         }
 | |
| 
 | |
|         return &cpath;
 | |
|     do_continue:;
 | |
|     }
 | |
| 
 | |
|     return nullptr;
 | |
| }
 | |
| 
 | |
| }
 |