mirror of
https://gitflic.ru/project/openide/openide.git
synced 2026-01-10 18:09:38 +07:00
(cherry picked from commit c69c6d560f47552b736cd530106447de19e85e90) GitOrigin-RevId: a29879d863e635020caf4322f8378b5789246a93
433 lines
16 KiB
Java
433 lines
16 KiB
Java
// Copyright 2000-2019 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license that can be found in the LICENSE file.
|
|
package com.jetbrains.python.debugger.pydev;
|
|
|
|
import com.google.common.collect.Lists;
|
|
import com.intellij.openapi.util.Pair;
|
|
import com.intellij.openapi.util.text.StringUtil;
|
|
import com.intellij.util.io.URLUtil;
|
|
import com.jetbrains.python.debugger.*;
|
|
import com.thoughtworks.xstream.io.naming.NoNameCoder;
|
|
import com.thoughtworks.xstream.io.xml.XppReader;
|
|
import org.jetbrains.annotations.Contract;
|
|
import org.jetbrains.annotations.NotNull;
|
|
import org.xmlpull.mxp1.MXParser;
|
|
|
|
import java.io.StringReader;
|
|
import java.util.LinkedList;
|
|
import java.util.List;
|
|
|
|
|
|
public class ProtocolParser {
|
|
|
|
private ProtocolParser() {
|
|
}
|
|
|
|
public static PySignature parseCallSignature(String payload) throws PyDebuggerException {
|
|
final XppReader reader = openReader(payload, true);
|
|
reader.moveDown();
|
|
if (!"call_signature".equals(reader.getNodeName())) {
|
|
throw new PyDebuggerException("Expected <call_signature>, found " + reader.getNodeName());
|
|
}
|
|
String file = reader.getAttribute("file");
|
|
if (file == null) file = "";
|
|
final String name = readString(reader, "name", "");
|
|
PySignature signature = new PySignature(file, name);
|
|
|
|
while (reader.hasMoreChildren()) {
|
|
reader.moveDown();
|
|
if ("arg".equals(reader.getNodeName())) {
|
|
signature.addArgument(readString(reader, "name", ""), readString(reader, "type", ""));
|
|
}
|
|
else if ("return".equals(reader.getNodeName())) {
|
|
signature.addReturnType(readString(reader, "type", ""));
|
|
}
|
|
else {
|
|
throw new PyDebuggerException("Expected <arg> or <return>, found " + reader.getNodeName());
|
|
}
|
|
|
|
reader.moveUp();
|
|
}
|
|
|
|
return signature;
|
|
}
|
|
|
|
public static PyConcurrencyEvent parseConcurrencyEvent(String payload,
|
|
final PyPositionConverter positionConverter) throws PyDebuggerException {
|
|
final XppReader reader = openReader(payload, true);
|
|
reader.moveDown();
|
|
String eventName = reader.getNodeName();
|
|
boolean isAsyncio;
|
|
if (eventName.equals("threading_event")) {
|
|
isAsyncio = false;
|
|
}
|
|
else if (eventName.equals("asyncio_event")) {
|
|
isAsyncio = true;
|
|
}
|
|
else {
|
|
throw new PyDebuggerException("Expected <threading_event> or <asyncio_event>, found " + reader.getNodeName());
|
|
}
|
|
|
|
final long time = Long.parseLong(readString(reader, "time", ""));
|
|
final String name = readString(reader, "name", "");
|
|
final String thread_id = readString(reader, "thread_id", "");
|
|
final String type = readString(reader, "type", "");
|
|
PyConcurrencyEvent threadingEvent;
|
|
if (type.equals("lock")) {
|
|
String lock_id = readString(reader, "lock_id", "0");
|
|
threadingEvent = new PyLockEvent(time, thread_id, name, lock_id, isAsyncio);
|
|
}
|
|
else if (type.equals("thread")) {
|
|
String parentThread = readString(reader, "parent", "");
|
|
if (!parentThread.isEmpty()) {
|
|
threadingEvent = new PyThreadEvent(time, thread_id, name, parentThread, isAsyncio);
|
|
}
|
|
else {
|
|
threadingEvent = new PyThreadEvent(time, thread_id, name, isAsyncio);
|
|
}
|
|
}
|
|
else {
|
|
throw new PyDebuggerException("Unknown type " + type);
|
|
}
|
|
|
|
final String eventType = readString(reader, "event", "");
|
|
if (eventType.equals("__init__")) {
|
|
threadingEvent.setType(PyConcurrencyEvent.EventType.CREATE);
|
|
}
|
|
else if (eventType.equals("start")) {
|
|
threadingEvent.setType(PyConcurrencyEvent.EventType.START);
|
|
}
|
|
else if (eventType.equals("join")) {
|
|
threadingEvent.setType(PyConcurrencyEvent.EventType.JOIN);
|
|
}
|
|
else if (eventType.equals("stop")) {
|
|
threadingEvent.setType(PyConcurrencyEvent.EventType.STOP);
|
|
}
|
|
else if (eventType.equals("acquire_begin") || eventType.equals("__enter___begin")
|
|
|| (eventType.equals("get_begin")) || (eventType.equals("put_begin"))) {
|
|
threadingEvent.setType(PyConcurrencyEvent.EventType.ACQUIRE_BEGIN);
|
|
}
|
|
else if (eventType.equals("acquire_end") || eventType.equals("__enter___end")
|
|
|| (eventType.equals("get_end")) || (eventType.equals("put_end"))) {
|
|
threadingEvent.setType(PyConcurrencyEvent.EventType.ACQUIRE_END);
|
|
}
|
|
else if (eventType.startsWith("release") || eventType.startsWith("__exit__")) {
|
|
// we record release begin and end on the Python side, but it is not important info
|
|
// for user. Maybe use it later
|
|
threadingEvent.setType(PyConcurrencyEvent.EventType.RELEASE);
|
|
}
|
|
else {
|
|
throw new PyDebuggerException("Unknown event " + eventType);
|
|
}
|
|
|
|
threadingEvent.setFileName(readString(reader, "file", ""));
|
|
threadingEvent.setLine(Integer.parseInt(readString(reader, "line", "")) - 1);
|
|
reader.moveUp();
|
|
|
|
final List<PyStackFrameInfo> frames = new LinkedList<>();
|
|
while (reader.hasMoreChildren()) {
|
|
reader.moveDown();
|
|
frames.add(parseFrame(reader, thread_id, positionConverter));
|
|
reader.moveUp();
|
|
}
|
|
threadingEvent.setFrames(frames);
|
|
return threadingEvent;
|
|
}
|
|
|
|
public static boolean parseInputCommand(String payload) {
|
|
return payload.equals("True");
|
|
}
|
|
|
|
public static Pair<Boolean, String> parseSetNextStatementCommand(String payload) throws PyDebuggerException {
|
|
String[] values = payload.split("\t");
|
|
if (values.length > 0) {
|
|
boolean success = values[0].equals("True");
|
|
String errorMessage = "Error";
|
|
if (values.length > 1) {
|
|
errorMessage = errorMessage + ": " + values[1];
|
|
}
|
|
return new Pair<>(success, errorMessage);
|
|
}
|
|
else {
|
|
throw new PyDebuggerException("Unable to parse value: " + payload);
|
|
}
|
|
}
|
|
|
|
public static String parseSourceContent(String payload) {
|
|
return payload;
|
|
}
|
|
|
|
public static String encodeExpression(final String expression) {
|
|
return StringUtil.replace(expression, "\n", "@LINE@");
|
|
}
|
|
|
|
public static PyIo parseIo(final String text) throws PyDebuggerException {
|
|
final XppReader reader = openReader(text, true);
|
|
reader.moveDown();
|
|
if (!"io".equals(reader.getNodeName())) {
|
|
throw new PyDebuggerException("Expected <io>, found " + reader.getNodeName());
|
|
}
|
|
final String s = readString(reader, "s", "");
|
|
final int ctx = readInt(reader, "ctx", 1);
|
|
return new PyIo(s, ctx);
|
|
}
|
|
|
|
@NotNull
|
|
public static PyThreadInfo parseThread(final String text, final PyPositionConverter positionConverter) throws PyDebuggerException {
|
|
final XppReader reader = openReader(text, true);
|
|
reader.moveDown();
|
|
if (!"thread".equals(reader.getNodeName())) {
|
|
throw new PyDebuggerException("Expected <thread>, found " + reader.getNodeName());
|
|
}
|
|
|
|
final String id = readString(reader, "id", null);
|
|
final String name = readString(reader, "name", "");
|
|
final int stopReason = readInt(reader, "stop_reason", 0);
|
|
String message = readString(reader, "message", "None");
|
|
if ("None".equals(message) || message.isEmpty()) {
|
|
message = null;
|
|
}
|
|
|
|
final List<PyStackFrameInfo> frames = new LinkedList<>();
|
|
while (reader.hasMoreChildren()) {
|
|
reader.moveDown();
|
|
frames.add(parseFrame(reader, id, positionConverter));
|
|
reader.moveUp();
|
|
}
|
|
|
|
return new PyThreadInfo(id, name, frames, stopReason, message);
|
|
}
|
|
|
|
@NotNull
|
|
public static String getThreadId(@NotNull String payload) {
|
|
return payload.split("\t")[0];
|
|
}
|
|
|
|
private static PyStackFrameInfo parseFrame(final XppReader reader, final String threadId, final PyPositionConverter positionConverter)
|
|
throws PyDebuggerException {
|
|
if (!"frame".equals(reader.getNodeName())) {
|
|
throw new PyDebuggerException("Expected <frame>, found " + reader.getNodeName());
|
|
}
|
|
|
|
final String id = readString(reader, "id", null);
|
|
final String name = readString(reader, "name", null);
|
|
final String file = reader.getAttribute("file");
|
|
final int line = readInt(reader, "line", 0);
|
|
|
|
return new PyStackFrameInfo(threadId, id, name, positionConverter.convertPythonToFrame(file, line));
|
|
}
|
|
|
|
@NotNull
|
|
public static PyDebugValue parseValue(final String text, final PyFrameAccessor frameAccessor) throws PyDebuggerException {
|
|
final XppReader reader = openReader(text, true);
|
|
reader.moveDown();
|
|
return parseValue(reader, frameAccessor);
|
|
}
|
|
|
|
@NotNull
|
|
public static List<PyDebugValue> parseReferrers(final String text, final PyFrameAccessor frameAccessor) throws PyDebuggerException {
|
|
final List<PyDebugValue> values = new LinkedList<>();
|
|
|
|
final XppReader reader = openReader(text, false);
|
|
|
|
while (reader.hasMoreChildren()) {
|
|
reader.moveDown();
|
|
if (reader.getNodeName().equals("var")) {
|
|
PyDebugValue value = parseValue(reader, frameAccessor);
|
|
value.setId(readString(reader, "id", null));
|
|
values.add(value);
|
|
}
|
|
else if (reader.getNodeName().equals("for")) {
|
|
//TODO
|
|
}
|
|
else {
|
|
throw new PyDebuggerException("Expected <var> or <for>, found " + reader.getNodeName());
|
|
}
|
|
reader.moveUp();
|
|
}
|
|
|
|
return values;
|
|
}
|
|
|
|
|
|
@NotNull
|
|
public static List<PyDebugValue> parseValues(final String text, final PyFrameAccessor frameAccessor) throws PyDebuggerException {
|
|
final List<PyDebugValue> values = new LinkedList<>();
|
|
|
|
final XppReader reader = openReader(text, false);
|
|
while (reader.hasMoreChildren()) {
|
|
reader.moveDown();
|
|
values.add(parseValue(reader, frameAccessor));
|
|
reader.moveUp();
|
|
}
|
|
|
|
return values;
|
|
}
|
|
|
|
private static PyDebugValue parseValue(final XppReader reader, PyFrameAccessor frameAccessor) throws PyDebuggerException {
|
|
if (!"var".equals(reader.getNodeName())) {
|
|
throw new PyDebuggerException("Expected <var>, found " + reader.getNodeName());
|
|
}
|
|
|
|
final String name = readString(reader, "name", null);
|
|
final String type = readString(reader, "type", null);
|
|
final String qualifier = readString(reader, "qualifier", ""); //to be able to get the fully qualified type if necessary
|
|
|
|
String value = readString(reader, "value", null);
|
|
final String isContainer = readString(reader, "isContainer", "");
|
|
final String isReturnedValue = readString(reader, "isRetVal", "");
|
|
final String isIPythonHidden = readString(reader, "isIPythonHidden", "");
|
|
final String isErrorOnEval = readString(reader, "isErrorOnEval", "");
|
|
String shape = readString(reader, "shape", "");
|
|
|
|
if (value.startsWith(type + ": ")) { // drop unneeded prefix
|
|
value = value.substring(type.length() + 2);
|
|
}
|
|
if (shape.isEmpty()) shape = null;
|
|
return new PyDebugValue(name, type, qualifier, value, "True".equals(isContainer), shape, "True".equals(isReturnedValue),
|
|
"True".equals(isIPythonHidden), "True".equals(isErrorOnEval), frameAccessor);
|
|
}
|
|
|
|
public static ArrayChunk parseArrayValues(final String text, final PyFrameAccessor frameAccessor) throws PyDebuggerException {
|
|
final XppReader reader = openReader(text, false);
|
|
ArrayChunkBuilder result = new ArrayChunkBuilder();
|
|
if (reader.hasMoreChildren()) {
|
|
reader.moveDown();
|
|
if (!"array".equals(reader.getNodeName())) {
|
|
throw new PyDebuggerException("Expected <array> at first node, found " + reader.getNodeName());
|
|
}
|
|
String slice = readString(reader, "slice", null);
|
|
result.setSlicePresentation(slice);
|
|
result.setRows(readInt(reader, "rows", null));
|
|
result.setColumns(readInt(reader, "cols", null));
|
|
result.setFormat("%" + readString(reader, "format", null));
|
|
result.setType(readString(reader, "type", null));
|
|
result.setMax(readString(reader, "max", null));
|
|
result.setMin(readString(reader, "min", null));
|
|
result.setValue(new PyDebugValue(slice, null, null, null, false, null, false, false, false, frameAccessor));
|
|
reader.moveUp();
|
|
}
|
|
if ("headerdata".equals(reader.peekNextChild())) {
|
|
parseArrayHeaderData(reader, result);
|
|
}
|
|
|
|
Object[][] data = parseArrayValues(reader, frameAccessor);
|
|
result.setData(data);
|
|
return result.createArrayChunk();
|
|
}
|
|
|
|
private static void parseArrayHeaderData(XppReader reader, ArrayChunkBuilder result) throws PyDebuggerException {
|
|
List<String> rowHeaders = Lists.newArrayList();
|
|
List<ArrayChunk.ColHeader> colHeaders = Lists.newArrayList();
|
|
reader.moveDown();
|
|
while (reader.hasMoreChildren()) {
|
|
reader.moveDown();
|
|
if ("colheader".equals(reader.getNodeName())) {
|
|
colHeaders.add(new ArrayChunk.ColHeader(
|
|
readString(reader, "label", null),
|
|
readString(reader, "type", null),
|
|
readString(reader, "format", null),
|
|
readString(reader, "max", null),
|
|
readString(reader, "min", null)));
|
|
}
|
|
else if ("rowheader".equals(reader.getNodeName())) {
|
|
rowHeaders.add(readString(reader, "label", null));
|
|
}
|
|
else {
|
|
throw new PyDebuggerException("Invalid node name" + reader.getNodeName());
|
|
}
|
|
reader.moveUp();
|
|
}
|
|
result.setColHeaders(colHeaders);
|
|
result.setRowLabels(rowHeaders);
|
|
reader.moveUp();
|
|
}
|
|
|
|
public static Object[][] parseArrayValues(final XppReader reader, final PyFrameAccessor frameAccessor) throws PyDebuggerException {
|
|
int rows = -1;
|
|
int cols = -1;
|
|
if (reader.hasMoreChildren()) {
|
|
reader.moveDown();
|
|
if (!"arraydata".equals(reader.getNodeName())) {
|
|
throw new PyDebuggerException("Expected <arraydata> at second node, found " + reader.getNodeName());
|
|
}
|
|
rows = readInt(reader, "rows", null);
|
|
cols = readInt(reader, "cols", null);
|
|
reader.moveUp();
|
|
}
|
|
|
|
if (rows <= 0 || cols <= 0) {
|
|
throw new PyDebuggerException("Array xml: bad rows or columns number: (" + rows + ", " + cols + ")");
|
|
}
|
|
Object[][] values = new Object[rows][cols];
|
|
|
|
int currRow = 0;
|
|
int currCol = 0;
|
|
while (reader.hasMoreChildren()) {
|
|
reader.moveDown();
|
|
if (!"var".equals(reader.getNodeName()) && !"row".equals(reader.getNodeName())) {
|
|
throw new PyDebuggerException("Expected <var> or <row>, found " + reader.getNodeName());
|
|
}
|
|
if ("row".equals(reader.getNodeName())) {
|
|
int index = readInt(reader, "index", null);
|
|
if (currRow != index) {
|
|
throw new PyDebuggerException("Array xml: expected " + currRow + " row, found " + index);
|
|
}
|
|
if (currRow > 0 && currCol != cols) {
|
|
throw new PyDebuggerException("Array xml: expected " + cols + " filled columns, got " + currCol + " instead.");
|
|
}
|
|
currRow += 1;
|
|
currCol = 0;
|
|
}
|
|
else {
|
|
PyDebugValue value = parseValue(reader, frameAccessor);
|
|
values[currRow - 1][currCol] = value.getValue();
|
|
currCol += 1;
|
|
}
|
|
reader.moveUp();
|
|
}
|
|
|
|
return values;
|
|
}
|
|
|
|
public static String parseWarning(final String text) throws PyDebuggerException {
|
|
final XppReader reader = openReader(text, true);
|
|
reader.moveDown();
|
|
return readString(reader, "id", null);
|
|
}
|
|
|
|
private static XppReader openReader(final String text, final boolean checkForContent) throws PyDebuggerException {
|
|
final XppReader reader = new XppReader(new StringReader(text), new MXParser(), new NoNameCoder());
|
|
if (checkForContent && !reader.hasMoreChildren()) {
|
|
throw new PyDebuggerException("Empty frame: " + text);
|
|
}
|
|
return reader;
|
|
}
|
|
|
|
private static String readString(final XppReader reader, final String name, final String fallback) throws PyDebuggerException {
|
|
final String value = read(reader, name, fallback == null);
|
|
return value == null ? fallback : value;
|
|
}
|
|
|
|
private static int readInt(final XppReader reader, final String name, final Integer fallback) throws PyDebuggerException {
|
|
final String value = read(reader, name, fallback == null);
|
|
if (value == null) {
|
|
return fallback;
|
|
}
|
|
try {
|
|
return Integer.parseInt(value);
|
|
}
|
|
catch (NumberFormatException e) {
|
|
throw new PyDebuggerException("Unable to decode " + value + ": " + e.getMessage());
|
|
}
|
|
}
|
|
|
|
@Contract("_, _, true -> !null")
|
|
private static String read(final XppReader reader, final String name, boolean isRequired) throws PyDebuggerException {
|
|
final String value = reader.getAttribute(name);
|
|
if (value == null && isRequired) {
|
|
throw new PyDebuggerException("Attribute not found: " + name);
|
|
}
|
|
return value == null ? null : URLUtil.decode(value);
|
|
}
|
|
}
|