1 | /**************************************************************************/ |
2 | /* debug_adapter_protocol.cpp */ |
3 | /**************************************************************************/ |
4 | /* This file is part of: */ |
5 | /* GODOT ENGINE */ |
6 | /* https://godotengine.org */ |
7 | /**************************************************************************/ |
8 | /* Copyright (c) 2014-present Godot Engine contributors (see AUTHORS.md). */ |
9 | /* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */ |
10 | /* */ |
11 | /* Permission is hereby granted, free of charge, to any person obtaining */ |
12 | /* a copy of this software and associated documentation files (the */ |
13 | /* "Software"), to deal in the Software without restriction, including */ |
14 | /* without limitation the rights to use, copy, modify, merge, publish, */ |
15 | /* distribute, sublicense, and/or sell copies of the Software, and to */ |
16 | /* permit persons to whom the Software is furnished to do so, subject to */ |
17 | /* the following conditions: */ |
18 | /* */ |
19 | /* The above copyright notice and this permission notice shall be */ |
20 | /* included in all copies or substantial portions of the Software. */ |
21 | /* */ |
22 | /* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */ |
23 | /* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */ |
24 | /* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. */ |
25 | /* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */ |
26 | /* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */ |
27 | /* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */ |
28 | /* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ |
29 | /**************************************************************************/ |
30 | |
31 | #include "debug_adapter_protocol.h" |
32 | |
33 | #include "core/config/project_settings.h" |
34 | #include "core/debugger/debugger_marshalls.h" |
35 | #include "core/io/json.h" |
36 | #include "editor/debugger/script_editor_debugger.h" |
37 | #include "editor/doc_tools.h" |
38 | #include "editor/editor_log.h" |
39 | #include "editor/editor_node.h" |
40 | #include "editor/editor_settings.h" |
41 | #include "editor/gui/editor_run_bar.h" |
42 | |
43 | DebugAdapterProtocol *DebugAdapterProtocol::singleton = nullptr; |
44 | |
45 | Error DAPeer::handle_data() { |
46 | int read = 0; |
47 | // Read headers |
48 | if (!has_header) { |
49 | if (!connection->get_available_bytes()) { |
50 | return OK; |
51 | } |
52 | while (true) { |
53 | if (req_pos >= DAP_MAX_BUFFER_SIZE) { |
54 | req_pos = 0; |
55 | ERR_FAIL_V_MSG(ERR_OUT_OF_MEMORY, "Response header too big" ); |
56 | } |
57 | Error err = connection->get_partial_data(&req_buf[req_pos], 1, read); |
58 | if (err != OK) { |
59 | return FAILED; |
60 | } else if (read != 1) { // Busy, wait until next poll |
61 | return ERR_BUSY; |
62 | } |
63 | char *r = (char *)req_buf; |
64 | int l = req_pos; |
65 | |
66 | // End of headers |
67 | if (l > 3 && r[l] == '\n' && r[l - 1] == '\r' && r[l - 2] == '\n' && r[l - 3] == '\r') { |
68 | r[l - 3] = '\0'; // Null terminate to read string |
69 | String ; |
70 | header.parse_utf8(r); |
71 | content_length = header.substr(16).to_int(); |
72 | has_header = true; |
73 | req_pos = 0; |
74 | break; |
75 | } |
76 | req_pos++; |
77 | } |
78 | } |
79 | if (has_header) { |
80 | while (req_pos < content_length) { |
81 | if (content_length >= DAP_MAX_BUFFER_SIZE) { |
82 | req_pos = 0; |
83 | has_header = false; |
84 | ERR_FAIL_COND_V_MSG(req_pos >= DAP_MAX_BUFFER_SIZE, ERR_OUT_OF_MEMORY, "Response content too big" ); |
85 | } |
86 | Error err = connection->get_partial_data(&req_buf[req_pos], content_length - req_pos, read); |
87 | if (err != OK) { |
88 | return FAILED; |
89 | } else if (read < content_length - req_pos) { |
90 | return ERR_BUSY; |
91 | } |
92 | req_pos += read; |
93 | } |
94 | |
95 | // Parse data |
96 | String msg; |
97 | msg.parse_utf8((const char *)req_buf, req_pos); |
98 | |
99 | // Apply a timestamp if it there's none yet |
100 | if (!timestamp) { |
101 | timestamp = OS::get_singleton()->get_ticks_msec(); |
102 | } |
103 | |
104 | // Response |
105 | if (DebugAdapterProtocol::get_singleton()->process_message(msg)) { |
106 | // Reset to read again |
107 | req_pos = 0; |
108 | has_header = false; |
109 | timestamp = 0; |
110 | } |
111 | } |
112 | return OK; |
113 | } |
114 | |
115 | Error DAPeer::send_data() { |
116 | while (res_queue.size()) { |
117 | Dictionary data = res_queue.front()->get(); |
118 | if (!data.has("seq" )) { |
119 | data["seq" ] = ++seq; |
120 | } |
121 | String formatted_data = format_output(data); |
122 | |
123 | int data_sent = 0; |
124 | while (data_sent < formatted_data.length()) { |
125 | int curr_sent = 0; |
126 | Error err = connection->put_partial_data((const uint8_t *)formatted_data.utf8().get_data(), formatted_data.size() - data_sent - 1, curr_sent); |
127 | if (err != OK) { |
128 | return err; |
129 | } |
130 | data_sent += curr_sent; |
131 | } |
132 | res_queue.pop_front(); |
133 | } |
134 | return OK; |
135 | } |
136 | |
137 | String DAPeer::format_output(const Dictionary &p_params) const { |
138 | String response = Variant(p_params).to_json_string(); |
139 | String = "Content-Length: " ; |
140 | CharString charstr = response.utf8(); |
141 | size_t len = charstr.length(); |
142 | header += itos(len); |
143 | header += "\r\n\r\n" ; |
144 | |
145 | return header + response; |
146 | } |
147 | |
148 | Error DebugAdapterProtocol::on_client_connected() { |
149 | ERR_FAIL_COND_V_MSG(clients.size() >= DAP_MAX_CLIENTS, FAILED, "Max client limits reached" ); |
150 | |
151 | Ref<StreamPeerTCP> tcp_peer = server->take_connection(); |
152 | tcp_peer->set_no_delay(true); |
153 | Ref<DAPeer> peer = memnew(DAPeer); |
154 | peer->connection = tcp_peer; |
155 | clients.push_back(peer); |
156 | |
157 | EditorDebuggerNode::get_singleton()->get_default_debugger()->set_move_to_foreground(false); |
158 | EditorNode::get_log()->add_message("[DAP] Connection Taken" , EditorLog::MSG_TYPE_EDITOR); |
159 | return OK; |
160 | } |
161 | |
162 | void DebugAdapterProtocol::on_client_disconnected(const Ref<DAPeer> &p_peer) { |
163 | clients.erase(p_peer); |
164 | if (!clients.size()) { |
165 | reset_ids(); |
166 | EditorDebuggerNode::get_singleton()->get_default_debugger()->set_move_to_foreground(true); |
167 | } |
168 | EditorNode::get_log()->add_message("[DAP] Disconnected" , EditorLog::MSG_TYPE_EDITOR); |
169 | } |
170 | |
171 | void DebugAdapterProtocol::reset_current_info() { |
172 | _current_request = "" ; |
173 | _current_peer.unref(); |
174 | } |
175 | |
176 | void DebugAdapterProtocol::reset_ids() { |
177 | breakpoint_id = 0; |
178 | breakpoint_list.clear(); |
179 | |
180 | reset_stack_info(); |
181 | } |
182 | |
183 | void DebugAdapterProtocol::reset_stack_info() { |
184 | stackframe_id = 0; |
185 | variable_id = 1; |
186 | |
187 | stackframe_list.clear(); |
188 | variable_list.clear(); |
189 | } |
190 | |
191 | int DebugAdapterProtocol::parse_variant(const Variant &p_var) { |
192 | switch (p_var.get_type()) { |
193 | case Variant::VECTOR2: |
194 | case Variant::VECTOR2I: { |
195 | int id = variable_id++; |
196 | Vector2 vec = p_var; |
197 | const String type_scalar = Variant::get_type_name(p_var.get_type() == Variant::VECTOR2 ? Variant::FLOAT : Variant::INT); |
198 | DAP::Variable x, y; |
199 | x.name = "x" ; |
200 | y.name = "y" ; |
201 | x.type = type_scalar; |
202 | y.type = type_scalar; |
203 | x.value = rtos(vec.x); |
204 | y.value = rtos(vec.y); |
205 | |
206 | Array arr; |
207 | arr.push_back(x.to_json()); |
208 | arr.push_back(y.to_json()); |
209 | variable_list.insert(id, arr); |
210 | return id; |
211 | } |
212 | case Variant::RECT2: |
213 | case Variant::RECT2I: { |
214 | int id = variable_id++; |
215 | Rect2 rect = p_var; |
216 | const String type_scalar = Variant::get_type_name(p_var.get_type() == Variant::RECT2 ? Variant::FLOAT : Variant::INT); |
217 | DAP::Variable x, y, w, h; |
218 | x.name = "x" ; |
219 | y.name = "y" ; |
220 | w.name = "w" ; |
221 | h.name = "h" ; |
222 | x.type = type_scalar; |
223 | y.type = type_scalar; |
224 | w.type = type_scalar; |
225 | h.type = type_scalar; |
226 | x.value = rtos(rect.position.x); |
227 | y.value = rtos(rect.position.y); |
228 | w.value = rtos(rect.size.x); |
229 | h.value = rtos(rect.size.y); |
230 | |
231 | Array arr; |
232 | arr.push_back(x.to_json()); |
233 | arr.push_back(y.to_json()); |
234 | arr.push_back(w.to_json()); |
235 | arr.push_back(h.to_json()); |
236 | variable_list.insert(id, arr); |
237 | return id; |
238 | } |
239 | case Variant::VECTOR3: |
240 | case Variant::VECTOR3I: { |
241 | int id = variable_id++; |
242 | Vector3 vec = p_var; |
243 | const String type_scalar = Variant::get_type_name(p_var.get_type() == Variant::VECTOR3 ? Variant::FLOAT : Variant::INT); |
244 | DAP::Variable x, y, z; |
245 | x.name = "x" ; |
246 | y.name = "y" ; |
247 | z.name = "z" ; |
248 | x.type = type_scalar; |
249 | y.type = type_scalar; |
250 | z.type = type_scalar; |
251 | x.value = rtos(vec.x); |
252 | y.value = rtos(vec.y); |
253 | z.value = rtos(vec.z); |
254 | |
255 | Array arr; |
256 | arr.push_back(x.to_json()); |
257 | arr.push_back(y.to_json()); |
258 | arr.push_back(z.to_json()); |
259 | variable_list.insert(id, arr); |
260 | return id; |
261 | } |
262 | case Variant::TRANSFORM2D: { |
263 | int id = variable_id++; |
264 | Transform2D transform = p_var; |
265 | const String type_vec2 = Variant::get_type_name(Variant::VECTOR2); |
266 | DAP::Variable x, y, origin; |
267 | x.name = "x" ; |
268 | y.name = "y" ; |
269 | origin.name = "origin" ; |
270 | x.type = type_vec2; |
271 | y.type = type_vec2; |
272 | origin.type = type_vec2; |
273 | x.value = transform.columns[0]; |
274 | y.value = transform.columns[1]; |
275 | origin.value = transform.columns[2]; |
276 | x.variablesReference = parse_variant(transform.columns[0]); |
277 | y.variablesReference = parse_variant(transform.columns[1]); |
278 | origin.variablesReference = parse_variant(transform.columns[2]); |
279 | |
280 | Array arr; |
281 | arr.push_back(x.to_json()); |
282 | arr.push_back(y.to_json()); |
283 | arr.push_back(origin.to_json()); |
284 | variable_list.insert(id, arr); |
285 | return id; |
286 | } |
287 | case Variant::PLANE: { |
288 | int id = variable_id++; |
289 | Plane plane = p_var; |
290 | DAP::Variable d, normal; |
291 | d.name = "d" ; |
292 | normal.name = "normal" ; |
293 | d.type = Variant::get_type_name(Variant::FLOAT); |
294 | normal.type = Variant::get_type_name(Variant::VECTOR3); |
295 | d.value = rtos(plane.d); |
296 | normal.value = plane.normal; |
297 | normal.variablesReference = parse_variant(plane.normal); |
298 | |
299 | Array arr; |
300 | arr.push_back(d.to_json()); |
301 | arr.push_back(normal.to_json()); |
302 | variable_list.insert(id, arr); |
303 | return id; |
304 | } |
305 | case Variant::QUATERNION: { |
306 | int id = variable_id++; |
307 | Quaternion quat = p_var; |
308 | const String type_float = Variant::get_type_name(Variant::FLOAT); |
309 | DAP::Variable x, y, z, w; |
310 | x.name = "x" ; |
311 | y.name = "y" ; |
312 | z.name = "z" ; |
313 | w.name = "w" ; |
314 | x.type = type_float; |
315 | y.type = type_float; |
316 | z.type = type_float; |
317 | w.type = type_float; |
318 | x.value = rtos(quat.x); |
319 | y.value = rtos(quat.y); |
320 | z.value = rtos(quat.z); |
321 | w.value = rtos(quat.w); |
322 | |
323 | Array arr; |
324 | arr.push_back(x.to_json()); |
325 | arr.push_back(y.to_json()); |
326 | arr.push_back(z.to_json()); |
327 | arr.push_back(w.to_json()); |
328 | variable_list.insert(id, arr); |
329 | return id; |
330 | } |
331 | case Variant::AABB: { |
332 | int id = variable_id++; |
333 | AABB aabb = p_var; |
334 | const String type_vec3 = Variant::get_type_name(Variant::VECTOR3); |
335 | DAP::Variable position, size; |
336 | position.name = "position" ; |
337 | size.name = "size" ; |
338 | position.type = type_vec3; |
339 | size.type = type_vec3; |
340 | position.value = aabb.position; |
341 | size.value = aabb.size; |
342 | position.variablesReference = parse_variant(aabb.position); |
343 | size.variablesReference = parse_variant(aabb.size); |
344 | |
345 | Array arr; |
346 | arr.push_back(position.to_json()); |
347 | arr.push_back(size.to_json()); |
348 | variable_list.insert(id, arr); |
349 | return id; |
350 | } |
351 | case Variant::BASIS: { |
352 | int id = variable_id++; |
353 | Basis basis = p_var; |
354 | const String type_vec3 = Variant::get_type_name(Variant::VECTOR3); |
355 | DAP::Variable x, y, z; |
356 | x.name = "x" ; |
357 | y.name = "y" ; |
358 | z.name = "z" ; |
359 | x.type = type_vec3; |
360 | y.type = type_vec3; |
361 | z.type = type_vec3; |
362 | x.value = basis.rows[0]; |
363 | y.value = basis.rows[1]; |
364 | z.value = basis.rows[2]; |
365 | x.variablesReference = parse_variant(basis.rows[0]); |
366 | y.variablesReference = parse_variant(basis.rows[1]); |
367 | z.variablesReference = parse_variant(basis.rows[2]); |
368 | |
369 | Array arr; |
370 | arr.push_back(x.to_json()); |
371 | arr.push_back(y.to_json()); |
372 | arr.push_back(z.to_json()); |
373 | variable_list.insert(id, arr); |
374 | return id; |
375 | } |
376 | case Variant::TRANSFORM3D: { |
377 | int id = variable_id++; |
378 | Transform3D transform = p_var; |
379 | DAP::Variable basis, origin; |
380 | basis.name = "basis" ; |
381 | origin.name = "origin" ; |
382 | basis.type = Variant::get_type_name(Variant::BASIS); |
383 | origin.type = Variant::get_type_name(Variant::VECTOR3); |
384 | basis.value = transform.basis; |
385 | origin.value = transform.origin; |
386 | basis.variablesReference = parse_variant(transform.basis); |
387 | origin.variablesReference = parse_variant(transform.origin); |
388 | |
389 | Array arr; |
390 | arr.push_back(basis.to_json()); |
391 | arr.push_back(origin.to_json()); |
392 | variable_list.insert(id, arr); |
393 | return id; |
394 | } |
395 | case Variant::COLOR: { |
396 | int id = variable_id++; |
397 | Color color = p_var; |
398 | const String type_float = Variant::get_type_name(Variant::FLOAT); |
399 | DAP::Variable r, g, b, a; |
400 | r.name = "r" ; |
401 | g.name = "g" ; |
402 | b.name = "b" ; |
403 | a.name = "a" ; |
404 | r.type = type_float; |
405 | g.type = type_float; |
406 | b.type = type_float; |
407 | a.type = type_float; |
408 | r.value = rtos(color.r); |
409 | g.value = rtos(color.g); |
410 | b.value = rtos(color.b); |
411 | a.value = rtos(color.a); |
412 | |
413 | Array arr; |
414 | arr.push_back(r.to_json()); |
415 | arr.push_back(g.to_json()); |
416 | arr.push_back(b.to_json()); |
417 | arr.push_back(a.to_json()); |
418 | variable_list.insert(id, arr); |
419 | return id; |
420 | } |
421 | case Variant::ARRAY: { |
422 | int id = variable_id++; |
423 | Array array = p_var; |
424 | DAP::Variable size; |
425 | size.name = "size" ; |
426 | size.type = Variant::get_type_name(Variant::INT); |
427 | size.value = itos(array.size()); |
428 | |
429 | Array arr; |
430 | arr.push_back(size.to_json()); |
431 | |
432 | for (int i = 0; i < array.size(); i++) { |
433 | DAP::Variable var; |
434 | var.name = itos(i); |
435 | var.type = Variant::get_type_name(array[i].get_type()); |
436 | var.value = array[i]; |
437 | var.variablesReference = parse_variant(array[i]); |
438 | arr.push_back(var.to_json()); |
439 | } |
440 | variable_list.insert(id, arr); |
441 | return id; |
442 | } |
443 | case Variant::DICTIONARY: { |
444 | int id = variable_id++; |
445 | Dictionary dictionary = p_var; |
446 | Array arr; |
447 | |
448 | for (int i = 0; i < dictionary.size(); i++) { |
449 | DAP::Variable var; |
450 | var.name = dictionary.get_key_at_index(i); |
451 | Variant value = dictionary.get_value_at_index(i); |
452 | var.type = Variant::get_type_name(value.get_type()); |
453 | var.value = value; |
454 | var.variablesReference = parse_variant(value); |
455 | arr.push_back(var.to_json()); |
456 | } |
457 | variable_list.insert(id, arr); |
458 | return id; |
459 | } |
460 | case Variant::PACKED_BYTE_ARRAY: { |
461 | int id = variable_id++; |
462 | PackedByteArray array = p_var; |
463 | DAP::Variable size; |
464 | size.name = "size" ; |
465 | size.type = Variant::get_type_name(Variant::INT); |
466 | size.value = itos(array.size()); |
467 | |
468 | Array arr; |
469 | arr.push_back(size.to_json()); |
470 | |
471 | for (int i = 0; i < array.size(); i++) { |
472 | DAP::Variable var; |
473 | var.name = itos(i); |
474 | var.type = "byte" ; |
475 | var.value = itos(array[i]); |
476 | arr.push_back(var.to_json()); |
477 | } |
478 | variable_list.insert(id, arr); |
479 | return id; |
480 | } |
481 | case Variant::PACKED_INT32_ARRAY: { |
482 | int id = variable_id++; |
483 | PackedInt32Array array = p_var; |
484 | DAP::Variable size; |
485 | size.name = "size" ; |
486 | size.type = Variant::get_type_name(Variant::INT); |
487 | size.value = itos(array.size()); |
488 | |
489 | Array arr; |
490 | arr.push_back(size.to_json()); |
491 | |
492 | for (int i = 0; i < array.size(); i++) { |
493 | DAP::Variable var; |
494 | var.name = itos(i); |
495 | var.type = "int" ; |
496 | var.value = itos(array[i]); |
497 | arr.push_back(var.to_json()); |
498 | } |
499 | variable_list.insert(id, arr); |
500 | return id; |
501 | } |
502 | case Variant::PACKED_INT64_ARRAY: { |
503 | int id = variable_id++; |
504 | PackedInt64Array array = p_var; |
505 | DAP::Variable size; |
506 | size.name = "size" ; |
507 | size.type = Variant::get_type_name(Variant::INT); |
508 | size.value = itos(array.size()); |
509 | |
510 | Array arr; |
511 | arr.push_back(size.to_json()); |
512 | |
513 | for (int i = 0; i < array.size(); i++) { |
514 | DAP::Variable var; |
515 | var.name = itos(i); |
516 | var.type = "long" ; |
517 | var.value = itos(array[i]); |
518 | arr.push_back(var.to_json()); |
519 | } |
520 | variable_list.insert(id, arr); |
521 | return id; |
522 | } |
523 | case Variant::PACKED_FLOAT32_ARRAY: { |
524 | int id = variable_id++; |
525 | PackedFloat32Array array = p_var; |
526 | DAP::Variable size; |
527 | size.name = "size" ; |
528 | size.type = Variant::get_type_name(Variant::INT); |
529 | size.value = itos(array.size()); |
530 | |
531 | Array arr; |
532 | arr.push_back(size.to_json()); |
533 | |
534 | for (int i = 0; i < array.size(); i++) { |
535 | DAP::Variable var; |
536 | var.name = itos(i); |
537 | var.type = "float" ; |
538 | var.value = rtos(array[i]); |
539 | arr.push_back(var.to_json()); |
540 | } |
541 | variable_list.insert(id, arr); |
542 | return id; |
543 | } |
544 | case Variant::PACKED_FLOAT64_ARRAY: { |
545 | int id = variable_id++; |
546 | PackedFloat64Array array = p_var; |
547 | DAP::Variable size; |
548 | size.name = "size" ; |
549 | size.type = Variant::get_type_name(Variant::INT); |
550 | size.value = itos(array.size()); |
551 | |
552 | Array arr; |
553 | arr.push_back(size.to_json()); |
554 | |
555 | for (int i = 0; i < array.size(); i++) { |
556 | DAP::Variable var; |
557 | var.name = itos(i); |
558 | var.type = "double" ; |
559 | var.value = rtos(array[i]); |
560 | arr.push_back(var.to_json()); |
561 | } |
562 | variable_list.insert(id, arr); |
563 | return id; |
564 | } |
565 | case Variant::PACKED_STRING_ARRAY: { |
566 | int id = variable_id++; |
567 | PackedStringArray array = p_var; |
568 | DAP::Variable size; |
569 | size.name = "size" ; |
570 | size.type = Variant::get_type_name(Variant::INT); |
571 | size.value = itos(array.size()); |
572 | |
573 | Array arr; |
574 | arr.push_back(size.to_json()); |
575 | |
576 | for (int i = 0; i < array.size(); i++) { |
577 | DAP::Variable var; |
578 | var.name = itos(i); |
579 | var.type = Variant::get_type_name(Variant::STRING); |
580 | var.value = array[i]; |
581 | arr.push_back(var.to_json()); |
582 | } |
583 | variable_list.insert(id, arr); |
584 | return id; |
585 | } |
586 | case Variant::PACKED_VECTOR2_ARRAY: { |
587 | int id = variable_id++; |
588 | PackedVector2Array array = p_var; |
589 | DAP::Variable size; |
590 | size.name = "size" ; |
591 | size.type = Variant::get_type_name(Variant::INT); |
592 | size.value = itos(array.size()); |
593 | |
594 | Array arr; |
595 | arr.push_back(size.to_json()); |
596 | |
597 | for (int i = 0; i < array.size(); i++) { |
598 | DAP::Variable var; |
599 | var.name = itos(i); |
600 | var.type = Variant::get_type_name(Variant::VECTOR2); |
601 | var.value = array[i]; |
602 | var.variablesReference = parse_variant(array[i]); |
603 | arr.push_back(var.to_json()); |
604 | } |
605 | variable_list.insert(id, arr); |
606 | return id; |
607 | } |
608 | case Variant::PACKED_VECTOR3_ARRAY: { |
609 | int id = variable_id++; |
610 | PackedVector2Array array = p_var; |
611 | DAP::Variable size; |
612 | size.name = "size" ; |
613 | size.type = Variant::get_type_name(Variant::INT); |
614 | size.value = itos(array.size()); |
615 | |
616 | Array arr; |
617 | arr.push_back(size.to_json()); |
618 | |
619 | for (int i = 0; i < array.size(); i++) { |
620 | DAP::Variable var; |
621 | var.name = itos(i); |
622 | var.type = Variant::get_type_name(Variant::VECTOR3); |
623 | var.value = array[i]; |
624 | var.variablesReference = parse_variant(array[i]); |
625 | arr.push_back(var.to_json()); |
626 | } |
627 | variable_list.insert(id, arr); |
628 | return id; |
629 | } |
630 | case Variant::PACKED_COLOR_ARRAY: { |
631 | int id = variable_id++; |
632 | PackedColorArray array = p_var; |
633 | DAP::Variable size; |
634 | size.name = "size" ; |
635 | size.type = Variant::get_type_name(Variant::INT); |
636 | size.value = itos(array.size()); |
637 | |
638 | Array arr; |
639 | arr.push_back(size.to_json()); |
640 | |
641 | for (int i = 0; i < array.size(); i++) { |
642 | DAP::Variable var; |
643 | var.name = itos(i); |
644 | var.type = Variant::get_type_name(Variant::COLOR); |
645 | var.value = array[i]; |
646 | var.variablesReference = parse_variant(array[i]); |
647 | arr.push_back(var.to_json()); |
648 | } |
649 | variable_list.insert(id, arr); |
650 | return id; |
651 | } |
652 | default: |
653 | // Simple atomic stuff, or too complex to be manipulated |
654 | return 0; |
655 | } |
656 | } |
657 | |
658 | bool DebugAdapterProtocol::process_message(const String &p_text) { |
659 | JSON json; |
660 | ERR_FAIL_COND_V_MSG(json.parse(p_text) != OK, true, "Malformed message!" ); |
661 | Dictionary params = json.get_data(); |
662 | bool completed = true; |
663 | |
664 | if (OS::get_singleton()->get_ticks_msec() - _current_peer->timestamp > _request_timeout) { |
665 | Dictionary response = parser->prepare_error_response(params, DAP::ErrorType::TIMEOUT); |
666 | _current_peer->res_queue.push_front(response); |
667 | return true; |
668 | } |
669 | |
670 | // Append "req_" to any command received; prevents name clash with existing functions, and possibly exploiting |
671 | String command = "req_" + (String)params["command" ]; |
672 | if (parser->has_method(command)) { |
673 | _current_request = params["command" ]; |
674 | |
675 | Array args; |
676 | args.push_back(params); |
677 | Dictionary response = parser->callv(command, args); |
678 | if (!response.is_empty()) { |
679 | _current_peer->res_queue.push_front(response); |
680 | } else { |
681 | completed = false; |
682 | } |
683 | } |
684 | |
685 | reset_current_info(); |
686 | return completed; |
687 | } |
688 | |
689 | void DebugAdapterProtocol::notify_initialized() { |
690 | Dictionary event = parser->ev_initialized(); |
691 | _current_peer->res_queue.push_back(event); |
692 | } |
693 | |
694 | void DebugAdapterProtocol::notify_process() { |
695 | String launch_mode = _current_peer->attached ? "attach" : "launch" ; |
696 | |
697 | Dictionary event = parser->ev_process(launch_mode); |
698 | for (List<Ref<DAPeer>>::Element *E = clients.front(); E; E = E->next()) { |
699 | E->get()->res_queue.push_back(event); |
700 | } |
701 | } |
702 | |
703 | void DebugAdapterProtocol::notify_terminated() { |
704 | Dictionary event = parser->ev_terminated(); |
705 | for (List<Ref<DAPeer>>::Element *E = clients.front(); E; E = E->next()) { |
706 | if ((_current_request == "launch" || _current_request == "restart" ) && _current_peer == E->get()) { |
707 | continue; |
708 | } |
709 | E->get()->res_queue.push_back(event); |
710 | } |
711 | } |
712 | |
713 | void DebugAdapterProtocol::notify_exited(const int &p_exitcode) { |
714 | Dictionary event = parser->ev_exited(p_exitcode); |
715 | for (List<Ref<DAPeer>>::Element *E = clients.front(); E; E = E->next()) { |
716 | if ((_current_request == "launch" || _current_request == "restart" ) && _current_peer == E->get()) { |
717 | continue; |
718 | } |
719 | E->get()->res_queue.push_back(event); |
720 | } |
721 | } |
722 | |
723 | void DebugAdapterProtocol::notify_stopped_paused() { |
724 | Dictionary event = parser->ev_stopped_paused(); |
725 | for (List<Ref<DAPeer>>::Element *E = clients.front(); E; E = E->next()) { |
726 | E->get()->res_queue.push_back(event); |
727 | } |
728 | } |
729 | |
730 | void DebugAdapterProtocol::notify_stopped_exception(const String &p_error) { |
731 | Dictionary event = parser->ev_stopped_exception(p_error); |
732 | for (List<Ref<DAPeer>>::Element *E = clients.front(); E; E = E->next()) { |
733 | E->get()->res_queue.push_back(event); |
734 | } |
735 | } |
736 | |
737 | void DebugAdapterProtocol::notify_stopped_breakpoint(const int &p_id) { |
738 | Dictionary event = parser->ev_stopped_breakpoint(p_id); |
739 | for (List<Ref<DAPeer>>::Element *E = clients.front(); E; E = E->next()) { |
740 | E->get()->res_queue.push_back(event); |
741 | } |
742 | } |
743 | |
744 | void DebugAdapterProtocol::notify_stopped_step() { |
745 | Dictionary event = parser->ev_stopped_step(); |
746 | for (List<Ref<DAPeer>>::Element *E = clients.front(); E; E = E->next()) { |
747 | E->get()->res_queue.push_back(event); |
748 | } |
749 | } |
750 | |
751 | void DebugAdapterProtocol::notify_continued() { |
752 | Dictionary event = parser->ev_continued(); |
753 | for (List<Ref<DAPeer>>::Element *E = clients.front(); E; E = E->next()) { |
754 | if (_current_request == "continue" && E->get() == _current_peer) { |
755 | continue; |
756 | } |
757 | E->get()->res_queue.push_back(event); |
758 | } |
759 | |
760 | reset_stack_info(); |
761 | } |
762 | |
763 | void DebugAdapterProtocol::notify_output(const String &p_message) { |
764 | Dictionary event = parser->ev_output(p_message); |
765 | for (List<Ref<DAPeer>>::Element *E = clients.front(); E; E = E->next()) { |
766 | E->get()->res_queue.push_back(event); |
767 | } |
768 | } |
769 | |
770 | void DebugAdapterProtocol::notify_custom_data(const String &p_msg, const Array &p_data) { |
771 | Dictionary event = parser->ev_custom_data(p_msg, p_data); |
772 | for (List<Ref<DAPeer>>::Element *E = clients.front(); E; E = E->next()) { |
773 | Ref<DAPeer> peer = E->get(); |
774 | if (peer->supportsCustomData) { |
775 | peer->res_queue.push_back(event); |
776 | } |
777 | } |
778 | } |
779 | |
780 | void DebugAdapterProtocol::notify_breakpoint(const DAP::Breakpoint &p_breakpoint, const bool &p_enabled) { |
781 | Dictionary event = parser->ev_breakpoint(p_breakpoint, p_enabled); |
782 | for (List<Ref<DAPeer>>::Element *E = clients.front(); E; E = E->next()) { |
783 | if (_current_request == "setBreakpoints" && E->get() == _current_peer) { |
784 | continue; |
785 | } |
786 | E->get()->res_queue.push_back(event); |
787 | } |
788 | } |
789 | |
790 | Array DebugAdapterProtocol::update_breakpoints(const String &p_path, const Array &p_lines) { |
791 | Array updated_breakpoints; |
792 | |
793 | // Add breakpoints |
794 | for (int i = 0; i < p_lines.size(); i++) { |
795 | EditorDebuggerNode::get_singleton()->get_default_debugger()->_set_breakpoint(p_path, p_lines[i], true); |
796 | DAP::Breakpoint breakpoint; |
797 | breakpoint.line = p_lines[i]; |
798 | breakpoint.source.path = p_path; |
799 | |
800 | ERR_FAIL_COND_V(!breakpoint_list.find(breakpoint), Array()); |
801 | updated_breakpoints.push_back(breakpoint_list.find(breakpoint)->get().to_json()); |
802 | } |
803 | |
804 | // Remove breakpoints |
805 | for (List<DAP::Breakpoint>::Element *E = breakpoint_list.front(); E; E = E->next()) { |
806 | DAP::Breakpoint b = E->get(); |
807 | if (b.source.path == p_path && !p_lines.has(b.line)) { |
808 | EditorDebuggerNode::get_singleton()->get_default_debugger()->_set_breakpoint(p_path, b.line, false); |
809 | } |
810 | } |
811 | |
812 | return updated_breakpoints; |
813 | } |
814 | |
815 | void DebugAdapterProtocol::on_debug_paused() { |
816 | if (EditorRunBar::get_singleton()->get_pause_button()->is_pressed()) { |
817 | notify_stopped_paused(); |
818 | } else { |
819 | notify_continued(); |
820 | } |
821 | } |
822 | |
823 | void DebugAdapterProtocol::on_debug_stopped() { |
824 | notify_exited(); |
825 | notify_terminated(); |
826 | } |
827 | |
828 | void DebugAdapterProtocol::on_debug_output(const String &p_message) { |
829 | notify_output(p_message); |
830 | } |
831 | |
832 | void DebugAdapterProtocol::on_debug_breaked(const bool &p_reallydid, const bool &p_can_debug, const String &p_reason, const bool &p_has_stackdump) { |
833 | if (!p_reallydid) { |
834 | notify_continued(); |
835 | return; |
836 | } |
837 | |
838 | if (p_reason == "Breakpoint" ) { |
839 | if (_stepping) { |
840 | notify_stopped_step(); |
841 | _stepping = false; |
842 | } else { |
843 | _processing_breakpoint = true; // Wait for stack_dump to find where the breakpoint happened |
844 | } |
845 | } else { |
846 | notify_stopped_exception(p_reason); |
847 | } |
848 | |
849 | _processing_stackdump = p_has_stackdump; |
850 | } |
851 | |
852 | void DebugAdapterProtocol::on_debug_breakpoint_toggled(const String &p_path, const int &p_line, const bool &p_enabled) { |
853 | DAP::Breakpoint breakpoint; |
854 | breakpoint.verified = true; |
855 | breakpoint.source.path = ProjectSettings::get_singleton()->globalize_path(p_path); |
856 | breakpoint.source.compute_checksums(); |
857 | breakpoint.line = p_line; |
858 | |
859 | if (p_enabled) { |
860 | // Add the breakpoint |
861 | breakpoint.id = breakpoint_id++; |
862 | breakpoint_list.push_back(breakpoint); |
863 | } else { |
864 | // Remove the breakpoint |
865 | List<DAP::Breakpoint>::Element *E = breakpoint_list.find(breakpoint); |
866 | if (E) { |
867 | breakpoint.id = E->get().id; |
868 | breakpoint_list.erase(E); |
869 | } |
870 | } |
871 | |
872 | notify_breakpoint(breakpoint, p_enabled); |
873 | } |
874 | |
875 | void DebugAdapterProtocol::on_debug_stack_dump(const Array &p_stack_dump) { |
876 | if (_processing_breakpoint && !p_stack_dump.is_empty()) { |
877 | // Find existing breakpoint |
878 | Dictionary d = p_stack_dump[0]; |
879 | DAP::Breakpoint breakpoint; |
880 | breakpoint.source.path = ProjectSettings::get_singleton()->globalize_path(d["file" ]); |
881 | breakpoint.line = d["line" ]; |
882 | |
883 | List<DAP::Breakpoint>::Element *E = breakpoint_list.find(breakpoint); |
884 | if (E) { |
885 | notify_stopped_breakpoint(E->get().id); |
886 | } |
887 | |
888 | _processing_breakpoint = false; |
889 | } |
890 | |
891 | stackframe_id = 0; |
892 | stackframe_list.clear(); |
893 | |
894 | // Fill in stacktrace information |
895 | for (int i = 0; i < p_stack_dump.size(); i++) { |
896 | Dictionary stack_info = p_stack_dump[i]; |
897 | DAP::StackFrame stackframe; |
898 | stackframe.id = stackframe_id++; |
899 | stackframe.name = stack_info["function" ]; |
900 | stackframe.line = stack_info["line" ]; |
901 | stackframe.column = 0; |
902 | stackframe.source.path = ProjectSettings::get_singleton()->globalize_path(stack_info["file" ]); |
903 | stackframe.source.compute_checksums(); |
904 | |
905 | // Information for "Locals", "Members" and "Globals" variables respectively |
906 | List<int> scope_ids; |
907 | for (int j = 0; j < 3; j++) { |
908 | scope_ids.push_back(variable_id++); |
909 | } |
910 | |
911 | stackframe_list.insert(stackframe, scope_ids); |
912 | } |
913 | |
914 | _current_frame = 0; |
915 | _processing_stackdump = false; |
916 | } |
917 | |
918 | void DebugAdapterProtocol::on_debug_stack_frame_vars(const int &p_size) { |
919 | _remaining_vars = p_size; |
920 | DAP::StackFrame frame; |
921 | frame.id = _current_frame; |
922 | ERR_FAIL_COND(!stackframe_list.has(frame)); |
923 | List<int> scope_ids = stackframe_list.find(frame)->value; |
924 | for (List<int>::Element *E = scope_ids.front(); E; E = E->next()) { |
925 | int var_id = E->get(); |
926 | if (variable_list.has(var_id)) { |
927 | variable_list.find(var_id)->value.clear(); |
928 | } else { |
929 | variable_list.insert(var_id, Array()); |
930 | } |
931 | } |
932 | } |
933 | |
934 | void DebugAdapterProtocol::on_debug_stack_frame_var(const Array &p_data) { |
935 | DebuggerMarshalls::ScriptStackVariable stack_var; |
936 | stack_var.deserialize(p_data); |
937 | |
938 | ERR_FAIL_COND(stackframe_list.is_empty()); |
939 | DAP::StackFrame frame; |
940 | frame.id = _current_frame; |
941 | |
942 | List<int> scope_ids = stackframe_list.find(frame)->value; |
943 | ERR_FAIL_COND(scope_ids.size() != 3); |
944 | ERR_FAIL_INDEX(stack_var.type, 3); |
945 | int var_id = scope_ids[stack_var.type]; |
946 | |
947 | DAP::Variable variable; |
948 | |
949 | variable.name = stack_var.name; |
950 | variable.value = stack_var.value; |
951 | variable.type = Variant::get_type_name(stack_var.value.get_type()); |
952 | variable.variablesReference = parse_variant(stack_var.value); |
953 | |
954 | variable_list.find(var_id)->value.push_back(variable.to_json()); |
955 | _remaining_vars--; |
956 | } |
957 | |
958 | void DebugAdapterProtocol::on_debug_data(const String &p_msg, const Array &p_data) { |
959 | // Ignore data that is already handled by DAP |
960 | if (p_msg == "debug_enter" || p_msg == "debug_exit" || p_msg == "stack_dump" || p_msg == "stack_frame_vars" || p_msg == "stack_frame_var" || p_msg == "output" || p_msg == "request_quit" ) { |
961 | return; |
962 | } |
963 | |
964 | notify_custom_data(p_msg, p_data); |
965 | } |
966 | |
967 | void DebugAdapterProtocol::poll() { |
968 | if (server->is_connection_available()) { |
969 | on_client_connected(); |
970 | } |
971 | List<Ref<DAPeer>> to_delete; |
972 | for (List<Ref<DAPeer>>::Element *E = clients.front(); E; E = E->next()) { |
973 | Ref<DAPeer> peer = E->get(); |
974 | peer->connection->poll(); |
975 | StreamPeerTCP::Status status = peer->connection->get_status(); |
976 | if (status == StreamPeerTCP::STATUS_NONE || status == StreamPeerTCP::STATUS_ERROR) { |
977 | to_delete.push_back(peer); |
978 | } else { |
979 | _current_peer = peer; |
980 | Error err = peer->handle_data(); |
981 | if (err != OK && err != ERR_BUSY) { |
982 | to_delete.push_back(peer); |
983 | } |
984 | err = peer->send_data(); |
985 | if (err != OK && err != ERR_BUSY) { |
986 | to_delete.push_back(peer); |
987 | } |
988 | } |
989 | } |
990 | |
991 | for (List<Ref<DAPeer>>::Element *E = to_delete.front(); E; E = E->next()) { |
992 | on_client_disconnected(E->get()); |
993 | } |
994 | to_delete.clear(); |
995 | } |
996 | |
997 | Error DebugAdapterProtocol::start(int p_port, const IPAddress &p_bind_ip) { |
998 | _request_timeout = (uint64_t)_EDITOR_GET("network/debug_adapter/request_timeout" ); |
999 | _sync_breakpoints = (bool)_EDITOR_GET("network/debug_adapter/sync_breakpoints" ); |
1000 | _initialized = true; |
1001 | return server->listen(p_port, p_bind_ip); |
1002 | } |
1003 | |
1004 | void DebugAdapterProtocol::stop() { |
1005 | for (List<Ref<DAPeer>>::Element *E = clients.front(); E; E = E->next()) { |
1006 | E->get()->connection->disconnect_from_host(); |
1007 | } |
1008 | |
1009 | clients.clear(); |
1010 | server->stop(); |
1011 | _initialized = false; |
1012 | } |
1013 | |
1014 | DebugAdapterProtocol::DebugAdapterProtocol() { |
1015 | server.instantiate(); |
1016 | singleton = this; |
1017 | parser = memnew(DebugAdapterParser); |
1018 | |
1019 | reset_ids(); |
1020 | |
1021 | EditorRunBar::get_singleton()->get_pause_button()->connect("pressed" , callable_mp(this, &DebugAdapterProtocol::on_debug_paused)); |
1022 | |
1023 | EditorDebuggerNode *debugger_node = EditorDebuggerNode::get_singleton(); |
1024 | debugger_node->connect("breakpoint_toggled" , callable_mp(this, &DebugAdapterProtocol::on_debug_breakpoint_toggled)); |
1025 | |
1026 | debugger_node->get_default_debugger()->connect("stopped" , callable_mp(this, &DebugAdapterProtocol::on_debug_stopped)); |
1027 | debugger_node->get_default_debugger()->connect("output" , callable_mp(this, &DebugAdapterProtocol::on_debug_output)); |
1028 | debugger_node->get_default_debugger()->connect("breaked" , callable_mp(this, &DebugAdapterProtocol::on_debug_breaked)); |
1029 | debugger_node->get_default_debugger()->connect("stack_dump" , callable_mp(this, &DebugAdapterProtocol::on_debug_stack_dump)); |
1030 | debugger_node->get_default_debugger()->connect("stack_frame_vars" , callable_mp(this, &DebugAdapterProtocol::on_debug_stack_frame_vars)); |
1031 | debugger_node->get_default_debugger()->connect("stack_frame_var" , callable_mp(this, &DebugAdapterProtocol::on_debug_stack_frame_var)); |
1032 | debugger_node->get_default_debugger()->connect("debug_data" , callable_mp(this, &DebugAdapterProtocol::on_debug_data)); |
1033 | } |
1034 | |
1035 | DebugAdapterProtocol::~DebugAdapterProtocol() { |
1036 | memdelete(parser); |
1037 | } |
1038 | |