|
@@ -33,8 +33,8 @@
|
|
#include "core/config/project_settings.h"
|
|
#include "core/config/project_settings.h"
|
|
#include "core/debugger/debugger_marshalls.h"
|
|
#include "core/debugger/debugger_marshalls.h"
|
|
#include "core/io/json.h"
|
|
#include "core/io/json.h"
|
|
|
|
+#include "core/io/marshalls.h"
|
|
#include "editor/debugger/script_editor_debugger.h"
|
|
#include "editor/debugger/script_editor_debugger.h"
|
|
-#include "editor/doc_tools.h"
|
|
|
|
#include "editor/editor_log.h"
|
|
#include "editor/editor_log.h"
|
|
#include "editor/editor_node.h"
|
|
#include "editor/editor_node.h"
|
|
#include "editor/editor_settings.h"
|
|
#include "editor/editor_settings.h"
|
|
@@ -186,6 +186,8 @@ void DebugAdapterProtocol::reset_stack_info() {
|
|
|
|
|
|
stackframe_list.clear();
|
|
stackframe_list.clear();
|
|
variable_list.clear();
|
|
variable_list.clear();
|
|
|
|
+ object_list.clear();
|
|
|
|
+ object_pending_set.clear();
|
|
}
|
|
}
|
|
|
|
|
|
int DebugAdapterProtocol::parse_variant(const Variant &p_var) {
|
|
int DebugAdapterProtocol::parse_variant(const Variant &p_var) {
|
|
@@ -671,12 +673,166 @@ int DebugAdapterProtocol::parse_variant(const Variant &p_var) {
|
|
variable_list.insert(id, arr);
|
|
variable_list.insert(id, arr);
|
|
return id;
|
|
return id;
|
|
}
|
|
}
|
|
|
|
+ case Variant::OBJECT: {
|
|
|
|
+ // Objects have to be requested from the debuggee. This has do be done
|
|
|
|
+ // in a lazy way, as retrieving object properties takes time.
|
|
|
|
+ EncodedObjectAsID *encoded_obj = Object::cast_to<EncodedObjectAsID>(p_var);
|
|
|
|
+
|
|
|
|
+ // Object may be null; in that case, return early.
|
|
|
|
+ if (!encoded_obj) {
|
|
|
|
+ return 0;
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ // Object may have been already requested.
|
|
|
|
+ ObjectID object_id = encoded_obj->get_object_id();
|
|
|
|
+ if (object_list.has(object_id)) {
|
|
|
|
+ return object_list[object_id];
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ // Queue requesting the object.
|
|
|
|
+ int id = variable_id++;
|
|
|
|
+ object_list.insert(object_id, id);
|
|
|
|
+ return id;
|
|
|
|
+ }
|
|
default:
|
|
default:
|
|
// Simple atomic stuff, or too complex to be manipulated
|
|
// Simple atomic stuff, or too complex to be manipulated
|
|
return 0;
|
|
return 0;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
|
|
+void DebugAdapterProtocol::parse_object(SceneDebuggerObject &p_obj) {
|
|
|
|
+ // If the object is not on the pending list, we weren't expecting it. Ignore it.
|
|
|
|
+ ObjectID object_id = p_obj.id;
|
|
|
|
+ if (!object_pending_set.erase(object_id)) {
|
|
|
|
+ return;
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ // Populate DAP::Variable's with the object's properties. These properties will be divided by categories.
|
|
|
|
+ Array properties;
|
|
|
|
+ Array script_members;
|
|
|
|
+ Array script_constants;
|
|
|
|
+ Array script_node;
|
|
|
|
+ DAP::Variable node_type;
|
|
|
|
+ Array node_properties;
|
|
|
|
+
|
|
|
|
+ for (SceneDebuggerObject::SceneDebuggerProperty &property : p_obj.properties) {
|
|
|
|
+ PropertyInfo &info = property.first;
|
|
|
|
+
|
|
|
|
+ // Script members ("Members/" prefix)
|
|
|
|
+ if (info.name.begins_with("Members/")) {
|
|
|
|
+ info.name = info.name.trim_prefix("Members/");
|
|
|
|
+ script_members.push_back(parse_object_variable(property));
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ // Script constants ("Constants/" prefix)
|
|
|
|
+ else if (info.name.begins_with("Constants/")) {
|
|
|
|
+ info.name = info.name.trim_prefix("Constants/");
|
|
|
|
+ script_constants.push_back(parse_object_variable(property));
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ // Script node ("Node/" prefix)
|
|
|
|
+ else if (info.name.begins_with("Node/")) {
|
|
|
|
+ info.name = info.name.trim_prefix("Node/");
|
|
|
|
+ script_node.push_back(parse_object_variable(property));
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ // Regular categories (with type Variant::NIL)
|
|
|
|
+ else if (info.type == Variant::NIL) {
|
|
|
|
+ if (!node_properties.is_empty()) {
|
|
|
|
+ node_type.value = itos(node_properties.size());
|
|
|
|
+ variable_list.insert(node_type.variablesReference, node_properties.duplicate());
|
|
|
|
+ properties.push_back(node_type.to_json());
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ node_type.name = info.name;
|
|
|
|
+ node_type.type = "Category";
|
|
|
|
+ node_type.variablesReference = variable_id++;
|
|
|
|
+ node_properties.clear();
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ // Regular properties.
|
|
|
|
+ else {
|
|
|
|
+ node_properties.push_back(parse_object_variable(property));
|
|
|
|
+ }
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ // Add the last category.
|
|
|
|
+ if (!node_properties.is_empty()) {
|
|
|
|
+ node_type.value = itos(node_properties.size());
|
|
|
|
+ variable_list.insert(node_type.variablesReference, node_properties.duplicate());
|
|
|
|
+ properties.push_back(node_type.to_json());
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ // Add the script categories, in reverse order to be at the front of the array:
|
|
|
|
+ // ( [members; constants; node; category1; category2; ...] )
|
|
|
|
+ if (!script_node.is_empty()) {
|
|
|
|
+ DAP::Variable node;
|
|
|
|
+ node.name = "Node";
|
|
|
|
+ node.type = "Category";
|
|
|
|
+ node.value = itos(script_node.size());
|
|
|
|
+ node.variablesReference = variable_id++;
|
|
|
|
+ variable_list.insert(node.variablesReference, script_node);
|
|
|
|
+ properties.push_front(node.to_json());
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ if (!script_constants.is_empty()) {
|
|
|
|
+ DAP::Variable constants;
|
|
|
|
+ constants.name = "Constants";
|
|
|
|
+ constants.type = "Category";
|
|
|
|
+ constants.value = itos(script_constants.size());
|
|
|
|
+ constants.variablesReference = variable_id++;
|
|
|
|
+ variable_list.insert(constants.variablesReference, script_constants);
|
|
|
|
+ properties.push_front(constants.to_json());
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ if (!script_members.is_empty()) {
|
|
|
|
+ DAP::Variable members;
|
|
|
|
+ members.name = "Members";
|
|
|
|
+ members.type = "Category";
|
|
|
|
+ members.value = itos(script_members.size());
|
|
|
|
+ members.variablesReference = variable_id++;
|
|
|
|
+ variable_list.insert(members.variablesReference, script_members);
|
|
|
|
+ properties.push_front(members.to_json());
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ ERR_FAIL_COND(!object_list.has(object_id));
|
|
|
|
+ variable_list.insert(object_list[object_id], properties);
|
|
|
|
+}
|
|
|
|
+
|
|
|
|
+const Variant DebugAdapterProtocol::parse_object_variable(const SceneDebuggerObject::SceneDebuggerProperty &p_property) {
|
|
|
|
+ const PropertyInfo &info = p_property.first;
|
|
|
|
+ const Variant &value = p_property.second;
|
|
|
|
+
|
|
|
|
+ DAP::Variable var;
|
|
|
|
+ var.name = info.name;
|
|
|
|
+ var.type = Variant::get_type_name(info.type);
|
|
|
|
+ var.value = value;
|
|
|
|
+ var.variablesReference = parse_variant(value);
|
|
|
|
+
|
|
|
|
+ return var.to_json();
|
|
|
|
+}
|
|
|
|
+
|
|
|
|
+ObjectID DebugAdapterProtocol::search_object_id(DAPVarID p_var_id) {
|
|
|
|
+ for (const KeyValue<ObjectID, DAPVarID> &E : object_list) {
|
|
|
|
+ if (E.value == p_var_id) {
|
|
|
|
+ return E.key;
|
|
|
|
+ }
|
|
|
|
+ }
|
|
|
|
+ return ObjectID();
|
|
|
|
+}
|
|
|
|
+
|
|
|
|
+bool DebugAdapterProtocol::request_remote_object(const ObjectID &p_object_id) {
|
|
|
|
+ // If the object is already on the pending list, we don't need to request it again.
|
|
|
|
+ if (object_pending_set.has(p_object_id)) {
|
|
|
|
+ return false;
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ EditorDebuggerNode::get_singleton()->get_default_debugger()->request_remote_object(p_object_id);
|
|
|
|
+ object_pending_set.insert(p_object_id);
|
|
|
|
+
|
|
|
|
+ return true;
|
|
|
|
+}
|
|
|
|
+
|
|
bool DebugAdapterProtocol::process_message(const String &p_text) {
|
|
bool DebugAdapterProtocol::process_message(const String &p_text) {
|
|
JSON json;
|
|
JSON json;
|
|
ERR_FAIL_COND_V_MSG(json.parse(p_text) != OK, true, "Malformed message!");
|
|
ERR_FAIL_COND_V_MSG(json.parse(p_text) != OK, true, "Malformed message!");
|
|
@@ -986,6 +1142,14 @@ void DebugAdapterProtocol::on_debug_data(const String &p_msg, const Array &p_dat
|
|
return;
|
|
return;
|
|
}
|
|
}
|
|
|
|
|
|
|
|
+ if (p_msg == "scene:inspect_object") {
|
|
|
|
+ // An object was requested from the debuggee; parse it.
|
|
|
|
+ SceneDebuggerObject remote_obj;
|
|
|
|
+ remote_obj.deserialize(p_data);
|
|
|
|
+
|
|
|
|
+ parse_object(remote_obj);
|
|
|
|
+ }
|
|
|
|
+
|
|
notify_custom_data(p_msg, p_data);
|
|
notify_custom_data(p_msg, p_data);
|
|
}
|
|
}
|
|
|
|
|