|
33 | 33 | #include "core/config/project_settings.h" |
34 | 34 | #include "core/debugger/debugger_marshalls.h" |
35 | 35 | #include "core/io/json.h" |
| 36 | +#include "core/io/marshalls.h" |
36 | 37 | #include "editor/debugger/script_editor_debugger.h" |
37 | | -#include "editor/doc_tools.h" |
38 | 38 | #include "editor/editor_log.h" |
39 | 39 | #include "editor/editor_node.h" |
40 | 40 | #include "editor/editor_settings.h" |
@@ -186,6 +186,8 @@ void DebugAdapterProtocol::reset_stack_info() { |
186 | 186 |
|
187 | 187 | stackframe_list.clear(); |
188 | 188 | variable_list.clear(); |
| 189 | + object_list.clear(); |
| 190 | + object_pending_set.clear(); |
189 | 191 | } |
190 | 192 |
|
191 | 193 | int DebugAdapterProtocol::parse_variant(const Variant &p_var) { |
@@ -671,12 +673,166 @@ int DebugAdapterProtocol::parse_variant(const Variant &p_var) { |
671 | 673 | variable_list.insert(id, arr); |
672 | 674 | return id; |
673 | 675 | } |
| 676 | + case Variant::OBJECT: { |
| 677 | + // Objects have to be requested from the debuggee. This has do be done |
| 678 | + // in a lazy way, as retrieving object properties takes time. |
| 679 | + EncodedObjectAsID *encoded_obj = Object::cast_to<EncodedObjectAsID>(p_var); |
| 680 | + |
| 681 | + // Object may be null; in that case, return early. |
| 682 | + if (!encoded_obj) { |
| 683 | + return 0; |
| 684 | + } |
| 685 | + |
| 686 | + // Object may have been already requested. |
| 687 | + ObjectID object_id = encoded_obj->get_object_id(); |
| 688 | + if (object_list.has(object_id)) { |
| 689 | + return object_list[object_id]; |
| 690 | + } |
| 691 | + |
| 692 | + // Queue requesting the object. |
| 693 | + int id = variable_id++; |
| 694 | + object_list.insert(object_id, id); |
| 695 | + return id; |
| 696 | + } |
674 | 697 | default: |
675 | 698 | // Simple atomic stuff, or too complex to be manipulated |
676 | 699 | return 0; |
677 | 700 | } |
678 | 701 | } |
679 | 702 |
|
| 703 | +void DebugAdapterProtocol::parse_object(SceneDebuggerObject &p_obj) { |
| 704 | + // If the object is not on the pending list, we weren't expecting it. Ignore it. |
| 705 | + ObjectID object_id = p_obj.id; |
| 706 | + if (!object_pending_set.erase(object_id)) { |
| 707 | + return; |
| 708 | + } |
| 709 | + |
| 710 | + // Populate DAP::Variable's with the object's properties. These properties will be divided by categories. |
| 711 | + Array properties; |
| 712 | + Array script_members; |
| 713 | + Array script_constants; |
| 714 | + Array script_node; |
| 715 | + DAP::Variable node_type; |
| 716 | + Array node_properties; |
| 717 | + |
| 718 | + for (SceneDebuggerObject::SceneDebuggerProperty &property : p_obj.properties) { |
| 719 | + PropertyInfo &info = property.first; |
| 720 | + |
| 721 | + // Script members ("Members/" prefix) |
| 722 | + if (info.name.begins_with("Members/")) { |
| 723 | + info.name = info.name.trim_prefix("Members/"); |
| 724 | + script_members.push_back(parse_object_variable(property)); |
| 725 | + } |
| 726 | + |
| 727 | + // Script constants ("Constants/" prefix) |
| 728 | + else if (info.name.begins_with("Constants/")) { |
| 729 | + info.name = info.name.trim_prefix("Constants/"); |
| 730 | + script_constants.push_back(parse_object_variable(property)); |
| 731 | + } |
| 732 | + |
| 733 | + // Script node ("Node/" prefix) |
| 734 | + else if (info.name.begins_with("Node/")) { |
| 735 | + info.name = info.name.trim_prefix("Node/"); |
| 736 | + script_node.push_back(parse_object_variable(property)); |
| 737 | + } |
| 738 | + |
| 739 | + // Regular categories (with type Variant::NIL) |
| 740 | + else if (info.type == Variant::NIL) { |
| 741 | + if (!node_properties.is_empty()) { |
| 742 | + node_type.value = itos(node_properties.size()); |
| 743 | + variable_list.insert(node_type.variablesReference, node_properties.duplicate()); |
| 744 | + properties.push_back(node_type.to_json()); |
| 745 | + } |
| 746 | + |
| 747 | + node_type.name = info.name; |
| 748 | + node_type.type = "Category"; |
| 749 | + node_type.variablesReference = variable_id++; |
| 750 | + node_properties.clear(); |
| 751 | + } |
| 752 | + |
| 753 | + // Regular properties. |
| 754 | + else { |
| 755 | + node_properties.push_back(parse_object_variable(property)); |
| 756 | + } |
| 757 | + } |
| 758 | + |
| 759 | + // Add the last category. |
| 760 | + if (!node_properties.is_empty()) { |
| 761 | + node_type.value = itos(node_properties.size()); |
| 762 | + variable_list.insert(node_type.variablesReference, node_properties.duplicate()); |
| 763 | + properties.push_back(node_type.to_json()); |
| 764 | + } |
| 765 | + |
| 766 | + // Add the script categories, in reverse order to be at the front of the array: |
| 767 | + // ( [members; constants; node; category1; category2; ...] ) |
| 768 | + if (!script_node.is_empty()) { |
| 769 | + DAP::Variable node; |
| 770 | + node.name = "Node"; |
| 771 | + node.type = "Category"; |
| 772 | + node.value = itos(script_node.size()); |
| 773 | + node.variablesReference = variable_id++; |
| 774 | + variable_list.insert(node.variablesReference, script_node); |
| 775 | + properties.push_front(node.to_json()); |
| 776 | + } |
| 777 | + |
| 778 | + if (!script_constants.is_empty()) { |
| 779 | + DAP::Variable constants; |
| 780 | + constants.name = "Constants"; |
| 781 | + constants.type = "Category"; |
| 782 | + constants.value = itos(script_constants.size()); |
| 783 | + constants.variablesReference = variable_id++; |
| 784 | + variable_list.insert(constants.variablesReference, script_constants); |
| 785 | + properties.push_front(constants.to_json()); |
| 786 | + } |
| 787 | + |
| 788 | + if (!script_members.is_empty()) { |
| 789 | + DAP::Variable members; |
| 790 | + members.name = "Members"; |
| 791 | + members.type = "Category"; |
| 792 | + members.value = itos(script_members.size()); |
| 793 | + members.variablesReference = variable_id++; |
| 794 | + variable_list.insert(members.variablesReference, script_members); |
| 795 | + properties.push_front(members.to_json()); |
| 796 | + } |
| 797 | + |
| 798 | + ERR_FAIL_COND(!object_list.has(object_id)); |
| 799 | + variable_list.insert(object_list[object_id], properties); |
| 800 | +} |
| 801 | + |
| 802 | +const Variant DebugAdapterProtocol::parse_object_variable(const SceneDebuggerObject::SceneDebuggerProperty &p_property) { |
| 803 | + const PropertyInfo &info = p_property.first; |
| 804 | + const Variant &value = p_property.second; |
| 805 | + |
| 806 | + DAP::Variable var; |
| 807 | + var.name = info.name; |
| 808 | + var.type = Variant::get_type_name(info.type); |
| 809 | + var.value = value; |
| 810 | + var.variablesReference = parse_variant(value); |
| 811 | + |
| 812 | + return var.to_json(); |
| 813 | +} |
| 814 | + |
| 815 | +ObjectID DebugAdapterProtocol::search_object_id(DAPVarID p_var_id) { |
| 816 | + for (const KeyValue<ObjectID, DAPVarID> &E : object_list) { |
| 817 | + if (E.value == p_var_id) { |
| 818 | + return E.key; |
| 819 | + } |
| 820 | + } |
| 821 | + return ObjectID(); |
| 822 | +} |
| 823 | + |
| 824 | +bool DebugAdapterProtocol::request_remote_object(const ObjectID &p_object_id) { |
| 825 | + // If the object is already on the pending list, we don't need to request it again. |
| 826 | + if (object_pending_set.has(p_object_id)) { |
| 827 | + return false; |
| 828 | + } |
| 829 | + |
| 830 | + EditorDebuggerNode::get_singleton()->get_default_debugger()->request_remote_object(p_object_id); |
| 831 | + object_pending_set.insert(p_object_id); |
| 832 | + |
| 833 | + return true; |
| 834 | +} |
| 835 | + |
680 | 836 | bool DebugAdapterProtocol::process_message(const String &p_text) { |
681 | 837 | JSON json; |
682 | 838 | 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 |
986 | 1142 | return; |
987 | 1143 | } |
988 | 1144 |
|
| 1145 | + if (p_msg == "scene:inspect_object") { |
| 1146 | + // An object was requested from the debuggee; parse it. |
| 1147 | + SceneDebuggerObject remote_obj; |
| 1148 | + remote_obj.deserialize(p_data); |
| 1149 | + |
| 1150 | + parse_object(remote_obj); |
| 1151 | + } |
| 1152 | + |
989 | 1153 | notify_custom_data(p_msg, p_data); |
990 | 1154 | } |
991 | 1155 |
|
|
0 commit comments