From f96283903424f97cf6adf7a33ada6eb35bf1809e Mon Sep 17 00:00:00 2001 From: Vitaliy Bibaev Date: Tue, 9 Aug 2016 12:58:54 +0300 Subject: [PATCH 001/151] Rename package: org.jetbrains.plugin -> org.jetbrains.debugger.memory --- action/ShowClassesWithDiff.java | 22 + action/ShowClassesWithInstanceAction.java | 22 + action/ShowInstancesByClassAction.java | 57 +++ component/MemoryViewManager.java | 80 +++ component/MemoryViewManagerState.java | 14 + event/MemoryViewManagerListener.java | 11 + sizeof/RetainedSizeFetcher.java | 109 ++++ sizeof/SizeOfEvalStrategy.java | 21 + toolwindow/MemoryViewCondition.java | 45 ++ toolwindow/MemoryViewToolWindowFactory.java | 149 ++++++ utils/AbstractTableColumnDescriptor.java | 21 + utils/AbstractTableModelWithColumns.java | 37 ++ utils/DebugCommand.java | 9 + utils/InstanceJavaValue.java | 31 ++ utils/InstanceValueDescriptor.java | 46 ++ utils/KeyboardUtils.java | 18 + utils/NamesUtils.java | 10 + utils/SingleAlarmWithMutableDelay.java | 23 + view/ClassesFilteredView.java | 199 ++++++++ view/ClassesTable.java | 307 +++++++++++ view/InstancesWindow.java | 531 ++++++++++++++++++++ 21 files changed, 1762 insertions(+) create mode 100644 action/ShowClassesWithDiff.java create mode 100644 action/ShowClassesWithInstanceAction.java create mode 100644 action/ShowInstancesByClassAction.java create mode 100644 component/MemoryViewManager.java create mode 100644 component/MemoryViewManagerState.java create mode 100644 event/MemoryViewManagerListener.java create mode 100644 sizeof/RetainedSizeFetcher.java create mode 100644 sizeof/SizeOfEvalStrategy.java create mode 100644 toolwindow/MemoryViewCondition.java create mode 100644 toolwindow/MemoryViewToolWindowFactory.java create mode 100644 utils/AbstractTableColumnDescriptor.java create mode 100644 utils/AbstractTableModelWithColumns.java create mode 100644 utils/DebugCommand.java create mode 100644 utils/InstanceJavaValue.java create mode 100644 utils/InstanceValueDescriptor.java create mode 100644 utils/KeyboardUtils.java create mode 100644 utils/NamesUtils.java create mode 100644 utils/SingleAlarmWithMutableDelay.java create mode 100644 view/ClassesFilteredView.java create mode 100644 view/ClassesTable.java create mode 100644 view/InstancesWindow.java diff --git a/action/ShowClassesWithDiff.java b/action/ShowClassesWithDiff.java new file mode 100644 index 000000000000..78081dc76baf --- /dev/null +++ b/action/ShowClassesWithDiff.java @@ -0,0 +1,22 @@ +package org.jetbrains.debugger.memory.action; + +import com.intellij.openapi.actionSystem.AnActionEvent; +import com.intellij.openapi.actionSystem.ToggleAction; +import com.intellij.openapi.project.Project; +import org.jetbrains.debugger.memory.component.MemoryViewManager; + +public class ShowClassesWithDiff extends ToggleAction { + @Override + public boolean isSelected(AnActionEvent e) { + Project project = e.getProject(); + return project != null && MemoryViewManager.getInstance(e.getProject()).isNeedShowDiffOnly(); + } + + @Override + public void setSelected(AnActionEvent e, boolean state) { + Project project = e.getProject(); + if (project != null) { + MemoryViewManager.getInstance(project).setShowDiffOnly(state); + } + } +} diff --git a/action/ShowClassesWithInstanceAction.java b/action/ShowClassesWithInstanceAction.java new file mode 100644 index 000000000000..9e30ffda8230 --- /dev/null +++ b/action/ShowClassesWithInstanceAction.java @@ -0,0 +1,22 @@ +package org.jetbrains.debugger.memory.action; + +import com.intellij.openapi.actionSystem.AnActionEvent; +import com.intellij.openapi.actionSystem.ToggleAction; +import com.intellij.openapi.project.Project; +import org.jetbrains.debugger.memory.component.MemoryViewManager; + +public class ShowClassesWithInstanceAction extends ToggleAction { + @Override + public boolean isSelected(AnActionEvent e) { + Project project = e.getProject(); + return project != null && MemoryViewManager.getInstance(project).isNeedShowInstancesOnly(); + } + + @Override + public void setSelected(AnActionEvent e, boolean state) { + Project project = e.getProject(); + if (project != null) { + MemoryViewManager.getInstance(project).setShowWithInstancesOnly(state); + } + } +} diff --git a/action/ShowInstancesByClassAction.java b/action/ShowInstancesByClassAction.java new file mode 100644 index 000000000000..8a96fc9891d2 --- /dev/null +++ b/action/ShowInstancesByClassAction.java @@ -0,0 +1,57 @@ +package org.jetbrains.debugger.memory.action; + +import com.intellij.debugger.ui.impl.watch.NodeDescriptorProvider; +import com.intellij.debugger.ui.tree.NodeDescriptor; +import com.intellij.debugger.ui.tree.ValueDescriptor; +import com.intellij.openapi.actionSystem.AnActionEvent; +import com.intellij.openapi.project.Project; +import com.intellij.openapi.util.text.StringUtil; +import com.intellij.xdebugger.XDebugSession; +import com.intellij.xdebugger.XDebuggerManager; +import com.intellij.xdebugger.frame.XValue; +import com.intellij.xdebugger.impl.ui.tree.actions.XDebuggerTreeActionBase; +import com.intellij.xdebugger.impl.ui.tree.nodes.XValueNodeImpl; +import com.sun.jdi.ObjectReference; +import com.sun.jdi.Value; +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; +import org.jetbrains.debugger.memory.view.InstancesWindow; + +public class ShowInstancesByClassAction extends XDebuggerTreeActionBase { + @Override + protected boolean isEnabled(@NotNull XValueNodeImpl node, @NotNull AnActionEvent e) { + ObjectReference ref = getObjectReference(node); + if(ref != null) { + String text = String.format("Show %s Objects...", StringUtil.getShortName(ref.referenceType().name())); + e.getPresentation().setText(text); + } + + return ref != null; + } + + @Override + protected void perform(XValueNodeImpl node, @NotNull String nodeName, AnActionEvent e) { + Project project = e.getProject(); + if (project != null) { + XDebugSession debugSession = XDebuggerManager.getInstance(project).getCurrentSession(); + ObjectReference ref = getObjectReference(node); + if(debugSession != null && ref != null){ + new InstancesWindow(project, debugSession, ref.referenceType()).show(); + } + } + } + + @Nullable + private ObjectReference getObjectReference(@NotNull XValueNodeImpl node) { + XValue valueContainer = node.getValueContainer(); + if (valueContainer instanceof NodeDescriptorProvider) { + NodeDescriptor descriptor = ((NodeDescriptorProvider) valueContainer).getDescriptor(); + if (descriptor instanceof ValueDescriptor) { + Value value = ((ValueDescriptor)descriptor).getValue(); + return value instanceof ObjectReference ? (ObjectReference) value : null; + } + } + + return null; + } +} diff --git a/component/MemoryViewManager.java b/component/MemoryViewManager.java new file mode 100644 index 000000000000..d7de9e532c95 --- /dev/null +++ b/component/MemoryViewManager.java @@ -0,0 +1,80 @@ +package org.jetbrains.debugger.memory.component; + +import com.intellij.openapi.components.*; +import com.intellij.openapi.project.Project; +import com.intellij.openapi.wm.ToolWindow; +import com.intellij.openapi.wm.ToolWindowManager; +import com.intellij.util.EventDispatcher; +import org.jetbrains.debugger.memory.event.MemoryViewManagerListener; +import org.jetbrains.debugger.memory.toolwindow.MemoryViewToolWindowFactory; +import org.jetbrains.annotations.NotNull; + +@State(name = "MemoryViewSettings", storages = @Storage(StoragePathMacros.WORKSPACE_FILE)) +public class MemoryViewManager extends AbstractProjectComponent + implements PersistentStateComponent { + private final EventDispatcher myDispatcher = + EventDispatcher.create(MemoryViewManagerListener.class); + private MemoryViewManagerState myState = new MemoryViewManagerState(); + + public static MemoryViewManager getInstance(Project project) { + return project.getComponent(MemoryViewManager.class); + } + + protected MemoryViewManager(Project project) { + super(project); + } + + @NotNull + @Override + public MemoryViewManagerState getState() { + return new MemoryViewManagerState(myState); + } + + @Override + public void loadState(MemoryViewManagerState state) { + if (state == null) { + state = new MemoryViewManagerState(); + } + + myState = state; + fireStateChanged(); + } + + public void setShowDiffOnly(boolean value) { + if (myState.isShowWithDiffOnly != value) { + myState.isShowWithDiffOnly = value; + fireStateChanged(); + } + } + + public void setShowWithInstancesOnly(boolean value) { + if (myState.isShowWithInstancesOnly != value) { + myState.isShowWithInstancesOnly = value; + fireStateChanged(); + } + } + + public boolean isNeedShowDiffOnly() { + return myState.isShowWithDiffOnly; + } + + public boolean isNeedShowInstancesOnly() { + return myState.isShowWithInstancesOnly; + } + + public void addMemoryViewManagerListener(MemoryViewManagerListener listener) { + myDispatcher.addListener(listener); + } + + public void removeMemoryViewManagerListener(MemoryViewManagerListener listener) { + myDispatcher.removeListener(listener); + } + + public ToolWindow getToolWindow() { + return ToolWindowManager.getInstance(myProject).getToolWindow(MemoryViewToolWindowFactory.TOOL_WINDOW_ID); + } + + private void fireStateChanged() { + myDispatcher.getMulticaster().stateChanged(new MemoryViewManagerState(myState)); + } +} diff --git a/component/MemoryViewManagerState.java b/component/MemoryViewManagerState.java new file mode 100644 index 000000000000..0694133b5842 --- /dev/null +++ b/component/MemoryViewManagerState.java @@ -0,0 +1,14 @@ +package org.jetbrains.debugger.memory.component; + +public final class MemoryViewManagerState { + public boolean isShowWithInstancesOnly = true; + public boolean isShowWithDiffOnly = false; + + MemoryViewManagerState() { + } + + MemoryViewManagerState(MemoryViewManagerState other) { + isShowWithInstancesOnly = other.isShowWithInstancesOnly; + isShowWithDiffOnly = other.isShowWithDiffOnly; + } +} diff --git a/event/MemoryViewManagerListener.java b/event/MemoryViewManagerListener.java new file mode 100644 index 000000000000..1c7e1cf8f031 --- /dev/null +++ b/event/MemoryViewManagerListener.java @@ -0,0 +1,11 @@ +package org.jetbrains.debugger.memory.event; + +import org.jetbrains.debugger.memory.component.MemoryViewManagerState; +import org.jetbrains.annotations.NotNull; + +import java.util.EventListener; + +@FunctionalInterface +public interface MemoryViewManagerListener extends EventListener { + void stateChanged(@NotNull MemoryViewManagerState state); +} diff --git a/sizeof/RetainedSizeFetcher.java b/sizeof/RetainedSizeFetcher.java new file mode 100644 index 000000000000..78a9f66079ba --- /dev/null +++ b/sizeof/RetainedSizeFetcher.java @@ -0,0 +1,109 @@ +package org.jetbrains.debugger.memory.sizeof; + +import com.sun.jdi.*; + +import java.util.Collection; +import java.util.concurrent.TimeUnit; + +public class RetainedSizeFetcher extends SizeOfEvalStrategy { + private final static long REFERENCE_SIZE = 4; + private final static long CHAR_SIZE = 2; + private final static long INT_SIZE = 4; + private final static long FLOAT_SIZE = 4; + private final static long DOUBLE_SIZE = 8; + private final static long BYTE_SIZE = 1; + private final static long BOOLEAN_SIZE = 1; // ? + private final static long LONG_SIZE = 8; + private final static long SHORT_SIZE = 2; + + private final boolean myCheckReferringObjects; + + public RetainedSizeFetcher(boolean checkReferringObjects) { + myCheckReferringObjects = checkReferringObjects; + } + + private static long getPrimitiveTypeSize(PrimitiveValue value) { + if (value instanceof IntegerValue) { + return INT_SIZE; + } + if (value instanceof LongValue) { + return LONG_SIZE; + } + if (value instanceof DoubleValue) { + return DOUBLE_SIZE; + } + if (value instanceof FloatValue) { + return FLOAT_SIZE; + } + if (value instanceof CharValue) { + return CHAR_SIZE; + } + if (value instanceof ByteValue) { + return BYTE_SIZE; + } + if (value instanceof BooleanValue) { + return BOOLEAN_SIZE; + } + + return SHORT_SIZE; + } + + private long getValueSize(Value value, Collection visited) { + if (value == null) { + return 0; + } + + if (value instanceof PrimitiveValue) { + return getPrimitiveTypeSize((PrimitiveValue) value); + } else { + ObjectReference ref = (ObjectReference) value; + if (visited.contains(ref)) { + return 0; + } + + if (myCheckReferringObjects) { + long startTime = System.nanoTime(); + int needReferringObjectCount = visited.size() + 1; + Collection referringObjects = ref.referringObjects(needReferringObjectCount); + long duration = System.nanoTime() - startTime; + System.out.println("referringObjects(" + needReferringObjectCount + ") duration = " + duration + "ns "+ + "(" + TimeUnit.NANOSECONDS.toMillis(duration) + " ms)."); + if (referringObjects.stream().anyMatch(objectReference -> !visited.contains(objectReference))) { + return 0; + } + } + + return sizeOfImpl(ref, visited, false); + } + } + + @Override + protected long sizeOfImpl(ObjectReference ref, Collection visited, boolean isRoot) { + visited.add(ref); + long result = 0; + + if (ref instanceof ArrayReference) { + ArrayReference arrRef = (ArrayReference) ref; + int length = arrRef.length(); + for (int i = 0; i < length; i++) { + result += getValueSize(arrRef.getValue(i), visited); + } + } + + for (Field field : ref.referenceType().allFields()) { + Value val = ref.getValue(field); + if (field.isStatic()) { + continue; + } + + result += REFERENCE_SIZE; + if (val == null) { + continue; + } + + result += getValueSize(val, visited); + } + + return result; + } +} diff --git a/sizeof/SizeOfEvalStrategy.java b/sizeof/SizeOfEvalStrategy.java new file mode 100644 index 000000000000..962909f3fbaf --- /dev/null +++ b/sizeof/SizeOfEvalStrategy.java @@ -0,0 +1,21 @@ +package org.jetbrains.debugger.memory.sizeof; + +import com.sun.jdi.ObjectReference; + +import java.util.Collection; +import java.util.HashSet; +import java.util.concurrent.TimeUnit; + +abstract class SizeOfEvalStrategy { + public long sizeOf(ObjectReference ref) { + long startTime = System.nanoTime(); + long result = sizeOfImpl(ref, new HashSet<>(), true); + long duration = System.nanoTime() - startTime; + System.out.printf("org.jetbrains.plugin.sizeof [%d] == %d. time = %d ns (%d ms).%n", + ref.uniqueID(), result, duration, TimeUnit.NANOSECONDS.toMillis(duration)); + + return result; + } + + protected abstract long sizeOfImpl(ObjectReference ref, Collection visited, boolean isRoot); +} diff --git a/toolwindow/MemoryViewCondition.java b/toolwindow/MemoryViewCondition.java new file mode 100644 index 000000000000..9bcf07002d4f --- /dev/null +++ b/toolwindow/MemoryViewCondition.java @@ -0,0 +1,45 @@ +package org.jetbrains.debugger.memory.toolwindow; + +import com.intellij.openapi.project.Project; +import com.intellij.openapi.util.Condition; +import com.intellij.openapi.wm.ToolWindow; +import com.intellij.util.messages.MessageBusConnection; +import com.intellij.xdebugger.XDebugProcess; +import com.intellij.xdebugger.XDebuggerManager; +import com.intellij.xdebugger.XDebuggerManagerListener; +import icons.MemoryViewIcons; +import org.jetbrains.debugger.memory.component.MemoryViewManager; +import org.jetbrains.annotations.NotNull; + +import javax.swing.*; +import java.util.Arrays; + +public class MemoryViewCondition implements Condition { + @Override + public boolean value(Project project) { + MessageBusConnection connection = project.getMessageBus().connect(project); + connection.subscribe(XDebuggerManager.TOPIC, new XDebuggerManagerListener() { + @Override + public void processStarted(@NotNull XDebugProcess debugProcess) { + updateIcon(debugProcess.getSession().getProject(), true); + } + + @Override + public void processStopped(@NotNull XDebugProcess debugProcess) { + Project project = debugProcess.getSession().getProject(); + boolean enabled = Arrays.stream(XDebuggerManager.getInstance(project) + .getDebugSessions()).anyMatch(session -> !session.getDebugProcess().equals(debugProcess)); + updateIcon(project, enabled); + } + + private void updateIcon(@NotNull Project project, boolean enabled) { + ToolWindow toolWindow = MemoryViewManager.getInstance(project).getToolWindow(); + if (toolWindow != null) { + SwingUtilities.invokeLater(() -> + toolWindow.setIcon(enabled ? MemoryViewIcons.MAIN_ENABLED : MemoryViewIcons.MAIN_DISABLED)); + } + } + }); + return true; + } +} diff --git a/toolwindow/MemoryViewToolWindowFactory.java b/toolwindow/MemoryViewToolWindowFactory.java new file mode 100644 index 000000000000..f258eb633050 --- /dev/null +++ b/toolwindow/MemoryViewToolWindowFactory.java @@ -0,0 +1,149 @@ +package org.jetbrains.debugger.memory.toolwindow; + +import com.intellij.execution.Executor; +import com.intellij.execution.ui.RunContentDescriptor; +import com.intellij.execution.ui.RunContentManager; +import com.intellij.execution.ui.RunContentWithExecutorListener; +import com.intellij.openapi.actionSystem.ActionGroup; +import com.intellij.openapi.actionSystem.ActionManager; +import com.intellij.openapi.actionSystem.DefaultActionGroup; +import com.intellij.openapi.project.DumbAware; +import com.intellij.openapi.project.Project; +import com.intellij.openapi.wm.ToolWindow; +import com.intellij.openapi.wm.ToolWindowFactory; +import com.intellij.openapi.wm.ex.ToolWindowEx; +import com.intellij.openapi.wm.ex.ToolWindowManagerAdapter; +import com.intellij.openapi.wm.impl.ToolWindowImpl; +import com.intellij.ui.components.JBLabel; +import com.intellij.util.containers.HashMap; +import com.intellij.util.messages.MessageBusConnection; +import com.intellij.xdebugger.XDebugProcess; +import com.intellij.xdebugger.XDebugSession; +import com.intellij.xdebugger.XDebuggerManager; +import com.intellij.xdebugger.XDebuggerManagerListener; +import org.jetbrains.debugger.memory.view.ClassesFilteredView; +import org.jetbrains.debugger.memory.component.MemoryViewManager; +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; + +import javax.swing.*; +import java.awt.*; + +public class MemoryViewToolWindowFactory implements ToolWindowFactory, DumbAware { + public final static String TOOL_WINDOW_ID = "Memory View"; + + private final JComponent myEmptyContent; + + private final HashMap myMemoryViews = new HashMap<>(); + + { + myEmptyContent = new JBLabel("Run debugging to see loaded classes", SwingConstants.CENTER); + } + + @Override + public void createToolWindowContent(@NotNull Project project, @NotNull ToolWindow toolWindow) { + MessageBusConnection connection = project.getMessageBus().connect(project); + connection.subscribe(XDebuggerManager.TOPIC, new MyDebuggerStatusChangedListener()); + connection.subscribe(RunContentManager.TOPIC, new RunContentWithExecutorListener() { + @Override + public void contentSelected(@Nullable RunContentDescriptor descriptor, @NotNull Executor executor) { + SwingUtilities.invokeLater(() -> updateCurrentMemoryView(project, toolWindow)); + } + + @Override + public void contentRemoved(@Nullable RunContentDescriptor descriptor, @NotNull Executor executor) { + SwingUtilities.invokeLater(() -> updateCurrentMemoryView(project, toolWindow)); + } + }); + + ((ToolWindowImpl) toolWindow).getToolWindowManager() + .addToolWindowManagerListener(new ToolWindowManagerAdapter() { + @Override + public void stateChanged() { + if (!myMemoryViews.isEmpty()) { + SwingUtilities.invokeLater(() -> + myMemoryViews.values().forEach(classesFilteredView -> + classesFilteredView.setNeedReloadClasses(toolWindow.isVisible()))); + } + } + }); + + ActionGroup group = new DefaultActionGroup( + ActionManager.getInstance().getAction("MemoryView.ShowOnlyWithInstances"), + ActionManager.getInstance().getAction("MemoryView.ShowOnlyWithDiff") + ); + ((ToolWindowEx) toolWindow).setAdditionalGearActions(group); + toolWindow.getComponent().setLayout(new BorderLayout()); + + for (XDebugSession session : XDebuggerManager.getInstance(project).getDebugSessions()) { + addNewSession(session); + } + + updateCurrentMemoryView(project, toolWindow); + } + + private void addNewSession(@NotNull XDebugSession session) { + ToolWindow toolWindow = getToolWindow(session.getProject()); + if (!myMemoryViews.containsKey(session)) { + ClassesFilteredView newView = new ClassesFilteredView(session); + newView.setNeedReloadClasses(toolWindow != null && toolWindow.isVisible()); + myMemoryViews.put(session, newView); + } + } + + private void removeSession(@NotNull XDebugSession session) { + myMemoryViews.remove(session); + } + + private void updateCurrentMemoryView(@NotNull Project project, @NotNull ToolWindow toolWindow) { + if (!project.isDisposed()) { + XDebugSession session = XDebuggerManager.getInstance(project).getCurrentSession(); + if (session != null && myMemoryViews.containsKey(session)) { + ClassesFilteredView view = myMemoryViews.get(session); + replaceToolWindowContent(toolWindow, view); + return; + } + } + + replaceToolWindowContent(toolWindow, myEmptyContent); + } + + private void replaceToolWindowContent(@NotNull ToolWindow toolWindow, JComponent comp) { + JComponent toolWindowComp = toolWindow.getComponent(); + toolWindowComp.removeAll(); + toolWindowComp.add(comp); + toolWindowComp.repaint(); + } + + @Nullable + private ToolWindow getToolWindow(@NotNull Project project) { + return MemoryViewManager.getInstance(project).getToolWindow(); + } + + private final class MyDebuggerStatusChangedListener implements XDebuggerManagerListener { + @Override + public void processStarted(@NotNull XDebugProcess xDebugProcess) { + SwingUtilities.invokeLater(() -> { + XDebugSession session = xDebugProcess.getSession(); + addNewSession(session); + updateView(session); + }); + } + + @Override + public void processStopped(@NotNull XDebugProcess xDebugProcess) { + SwingUtilities.invokeLater(() -> { + XDebugSession session = xDebugProcess.getSession(); + removeSession(session); + updateView(session); + }); + } + + private void updateView(@NotNull XDebugSession debugSession) { + ToolWindow toolWindow = getToolWindow(debugSession.getProject()); + if (toolWindow != null) { + updateCurrentMemoryView(debugSession.getProject(), toolWindow); + } + } + } +} diff --git a/utils/AbstractTableColumnDescriptor.java b/utils/AbstractTableColumnDescriptor.java new file mode 100644 index 000000000000..3abb30a98e89 --- /dev/null +++ b/utils/AbstractTableColumnDescriptor.java @@ -0,0 +1,21 @@ +package org.jetbrains.debugger.memory.utils; + +public abstract class AbstractTableColumnDescriptor implements AbstractTableModelWithColumns.TableColumnDescriptor { + private final String myName; + private final Class myClass; + protected AbstractTableColumnDescriptor(String name, Class elementClass) { + myName = name; + myClass = elementClass; + } + + @Override + public Class getColumnClass() { + return myClass; + } + + + @Override + public String getName() { + return myName; + } +} diff --git a/utils/AbstractTableModelWithColumns.java b/utils/AbstractTableModelWithColumns.java new file mode 100644 index 000000000000..d0a67129078c --- /dev/null +++ b/utils/AbstractTableModelWithColumns.java @@ -0,0 +1,37 @@ +package org.jetbrains.debugger.memory.utils; + +import javax.swing.table.AbstractTableModel; + +public abstract class AbstractTableModelWithColumns extends AbstractTableModel { + + private final TableColumnDescriptor[] myColumnDescriptors; + public AbstractTableModelWithColumns(TableColumnDescriptor[] descriptors) { + myColumnDescriptors = descriptors; + } + + @Override + public Class getColumnClass(int columnIndex) { + return myColumnDescriptors[columnIndex].getColumnClass(); + } + + @Override + public String getColumnName(int column) { + return myColumnDescriptors[column].getName(); + } + + @Override + public int getColumnCount() { + return myColumnDescriptors.length; + } + + @Override + public Object getValueAt(int rowIndex, int columnIndex) { + return myColumnDescriptors[columnIndex].getValue(rowIndex); + } + + interface TableColumnDescriptor { + Class getColumnClass(); + Object getValue(int ix); + String getName(); + } +} diff --git a/utils/DebugCommand.java b/utils/DebugCommand.java new file mode 100644 index 000000000000..3076fb3d3ebc --- /dev/null +++ b/utils/DebugCommand.java @@ -0,0 +1,9 @@ +package org.jetbrains.debugger.memory.utils; + +import com.intellij.debugger.engine.managerThread.DebuggerCommand; + +public interface DebugCommand extends DebuggerCommand { + @Override + default void commandCancelled() { + } +} diff --git a/utils/InstanceJavaValue.java b/utils/InstanceJavaValue.java new file mode 100644 index 000000000000..0c8046e6c989 --- /dev/null +++ b/utils/InstanceJavaValue.java @@ -0,0 +1,31 @@ +package org.jetbrains.debugger.memory.utils; + +import com.intellij.debugger.engine.JavaValue; +import com.intellij.debugger.engine.evaluation.EvaluationContextImpl; +import com.intellij.debugger.ui.impl.watch.NodeManagerImpl; +import com.intellij.debugger.ui.impl.watch.ValueDescriptorImpl; +import com.sun.jdi.ObjectReference; +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; + +public class InstanceJavaValue extends JavaValue { + public InstanceJavaValue(JavaValue javaValue, + @NotNull ValueDescriptorImpl valueDescriptor, + @NotNull EvaluationContextImpl evaluationContext, + NodeManagerImpl nodeManager, + boolean contextSet) { + super(javaValue, valueDescriptor, evaluationContext, nodeManager, contextSet); + } + + @Nullable + @Override + public String getEvaluationExpression() { + ObjectReference ref = ((ObjectReference)getDescriptor().getValue()); + return NamesUtils.getUniqueName(ref); + } + + @Override + public boolean canNavigateToSource() { + return false; + } +} diff --git a/utils/InstanceValueDescriptor.java b/utils/InstanceValueDescriptor.java new file mode 100644 index 000000000000..2c1b65f4f7c9 --- /dev/null +++ b/utils/InstanceValueDescriptor.java @@ -0,0 +1,46 @@ +package org.jetbrains.debugger.memory.utils; + +import com.intellij.debugger.DebuggerContext; +import com.intellij.debugger.engine.ContextUtil; +import com.intellij.debugger.engine.evaluation.EvaluateException; +import com.intellij.debugger.engine.evaluation.EvaluationContextImpl; +import com.intellij.debugger.ui.impl.watch.ValueDescriptorImpl; +import com.intellij.openapi.project.Project; +import com.intellij.psi.JavaPsiFacade; +import com.intellij.psi.PsiElementFactory; +import com.intellij.psi.PsiExpression; +import com.sun.jdi.ObjectReference; +import com.sun.jdi.Value; + +public class InstanceValueDescriptor extends ValueDescriptorImpl { + + public InstanceValueDescriptor(Project project, Value value) { + super(project, value); + } + + @Override + public String calcValueName() { + ObjectReference ref = ((ObjectReference)getValue()); + return NamesUtils.getUniqueName(ref); + } + + @Override + public Value calcValue(EvaluationContextImpl evaluationContext) throws EvaluateException { + return getValue(); + } + + @Override + public boolean isShowIdLabel() { + return false; + } + + @Override + public PsiExpression getDescriptorEvaluation(DebuggerContext debuggerContext) throws EvaluateException { + PsiElementFactory elementFactory = JavaPsiFacade.getInstance(myProject).getElementFactory(); + ObjectReference ref = ((ObjectReference)getValue()); + String name = NamesUtils.getUniqueName(ref).replace("@", ""); + String presentation = String.format("%s_DebugLabel", name); + + return elementFactory.createExpressionFromText(presentation, ContextUtil.getContextElement(debuggerContext)); + } +} diff --git a/utils/KeyboardUtils.java b/utils/KeyboardUtils.java new file mode 100644 index 000000000000..d7818f4bccf5 --- /dev/null +++ b/utils/KeyboardUtils.java @@ -0,0 +1,18 @@ +package org.jetbrains.debugger.memory.utils; + +import java.awt.event.KeyEvent; + +public class KeyboardUtils { + public static boolean isEnterKey(int keyCode) { + return keyCode == KeyEvent.VK_ENTER; + } + + public static boolean isArrowKey(int keyCode) { + return keyCode == KeyEvent.VK_UP || keyCode == KeyEvent.VK_LEFT || + keyCode == KeyEvent.VK_DOWN || keyCode == KeyEvent.VK_RIGHT; + } + + public static boolean isCharacter(int keyCode) { + return KeyEvent.getKeyText(keyCode).length() == 1; + } +} diff --git a/utils/NamesUtils.java b/utils/NamesUtils.java new file mode 100644 index 000000000000..8b4363ae1cd1 --- /dev/null +++ b/utils/NamesUtils.java @@ -0,0 +1,10 @@ +package org.jetbrains.debugger.memory.utils; + +import com.sun.jdi.ObjectReference; + +class NamesUtils { + static String getUniqueName(ObjectReference ref) { + String name = ref.referenceType().name().replace("[]", "Array"); + return String.format("%s@%d", name.substring(name.lastIndexOf('.') + 1), ref.uniqueID()); + } +} diff --git a/utils/SingleAlarmWithMutableDelay.java b/utils/SingleAlarmWithMutableDelay.java new file mode 100644 index 000000000000..43687ce17139 --- /dev/null +++ b/utils/SingleAlarmWithMutableDelay.java @@ -0,0 +1,23 @@ +package org.jetbrains.debugger.memory.utils; + +import com.intellij.util.Alarm; +import org.jetbrains.annotations.NotNull; + +public class SingleAlarmWithMutableDelay extends Alarm { + private final Runnable myTask; + private volatile int myDelay; + public SingleAlarmWithMutableDelay(@NotNull Runnable task) { + myTask = task; + } + + public void setDelay(int value) { + myDelay = value; + } + + public void cancelAndRequest() { + if(!isDisposed()) { + cancelAllRequests(); + addRequest(myTask, myDelay); + } + } +} diff --git a/view/ClassesFilteredView.java b/view/ClassesFilteredView.java new file mode 100644 index 000000000000..a9a42a79c42b --- /dev/null +++ b/view/ClassesFilteredView.java @@ -0,0 +1,199 @@ +package org.jetbrains.debugger.memory.view; + +import com.intellij.debugger.DebuggerManager; +import com.intellij.debugger.engine.DebugProcess; +import com.intellij.debugger.engine.DebuggerManagerThreadImpl; +import com.intellij.debugger.engine.SuspendContextImpl; +import com.intellij.debugger.engine.events.SuspendContextCommandImpl; +import com.intellij.debugger.impl.DebuggerManagerImpl; +import com.intellij.openapi.diagnostic.Logger; +import com.intellij.openapi.project.Project; +import com.intellij.ui.DocumentAdapter; +import com.intellij.ui.DoubleClickListener; +import com.intellij.ui.SearchTextField; +import com.intellij.ui.components.JBScrollPane; +import com.intellij.util.ui.components.BorderLayoutPanel; +import com.intellij.xdebugger.XDebugSession; +import com.intellij.xdebugger.XDebugSessionListener; +import com.sun.jdi.ReferenceType; +import com.sun.jdi.VirtualMachine; +import org.jetbrains.debugger.memory.event.MemoryViewManagerListener; +import org.jetbrains.debugger.memory.component.MemoryViewManager; +import org.jetbrains.debugger.memory.component.MemoryViewManagerState; +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; +import org.jetbrains.debugger.memory.utils.KeyboardUtils; +import org.jetbrains.debugger.memory.utils.SingleAlarmWithMutableDelay; + +import javax.swing.*; +import javax.swing.event.DocumentEvent; +import java.awt.event.KeyAdapter; +import java.awt.event.KeyEvent; +import java.awt.event.MouseEvent; +import java.util.List; +import java.util.concurrent.TimeUnit; + +public class ClassesFilteredView extends BorderLayoutPanel { + private static final Logger LOG = Logger.getInstance(ClassesFilteredView.class); + private final static double DELAY_BEFORE_INSTANCES_QUERY_COEFFICIENT = 0.5; + + private final Project myProject; + private final XDebugSession myDebugSession; + private final DebugProcess myDebugProcess; + private final SingleAlarmWithMutableDelay mySingleAlarm; + + private final SearchTextField myFilterTextField = new SearchTextField(false); + private final ClassesTable myTable; + + private boolean myNeedReloadClasses = false; + + public ClassesFilteredView(@NotNull XDebugSession debugSession) { + super(); + + myProject = debugSession.getProject(); + myDebugSession = debugSession; + myDebugProcess = DebuggerManager.getInstance(myProject) + .getDebugProcess(myDebugSession.getDebugProcess().getProcessHandler()); + + MemoryViewManagerState memoryViewManagerState = MemoryViewManager.getInstance(myProject).getState(); + + myTable = new ClassesTable(memoryViewManagerState.isShowWithDiffOnly, + memoryViewManagerState.isShowWithInstancesOnly); + + myTable.addKeyListener(new KeyAdapter() { + @Override + public void keyReleased(KeyEvent e) { + int keyCode = e.getKeyCode(); + if (KeyboardUtils.isEnterKey(keyCode)) { + handleClassSelection(myTable.getSelectedClass()); + } else if (!KeyboardUtils.isArrowKey(keyCode) && KeyboardUtils.isCharacter(keyCode)) { + SwingUtilities.invokeLater(myFilterTextField::requestFocusInWindow); + String text = myFilterTextField.getText(); + myFilterTextField.setText(text + KeyEvent.getKeyText(keyCode).toLowerCase()); + } + } + }); + + new DoubleClickListener() { + @Override + protected boolean onDoubleClick(MouseEvent event) { + handleClassSelection(myTable.getSelectedClass()); + return true; + } + }.installOn(myTable); + + final MemoryViewManagerListener memoryViewManagerListener = state -> { + myTable.setFilteringByDiffNonZero(state.isShowWithDiffOnly); + myTable.setFilteringByInstanceExists(state.isShowWithInstancesOnly); + }; + + MemoryViewManager.getInstance(myProject).addMemoryViewManagerListener(memoryViewManagerListener); + + myDebugSession.addSessionListener(new XDebugSessionListener() { + @Override + public void sessionResumed() { + SwingUtilities.invokeLater(myTable::hideContent); + } + + @Override + public void sessionStopped() { + debugSession.removeSessionListener(this); + MemoryViewManager.getInstance(myProject).removeMemoryViewManagerListener(memoryViewManagerListener); + } + + @Override + public void sessionPaused() { + SwingUtilities.invokeLater(myTable::showContent); + if (myNeedReloadClasses) { + updateClassesAndCounts(); + } + } + }); + + myFilterTextField.addDocumentListener(new DocumentAdapter() { + @Override + protected void textChanged(DocumentEvent e) { + myTable.setFilterPattern(myFilterTextField.getText()); + } + }); + + mySingleAlarm = new SingleAlarmWithMutableDelay(() -> { + SuspendContextImpl suspendContext = getSuspendContext(); + if (suspendContext != null) { + SwingUtilities.invokeLater(() -> myTable.setBusy(true)); + + ((DebuggerManagerThreadImpl) myDebugProcess.getManagerThread()) + .schedule(new MyUpdateClassesCommand(suspendContext)); + } + }); + + JBScrollPane scroll = new JBScrollPane(myTable); + addToTop(myFilterTextField); + addToCenter(scroll); + } + + public void setNeedReloadClasses(boolean value) { + if (myNeedReloadClasses != value) { + myNeedReloadClasses = value; + if (myNeedReloadClasses) { + updateClassesAndCounts(); + } + } + } + + private void handleClassSelection(@Nullable ReferenceType ref) { + if (ref != null && myDebugSession.isSuspended()) { + InstancesWindow window = new InstancesWindow(myProject, myDebugSession, ref); + window.show(); + } + } + + private SuspendContextImpl getSuspendContext() { + return DebuggerManagerImpl.getInstanceEx(myProject).getContext().getSuspendContext(); + } + + private void updateClassesAndCounts() { + if (myDebugProcess.isAttached()) { + mySingleAlarm.cancelAndRequest(); + } + } + + private final class MyUpdateClassesCommand extends SuspendContextCommandImpl { + MyUpdateClassesCommand(@Nullable SuspendContextImpl suspendContext) { + super(suspendContext); + } + + @Override + public Priority getPriority() { + return Priority.LOWEST; + } + + @Override + public void contextAction(@NotNull SuspendContextImpl suspendContext) throws Exception { + loadClassesAndCounts(); + } + + @Override + public void commandCancelled() { + } + } + + private void loadClassesAndCounts() { + assert DebuggerManager.getInstance(myProject).isDebuggerManagerThread(); + myDebugProcess.getVirtualMachineProxy(); + List classes = myDebugProcess.getVirtualMachineProxy().allClasses(); + + if (classes.isEmpty()) { + return; + } + + VirtualMachine vm = classes.get(0).virtualMachine(); + long start = System.nanoTime(); + long[] counts = vm.instanceCounts(classes); + long delay = TimeUnit.NANOSECONDS.toMillis(System.nanoTime() - start); + mySingleAlarm.setDelay((int) (DELAY_BEFORE_INSTANCES_QUERY_COEFFICIENT * delay)); + + LOG.info(String.format("Instances query time = %d ms. Count = %d%n", delay, classes.size())); + SwingUtilities.invokeLater(() -> myTable.setClassesAndCounts(classes, counts)); + } +} diff --git a/view/ClassesTable.java b/view/ClassesTable.java new file mode 100644 index 000000000000..495350a048d5 --- /dev/null +++ b/view/ClassesTable.java @@ -0,0 +1,307 @@ +package org.jetbrains.debugger.memory.view; + +import com.intellij.icons.AllIcons; +import com.intellij.ui.ColoredTableCellRenderer; +import com.intellij.ui.table.JBTable; +import com.intellij.util.ui.JBUI; +import com.sun.jdi.ReferenceType; +import org.jetbrains.annotations.Nullable; +import org.jetbrains.debugger.memory.utils.AbstractTableColumnDescriptor; +import org.jetbrains.debugger.memory.utils.AbstractTableModelWithColumns; + +import javax.swing.*; +import javax.swing.table.TableColumn; +import javax.swing.table.TableRowSorter; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.Collections; +import java.util.List; +import java.util.concurrent.ConcurrentHashMap; + +class ClassesTable extends JBTable { + private static final int CLASSES_COLUMN_PREFERRED_WIDTH = 250; + private static final int COUNT_COLUMN_MIN_WIDTH = 80; + private static final int COUNT_COLUMN_MAX_WIDTH = 100; + private static final int DIFF_COLUMN_MIN_WIDTH = 80; + private static final int DIFF_COLUMN_MAX_WIDTH = 100; + + private static final String EMPTY_TABLE_CONTENT = "The application is running"; + + private final DiffViewTableModel myModel = new DiffViewTableModel(); + private final UnknownDiffValue myUnknownValue = new UnknownDiffValue(); + + private boolean myOnlyWithDiff; + private boolean myOnlyWithInstances; + + private String myFilteringPattern = ""; + private ConcurrentHashMap myCounts = new ConcurrentHashMap<>(); + + private volatile List myElems = Collections.unmodifiableList(new ArrayList<>()); + + ClassesTable(boolean onlyWithDiff, boolean onlyWithInstances) { + setModel(myModel); + + myOnlyWithDiff = onlyWithDiff; + myOnlyWithInstances = onlyWithInstances; + + getEmptyText().setText(EMPTY_TABLE_CONTENT); + + TableColumn classesColumn = getColumnModel().getColumn(DiffViewTableModel.CLASSNAME_COLUMN_INDEX); + TableColumn countColumn = getColumnModel().getColumn(DiffViewTableModel.COUNT_COLUMN_INDEX); + TableColumn diffColumn = getColumnModel().getColumn(DiffViewTableModel.DIFF_COLUMN_INDEX); + + setAutoResizeMode(AUTO_RESIZE_ALL_COLUMNS); + classesColumn.setPreferredWidth(JBUI.scale(CLASSES_COLUMN_PREFERRED_WIDTH)); + classesColumn.setResizable(false); + + countColumn.setMinWidth(JBUI.scale(COUNT_COLUMN_MIN_WIDTH)); + countColumn.setMaxWidth(JBUI.scale(COUNT_COLUMN_MAX_WIDTH)); + countColumn.setResizable(false); + + diffColumn.setMinWidth(JBUI.scale(DIFF_COLUMN_MIN_WIDTH)); + diffColumn.setMaxWidth(JBUI.scale(DIFF_COLUMN_MAX_WIDTH)); + diffColumn.setResizable(false); + + setDefaultRenderer(ReferenceType.class, new ColoredTableCellRenderer() { + @Override + protected void customizeCellRenderer(JTable table, @Nullable Object value, + boolean isSelected, boolean hasFocus, int row, int column) { + String presentation = value == null ? "null" : ((ReferenceType) value).name(); + append(presentation); + setTransparentIconBackground(true); + setIcon(AllIcons.Nodes.Class); + } + }); + + setDefaultRenderer(Integer.class, new ColoredTableCellRenderer() { + @Override + protected void customizeCellRenderer(JTable table, @Nullable Object value, + boolean selected, boolean hasFocus, int row, int column) { + if (value != null) { + int val = (Integer) value; + append(String.format("%s%d", val > 0 ? "+" : "", val)); + setTextAlign(SwingConstants.RIGHT); + } + } + }); + + TableRowSorter sorter = new TableRowSorter<>(myModel); + sorter.setRowFilter(new RowFilter() { + @Override + public boolean include(Entry entry) { + int ix = entry.getIdentifier(); + ReferenceType ref = myElems.get(ix); + DiffValue diff = myCounts.getOrDefault(ref, myUnknownValue); + if (myOnlyWithDiff && diff.diff() == 0 || + myOnlyWithInstances && !diff.hasInstance()) { + return false; + } + + String name = ref.name().toLowerCase(); + String pattern = myFilteringPattern; + return name.contains(pattern) || match(pattern.toLowerCase(), name.substring(name.lastIndexOf('.') + 1)); + } + }); + + List myDefaultSortingKeys = Arrays.asList( + new RowSorter.SortKey(DiffViewTableModel.DIFF_COLUMN_INDEX, SortOrder.DESCENDING), + new RowSorter.SortKey(DiffViewTableModel.COUNT_COLUMN_INDEX, SortOrder.DESCENDING), + new RowSorter.SortKey(DiffViewTableModel.CLASSNAME_COLUMN_INDEX, SortOrder.ASCENDING) + ); + sorter.setSortKeys(myDefaultSortingKeys); + setRowSorter(sorter); + setSelectionMode(ListSelectionModel.SINGLE_SELECTION); + } + + @Nullable + ReferenceType getSelectedClass() { + int selectedRow = getSelectedRow(); + if (selectedRow != -1) { + int ix = convertRowIndexToModel(selectedRow); + return myElems.get(ix); + } + + return null; + } + + void setBusy(boolean value) { + setPaintBusy(value); + } + + void setFilterPattern(String pattern) { + if (!myFilteringPattern.equals(pattern)) { + myFilteringPattern = pattern; + getRowSorter().allRowsChanged(); + } + } + + void setFilteringByInstanceExists(boolean value) { + if (value != myOnlyWithInstances) { + myOnlyWithInstances = value; + getRowSorter().allRowsChanged(); + } + } + + void setFilteringByDiffNonZero(boolean value) { + if (myOnlyWithDiff != value) { + myOnlyWithDiff = value; + getRowSorter().allRowsChanged(); + } + } + + void setClassesAndCounts(List classes, long[] counts) { + ReferenceType selectedClass = myModel.getSelectedClassBeforeHided(); + + final int newSelectedIndex = classes.indexOf(selectedClass); + myElems = Collections.unmodifiableList(new ArrayList<>(classes)); + for (int i = 0, size = myElems.size(); i < size; i++) { + ReferenceType ref = myElems.get(i); + myCounts.put(ref, myCounts.getOrDefault(ref, myUnknownValue).update(counts[i])); + } + + getRowSorter().allRowsChanged(); + + if (newSelectedIndex != -1) { + changeSelection(convertRowIndexToView(newSelectedIndex), + DiffViewTableModel.CLASSNAME_COLUMN_INDEX, false, false); + } + + setBusy(false); + } + + void hideContent() { + myModel.hide(); + } + + void showContent() { + myModel.show(); + } + + // TODO: use jb filter + private boolean match(String pattern, String value) { + int i = 0; + int j = 0; + while (j < value.length()) { + if (i < pattern.length() && value.charAt(j) == pattern.charAt(i)) { + i++; + } + j++; + } + + return i == pattern.length(); + } + + private class DiffViewTableModel extends AbstractTableModelWithColumns { + private final static int CLASSNAME_COLUMN_INDEX = 0; + private final static int COUNT_COLUMN_INDEX = 1; + private final static int DIFF_COLUMN_INDEX = 2; + + // Workaround: save selection after content of classes table has been hided + private ReferenceType selectedClassWhenHided = null; + private boolean isHide = false; + + DiffViewTableModel() { + super(new AbstractTableColumnDescriptor[]{ + new AbstractTableColumnDescriptor("Class", ReferenceType.class) { + @Override + public Object getValue(int ix) { + return myElems.get(ix); + } + }, + new AbstractTableColumnDescriptor("Count", Long.class) { + @Override + public Object getValue(int ix) { + return myCounts.getOrDefault(myElems.get(ix), myUnknownValue).myCurrentCount; + } + }, + new AbstractTableColumnDescriptor("Diff", Integer.class) { + @Override + public Object getValue(int ix) { + return myCounts.getOrDefault(myElems.get(ix), myUnknownValue).diff(); + } + } + }); + } + + ReferenceType getSelectedClassBeforeHided() { + return selectedClassWhenHided; + } + + void hide() { + if (!isHide) { + selectedClassWhenHided = getSelectedClass(); + isHide = true; + getRowSorter().allRowsChanged(); + } + } + + void show() { + if (isHide) { + isHide = false; + getRowSorter().allRowsChanged(); + } + } + + @Override + public int getRowCount() { + return isHide ? 0 : myElems.size(); + } + } + + private static class UnknownDiffValue extends DiffValue { + UnknownDiffValue() { + super(0); + } + + @Override + boolean hasInstance() { + return true; + } + + @Override + DiffValue update(long count) { + return new DiffValue(count, count); + } + + @Override + public String toString() { + return "?"; + } + } + + private static class DiffValue { + private final long myOldCount; + private final long myCurrentCount; + + DiffValue(long count) { + this(count, count); + } + + DiffValue(long old, long current) { + myCurrentCount = current; + myOldCount = old; + } + + DiffValue update(long count) { + return new DiffValue(myCurrentCount, count); + } + + boolean hasInstance() { + return myCurrentCount > 0; + } + + int diff() { + return (int) (myCurrentCount - myOldCount); + } + + @Override + public String toString() { + if (myOldCount == myCurrentCount) { + return String.valueOf(myOldCount); + } + + long diff = diff(); + return String.format("%d (%s%d)", myCurrentCount, diff > 0 ? "+" : "-", Math.abs(diff)); + } + } +} diff --git a/view/InstancesWindow.java b/view/InstancesWindow.java new file mode 100644 index 000000000000..05d8fed6e311 --- /dev/null +++ b/view/InstancesWindow.java @@ -0,0 +1,531 @@ +package org.jetbrains.debugger.memory.view; + +import com.intellij.debugger.DebuggerManager; +import com.intellij.debugger.engine.*; +import com.intellij.debugger.engine.evaluation.EvaluateException; +import com.intellij.debugger.engine.evaluation.EvaluationContext; +import com.intellij.debugger.engine.evaluation.EvaluationContextImpl; +import com.intellij.debugger.engine.evaluation.TextWithImportsImpl; +import com.intellij.debugger.engine.evaluation.expression.ExpressionEvaluator; +import com.intellij.debugger.engine.events.DebuggerContextCommandImpl; +import com.intellij.debugger.ui.impl.watch.DebuggerTreeNodeImpl; +import com.intellij.debugger.ui.impl.watch.MessageDescriptor; +import com.intellij.debugger.ui.impl.watch.NodeManagerImpl; +import com.intellij.debugger.ui.tree.NodeDescriptor; +import com.intellij.debugger.ui.tree.render.CachedEvaluator; +import com.intellij.icons.AllIcons; +import com.intellij.openapi.Disposable; +import com.intellij.openapi.actionSystem.*; +import com.intellij.openapi.actionSystem.ex.AnActionListener; +import com.intellij.openapi.application.ApplicationManager; +import com.intellij.openapi.project.Project; +import com.intellij.openapi.ui.DialogWrapper; +import com.intellij.openapi.util.Disposer; +import com.intellij.openapi.util.Pair; +import com.intellij.psi.PsiClass; +import com.intellij.psi.search.GlobalSearchScope; +import com.intellij.ui.JBColor; +import com.intellij.ui.JBProgressBar; +import com.intellij.ui.components.JBLabel; +import com.intellij.ui.components.JBPanel; +import com.intellij.ui.components.JBScrollPane; +import com.intellij.util.ui.JBDimension; +import com.intellij.util.ui.JBUI; +import com.intellij.util.ui.UIUtil; +import com.intellij.xdebugger.XDebugSession; +import com.intellij.xdebugger.XDebugSessionListener; +import com.intellij.xdebugger.XExpression; +import com.intellij.xdebugger.frame.*; +import com.intellij.xdebugger.frame.presentation.XValuePresentation; +import com.intellij.xdebugger.impl.XDebugSessionImpl; +import com.intellij.xdebugger.impl.XSourcePositionImpl; +import com.intellij.xdebugger.impl.actions.XDebuggerActionBase; +import com.intellij.xdebugger.impl.breakpoints.XExpressionImpl; +import com.intellij.xdebugger.impl.evaluate.quick.XDebuggerTreeCreator; +import com.intellij.xdebugger.impl.frame.XValueMarkers; +import com.intellij.xdebugger.impl.ui.XDebuggerExpressionEditor; +import com.intellij.xdebugger.impl.ui.XDebuggerUIConstants; +import com.intellij.xdebugger.impl.ui.tree.ValueMarkup; +import com.intellij.xdebugger.impl.ui.tree.XDebuggerTree; +import com.intellij.xdebugger.impl.ui.tree.XDebuggerTreeState; +import com.intellij.xdebugger.impl.ui.tree.actions.XDebuggerTreeActionBase; +import com.intellij.xdebugger.impl.ui.tree.nodes.XValueNodeImpl; +import com.sun.jdi.BooleanValue; +import com.sun.jdi.ObjectReference; +import com.sun.jdi.ReferenceType; +import com.sun.jdi.Value; +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; +import org.jetbrains.java.debugger.JavaDebuggerEditorsProvider; +import org.jetbrains.debugger.memory.utils.DebugCommand; +import org.jetbrains.debugger.memory.utils.InstanceJavaValue; +import org.jetbrains.debugger.memory.utils.InstanceValueDescriptor; + +import javax.swing.*; +import javax.swing.tree.TreeNode; +import java.awt.*; +import java.awt.event.MouseAdapter; +import java.awt.event.MouseEvent; +import java.util.List; +import java.util.concurrent.atomic.AtomicInteger; + +import static java.lang.Math.min; + +public class InstancesWindow extends DialogWrapper { + private static final int DEFAULT_WINDOW_WIDTH = 700; + private static final int DEFAULT_WINDOW_HEIGHT = 400; + private static final int FILTERING_BUTTON_ADDITIONAL_WIDTH = 30; + private static final int BORDER_LAYOUT_DEFAULT_GAP = 5; + + private final Project myProject; + private final XDebugSession myDebugSession; + private final ReferenceType myReferenceType; + private MyInstancesView myInstancesView; + + public InstancesWindow(@NotNull Project project, + @NotNull XDebugSession session, + @NotNull ReferenceType referenceType) { + super(project, false); + myProject = project; + myDebugSession = session; + myReferenceType = referenceType; + setTitle("Instances of " + referenceType.name()); + myDebugSession.addSessionListener(new XDebugSessionListener() { + @Override + public void sessionStopped() { + SwingUtilities.invokeLater(() -> close(OK_EXIT_CODE)); + } + }, myDisposable); + setModal(false); + init(); + JRootPane root = myInstancesView.getRootPane(); + root.setDefaultButton(myInstancesView.myFilterButton); + } + + @NotNull + @Override + protected String getDimensionServiceKey() { + return "#org.jetbrains.plugin.view.InstancesWindow"; + } + + @Nullable + @Override + protected JComponent createCenterPanel() { + myInstancesView = new MyInstancesView(); + myInstancesView.setPreferredSize( + new JBDimension(DEFAULT_WINDOW_WIDTH, DEFAULT_WINDOW_HEIGHT)); + return myInstancesView; + } + + @Nullable + @Override + protected JComponent createSouthPanel() { + JComponent comp = super.createSouthPanel(); + if (comp != null) { + comp.add(myInstancesView.myProgressPanel, BorderLayout.WEST); + } + + return comp; + } + + @Override + protected void dispose() { + super.dispose(); + myInstancesView.dispose(); + } + + @NotNull + @Override + protected Action[] createActions() { + return new Action[]{new DialogWrapperExitAction("Close", CLOSE_EXIT_CODE)}; + } + + private class MyInstancesView extends JBPanel implements Disposable { + private static final int MAX_TREE_NODE_COUNT = 1000; + private static final int FILTERING_CHUNK_SIZE = 30; + + private final XDebuggerTree myInstancesTree; + private final XDebuggerExpressionEditor myFilterConditionEditor; + private final XDebugSessionListener myDebugSessionListener = new MySessionListener(); + + private final MyCachedEvaluator myEvaluator = new MyCachedEvaluator(); + private final MyNodeManager myNodeManager = new MyNodeManager(myProject); + + private final JBProgressBar myFilteringProgressBar = new JBProgressBar(); + private final JButton myFilterButton = new JButton("Filter"); + private final JComponent myProgressPanel = new JBPanel<>(new BorderLayout()); + + private final AnActionListener.Adapter myActionListener = new MyActionListener(); + private final Object myFilteringTaskLock = new Object(); + + private volatile MyFilteringWorker myFilteringTask = null; + + MyInstancesView() { + super(new BorderLayout(0, JBUI.scale(BORDER_LAYOUT_DEFAULT_GAP))); + + XValueMarkers markers = getValueMarkers(); + ActionManager.getInstance().addAnActionListener(myActionListener, InstancesWindow.this.myDisposable); + myDebugSession.addSessionListener(myDebugSessionListener, InstancesWindow.this.myDisposable); + JavaDebuggerEditorsProvider editorsProvider = new JavaDebuggerEditorsProvider(); + + myFilterConditionEditor = new XDebuggerExpressionEditor(myProject, editorsProvider, "FilterExpression", + null, XExpressionImpl.EMPTY_EXPRESSION, false, true, true); + new SwingWorker() { + @Override + protected Void doInBackground() throws Exception { + ApplicationManager.getApplication().runReadAction(() -> { + final PsiClass psiClass = DebuggerUtils.findClass(myReferenceType.name(), + myProject, GlobalSearchScope.allScope(myProject)); + XSourcePositionImpl position = XSourcePositionImpl.createByElement(psiClass); + SwingUtilities.invokeLater(() -> myFilterConditionEditor.setSourcePosition(position)); + }); + return null; + } + }.execute(); + + myFilterButton.setBorder(BorderFactory.createEmptyBorder()); + Dimension progressbarPreferredSize = myFilterConditionEditor.getEditorComponent().getPreferredSize(); + progressbarPreferredSize.width = JBUI.scale(FILTERING_BUTTON_ADDITIONAL_WIDTH) + + myFilterButton.getPreferredSize().width; + myFilterButton.setPreferredSize(progressbarPreferredSize); + + JBPanel filteringPane = new JBPanel(new BorderLayout(JBUI.scale(BORDER_LAYOUT_DEFAULT_GAP), 0)); + + filteringPane.add(new JBLabel("Condition:"), BorderLayout.WEST); + filteringPane.add(myFilterConditionEditor.getComponent(), BorderLayout.CENTER); + filteringPane.add(myFilterButton, BorderLayout.EAST); + + JLabel stopFilteringButton = + new JLabel(UIUtil.isUnderDarcula() ? AllIcons.Actions.Clean : AllIcons.Actions.CleanLight); + stopFilteringButton.setOpaque(true); + stopFilteringButton.addMouseListener(new MouseAdapter() { + public void mousePressed(MouseEvent e) { + cancelFilteringTask(); + } + }); + + myProgressPanel.add(myFilteringProgressBar, BorderLayout.CENTER); + myProgressPanel.add(stopFilteringButton, BorderLayout.EAST); + + XDebuggerTreeCreator treeCreator = + new XDebuggerTreeCreator(myProject, editorsProvider, null, markers); + myInstancesTree = (XDebuggerTree) treeCreator.createTree(Pair.pair(new XNamedValue("root") { + @Override + public void computeChildren(@NotNull XCompositeNode node) { + updateInstances(); + } + + @Override + public void computePresentation(@NotNull XValueNode xValueNode, @NotNull XValuePlace xValuePlace) { + xValueNode.setPresentation(null, "", "", true); + } + }, "root")); + + myInstancesTree.setRootVisible(false); + myInstancesTree.getRoot().setLeaf(false); + myInstancesTree.setExpandableItemsEnabled(true); + + myFilterButton.addActionListener(e -> + myInstancesTree.rebuildAndRestore(XDebuggerTreeState.saveState(myInstancesTree))); + + JBScrollPane treeScrollPane = new JBScrollPane(myInstancesTree); + add(filteringPane, BorderLayout.NORTH); + add(treeScrollPane, BorderLayout.CENTER); + + } + + @Override + public void dispose() { + ActionManager.getInstance().removeAnActionListener(myActionListener); + myDebugSession.removeSessionListener(myDebugSessionListener); + cancelFilteringTask(); + Disposer.dispose(myInstancesTree); + } + + private void updateInstances() { + DebugProcess debugProcess = DebuggerManager.getInstance(myProject) + .getDebugProcess(myDebugSession.getDebugProcess().getProcessHandler()); + + cancelFilteringTask(); + + debugProcess.getManagerThread().invokeCommand((DebugCommand) () -> { + List instances = myReferenceType.instances(0); + EvaluationContextImpl evaluationContext = ((DebugProcessImpl) debugProcess) + .getDebuggerContext().createEvaluationContext(); + + if(evaluationContext != null) { + synchronized (myFilteringTaskLock) { + myFilteringTask = new MyFilteringWorker(instances, evaluationContext, createEvaluator()); + myFilteringTask.execute(); + } + } + }); + } + + private void showProgressPane(int progressBarMaximum) { + myFilteringProgressBar.setMinimum(0); + myFilteringProgressBar.setMaximum(progressBarMaximum); + myProgressPanel.setVisible(true); + } + + private void hideProgressPane() { + myProgressPanel.setVisible(false); + } + + @Nullable + private ExpressionEvaluator createEvaluator() { + ExpressionEvaluator evaluator = null; + + XExpression expression = myFilterConditionEditor.getExpression(); + if (expression != null && !expression.getExpression().isEmpty()) { + try { + myEvaluator.setReferenceExpression(TextWithImportsImpl. + fromXExpression(myFilterConditionEditor.getExpression())); + evaluator = myEvaluator.getEvaluator(); + } catch (EvaluateException ignored) { + } + } + + return evaluator; + } + + private void cancelFilteringTask() { + if (myFilteringTask != null) { + synchronized (myFilteringTaskLock) { + if (myFilteringTask != null) { + myFilteringTask.cancel(false); + myFilteringTask = null; + } + } + } + } + + private XValueMarkers getValueMarkers() { + return myDebugSession instanceof XDebugSessionImpl + ? ((XDebugSessionImpl) myDebugSession).getValueMarkers() + : null; + } + + private class MySessionListener implements XDebugSessionListener { + private final XValueChildrenList myRunningAppChildNode = new XValueChildrenList(); + + private volatile XDebuggerTreeState myTreeState = null; + + { + myRunningAppChildNode.add(new XNamedValue("") { + @Override + public void computePresentation(@NotNull XValueNode node, @NotNull XValuePlace place) { + node.setPresentation(XDebuggerUIConstants.INFORMATION_MESSAGE_ICON, new XValuePresentation() { + @NotNull + @Override + public String getSeparator() { + return ""; + } + + @Override + public void renderValue(@NotNull XValueTextRenderer renderer) { + renderer.renderValue("The application is running"); + } + }, false); + } + }); + } + + @Override + public void sessionResumed() { + SwingUtilities.invokeLater(() -> { + myTreeState = XDebuggerTreeState.saveState(myInstancesTree); + myInstancesTree.getRoot().clearChildren(); + ((XValueNodeImpl) myInstancesTree.getRoot()).addChildren(myRunningAppChildNode, true); + }); + } + + @Override + public void sessionPaused() { + SwingUtilities.invokeLater(() -> { + myInstancesTree.setRootVisible(false); + myInstancesTree.rebuildAndRestore(myTreeState); + }); + } + } + + private class MyActionListener extends AnActionListener.Adapter { + @Override + public void afterActionPerformed(AnAction action, DataContext dataContext, AnActionEvent event) { + if (dataContext.getData(PlatformDataKeys.CONTEXT_COMPONENT) == myInstancesView.myInstancesTree && + (isAddToWatchesAction(action) || isEvaluateExpressionAction(action))) { + myInstancesTree.rebuildAndRestore(XDebuggerTreeState.saveState(myInstancesTree)); + } + } + + @Override + public void beforeActionPerformed(AnAction action, DataContext dataContext, AnActionEvent event) { + if (dataContext.getData(PlatformDataKeys.CONTEXT_COMPONENT) == myInstancesView.myInstancesTree && + (isAddToWatchesAction(action) || isEvaluateExpressionAction(action))) { + XValueNodeImpl selectedNode = XDebuggerTreeActionBase.getSelectedNode(dataContext); + XValueMarkers markers = getValueMarkers(); + + if (markers != null && selectedNode != null) { + + TreeNode currentNode = selectedNode; + while (!myInstancesTree.getRoot().equals(currentNode.getParent())) { + currentNode = currentNode.getParent(); + } + + XValue valueContainer = ((XValueNodeImpl) currentNode).getValueContainer(); + + String expression = valueContainer.getEvaluationExpression(); + if (expression != null) { + markers.markValue(valueContainer, + new ValueMarkup(expression.replace("@", ""), new JBColor(0, 0), null)); + } + } + } + } + + private boolean isAddToWatchesAction(AnAction action) { + String className = action.getClass().getSimpleName(); + return action instanceof XDebuggerTreeActionBase && className.equals("XAddToWatchesAction"); + } + + private boolean isEvaluateExpressionAction(AnAction action) { + String className = action.getClass().getSimpleName(); + return action instanceof XDebuggerActionBase && className.equals("EvaluateAction"); + } + } + + private final class MyCachedEvaluator extends CachedEvaluator { + @Override + @NotNull + protected String getClassName() { + return myReferenceType.name(); + } + + ExpressionEvaluator getEvaluator() throws EvaluateException { + return getEvaluator(myProject); + } + } + + private class MyFilteringWorker extends SwingWorker { + private final List myReferences; + private final EvaluationContextImpl myEvaluationContext; + private final ExpressionEvaluator myExpressionEvaluator; + private volatile boolean myDebuggerTaskCompleted; + + MyFilteringWorker(@NotNull List refs, + @NotNull EvaluationContextImpl evaluationContext, + @Nullable ExpressionEvaluator evaluator) { + myReferences = refs; + myEvaluationContext = evaluationContext; + myExpressionEvaluator = evaluator; + } + + @Override + protected Void doInBackground() throws Exception { + DebugProcessImpl debugProcess = (DebugProcessImpl) DebuggerManager.getInstance(myProject) + .getDebugProcess(myDebugSession.getDebugProcess().getProcessHandler()); + + SwingUtilities.invokeLater(() -> showProgressPane(myReferences.size())); + + AtomicInteger totalChildren = new AtomicInteger(0); + for (int i = 0, size = myReferences.size(); i < size && !isCancelled(); i += FILTERING_CHUNK_SIZE) { + myDebuggerTaskCompleted = false; + final int chunkBegin = i; + debugProcess.getManagerThread().schedule(new DebuggerContextCommandImpl(debugProcess.getDebuggerContext()) { + @Override + public Priority getPriority() { + return Priority.LOWEST; + } + + @Override + public void threadAction(@NotNull SuspendContextImpl suspendContext) { + XValueChildrenList children = new XValueChildrenList(FILTERING_CHUNK_SIZE); + int endOfChunk = min(chunkBegin + FILTERING_CHUNK_SIZE, size); + for (int j = chunkBegin; j < endOfChunk + && !isCancelled() && totalChildren.get() < MAX_TREE_NODE_COUNT; j++) { + ObjectReference ref = myReferences.get(j); + if (myExpressionEvaluator != null && isSatisfy(myExpressionEvaluator, ref) != MyFilteringResult.MATCH) { + continue; + } + + JavaValue val = new InstanceJavaValue(null, new InstanceValueDescriptor(myProject, ref), + myEvaluationContext, myNodeManager, true); + children.add(val); + } + + if (children.size() > 0) { + totalChildren.addAndGet(children.size()); + SwingUtilities.invokeLater(() -> + ((XValueNodeImpl) myInstancesTree.getRoot()).addChildren(children, false)); + } + + SwingUtilities.invokeLater(() -> myFilteringProgressBar.setValue(endOfChunk)); + + synchronized (MyFilteringWorker.this) { + myDebuggerTaskCompleted = true; + MyFilteringWorker.this.notify(); + } + } + }); + debugProcess.getManagerThread().invokeCommand((DebugCommand) () -> { + + }); + + synchronized (this) { + while (!myDebuggerTaskCompleted) { + wait(); + } + } + if (totalChildren.get() >= MAX_TREE_NODE_COUNT) { + break; + } + } + + SwingUtilities.invokeLater(() -> { + ((XValueNodeImpl) myInstancesTree.getRoot()).addChildren(XValueChildrenList.EMPTY, true); + hideProgressPane(); + }); + + return null; + } + + private MyFilteringResult isSatisfy(@NotNull ExpressionEvaluator evaluator, @NotNull Value value) { + try { + Value evaluate = evaluator.evaluate(myEvaluationContext.createEvaluationContext(value)); + if (evaluate instanceof BooleanValue && ((BooleanValue) evaluate).value()) { + return MyFilteringResult.MATCH; + } + } catch (EvaluateException e) { + return MyFilteringResult.EVAL_ERROR; + } + + return MyFilteringResult.NO_MATCH; + } + } + } + + private final static class MyNodeManager extends NodeManagerImpl { + MyNodeManager(Project project) { + super(project, null); + } + + @Override + public DebuggerTreeNodeImpl createNode(final NodeDescriptor descriptor, EvaluationContext evaluationContext) { + return new DebuggerTreeNodeImpl(null, descriptor); + } + + @Override + public DebuggerTreeNodeImpl createMessageNode(MessageDescriptor descriptor) { + return new DebuggerTreeNodeImpl(null, descriptor); + } + + @Override + public DebuggerTreeNodeImpl createMessageNode(String message) { + return new DebuggerTreeNodeImpl(null, new MessageDescriptor(message)); + } + } + + private enum MyFilteringResult { + MATCH, NO_MATCH, EVAL_ERROR + } +} From 58ac0267080e7b5fbae8c52e6900e5d551a661f2 Mon Sep 17 00:00:00 2001 From: "Egor.Ushakov" Date: Tue, 9 Aug 2016 13:53:34 +0300 Subject: [PATCH 002/151] removed table borders --- view/ClassesFilteredView.java | 7 ++----- 1 file changed, 2 insertions(+), 5 deletions(-) diff --git a/view/ClassesFilteredView.java b/view/ClassesFilteredView.java index a9a42a79c42b..5b78834bcc11 100644 --- a/view/ClassesFilteredView.java +++ b/view/ClassesFilteredView.java @@ -8,10 +8,7 @@ import com.intellij.debugger.engine.events.SuspendContextCommandImpl; import com.intellij.debugger.impl.DebuggerManagerImpl; import com.intellij.openapi.diagnostic.Logger; import com.intellij.openapi.project.Project; -import com.intellij.ui.DocumentAdapter; -import com.intellij.ui.DoubleClickListener; -import com.intellij.ui.SearchTextField; -import com.intellij.ui.components.JBScrollPane; +import com.intellij.ui.*; import com.intellij.util.ui.components.BorderLayoutPanel; import com.intellij.xdebugger.XDebugSession; import com.intellij.xdebugger.XDebugSessionListener; @@ -127,7 +124,7 @@ public class ClassesFilteredView extends BorderLayoutPanel { } }); - JBScrollPane scroll = new JBScrollPane(myTable); + JScrollPane scroll = ScrollPaneFactory.createScrollPane(myTable, SideBorder.TOP); addToTop(myFilterTextField); addToCenter(scroll); } From 01d2b8cce13c58a500cd9b1fa2b1db1ae45cbd69 Mon Sep 17 00:00:00 2001 From: Vitaliy Bibaev Date: Tue, 9 Aug 2016 18:49:25 +0300 Subject: [PATCH 003/151] Fix ArrayOufOfRangeException after debugger step. Hide content of table when it is updating --- view/ClassesFilteredView.java | 1 - view/ClassesTable.java | 30 +++++++++++++++++------------- 2 files changed, 17 insertions(+), 14 deletions(-) diff --git a/view/ClassesFilteredView.java b/view/ClassesFilteredView.java index 5b78834bcc11..8325b38bfe50 100644 --- a/view/ClassesFilteredView.java +++ b/view/ClassesFilteredView.java @@ -100,7 +100,6 @@ public class ClassesFilteredView extends BorderLayoutPanel { @Override public void sessionPaused() { - SwingUtilities.invokeLater(myTable::showContent); if (myNeedReloadClasses) { updateClassesAndCounts(); } diff --git a/view/ClassesTable.java b/view/ClassesTable.java index 495350a048d5..f4124b8d020d 100644 --- a/view/ClassesTable.java +++ b/view/ClassesTable.java @@ -151,7 +151,6 @@ class ClassesTable extends JBTable { void setClassesAndCounts(List classes, long[] counts) { ReferenceType selectedClass = myModel.getSelectedClassBeforeHided(); - final int newSelectedIndex = classes.indexOf(selectedClass); myElems = Collections.unmodifiableList(new ArrayList<>(classes)); for (int i = 0, size = myElems.size(); i < size; i++) { @@ -159,9 +158,9 @@ class ClassesTable extends JBTable { myCounts.put(ref, myCounts.getOrDefault(ref, myUnknownValue).update(counts[i])); } - getRowSorter().allRowsChanged(); + showContent(); - if (newSelectedIndex != -1) { + if (newSelectedIndex != -1 && !myModel.isHided()) { changeSelection(convertRowIndexToView(newSelectedIndex), DiffViewTableModel.CLASSNAME_COLUMN_INDEX, false, false); } @@ -173,7 +172,7 @@ class ClassesTable extends JBTable { myModel.hide(); } - void showContent() { + private void showContent() { myModel.show(); } @@ -197,8 +196,8 @@ class ClassesTable extends JBTable { private final static int DIFF_COLUMN_INDEX = 2; // Workaround: save selection after content of classes table has been hided - private ReferenceType selectedClassWhenHided = null; - private boolean isHide = false; + private ReferenceType mySelectedClassWhenHided = null; + private boolean myIsWithContent = true; DiffViewTableModel() { super(new AbstractTableColumnDescriptor[]{ @@ -224,27 +223,32 @@ class ClassesTable extends JBTable { } ReferenceType getSelectedClassBeforeHided() { - return selectedClassWhenHided; + return mySelectedClassWhenHided; } void hide() { - if (!isHide) { - selectedClassWhenHided = getSelectedClass(); - isHide = true; + if (myIsWithContent) { + mySelectedClassWhenHided = getSelectedClass(); + myIsWithContent = false; + clearSelection(); getRowSorter().allRowsChanged(); } } void show() { - if (isHide) { - isHide = false; + if (!myIsWithContent) { + myIsWithContent = true; getRowSorter().allRowsChanged(); } } + boolean isHided() { + return !myIsWithContent; + } + @Override public int getRowCount() { - return isHide ? 0 : myElems.size(); + return myIsWithContent ? myElems.size() : 0; } } From 49fcb9f0117279ed5acb6ee52007f8aec57c5090 Mon Sep 17 00:00:00 2001 From: Vitaliy Bibaev Date: Tue, 9 Aug 2016 19:26:55 +0300 Subject: [PATCH 004/151] Fix classes loading on first breakpoint. Remove redundant methods toString --- view/ClassesTable.java | 17 +---------------- 1 file changed, 1 insertion(+), 16 deletions(-) diff --git a/view/ClassesTable.java b/view/ClassesTable.java index f4124b8d020d..f2c4c67b81e9 100644 --- a/view/ClassesTable.java +++ b/view/ClassesTable.java @@ -197,7 +197,7 @@ class ClassesTable extends JBTable { // Workaround: save selection after content of classes table has been hided private ReferenceType mySelectedClassWhenHided = null; - private boolean myIsWithContent = true; + private boolean myIsWithContent = false; DiffViewTableModel() { super(new AbstractTableColumnDescriptor[]{ @@ -266,11 +266,6 @@ class ClassesTable extends JBTable { DiffValue update(long count) { return new DiffValue(count, count); } - - @Override - public String toString() { - return "?"; - } } private static class DiffValue { @@ -297,15 +292,5 @@ class ClassesTable extends JBTable { int diff() { return (int) (myCurrentCount - myOldCount); } - - @Override - public String toString() { - if (myOldCount == myCurrentCount) { - return String.valueOf(myOldCount); - } - - long diff = diff(); - return String.format("%d (%s%d)", myCurrentCount, diff > 0 ? "+" : "-", Math.abs(diff)); - } } } From 2ab97c7d97dce4737ad1a50abfc4ae8bf2e2e4f7 Mon Sep 17 00:00:00 2001 From: Vitaliy Bibaev Date: Wed, 10 Aug 2016 15:01:39 +0300 Subject: [PATCH 005/151] IDEA-159603 Save history for filtering expression --- view/ExpressionEditorWithHistory.java | 75 +++++++++++++++++++++++++++ view/InstancesWindow.java | 28 +++++----- 2 files changed, 88 insertions(+), 15 deletions(-) create mode 100644 view/ExpressionEditorWithHistory.java diff --git a/view/ExpressionEditorWithHistory.java b/view/ExpressionEditorWithHistory.java new file mode 100644 index 000000000000..7894d61ec5e2 --- /dev/null +++ b/view/ExpressionEditorWithHistory.java @@ -0,0 +1,75 @@ +package org.jetbrains.debugger.memory.view; + +import com.intellij.codeInsight.lookup.LookupManager; +import com.intellij.openapi.Disposable; +import com.intellij.openapi.actionSystem.AnAction; +import com.intellij.openapi.actionSystem.AnActionEvent; +import com.intellij.openapi.actionSystem.CustomShortcutSet; +import com.intellij.openapi.editor.ex.util.EditorUtil; +import com.intellij.openapi.project.Project; +import com.intellij.openapi.ui.popup.PopupStep; +import com.intellij.openapi.ui.popup.util.BaseListPopupStep; +import com.intellij.ui.ColoredListCellRenderer; +import com.intellij.ui.popup.list.ListPopupImpl; +import com.intellij.xdebugger.XExpression; +import com.intellij.xdebugger.XSourcePosition; +import com.intellij.xdebugger.evaluation.XDebuggerEditorsProvider; +import com.intellij.xdebugger.impl.breakpoints.XExpressionImpl; +import com.intellij.xdebugger.impl.ui.XDebuggerExpressionEditor; +import org.jetbrains.annotations.NonNls; +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; + +import javax.swing.*; +import java.util.List; + +class ExpressionEditorWithHistory extends XDebuggerExpressionEditor { + ExpressionEditorWithHistory(@NotNull Project project, + @NotNull XDebuggerEditorsProvider debuggerEditorsProvider, + @Nullable @NonNls String historyId, + @Nullable XSourcePosition sourcePosition, + @Nullable Disposable parentDisposable) { + super(project, debuggerEditorsProvider, historyId, sourcePosition, XExpressionImpl.EMPTY_EXPRESSION, + false, true, true); + + new AnAction("InstancesWindow.ShowHistory") { + @Override + public void actionPerformed(AnActionEvent e) { + showHistory(); + } + + @Override + public void update(AnActionEvent e) { + e.getPresentation().setEnabled(LookupManager.getActiveLookup(getEditor()) == null); + } + }.registerCustomShortcutSet(CustomShortcutSet.fromString("DOWN"), getComponent(), parentDisposable); + } + + private void showHistory() { + List expressions = getRecentExpressions(); + if (!expressions.isEmpty()) { + ListPopupImpl historyPopup = new ListPopupImpl(new BaseListPopupStep(null, expressions) { + @Override + public PopupStep onChosen(XExpression selectedValue, boolean finalChoice) { + setExpression(selectedValue); + requestFocusInEditor(); + return FINAL_CHOICE; + } + }) { + @Override + protected ListCellRenderer getListElementRenderer() { + return new ColoredListCellRenderer() { + @Override + protected void customizeCellRenderer(@NotNull JList list, XExpression value, int index, + boolean selected, boolean hasFocus) { + append(value.getExpression()); + } + }; + } + }; + + historyPopup.getList().setFont(EditorUtil.getEditorFont()); + historyPopup.showUnderneathOf(getEditorComponent()); + } + } +} diff --git a/view/InstancesWindow.java b/view/InstancesWindow.java index 05d8fed6e311..5c56b551386e 100644 --- a/view/InstancesWindow.java +++ b/view/InstancesWindow.java @@ -40,7 +40,6 @@ import com.intellij.xdebugger.frame.presentation.XValuePresentation; import com.intellij.xdebugger.impl.XDebugSessionImpl; import com.intellij.xdebugger.impl.XSourcePositionImpl; import com.intellij.xdebugger.impl.actions.XDebuggerActionBase; -import com.intellij.xdebugger.impl.breakpoints.XExpressionImpl; import com.intellij.xdebugger.impl.evaluate.quick.XDebuggerTreeCreator; import com.intellij.xdebugger.impl.frame.XValueMarkers; import com.intellij.xdebugger.impl.ui.XDebuggerExpressionEditor; @@ -56,10 +55,10 @@ import com.sun.jdi.ReferenceType; import com.sun.jdi.Value; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; -import org.jetbrains.java.debugger.JavaDebuggerEditorsProvider; import org.jetbrains.debugger.memory.utils.DebugCommand; import org.jetbrains.debugger.memory.utils.InstanceJavaValue; import org.jetbrains.debugger.memory.utils.InstanceValueDescriptor; +import org.jetbrains.java.debugger.JavaDebuggerEditorsProvider; import javax.swing.*; import javax.swing.tree.TreeNode; @@ -141,6 +140,7 @@ public class InstancesWindow extends DialogWrapper { } private class MyInstancesView extends JBPanel implements Disposable { + private static final String HISTORY_ID_PREFIX = "filtering"; private static final int MAX_TREE_NODE_COUNT = 1000; private static final int FILTERING_CHUNK_SIZE = 30; @@ -168,8 +168,8 @@ public class InstancesWindow extends DialogWrapper { myDebugSession.addSessionListener(myDebugSessionListener, InstancesWindow.this.myDisposable); JavaDebuggerEditorsProvider editorsProvider = new JavaDebuggerEditorsProvider(); - myFilterConditionEditor = new XDebuggerExpressionEditor(myProject, editorsProvider, "FilterExpression", - null, XExpressionImpl.EMPTY_EXPRESSION, false, true, true); + myFilterConditionEditor = new ExpressionEditorWithHistory(myProject, editorsProvider, + HISTORY_ID_PREFIX + myReferenceType.name(), null, InstancesWindow.this.myDisposable); new SwingWorker() { @Override protected Void doInBackground() throws Exception { @@ -225,13 +225,17 @@ public class InstancesWindow extends DialogWrapper { myInstancesTree.getRoot().setLeaf(false); myInstancesTree.setExpandableItemsEnabled(true); - myFilterButton.addActionListener(e -> - myInstancesTree.rebuildAndRestore(XDebuggerTreeState.saveState(myInstancesTree))); + myFilterButton.addActionListener(e -> { + String expression = myFilterConditionEditor.getExpression().getExpression(); + if(!expression.isEmpty()) { + myFilterConditionEditor.saveTextInHistory(); + } + myInstancesTree.rebuildAndRestore(XDebuggerTreeState.saveState(myInstancesTree)); + }); JBScrollPane treeScrollPane = new JBScrollPane(myInstancesTree); add(filteringPane, BorderLayout.NORTH); add(treeScrollPane, BorderLayout.CENTER); - } @Override @@ -350,14 +354,6 @@ public class InstancesWindow extends DialogWrapper { } private class MyActionListener extends AnActionListener.Adapter { - @Override - public void afterActionPerformed(AnAction action, DataContext dataContext, AnActionEvent event) { - if (dataContext.getData(PlatformDataKeys.CONTEXT_COMPONENT) == myInstancesView.myInstancesTree && - (isAddToWatchesAction(action) || isEvaluateExpressionAction(action))) { - myInstancesTree.rebuildAndRestore(XDebuggerTreeState.saveState(myInstancesTree)); - } - } - @Override public void beforeActionPerformed(AnAction action, DataContext dataContext, AnActionEvent event) { if (dataContext.getData(PlatformDataKeys.CONTEXT_COMPONENT) == myInstancesView.myInstancesTree && @@ -379,6 +375,8 @@ public class InstancesWindow extends DialogWrapper { markers.markValue(valueContainer, new ValueMarkup(expression.replace("@", ""), new JBColor(0, 0), null)); } + + myInstancesTree.rebuildAndRestore(XDebuggerTreeState.saveState(myInstancesTree)); } } } From 2c22abb615b967958c805531593dba999e04c2ed Mon Sep 17 00:00:00 2001 From: Vitaliy Bibaev Date: Tue, 9 Aug 2016 16:58:55 +0300 Subject: [PATCH 006/151] Disable grid in classes table --- view/ClassesTable.java | 2 ++ 1 file changed, 2 insertions(+) diff --git a/view/ClassesTable.java b/view/ClassesTable.java index f2c4c67b81e9..cfd08d70b597 100644 --- a/view/ClassesTable.java +++ b/view/ClassesTable.java @@ -62,6 +62,8 @@ class ClassesTable extends JBTable { diffColumn.setMaxWidth(JBUI.scale(DIFF_COLUMN_MAX_WIDTH)); diffColumn.setResizable(false); + setShowGrid(false); + setDefaultRenderer(ReferenceType.class, new ColoredTableCellRenderer() { @Override protected void customizeCellRenderer(JTable table, @Nullable Object value, From 5304dfd04873d48cf54c0c4660a4ab2e8bd67919 Mon Sep 17 00:00:00 2001 From: Vitaliy Bibaev Date: Tue, 9 Aug 2016 17:12:23 +0300 Subject: [PATCH 007/151] Add action for popup menu --- action/ShowInstancesFromClassesViewAction.java | 10 ++++++++++ view/ClassesTable.java | 11 +++++++++++ 2 files changed, 21 insertions(+) create mode 100644 action/ShowInstancesFromClassesViewAction.java diff --git a/action/ShowInstancesFromClassesViewAction.java b/action/ShowInstancesFromClassesViewAction.java new file mode 100644 index 000000000000..272b9d8625e2 --- /dev/null +++ b/action/ShowInstancesFromClassesViewAction.java @@ -0,0 +1,10 @@ +package org.jetbrains.debugger.memory.action; + +import com.intellij.openapi.actionSystem.AnAction; +import com.intellij.openapi.actionSystem.AnActionEvent; + +public class ShowInstancesFromClassesViewAction extends AnAction { + @Override + public void actionPerformed(AnActionEvent e) { + } +} diff --git a/view/ClassesTable.java b/view/ClassesTable.java index cfd08d70b597..5a81dcc8a286 100644 --- a/view/ClassesTable.java +++ b/view/ClassesTable.java @@ -12,6 +12,8 @@ import org.jetbrains.debugger.memory.utils.AbstractTableModelWithColumns; import javax.swing.*; import javax.swing.table.TableColumn; import javax.swing.table.TableRowSorter; +import java.awt.event.MouseAdapter; +import java.awt.event.MouseEvent; import java.util.ArrayList; import java.util.Arrays; import java.util.Collections; @@ -64,6 +66,15 @@ class ClassesTable extends JBTable { setShowGrid(false); + addMouseListener(new MouseAdapter() { + @Override + public void mouseClicked(MouseEvent e) { + if(SwingUtilities.isRightMouseButton(e) && e.getClickCount() == 1) { + System.out.println("right clicked"); + } + } + }); + setDefaultRenderer(ReferenceType.class, new ColoredTableCellRenderer() { @Override protected void customizeCellRenderer(JTable table, @Nullable Object value, From cfc4a639ee704e04c8ea6494f5a0a4cef84b9467 Mon Sep 17 00:00:00 2001 From: Vitaliy Bibaev Date: Wed, 10 Aug 2016 16:09:22 +0300 Subject: [PATCH 008/151] IDEA-159449 Show popup on right click on class --- view/ClassesFilteredView.java | 21 ++++++++++++++++++--- 1 file changed, 18 insertions(+), 3 deletions(-) diff --git a/view/ClassesFilteredView.java b/view/ClassesFilteredView.java index 8325b38bfe50..89a83e9f640d 100644 --- a/view/ClassesFilteredView.java +++ b/view/ClassesFilteredView.java @@ -6,6 +6,9 @@ import com.intellij.debugger.engine.DebuggerManagerThreadImpl; import com.intellij.debugger.engine.SuspendContextImpl; import com.intellij.debugger.engine.events.SuspendContextCommandImpl; import com.intellij.debugger.impl.DebuggerManagerImpl; +import com.intellij.openapi.actionSystem.ActionManager; +import com.intellij.openapi.actionSystem.ActionPopupMenu; +import com.intellij.openapi.actionSystem.DefaultActionGroup; import com.intellij.openapi.diagnostic.Logger; import com.intellij.openapi.project.Project; import com.intellij.ui.*; @@ -14,16 +17,17 @@ import com.intellij.xdebugger.XDebugSession; import com.intellij.xdebugger.XDebugSessionListener; import com.sun.jdi.ReferenceType; import com.sun.jdi.VirtualMachine; -import org.jetbrains.debugger.memory.event.MemoryViewManagerListener; -import org.jetbrains.debugger.memory.component.MemoryViewManager; -import org.jetbrains.debugger.memory.component.MemoryViewManagerState; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; +import org.jetbrains.debugger.memory.component.MemoryViewManager; +import org.jetbrains.debugger.memory.component.MemoryViewManagerState; +import org.jetbrains.debugger.memory.event.MemoryViewManagerListener; import org.jetbrains.debugger.memory.utils.KeyboardUtils; import org.jetbrains.debugger.memory.utils.SingleAlarmWithMutableDelay; import javax.swing.*; import javax.swing.event.DocumentEvent; +import java.awt.*; import java.awt.event.KeyAdapter; import java.awt.event.KeyEvent; import java.awt.event.MouseEvent; @@ -123,6 +127,17 @@ public class ClassesFilteredView extends BorderLayoutPanel { } }); + myTable.addMouseListener(new PopupHandler() { + @Override + public void invokePopup(Component comp, int x, int y) { + DefaultActionGroup group = (DefaultActionGroup) ActionManager.getInstance().getAction("ClassesView.PopupActionGroup"); + ActionPopupMenu popupMenu = ActionManager.getInstance().createActionPopupMenu("ClassesView.PopupActionGroup", group); + if (popupMenu != null) { + popupMenu.getComponent().show(comp, x, y); + } + } + }); + JScrollPane scroll = ScrollPaneFactory.createScrollPane(myTable, SideBorder.TOP); addToTop(myFilterTextField); addToCenter(scroll); From 0678c79cba9a6f4452881c8c9a8f7b4401ea5d3f Mon Sep 17 00:00:00 2001 From: Vitaliy Bibaev Date: Wed, 10 Aug 2016 17:51:15 +0300 Subject: [PATCH 009/151] IDEA-159449 Open instances window after choosing item in popup menu --- action/ClassesActionBase.java | 35 +++++++++++++++++++ action/ShowInstancesByClassAction.java | 2 +- .../ShowInstancesFromClassesViewAction.java | 20 +++++++++-- view/ClassesFilteredView.java | 19 +++++----- view/ClassesTable.java | 15 ++++++-- view/InstancesWindow.java | 8 ++--- 6 files changed, 80 insertions(+), 19 deletions(-) create mode 100644 action/ClassesActionBase.java diff --git a/action/ClassesActionBase.java b/action/ClassesActionBase.java new file mode 100644 index 000000000000..5d8ce09b6441 --- /dev/null +++ b/action/ClassesActionBase.java @@ -0,0 +1,35 @@ +package org.jetbrains.debugger.memory.action; + +import com.intellij.openapi.actionSystem.AnAction; +import com.intellij.openapi.actionSystem.AnActionEvent; +import com.intellij.openapi.actionSystem.PlatformDataKeys; +import com.intellij.openapi.project.Project; +import org.jetbrains.annotations.Nullable; +import org.jetbrains.debugger.memory.view.ClassesTable; + +import java.awt.*; + +abstract class ClassesActionBase extends AnAction { + @Override + public void update(AnActionEvent e) { + e.getPresentation().setEnabled(isEnabled(e)); + } + + @Override + public void actionPerformed(AnActionEvent e) { + perform(e); + } + + protected boolean isEnabled(AnActionEvent e) { + Project project = e.getProject(); + return project != null && !project.isDisposed(); + } + + protected abstract void perform(AnActionEvent e); + + @Nullable + ClassesTable getTable(AnActionEvent e) { + Component component = e.getData(PlatformDataKeys.CONTEXT_COMPONENT); + return component == null ? null : (ClassesTable) component; + } +} diff --git a/action/ShowInstancesByClassAction.java b/action/ShowInstancesByClassAction.java index 8a96fc9891d2..7bc2b3c19a8f 100644 --- a/action/ShowInstancesByClassAction.java +++ b/action/ShowInstancesByClassAction.java @@ -36,7 +36,7 @@ public class ShowInstancesByClassAction extends XDebuggerTreeActionBase { XDebugSession debugSession = XDebuggerManager.getInstance(project).getCurrentSession(); ObjectReference ref = getObjectReference(node); if(debugSession != null && ref != null){ - new InstancesWindow(project, debugSession, ref.referenceType()).show(); + new InstancesWindow(debugSession, ref.referenceType()).show(); } } } diff --git a/action/ShowInstancesFromClassesViewAction.java b/action/ShowInstancesFromClassesViewAction.java index 272b9d8625e2..5faf9bba1731 100644 --- a/action/ShowInstancesFromClassesViewAction.java +++ b/action/ShowInstancesFromClassesViewAction.java @@ -1,10 +1,24 @@ package org.jetbrains.debugger.memory.action; -import com.intellij.openapi.actionSystem.AnAction; import com.intellij.openapi.actionSystem.AnActionEvent; +import com.sun.jdi.ReferenceType; +import org.jetbrains.debugger.memory.view.ClassesTable; +import org.jetbrains.debugger.memory.view.InstancesWindow; -public class ShowInstancesFromClassesViewAction extends AnAction { +public class ShowInstancesFromClassesViewAction extends ClassesActionBase { @Override - public void actionPerformed(AnActionEvent e) { + protected boolean isEnabled(AnActionEvent e) { + return super.isEnabled(e); + } + + @Override + protected void perform(AnActionEvent e) { + ClassesTable classesView = getTable(e); + if(classesView != null){ + ReferenceType selectedClass = classesView.getSelectedClass(); + if(selectedClass != null) { + new InstancesWindow(classesView.getDebugSession(), selectedClass).show(); + } + } } } diff --git a/view/ClassesFilteredView.java b/view/ClassesFilteredView.java index 89a83e9f640d..7c967fffc129 100644 --- a/view/ClassesFilteredView.java +++ b/view/ClassesFilteredView.java @@ -6,9 +6,9 @@ import com.intellij.debugger.engine.DebuggerManagerThreadImpl; import com.intellij.debugger.engine.SuspendContextImpl; import com.intellij.debugger.engine.events.SuspendContextCommandImpl; import com.intellij.debugger.impl.DebuggerManagerImpl; +import com.intellij.openapi.actionSystem.ActionGroup; import com.intellij.openapi.actionSystem.ActionManager; import com.intellij.openapi.actionSystem.ActionPopupMenu; -import com.intellij.openapi.actionSystem.DefaultActionGroup; import com.intellij.openapi.diagnostic.Logger; import com.intellij.openapi.project.Project; import com.intellij.ui.*; @@ -58,7 +58,7 @@ public class ClassesFilteredView extends BorderLayoutPanel { MemoryViewManagerState memoryViewManagerState = MemoryViewManager.getInstance(myProject).getState(); - myTable = new ClassesTable(memoryViewManagerState.isShowWithDiffOnly, + myTable = new ClassesTable(myDebugSession, memoryViewManagerState.isShowWithDiffOnly, memoryViewManagerState.isShowWithInstancesOnly); myTable.addKeyListener(new KeyAdapter() { @@ -130,10 +130,9 @@ public class ClassesFilteredView extends BorderLayoutPanel { myTable.addMouseListener(new PopupHandler() { @Override public void invokePopup(Component comp, int x, int y) { - DefaultActionGroup group = (DefaultActionGroup) ActionManager.getInstance().getAction("ClassesView.PopupActionGroup"); - ActionPopupMenu popupMenu = ActionManager.getInstance().createActionPopupMenu("ClassesView.PopupActionGroup", group); - if (popupMenu != null) { - popupMenu.getComponent().show(comp, x, y); + ActionPopupMenu menu = createContextMenu(); + if (menu != null) { + menu.getComponent().show(comp, x, y); } } }); @@ -154,8 +153,7 @@ public class ClassesFilteredView extends BorderLayoutPanel { private void handleClassSelection(@Nullable ReferenceType ref) { if (ref != null && myDebugSession.isSuspended()) { - InstancesWindow window = new InstancesWindow(myProject, myDebugSession, ref); - window.show(); + new InstancesWindow(myDebugSession, ref).show(); } } @@ -189,6 +187,11 @@ public class ClassesFilteredView extends BorderLayoutPanel { } } + private ActionPopupMenu createContextMenu() { + ActionGroup group = (ActionGroup) ActionManager.getInstance().getAction("ClassesView.PopupActionGroup"); + return ActionManager.getInstance().createActionPopupMenu("ClassesView.PopupActionGroup", group); + } + private void loadClassesAndCounts() { assert DebuggerManager.getInstance(myProject).isDebuggerManagerThread(); myDebugProcess.getVirtualMachineProxy(); diff --git a/view/ClassesTable.java b/view/ClassesTable.java index 5a81dcc8a286..515c44581291 100644 --- a/view/ClassesTable.java +++ b/view/ClassesTable.java @@ -4,7 +4,9 @@ import com.intellij.icons.AllIcons; import com.intellij.ui.ColoredTableCellRenderer; import com.intellij.ui.table.JBTable; import com.intellij.util.ui.JBUI; +import com.intellij.xdebugger.XDebugSession; import com.sun.jdi.ReferenceType; +import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; import org.jetbrains.debugger.memory.utils.AbstractTableColumnDescriptor; import org.jetbrains.debugger.memory.utils.AbstractTableModelWithColumns; @@ -20,7 +22,7 @@ import java.util.Collections; import java.util.List; import java.util.concurrent.ConcurrentHashMap; -class ClassesTable extends JBTable { +public class ClassesTable extends JBTable { private static final int CLASSES_COLUMN_PREFERRED_WIDTH = 250; private static final int COUNT_COLUMN_MIN_WIDTH = 80; private static final int COUNT_COLUMN_MAX_WIDTH = 100; @@ -31,6 +33,7 @@ class ClassesTable extends JBTable { private final DiffViewTableModel myModel = new DiffViewTableModel(); private final UnknownDiffValue myUnknownValue = new UnknownDiffValue(); + private final XDebugSession myDebugSession; private boolean myOnlyWithDiff; private boolean myOnlyWithInstances; @@ -40,9 +43,10 @@ class ClassesTable extends JBTable { private volatile List myElems = Collections.unmodifiableList(new ArrayList<>()); - ClassesTable(boolean onlyWithDiff, boolean onlyWithInstances) { + ClassesTable(@NotNull XDebugSession session, boolean onlyWithDiff, boolean onlyWithInstances) { setModel(myModel); + myDebugSession = session; myOnlyWithDiff = onlyWithDiff; myOnlyWithInstances = onlyWithInstances; @@ -126,8 +130,13 @@ class ClassesTable extends JBTable { setSelectionMode(ListSelectionModel.SINGLE_SELECTION); } + @NotNull + public XDebugSession getDebugSession() { + return myDebugSession; + } + @Nullable - ReferenceType getSelectedClass() { + public ReferenceType getSelectedClass() { int selectedRow = getSelectedRow(); if (selectedRow != -1) { int ix = convertRowIndexToModel(selectedRow); diff --git a/view/InstancesWindow.java b/view/InstancesWindow.java index 5c56b551386e..27b88adf8c57 100644 --- a/view/InstancesWindow.java +++ b/view/InstancesWindow.java @@ -81,11 +81,11 @@ public class InstancesWindow extends DialogWrapper { private final ReferenceType myReferenceType; private MyInstancesView myInstancesView; - public InstancesWindow(@NotNull Project project, - @NotNull XDebugSession session, + public InstancesWindow(@NotNull XDebugSession session, @NotNull ReferenceType referenceType) { - super(project, false); - myProject = project; + super(session.getProject(), false); + + myProject = session.getProject(); myDebugSession = session; myReferenceType = referenceType; setTitle("Instances of " + referenceType.name()); From 8a119d6e3e667e960bcca0ba3cf148c8c2268de5 Mon Sep 17 00:00:00 2001 From: Vitaliy Bibaev Date: Wed, 10 Aug 2016 18:45:13 +0300 Subject: [PATCH 010/151] IDEA-159449 Review fixes --- action/ClassesActionBase.java | 15 +++++--- .../ShowInstancesFromClassesViewAction.java | 12 +++--- view/ClassesTable.java | 38 ++++++++++--------- 3 files changed, 34 insertions(+), 31 deletions(-) diff --git a/action/ClassesActionBase.java b/action/ClassesActionBase.java index 5d8ce09b6441..51e2c3a550a5 100644 --- a/action/ClassesActionBase.java +++ b/action/ClassesActionBase.java @@ -2,13 +2,12 @@ package org.jetbrains.debugger.memory.action; import com.intellij.openapi.actionSystem.AnAction; import com.intellij.openapi.actionSystem.AnActionEvent; -import com.intellij.openapi.actionSystem.PlatformDataKeys; import com.intellij.openapi.project.Project; +import com.intellij.xdebugger.XDebugSession; +import com.sun.jdi.ReferenceType; import org.jetbrains.annotations.Nullable; import org.jetbrains.debugger.memory.view.ClassesTable; -import java.awt.*; - abstract class ClassesActionBase extends AnAction { @Override public void update(AnActionEvent e) { @@ -28,8 +27,12 @@ abstract class ClassesActionBase extends AnAction { protected abstract void perform(AnActionEvent e); @Nullable - ClassesTable getTable(AnActionEvent e) { - Component component = e.getData(PlatformDataKeys.CONTEXT_COMPONENT); - return component == null ? null : (ClassesTable) component; + ReferenceType getSelectedClass(AnActionEvent e) { + return e.getData(ClassesTable.SELECTED_CLASS_KEY); + } + + @Nullable + XDebugSession getDebugSession(AnActionEvent e) { + return e.getData(ClassesTable.DEBUG_SESSION_KEY); } } diff --git a/action/ShowInstancesFromClassesViewAction.java b/action/ShowInstancesFromClassesViewAction.java index 5faf9bba1731..9034d583091a 100644 --- a/action/ShowInstancesFromClassesViewAction.java +++ b/action/ShowInstancesFromClassesViewAction.java @@ -1,8 +1,8 @@ package org.jetbrains.debugger.memory.action; import com.intellij.openapi.actionSystem.AnActionEvent; +import com.intellij.xdebugger.XDebugSession; import com.sun.jdi.ReferenceType; -import org.jetbrains.debugger.memory.view.ClassesTable; import org.jetbrains.debugger.memory.view.InstancesWindow; public class ShowInstancesFromClassesViewAction extends ClassesActionBase { @@ -13,12 +13,10 @@ public class ShowInstancesFromClassesViewAction extends ClassesActionBase { @Override protected void perform(AnActionEvent e) { - ClassesTable classesView = getTable(e); - if(classesView != null){ - ReferenceType selectedClass = classesView.getSelectedClass(); - if(selectedClass != null) { - new InstancesWindow(classesView.getDebugSession(), selectedClass).show(); - } + XDebugSession debugSession = getDebugSession(e); + ReferenceType selectedClass = getSelectedClass(e); + if (debugSession != null && selectedClass != null) { + new InstancesWindow(debugSession, selectedClass).show(); } } } diff --git a/view/ClassesTable.java b/view/ClassesTable.java index 515c44581291..9c4834096abe 100644 --- a/view/ClassesTable.java +++ b/view/ClassesTable.java @@ -1,11 +1,14 @@ package org.jetbrains.debugger.memory.view; import com.intellij.icons.AllIcons; +import com.intellij.openapi.actionSystem.DataKey; +import com.intellij.openapi.actionSystem.DataProvider; import com.intellij.ui.ColoredTableCellRenderer; import com.intellij.ui.table.JBTable; import com.intellij.util.ui.JBUI; import com.intellij.xdebugger.XDebugSession; import com.sun.jdi.ReferenceType; +import org.jetbrains.annotations.NonNls; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; import org.jetbrains.debugger.memory.utils.AbstractTableColumnDescriptor; @@ -14,15 +17,15 @@ import org.jetbrains.debugger.memory.utils.AbstractTableModelWithColumns; import javax.swing.*; import javax.swing.table.TableColumn; import javax.swing.table.TableRowSorter; -import java.awt.event.MouseAdapter; -import java.awt.event.MouseEvent; import java.util.ArrayList; import java.util.Arrays; import java.util.Collections; import java.util.List; import java.util.concurrent.ConcurrentHashMap; -public class ClassesTable extends JBTable { +public class ClassesTable extends JBTable implements DataProvider { + public static final DataKey SELECTED_CLASS_KEY = DataKey.create("ClassesTable.SelectedClass"); + public static final DataKey DEBUG_SESSION_KEY = DataKey.create("ClassesTable.DebugSession"); private static final int CLASSES_COLUMN_PREFERRED_WIDTH = 250; private static final int COUNT_COLUMN_MIN_WIDTH = 80; private static final int COUNT_COLUMN_MAX_WIDTH = 100; @@ -70,15 +73,6 @@ public class ClassesTable extends JBTable { setShowGrid(false); - addMouseListener(new MouseAdapter() { - @Override - public void mouseClicked(MouseEvent e) { - if(SwingUtilities.isRightMouseButton(e) && e.getClickCount() == 1) { - System.out.println("right clicked"); - } - } - }); - setDefaultRenderer(ReferenceType.class, new ColoredTableCellRenderer() { @Override protected void customizeCellRenderer(JTable table, @Nullable Object value, @@ -130,13 +124,8 @@ public class ClassesTable extends JBTable { setSelectionMode(ListSelectionModel.SINGLE_SELECTION); } - @NotNull - public XDebugSession getDebugSession() { - return myDebugSession; - } - @Nullable - public ReferenceType getSelectedClass() { + ReferenceType getSelectedClass() { int selectedRow = getSelectedRow(); if (selectedRow != -1) { int ix = convertRowIndexToModel(selectedRow); @@ -212,6 +201,19 @@ public class ClassesTable extends JBTable { return i == pattern.length(); } + @Nullable + @Override + public Object getData(@NonNls String dataId) { + if(SELECTED_CLASS_KEY.is(dataId)) { + return getSelectedClass(); + } + if(DEBUG_SESSION_KEY.is(dataId)) { + return myDebugSession; + } + + return null; + } + private class DiffViewTableModel extends AbstractTableModelWithColumns { private final static int CLASSNAME_COLUMN_INDEX = 0; private final static int COUNT_COLUMN_INDEX = 1; From 76c0040d7451c1db16955e83fe2dca0c99a242f3 Mon Sep 17 00:00:00 2001 From: Vitaliy Bibaev Date: Thu, 11 Aug 2016 12:26:32 +0300 Subject: [PATCH 011/151] Save settings of toolwindow per application --- action/ShowClassesWithDiff.java | 5 ++--- action/ShowClassesWithInstanceAction.java | 5 ++--- component/MemoryViewManager.java | 19 ++++++++----------- toolwindow/MemoryViewCondition.java | 2 +- toolwindow/MemoryViewToolWindowFactory.java | 2 +- view/ClassesFilteredView.java | 6 +++--- 6 files changed, 17 insertions(+), 22 deletions(-) diff --git a/action/ShowClassesWithDiff.java b/action/ShowClassesWithDiff.java index 78081dc76baf..8ca30ec247b8 100644 --- a/action/ShowClassesWithDiff.java +++ b/action/ShowClassesWithDiff.java @@ -8,15 +8,14 @@ import org.jetbrains.debugger.memory.component.MemoryViewManager; public class ShowClassesWithDiff extends ToggleAction { @Override public boolean isSelected(AnActionEvent e) { - Project project = e.getProject(); - return project != null && MemoryViewManager.getInstance(e.getProject()).isNeedShowDiffOnly(); + return MemoryViewManager.getInstance().isNeedShowDiffOnly(); } @Override public void setSelected(AnActionEvent e, boolean state) { Project project = e.getProject(); if (project != null) { - MemoryViewManager.getInstance(project).setShowDiffOnly(state); + MemoryViewManager.getInstance().setShowDiffOnly(state); } } } diff --git a/action/ShowClassesWithInstanceAction.java b/action/ShowClassesWithInstanceAction.java index 9e30ffda8230..17681f97dfb9 100644 --- a/action/ShowClassesWithInstanceAction.java +++ b/action/ShowClassesWithInstanceAction.java @@ -8,15 +8,14 @@ import org.jetbrains.debugger.memory.component.MemoryViewManager; public class ShowClassesWithInstanceAction extends ToggleAction { @Override public boolean isSelected(AnActionEvent e) { - Project project = e.getProject(); - return project != null && MemoryViewManager.getInstance(project).isNeedShowInstancesOnly(); + return MemoryViewManager.getInstance().isNeedShowInstancesOnly(); } @Override public void setSelected(AnActionEvent e, boolean state) { Project project = e.getProject(); if (project != null) { - MemoryViewManager.getInstance(project).setShowWithInstancesOnly(state); + MemoryViewManager.getInstance().setShowWithInstancesOnly(state); } } } diff --git a/component/MemoryViewManager.java b/component/MemoryViewManager.java index d7de9e532c95..d6d0f93c42d8 100644 --- a/component/MemoryViewManager.java +++ b/component/MemoryViewManager.java @@ -1,27 +1,24 @@ package org.jetbrains.debugger.memory.component; +import com.intellij.openapi.application.ApplicationManager; import com.intellij.openapi.components.*; import com.intellij.openapi.project.Project; import com.intellij.openapi.wm.ToolWindow; import com.intellij.openapi.wm.ToolWindowManager; import com.intellij.util.EventDispatcher; +import org.jetbrains.annotations.NotNull; import org.jetbrains.debugger.memory.event.MemoryViewManagerListener; import org.jetbrains.debugger.memory.toolwindow.MemoryViewToolWindowFactory; -import org.jetbrains.annotations.NotNull; -@State(name = "MemoryViewSettings", storages = @Storage(StoragePathMacros.WORKSPACE_FILE)) -public class MemoryViewManager extends AbstractProjectComponent +@State(name = "MemoryViewSettings", storages = @Storage("memory.view.xml")) +public class MemoryViewManager extends ApplicationComponent.Adapter implements PersistentStateComponent { private final EventDispatcher myDispatcher = EventDispatcher.create(MemoryViewManagerListener.class); private MemoryViewManagerState myState = new MemoryViewManagerState(); - public static MemoryViewManager getInstance(Project project) { - return project.getComponent(MemoryViewManager.class); - } - - protected MemoryViewManager(Project project) { - super(project); + public static MemoryViewManager getInstance() { + return ApplicationManager.getApplication().getComponent(MemoryViewManager.class); } @NotNull @@ -70,8 +67,8 @@ public class MemoryViewManager extends AbstractProjectComponent myDispatcher.removeListener(listener); } - public ToolWindow getToolWindow() { - return ToolWindowManager.getInstance(myProject).getToolWindow(MemoryViewToolWindowFactory.TOOL_WINDOW_ID); + public ToolWindow getToolWindow(Project project) { + return ToolWindowManager.getInstance(project).getToolWindow(MemoryViewToolWindowFactory.TOOL_WINDOW_ID); } private void fireStateChanged() { diff --git a/toolwindow/MemoryViewCondition.java b/toolwindow/MemoryViewCondition.java index 9bcf07002d4f..e50a25d2decf 100644 --- a/toolwindow/MemoryViewCondition.java +++ b/toolwindow/MemoryViewCondition.java @@ -33,7 +33,7 @@ public class MemoryViewCondition implements Condition { } private void updateIcon(@NotNull Project project, boolean enabled) { - ToolWindow toolWindow = MemoryViewManager.getInstance(project).getToolWindow(); + ToolWindow toolWindow = MemoryViewManager.getInstance().getToolWindow(project); if (toolWindow != null) { SwingUtilities.invokeLater(() -> toolWindow.setIcon(enabled ? MemoryViewIcons.MAIN_ENABLED : MemoryViewIcons.MAIN_DISABLED)); diff --git a/toolwindow/MemoryViewToolWindowFactory.java b/toolwindow/MemoryViewToolWindowFactory.java index f258eb633050..1d8807bbaabd 100644 --- a/toolwindow/MemoryViewToolWindowFactory.java +++ b/toolwindow/MemoryViewToolWindowFactory.java @@ -117,7 +117,7 @@ public class MemoryViewToolWindowFactory implements ToolWindowFactory, DumbAware @Nullable private ToolWindow getToolWindow(@NotNull Project project) { - return MemoryViewManager.getInstance(project).getToolWindow(); + return MemoryViewManager.getInstance().getToolWindow(project); } private final class MyDebuggerStatusChangedListener implements XDebuggerManagerListener { diff --git a/view/ClassesFilteredView.java b/view/ClassesFilteredView.java index 7c967fffc129..d17e096e46cd 100644 --- a/view/ClassesFilteredView.java +++ b/view/ClassesFilteredView.java @@ -56,7 +56,7 @@ public class ClassesFilteredView extends BorderLayoutPanel { myDebugProcess = DebuggerManager.getInstance(myProject) .getDebugProcess(myDebugSession.getDebugProcess().getProcessHandler()); - MemoryViewManagerState memoryViewManagerState = MemoryViewManager.getInstance(myProject).getState(); + MemoryViewManagerState memoryViewManagerState = MemoryViewManager.getInstance().getState(); myTable = new ClassesTable(myDebugSession, memoryViewManagerState.isShowWithDiffOnly, memoryViewManagerState.isShowWithInstancesOnly); @@ -88,7 +88,7 @@ public class ClassesFilteredView extends BorderLayoutPanel { myTable.setFilteringByInstanceExists(state.isShowWithInstancesOnly); }; - MemoryViewManager.getInstance(myProject).addMemoryViewManagerListener(memoryViewManagerListener); + MemoryViewManager.getInstance().addMemoryViewManagerListener(memoryViewManagerListener); myDebugSession.addSessionListener(new XDebugSessionListener() { @Override @@ -99,7 +99,7 @@ public class ClassesFilteredView extends BorderLayoutPanel { @Override public void sessionStopped() { debugSession.removeSessionListener(this); - MemoryViewManager.getInstance(myProject).removeMemoryViewManagerListener(memoryViewManagerListener); + MemoryViewManager.getInstance().removeMemoryViewManagerListener(memoryViewManagerListener); } @Override From 1784b2cf75c749307bc9bfbf30ce9867d0a7ac52 Mon Sep 17 00:00:00 2001 From: Vitaliy Bibaev Date: Thu, 11 Aug 2016 13:15:46 +0300 Subject: [PATCH 012/151] Remove redundant invokeLater call --- toolwindow/MemoryViewToolWindowFactory.java | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/toolwindow/MemoryViewToolWindowFactory.java b/toolwindow/MemoryViewToolWindowFactory.java index 1d8807bbaabd..ae43aaebf52f 100644 --- a/toolwindow/MemoryViewToolWindowFactory.java +++ b/toolwindow/MemoryViewToolWindowFactory.java @@ -61,9 +61,8 @@ public class MemoryViewToolWindowFactory implements ToolWindowFactory, DumbAware @Override public void stateChanged() { if (!myMemoryViews.isEmpty()) { - SwingUtilities.invokeLater(() -> - myMemoryViews.values().forEach(classesFilteredView -> - classesFilteredView.setNeedReloadClasses(toolWindow.isVisible()))); + myMemoryViews.values().forEach(classesFilteredView -> + classesFilteredView.setNeedReloadClasses(toolWindow.isVisible())); } } }); From d1dc2ee62a61d4af71b8f24ba7750b6cd9e1cdc9 Mon Sep 17 00:00:00 2001 From: Vitaliy Bibaev Date: Thu, 11 Aug 2016 13:29:21 +0300 Subject: [PATCH 013/151] Run updating of classes after toolwindow was opened only when session has been continued --- view/ClassesFilteredView.java | 12 ++++++++---- 1 file changed, 8 insertions(+), 4 deletions(-) diff --git a/view/ClassesFilteredView.java b/view/ClassesFilteredView.java index d17e096e46cd..43a02b99e23e 100644 --- a/view/ClassesFilteredView.java +++ b/view/ClassesFilteredView.java @@ -46,6 +46,7 @@ public class ClassesFilteredView extends BorderLayoutPanel { private final SearchTextField myFilterTextField = new SearchTextField(false); private final ClassesTable myTable; + private volatile SuspendContextImpl myLastSuspendContext; private boolean myNeedReloadClasses = false; public ClassesFilteredView(@NotNull XDebugSession debugSession) { @@ -118,12 +119,12 @@ public class ClassesFilteredView extends BorderLayoutPanel { }); mySingleAlarm = new SingleAlarmWithMutableDelay(() -> { - SuspendContextImpl suspendContext = getSuspendContext(); - if (suspendContext != null) { + myLastSuspendContext = getSuspendContext(); + if (myLastSuspendContext != null) { SwingUtilities.invokeLater(() -> myTable.setBusy(true)); ((DebuggerManagerThreadImpl) myDebugProcess.getManagerThread()) - .schedule(new MyUpdateClassesCommand(suspendContext)); + .schedule(new MyUpdateClassesCommand(myLastSuspendContext)); } }); @@ -146,7 +147,10 @@ public class ClassesFilteredView extends BorderLayoutPanel { if (myNeedReloadClasses != value) { myNeedReloadClasses = value; if (myNeedReloadClasses) { - updateClassesAndCounts(); + SuspendContextImpl suspendContext = getSuspendContext(); + if (suspendContext != null && !suspendContext.equals(myLastSuspendContext)) { + updateClassesAndCounts(); + } } } } From cc6d07b69f3595e770ae7f332cbc52ee821881ef Mon Sep 17 00:00:00 2001 From: Vitaliy Bibaev Date: Thu, 11 Aug 2016 13:43:19 +0300 Subject: [PATCH 014/151] Make condition for ToolWindowFactory as its inner class --- toolwindow/MemoryViewCondition.java | 45 --------------------- toolwindow/MemoryViewToolWindowFactory.java | 36 ++++++++++++++++- 2 files changed, 34 insertions(+), 47 deletions(-) delete mode 100644 toolwindow/MemoryViewCondition.java diff --git a/toolwindow/MemoryViewCondition.java b/toolwindow/MemoryViewCondition.java deleted file mode 100644 index e50a25d2decf..000000000000 --- a/toolwindow/MemoryViewCondition.java +++ /dev/null @@ -1,45 +0,0 @@ -package org.jetbrains.debugger.memory.toolwindow; - -import com.intellij.openapi.project.Project; -import com.intellij.openapi.util.Condition; -import com.intellij.openapi.wm.ToolWindow; -import com.intellij.util.messages.MessageBusConnection; -import com.intellij.xdebugger.XDebugProcess; -import com.intellij.xdebugger.XDebuggerManager; -import com.intellij.xdebugger.XDebuggerManagerListener; -import icons.MemoryViewIcons; -import org.jetbrains.debugger.memory.component.MemoryViewManager; -import org.jetbrains.annotations.NotNull; - -import javax.swing.*; -import java.util.Arrays; - -public class MemoryViewCondition implements Condition { - @Override - public boolean value(Project project) { - MessageBusConnection connection = project.getMessageBus().connect(project); - connection.subscribe(XDebuggerManager.TOPIC, new XDebuggerManagerListener() { - @Override - public void processStarted(@NotNull XDebugProcess debugProcess) { - updateIcon(debugProcess.getSession().getProject(), true); - } - - @Override - public void processStopped(@NotNull XDebugProcess debugProcess) { - Project project = debugProcess.getSession().getProject(); - boolean enabled = Arrays.stream(XDebuggerManager.getInstance(project) - .getDebugSessions()).anyMatch(session -> !session.getDebugProcess().equals(debugProcess)); - updateIcon(project, enabled); - } - - private void updateIcon(@NotNull Project project, boolean enabled) { - ToolWindow toolWindow = MemoryViewManager.getInstance().getToolWindow(project); - if (toolWindow != null) { - SwingUtilities.invokeLater(() -> - toolWindow.setIcon(enabled ? MemoryViewIcons.MAIN_ENABLED : MemoryViewIcons.MAIN_DISABLED)); - } - } - }); - return true; - } -} diff --git a/toolwindow/MemoryViewToolWindowFactory.java b/toolwindow/MemoryViewToolWindowFactory.java index ae43aaebf52f..9260030988f1 100644 --- a/toolwindow/MemoryViewToolWindowFactory.java +++ b/toolwindow/MemoryViewToolWindowFactory.java @@ -21,13 +21,15 @@ import com.intellij.xdebugger.XDebugProcess; import com.intellij.xdebugger.XDebugSession; import com.intellij.xdebugger.XDebuggerManager; import com.intellij.xdebugger.XDebuggerManagerListener; -import org.jetbrains.debugger.memory.view.ClassesFilteredView; -import org.jetbrains.debugger.memory.component.MemoryViewManager; +import icons.MemoryViewIcons; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; +import org.jetbrains.debugger.memory.component.MemoryViewManager; +import org.jetbrains.debugger.memory.view.ClassesFilteredView; import javax.swing.*; import java.awt.*; +import java.util.Arrays; public class MemoryViewToolWindowFactory implements ToolWindowFactory, DumbAware { public final static String TOOL_WINDOW_ID = "Memory View"; @@ -119,6 +121,36 @@ public class MemoryViewToolWindowFactory implements ToolWindowFactory, DumbAware return MemoryViewManager.getInstance().getToolWindow(project); } + public static class Condition implements com.intellij.openapi.util.Condition { + @Override + public boolean value(Project project) { + MessageBusConnection connection = project.getMessageBus().connect(project); + connection.subscribe(XDebuggerManager.TOPIC, new XDebuggerManagerListener() { + @Override + public void processStarted(@NotNull XDebugProcess debugProcess) { + updateIcon(debugProcess.getSession().getProject(), true); + } + + @Override + public void processStopped(@NotNull XDebugProcess debugProcess) { + Project project = debugProcess.getSession().getProject(); + boolean enabled = Arrays.stream(XDebuggerManager.getInstance(project) + .getDebugSessions()).anyMatch(session -> !session.getDebugProcess().equals(debugProcess)); + updateIcon(project, enabled); + } + + private void updateIcon(@NotNull Project project, boolean enabled) { + ToolWindow toolWindow = MemoryViewManager.getInstance().getToolWindow(project); + if (toolWindow != null) { + SwingUtilities.invokeLater(() -> + toolWindow.setIcon(enabled ? MemoryViewIcons.MAIN_ENABLED : MemoryViewIcons.MAIN_DISABLED)); + } + } + }); + return true; + } + } + private final class MyDebuggerStatusChangedListener implements XDebuggerManagerListener { @Override public void processStarted(@NotNull XDebugProcess xDebugProcess) { From 82398a3abd529537cd4b2c4d1218e03c38b99dea Mon Sep 17 00:00:00 2001 From: Vitaliy Bibaev Date: Thu, 11 Aug 2016 18:01:55 +0300 Subject: [PATCH 015/151] Rename dimension service key for instances window --- view/InstancesWindow.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/view/InstancesWindow.java b/view/InstancesWindow.java index 27b88adf8c57..790c9e9368aa 100644 --- a/view/InstancesWindow.java +++ b/view/InstancesWindow.java @@ -104,7 +104,7 @@ public class InstancesWindow extends DialogWrapper { @NotNull @Override protected String getDimensionServiceKey() { - return "#org.jetbrains.plugin.view.InstancesWindow"; + return "#org.jetbrains.debugger.memory.view.InstancesWindow"; } @Nullable From ba07eeb58c266c1dd2870792de8425873aaa7eff Mon Sep 17 00:00:00 2001 From: Vitaliy Bibaev Date: Thu, 11 Aug 2016 18:02:17 +0300 Subject: [PATCH 016/151] Annotate getToolWindow method in manager --- component/MemoryViewManager.java | 2 ++ 1 file changed, 2 insertions(+) diff --git a/component/MemoryViewManager.java b/component/MemoryViewManager.java index d6d0f93c42d8..42949b2bc59c 100644 --- a/component/MemoryViewManager.java +++ b/component/MemoryViewManager.java @@ -7,6 +7,7 @@ import com.intellij.openapi.wm.ToolWindow; import com.intellij.openapi.wm.ToolWindowManager; import com.intellij.util.EventDispatcher; import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; import org.jetbrains.debugger.memory.event.MemoryViewManagerListener; import org.jetbrains.debugger.memory.toolwindow.MemoryViewToolWindowFactory; @@ -67,6 +68,7 @@ public class MemoryViewManager extends ApplicationComponent.Adapter myDispatcher.removeListener(listener); } + @Nullable public ToolWindow getToolWindow(Project project) { return ToolWindowManager.getInstance(project).getToolWindow(MemoryViewToolWindowFactory.TOOL_WINDOW_ID); } From 33e9abf776e58d49b2e3d03719ed24191f16159d Mon Sep 17 00:00:00 2001 From: Vitaliy Bibaev Date: Thu, 11 Aug 2016 18:11:37 +0300 Subject: [PATCH 017/151] Small refactor InstancesWindow class --- utils/DebugCommand.java | 9 ---- view/InstancesWindow.java | 105 ++++++++++++++++++++++---------------- 2 files changed, 60 insertions(+), 54 deletions(-) delete mode 100644 utils/DebugCommand.java diff --git a/utils/DebugCommand.java b/utils/DebugCommand.java deleted file mode 100644 index 3076fb3d3ebc..000000000000 --- a/utils/DebugCommand.java +++ /dev/null @@ -1,9 +0,0 @@ -package org.jetbrains.debugger.memory.utils; - -import com.intellij.debugger.engine.managerThread.DebuggerCommand; - -public interface DebugCommand extends DebuggerCommand { - @Override - default void commandCancelled() { - } -} diff --git a/view/InstancesWindow.java b/view/InstancesWindow.java index 790c9e9368aa..640f3e0c031c 100644 --- a/view/InstancesWindow.java +++ b/view/InstancesWindow.java @@ -1,7 +1,10 @@ package org.jetbrains.debugger.memory.view; import com.intellij.debugger.DebuggerManager; -import com.intellij.debugger.engine.*; +import com.intellij.debugger.engine.DebugProcessImpl; +import com.intellij.debugger.engine.DebuggerUtils; +import com.intellij.debugger.engine.JavaValue; +import com.intellij.debugger.engine.SuspendContextImpl; import com.intellij.debugger.engine.evaluation.EvaluateException; import com.intellij.debugger.engine.evaluation.EvaluationContext; import com.intellij.debugger.engine.evaluation.EvaluationContextImpl; @@ -55,7 +58,6 @@ import com.sun.jdi.ReferenceType; import com.sun.jdi.Value; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; -import org.jetbrains.debugger.memory.utils.DebugCommand; import org.jetbrains.debugger.memory.utils.InstanceJavaValue; import org.jetbrains.debugger.memory.utils.InstanceValueDescriptor; import org.jetbrains.java.debugger.JavaDebuggerEditorsProvider; @@ -104,7 +106,7 @@ public class InstancesWindow extends DialogWrapper { @NotNull @Override protected String getDimensionServiceKey() { - return "#org.jetbrains.debugger.memory.view.InstancesWindow"; + return "#org.jetbrains.plugin.view.InstancesWindow"; } @Nullable @@ -170,18 +172,7 @@ public class InstancesWindow extends DialogWrapper { myFilterConditionEditor = new ExpressionEditorWithHistory(myProject, editorsProvider, HISTORY_ID_PREFIX + myReferenceType.name(), null, InstancesWindow.this.myDisposable); - new SwingWorker() { - @Override - protected Void doInBackground() throws Exception { - ApplicationManager.getApplication().runReadAction(() -> { - final PsiClass psiClass = DebuggerUtils.findClass(myReferenceType.name(), - myProject, GlobalSearchScope.allScope(myProject)); - XSourcePositionImpl position = XSourcePositionImpl.createByElement(psiClass); - SwingUtilities.invokeLater(() -> myFilterConditionEditor.setSourcePosition(position)); - }); - return null; - } - }.execute(); + setSourcePositionForEditor(); myFilterButton.setBorder(BorderFactory.createEmptyBorder()); Dimension progressbarPreferredSize = myFilterConditionEditor.getEditorComponent().getPreferredSize(); @@ -209,25 +200,15 @@ public class InstancesWindow extends DialogWrapper { XDebuggerTreeCreator treeCreator = new XDebuggerTreeCreator(myProject, editorsProvider, null, markers); - myInstancesTree = (XDebuggerTree) treeCreator.createTree(Pair.pair(new XNamedValue("root") { - @Override - public void computeChildren(@NotNull XCompositeNode node) { - updateInstances(); - } - - @Override - public void computePresentation(@NotNull XValueNode xValueNode, @NotNull XValuePlace xValuePlace) { - xValueNode.setPresentation(null, "", "", true); - } - }, "root")); + myInstancesTree = (XDebuggerTree) treeCreator.createTree(getTreeRootDescriptor()); myInstancesTree.setRootVisible(false); myInstancesTree.getRoot().setLeaf(false); myInstancesTree.setExpandableItemsEnabled(true); myFilterButton.addActionListener(e -> { String expression = myFilterConditionEditor.getExpression().getExpression(); - if(!expression.isEmpty()) { + if (!expression.isEmpty()) { myFilterConditionEditor.saveTextInHistory(); } myInstancesTree.rebuildAndRestore(XDebuggerTreeState.saveState(myInstancesTree)); @@ -247,20 +228,28 @@ public class InstancesWindow extends DialogWrapper { } private void updateInstances() { - DebugProcess debugProcess = DebuggerManager.getInstance(myProject) + DebugProcessImpl debugProcess = (DebugProcessImpl) DebuggerManager.getInstance(myProject) .getDebugProcess(myDebugSession.getDebugProcess().getProcessHandler()); cancelFilteringTask(); - debugProcess.getManagerThread().invokeCommand((DebugCommand) () -> { - List instances = myReferenceType.instances(0); - EvaluationContextImpl evaluationContext = ((DebugProcessImpl) debugProcess) - .getDebuggerContext().createEvaluationContext(); + debugProcess.getManagerThread().schedule(new DebuggerContextCommandImpl(debugProcess.getDebuggerContext()) { + @Override + public Priority getPriority() { + return Priority.LOWEST; + } - if(evaluationContext != null) { - synchronized (myFilteringTaskLock) { - myFilteringTask = new MyFilteringWorker(instances, evaluationContext, createEvaluator()); - myFilteringTask.execute(); + @Override + public void threadAction(@NotNull SuspendContextImpl suspendContext) { + List instances = myReferenceType.instances(0); + EvaluationContextImpl evaluationContext = debugProcess + .getDebuggerContext().createEvaluationContext(); + + if(evaluationContext != null) { + synchronized (myFilteringTaskLock) { + myFilteringTask = new MyFilteringWorker(instances, evaluationContext, createEvaluator()); + myFilteringTask.execute(); + } } } }); @@ -284,7 +273,7 @@ public class InstancesWindow extends DialogWrapper { if (expression != null && !expression.getExpression().isEmpty()) { try { myEvaluator.setReferenceExpression(TextWithImportsImpl. - fromXExpression(myFilterConditionEditor.getExpression())); + fromXExpression(expression)); evaluator = myEvaluator.getEvaluator(); } catch (EvaluateException ignored) { } @@ -310,6 +299,36 @@ public class InstancesWindow extends DialogWrapper { : null; } + private void setSourcePositionForEditor() { + new SwingWorker() { + @Override + protected Void doInBackground() throws Exception { + ApplicationManager.getApplication().runReadAction(() -> { + final PsiClass psiClass = DebuggerUtils.findClass(myReferenceType.name(), + myProject, GlobalSearchScope.allScope(myProject)); + XSourcePositionImpl position = XSourcePositionImpl.createByElement(psiClass); + SwingUtilities.invokeLater(() -> myFilterConditionEditor.setSourcePosition(position)); + }); + return null; + } + }.execute(); + } + + @NotNull + private Pair getTreeRootDescriptor() { + return Pair.pair(new XValue() { + @Override + public void computeChildren(@NotNull XCompositeNode node) { + updateInstances(); + } + + @Override + public void computePresentation(@NotNull XValueNode node, @NotNull XValuePlace place) { + node.setPresentation(null, "", "", true); + } + }, "root"); + } + private class MySessionListener implements XDebugSessionListener { private final XValueChildrenList myRunningAppChildNode = new XValueChildrenList(); @@ -339,6 +358,7 @@ public class InstancesWindow extends DialogWrapper { public void sessionResumed() { SwingUtilities.invokeLater(() -> { myTreeState = XDebuggerTreeState.saveState(myInstancesTree); + cancelFilteringTask(); myInstancesTree.getRoot().clearChildren(); ((XValueNodeImpl) myInstancesTree.getRoot()).addChildren(myRunningAppChildNode, true); }); @@ -346,10 +366,7 @@ public class InstancesWindow extends DialogWrapper { @Override public void sessionPaused() { - SwingUtilities.invokeLater(() -> { - myInstancesTree.setRootVisible(false); - myInstancesTree.rebuildAndRestore(myTreeState); - }); + SwingUtilities.invokeLater(() -> myInstancesTree.rebuildAndRestore(myTreeState)); } } @@ -408,7 +425,8 @@ public class InstancesWindow extends DialogWrapper { private final List myReferences; private final EvaluationContextImpl myEvaluationContext; private final ExpressionEvaluator myExpressionEvaluator; - private volatile boolean myDebuggerTaskCompleted; + + private volatile boolean myDebuggerTaskCompleted = false; MyFilteringWorker(@NotNull List refs, @NotNull EvaluationContextImpl evaluationContext, @@ -465,9 +483,6 @@ public class InstancesWindow extends DialogWrapper { } } }); - debugProcess.getManagerThread().invokeCommand((DebugCommand) () -> { - - }); synchronized (this) { while (!myDebuggerTaskCompleted) { From 02342c3d2b379d84fab5ef08c5128b21b40f2d89 Mon Sep 17 00:00:00 2001 From: Vitaliy Bibaev Date: Thu, 11 Aug 2016 18:34:19 +0300 Subject: [PATCH 018/151] Fix race condition when old task cancelled, and new task starts executing --- view/InstancesWindow.java | 17 +++++++++-------- 1 file changed, 9 insertions(+), 8 deletions(-) diff --git a/view/InstancesWindow.java b/view/InstancesWindow.java index 640f3e0c031c..916fbc62755a 100644 --- a/view/InstancesWindow.java +++ b/view/InstancesWindow.java @@ -286,7 +286,7 @@ public class InstancesWindow extends DialogWrapper { if (myFilteringTask != null) { synchronized (myFilteringTaskLock) { if (myFilteringTask != null) { - myFilteringTask.cancel(false); + myFilteringTask.cancel(true); myFilteringTask = null; } } @@ -436,6 +436,12 @@ public class InstancesWindow extends DialogWrapper { myExpressionEvaluator = evaluator; } + @Override + protected void done() { + ((XValueNodeImpl) myInstancesTree.getRoot()).addChildren(XValueChildrenList.EMPTY, true); + hideProgressPane(); + } + @Override protected Void doInBackground() throws Exception { DebugProcessImpl debugProcess = (DebugProcessImpl) DebuggerManager.getInstance(myProject) @@ -484,9 +490,9 @@ public class InstancesWindow extends DialogWrapper { } }); - synchronized (this) { + synchronized (MyFilteringWorker.this) { while (!myDebuggerTaskCompleted) { - wait(); + MyFilteringWorker.this.wait(); } } if (totalChildren.get() >= MAX_TREE_NODE_COUNT) { @@ -494,11 +500,6 @@ public class InstancesWindow extends DialogWrapper { } } - SwingUtilities.invokeLater(() -> { - ((XValueNodeImpl) myInstancesTree.getRoot()).addChildren(XValueChildrenList.EMPTY, true); - hideProgressPane(); - }); - return null; } From 3e84814fa0448d67ee9ebdd4d622caf2072a243d Mon Sep 17 00:00:00 2001 From: Vitaliy Bibaev Date: Thu, 11 Aug 2016 19:25:44 +0300 Subject: [PATCH 019/151] Fix NPE when instances window closing while filtering is running --- view/InstancesWindow.java | 30 +++++++++++++++++++++--------- 1 file changed, 21 insertions(+), 9 deletions(-) diff --git a/view/InstancesWindow.java b/view/InstancesWindow.java index 916fbc62755a..e64f71e50236 100644 --- a/view/InstancesWindow.java +++ b/view/InstancesWindow.java @@ -51,6 +51,7 @@ import com.intellij.xdebugger.impl.ui.tree.ValueMarkup; import com.intellij.xdebugger.impl.ui.tree.XDebuggerTree; import com.intellij.xdebugger.impl.ui.tree.XDebuggerTreeState; import com.intellij.xdebugger.impl.ui.tree.actions.XDebuggerTreeActionBase; +import com.intellij.xdebugger.impl.ui.tree.nodes.XDebuggerTreeNode; import com.intellij.xdebugger.impl.ui.tree.nodes.XValueNodeImpl; import com.sun.jdi.BooleanValue; import com.sun.jdi.ObjectReference; @@ -245,7 +246,7 @@ public class InstancesWindow extends DialogWrapper { EvaluationContextImpl evaluationContext = debugProcess .getDebuggerContext().createEvaluationContext(); - if(evaluationContext != null) { + if (evaluationContext != null) { synchronized (myFilteringTaskLock) { myFilteringTask = new MyFilteringWorker(instances, evaluationContext, createEvaluator()); myFilteringTask.execute(); @@ -299,6 +300,13 @@ public class InstancesWindow extends DialogWrapper { : null; } + private void addChildrenToTree(XValueChildrenList children, boolean last) { + XDebuggerTreeNode root = myInstancesTree.getRoot(); + if(root != null) { + ((XValueNodeImpl)root).addChildren(children, last); + } + } + private void setSourcePositionForEditor() { new SwingWorker() { @Override @@ -359,8 +367,12 @@ public class InstancesWindow extends DialogWrapper { SwingUtilities.invokeLater(() -> { myTreeState = XDebuggerTreeState.saveState(myInstancesTree); cancelFilteringTask(); - myInstancesTree.getRoot().clearChildren(); - ((XValueNodeImpl) myInstancesTree.getRoot()).addChildren(myRunningAppChildNode, true); + + XDebuggerTreeNode root = myInstancesTree.getRoot(); + if(root != null) { + root.clearChildren(); + addChildrenToTree(myRunningAppChildNode, true); + } }); } @@ -438,7 +450,7 @@ public class InstancesWindow extends DialogWrapper { @Override protected void done() { - ((XValueNodeImpl) myInstancesTree.getRoot()).addChildren(XValueChildrenList.EMPTY, true); + addChildrenToTree(XValueChildrenList.EMPTY, true); hideProgressPane(); } @@ -461,7 +473,7 @@ public class InstancesWindow extends DialogWrapper { @Override public void threadAction(@NotNull SuspendContextImpl suspendContext) { - XValueChildrenList children = new XValueChildrenList(FILTERING_CHUNK_SIZE); + XValueChildrenList children = new XValueChildrenList(); int endOfChunk = min(chunkBegin + FILTERING_CHUNK_SIZE, size); for (int j = chunkBegin; j < endOfChunk && !isCancelled() && totalChildren.get() < MAX_TREE_NODE_COUNT; j++) { @@ -477,8 +489,7 @@ public class InstancesWindow extends DialogWrapper { if (children.size() > 0) { totalChildren.addAndGet(children.size()); - SwingUtilities.invokeLater(() -> - ((XValueNodeImpl) myInstancesTree.getRoot()).addChildren(children, false)); + SwingUtilities.invokeLater(() -> addChildrenToTree(children, false)); } SwingUtilities.invokeLater(() -> myFilteringProgressBar.setValue(endOfChunk)); @@ -505,11 +516,12 @@ public class InstancesWindow extends DialogWrapper { private MyFilteringResult isSatisfy(@NotNull ExpressionEvaluator evaluator, @NotNull Value value) { try { - Value evaluate = evaluator.evaluate(myEvaluationContext.createEvaluationContext(value)); - if (evaluate instanceof BooleanValue && ((BooleanValue) evaluate).value()) { + Value result = evaluator.evaluate(myEvaluationContext.createEvaluationContext(value)); + if (result instanceof BooleanValue && ((BooleanValue) result).value()) { return MyFilteringResult.MATCH; } } catch (EvaluateException e) { + System.out.println(e.getMessage()); return MyFilteringResult.EVAL_ERROR; } From 99a587a82e4b80dd08d14c2cf01fea366aab2b18 Mon Sep 17 00:00:00 2001 From: Vitaliy Bibaev Date: Fri, 12 Aug 2016 15:20:30 +0300 Subject: [PATCH 020/151] Fix bug with usage of project which already disposed --- toolwindow/MemoryViewToolWindowFactory.java | 13 ++++++++----- view/InstancesWindow.java | 9 +++++---- 2 files changed, 13 insertions(+), 9 deletions(-) diff --git a/toolwindow/MemoryViewToolWindowFactory.java b/toolwindow/MemoryViewToolWindowFactory.java index 9260030988f1..88f425b3d312 100644 --- a/toolwindow/MemoryViewToolWindowFactory.java +++ b/toolwindow/MemoryViewToolWindowFactory.java @@ -62,12 +62,12 @@ public class MemoryViewToolWindowFactory implements ToolWindowFactory, DumbAware .addToolWindowManagerListener(new ToolWindowManagerAdapter() { @Override public void stateChanged() { - if (!myMemoryViews.isEmpty()) { + if (!myMemoryViews.isEmpty() && !toolWindow.isDisposed()) { myMemoryViews.values().forEach(classesFilteredView -> classesFilteredView.setNeedReloadClasses(toolWindow.isVisible())); } } - }); + }, project); ActionGroup group = new DefaultActionGroup( ActionManager.getInstance().getAction("MemoryView.ShowOnlyWithInstances"), @@ -171,9 +171,12 @@ public class MemoryViewToolWindowFactory implements ToolWindowFactory, DumbAware } private void updateView(@NotNull XDebugSession debugSession) { - ToolWindow toolWindow = getToolWindow(debugSession.getProject()); - if (toolWindow != null) { - updateCurrentMemoryView(debugSession.getProject(), toolWindow); + Project project = debugSession.getProject(); + if (!project.isDisposed()) { + ToolWindow toolWindow = getToolWindow(project); + if (toolWindow != null) { + updateCurrentMemoryView(project, toolWindow); + } } } } diff --git a/view/InstancesWindow.java b/view/InstancesWindow.java index e64f71e50236..8d8610d6fd1c 100644 --- a/view/InstancesWindow.java +++ b/view/InstancesWindow.java @@ -225,6 +225,7 @@ public class InstancesWindow extends DialogWrapper { ActionManager.getInstance().removeAnActionListener(myActionListener); myDebugSession.removeSessionListener(myDebugSessionListener); cancelFilteringTask(); + Disposer.dispose(myInstancesTree); } @@ -301,9 +302,9 @@ public class InstancesWindow extends DialogWrapper { } private void addChildrenToTree(XValueChildrenList children, boolean last) { - XDebuggerTreeNode root = myInstancesTree.getRoot(); - if(root != null) { - ((XValueNodeImpl)root).addChildren(children, last); + XDebuggerTreeNode root = myInstancesTree.getRoot(); + if (root != null) { + ((XValueNodeImpl) root).addChildren(children, last); } } @@ -369,7 +370,7 @@ public class InstancesWindow extends DialogWrapper { cancelFilteringTask(); XDebuggerTreeNode root = myInstancesTree.getRoot(); - if(root != null) { + if (root != null) { root.clearChildren(); addChildrenToTree(myRunningAppChildNode, true); } From 2bbabfb8289e51b6ecaf8a6ca2a82dd32fa7b7b0 Mon Sep 17 00:00:00 2001 From: Vitaliy Bibaev Date: Fri, 12 Aug 2016 16:09:11 +0300 Subject: [PATCH 021/151] Remove a icon of class from classes table item --- view/ClassesTable.java | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/view/ClassesTable.java b/view/ClassesTable.java index 9c4834096abe..88a8af377fcb 100644 --- a/view/ClassesTable.java +++ b/view/ClassesTable.java @@ -1,9 +1,9 @@ package org.jetbrains.debugger.memory.view; -import com.intellij.icons.AllIcons; import com.intellij.openapi.actionSystem.DataKey; import com.intellij.openapi.actionSystem.DataProvider; import com.intellij.ui.ColoredTableCellRenderer; +import com.intellij.ui.SimpleTextAttributes; import com.intellij.ui.table.JBTable; import com.intellij.util.ui.JBUI; import com.intellij.xdebugger.XDebugSession; @@ -78,9 +78,7 @@ public class ClassesTable extends JBTable implements DataProvider { protected void customizeCellRenderer(JTable table, @Nullable Object value, boolean isSelected, boolean hasFocus, int row, int column) { String presentation = value == null ? "null" : ((ReferenceType) value).name(); - append(presentation); - setTransparentIconBackground(true); - setIcon(AllIcons.Nodes.Class); + append(String.format(" %s", presentation), SimpleTextAttributes.REGULAR_ATTRIBUTES); } }); From 5ea55355f47dd132dc3e3681529f4437e971e977 Mon Sep 17 00:00:00 2001 From: roenke Date: Mon, 15 Aug 2016 16:03:19 +0300 Subject: [PATCH 022/151] Small fixes before 0.0.8 --- view/ClassesFilteredView.java | 64 ++++++++++++++++++++--------------- view/ClassesTable.java | 6 ++-- view/InstancesWindow.java | 20 ++++++----- 3 files changed, 50 insertions(+), 40 deletions(-) diff --git a/view/ClassesFilteredView.java b/view/ClassesFilteredView.java index 43a02b99e23e..6652553c6d51 100644 --- a/view/ClassesFilteredView.java +++ b/view/ClassesFilteredView.java @@ -47,7 +47,7 @@ public class ClassesFilteredView extends BorderLayoutPanel { private final ClassesTable myTable; private volatile SuspendContextImpl myLastSuspendContext; - private boolean myNeedReloadClasses = false; + private volatile boolean myNeedReloadClasses = false; public ClassesFilteredView(@NotNull XDebugSession debugSession) { super(); @@ -95,6 +95,7 @@ public class ClassesFilteredView extends BorderLayoutPanel { @Override public void sessionResumed() { SwingUtilities.invokeLater(myTable::hideContent); + mySingleAlarm.cancelAllRequests(); } @Override @@ -121,10 +122,11 @@ public class ClassesFilteredView extends BorderLayoutPanel { mySingleAlarm = new SingleAlarmWithMutableDelay(() -> { myLastSuspendContext = getSuspendContext(); if (myLastSuspendContext != null) { - SwingUtilities.invokeLater(() -> myTable.setBusy(true)); - - ((DebuggerManagerThreadImpl) myDebugProcess.getManagerThread()) - .schedule(new MyUpdateClassesCommand(myLastSuspendContext)); + SwingUtilities.invokeLater(() -> { + myTable.setBusy(true); + ((DebuggerManagerThreadImpl) myDebugProcess.getManagerThread()) + .schedule(new MyUpdateClassesCommand(myLastSuspendContext)); + }); } }); @@ -171,7 +173,13 @@ public class ClassesFilteredView extends BorderLayoutPanel { } } + private ActionPopupMenu createContextMenu() { + ActionGroup group = (ActionGroup) ActionManager.getInstance().getAction("ClassesView.PopupActionGroup"); + return ActionManager.getInstance().createActionPopupMenu("ClassesView.PopupActionGroup", group); + } + private final class MyUpdateClassesCommand extends SuspendContextCommandImpl { + MyUpdateClassesCommand(@Nullable SuspendContextImpl suspendContext) { super(suspendContext); } @@ -183,35 +191,35 @@ public class ClassesFilteredView extends BorderLayoutPanel { @Override public void contextAction(@NotNull SuspendContextImpl suspendContext) throws Exception { - loadClassesAndCounts(); + myDebugProcess.getVirtualMachineProxy(); + List classes = myDebugProcess.getVirtualMachineProxy().allClasses(); + + if (classes.isEmpty()) { + return; + } + + VirtualMachine vm = classes.get(0).virtualMachine(); + long start = System.nanoTime(); + long[] counts = vm.instanceCounts(classes); + long delay = TimeUnit.NANOSECONDS.toMillis(System.nanoTime() - start); + mySingleAlarm.setDelay((int) (DELAY_BEFORE_INSTANCES_QUERY_COEFFICIENT * delay)); + + LOG.info(String.format("Instances query time = %d ms. Count = %d", delay, classes.size())); + SwingUtilities.invokeLater(() -> { + if (contextIsValid()) { + myTable.setClassesAndCounts(classes, counts); + } + + myTable.setBusy(false); + }); } @Override public void commandCancelled() { } - } - private ActionPopupMenu createContextMenu() { - ActionGroup group = (ActionGroup) ActionManager.getInstance().getAction("ClassesView.PopupActionGroup"); - return ActionManager.getInstance().createActionPopupMenu("ClassesView.PopupActionGroup", group); - } - - private void loadClassesAndCounts() { - assert DebuggerManager.getInstance(myProject).isDebuggerManagerThread(); - myDebugProcess.getVirtualMachineProxy(); - List classes = myDebugProcess.getVirtualMachineProxy().allClasses(); - - if (classes.isEmpty()) { - return; + private boolean contextIsValid() { + return ClassesFilteredView.this.getSuspendContext() == getSuspendContext(); } - - VirtualMachine vm = classes.get(0).virtualMachine(); - long start = System.nanoTime(); - long[] counts = vm.instanceCounts(classes); - long delay = TimeUnit.NANOSECONDS.toMillis(System.nanoTime() - start); - mySingleAlarm.setDelay((int) (DELAY_BEFORE_INSTANCES_QUERY_COEFFICIENT * delay)); - - LOG.info(String.format("Instances query time = %d ms. Count = %d%n", delay, classes.size())); - SwingUtilities.invokeLater(() -> myTable.setClassesAndCounts(classes, counts)); } } diff --git a/view/ClassesTable.java b/view/ClassesTable.java index 88a8af377fcb..51420e56de68 100644 --- a/view/ClassesTable.java +++ b/view/ClassesTable.java @@ -173,8 +173,6 @@ public class ClassesTable extends JBTable implements DataProvider { changeSelection(convertRowIndexToView(newSelectedIndex), DiffViewTableModel.CLASSNAME_COLUMN_INDEX, false, false); } - - setBusy(false); } void hideContent() { @@ -202,10 +200,10 @@ public class ClassesTable extends JBTable implements DataProvider { @Nullable @Override public Object getData(@NonNls String dataId) { - if(SELECTED_CLASS_KEY.is(dataId)) { + if (SELECTED_CLASS_KEY.is(dataId)) { return getSelectedClass(); } - if(DEBUG_SESSION_KEY.is(dataId)) { + if (DEBUG_SESSION_KEY.is(dataId)) { return myDebugSession; } diff --git a/view/InstancesWindow.java b/view/InstancesWindow.java index 8d8610d6fd1c..8d9cefcf6b1d 100644 --- a/view/InstancesWindow.java +++ b/view/InstancesWindow.java @@ -176,10 +176,10 @@ public class InstancesWindow extends DialogWrapper { setSourcePositionForEditor(); myFilterButton.setBorder(BorderFactory.createEmptyBorder()); - Dimension progressbarPreferredSize = myFilterConditionEditor.getEditorComponent().getPreferredSize(); - progressbarPreferredSize.width = JBUI.scale(FILTERING_BUTTON_ADDITIONAL_WIDTH) + + Dimension filteringButtonSize = myFilterConditionEditor.getEditorComponent().getPreferredSize(); + filteringButtonSize.width = JBUI.scale(FILTERING_BUTTON_ADDITIONAL_WIDTH) + myFilterButton.getPreferredSize().width; - myFilterButton.setPreferredSize(progressbarPreferredSize); + myFilterButton.setPreferredSize(filteringButtonSize); JBPanel filteringPane = new JBPanel(new BorderLayout(JBUI.scale(BORDER_LAYOUT_DEFAULT_GAP), 0)); @@ -261,10 +261,12 @@ public class InstancesWindow extends DialogWrapper { myFilteringProgressBar.setMinimum(0); myFilteringProgressBar.setMaximum(progressBarMaximum); myProgressPanel.setVisible(true); + myProgressPanel.repaint(); } private void hideProgressPane() { myProgressPanel.setVisible(false); + myProgressPanel.repaint(); } @Nullable @@ -463,7 +465,7 @@ public class InstancesWindow extends DialogWrapper { SwingUtilities.invokeLater(() -> showProgressPane(myReferences.size())); AtomicInteger totalChildren = new AtomicInteger(0); - for (int i = 0, size = myReferences.size(); i < size && !isCancelled(); i += FILTERING_CHUNK_SIZE) { + for (int i = 0, size = myReferences.size(); i < size; i += FILTERING_CHUNK_SIZE) { myDebuggerTaskCompleted = false; final int chunkBegin = i; debugProcess.getManagerThread().schedule(new DebuggerContextCommandImpl(debugProcess.getDebuggerContext()) { @@ -476,8 +478,7 @@ public class InstancesWindow extends DialogWrapper { public void threadAction(@NotNull SuspendContextImpl suspendContext) { XValueChildrenList children = new XValueChildrenList(); int endOfChunk = min(chunkBegin + FILTERING_CHUNK_SIZE, size); - for (int j = chunkBegin; j < endOfChunk - && !isCancelled() && totalChildren.get() < MAX_TREE_NODE_COUNT; j++) { + for (int j = chunkBegin; j < endOfChunk && totalChildren.get() < MAX_TREE_NODE_COUNT; j++) { ObjectReference ref = myReferences.get(j); if (myExpressionEvaluator != null && isSatisfy(myExpressionEvaluator, ref) != MyFilteringResult.MATCH) { continue; @@ -490,7 +491,11 @@ public class InstancesWindow extends DialogWrapper { if (children.size() > 0) { totalChildren.addAndGet(children.size()); - SwingUtilities.invokeLater(() -> addChildrenToTree(children, false)); + SwingUtilities.invokeLater(() -> { + if(MyFilteringWorker.this == myFilteringTask) { + addChildrenToTree(children, false); + } + }); } SwingUtilities.invokeLater(() -> myFilteringProgressBar.setValue(endOfChunk)); @@ -522,7 +527,6 @@ public class InstancesWindow extends DialogWrapper { return MyFilteringResult.MATCH; } } catch (EvaluateException e) { - System.out.println(e.getMessage()); return MyFilteringResult.EVAL_ERROR; } From 859c43f86a5886f016424b5c7deef86f05195193 Mon Sep 17 00:00:00 2001 From: roenke Date: Mon, 15 Aug 2016 19:40:48 +0300 Subject: [PATCH 023/151] IDEA-159837 Add default focus on text editor for filtering --- view/InstancesWindow.java | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/view/InstancesWindow.java b/view/InstancesWindow.java index 8d9cefcf6b1d..77e55c68ffbd 100644 --- a/view/InstancesWindow.java +++ b/view/InstancesWindow.java @@ -25,6 +25,7 @@ import com.intellij.openapi.project.Project; import com.intellij.openapi.ui.DialogWrapper; import com.intellij.openapi.util.Disposer; import com.intellij.openapi.util.Pair; +import com.intellij.openapi.wm.IdeFocusManager; import com.intellij.psi.PsiClass; import com.intellij.psi.search.GlobalSearchScope; import com.intellij.ui.JBColor; @@ -35,6 +36,7 @@ import com.intellij.ui.components.JBScrollPane; import com.intellij.util.ui.JBDimension; import com.intellij.util.ui.JBUI; import com.intellij.util.ui.UIUtil; +import com.intellij.util.ui.update.UiNotifyConnector; import com.intellij.xdebugger.XDebugSession; import com.intellij.xdebugger.XDebugSessionListener; import com.intellij.xdebugger.XExpression; @@ -218,6 +220,11 @@ public class InstancesWindow extends DialogWrapper { JBScrollPane treeScrollPane = new JBScrollPane(myInstancesTree); add(filteringPane, BorderLayout.NORTH); add(treeScrollPane, BorderLayout.CENTER); + + JComponent focusedComponent = myFilterConditionEditor.getEditorComponent(); + UiNotifyConnector.doWhenFirstShown(focusedComponent, () -> + IdeFocusManager.findInstanceByComponent(focusedComponent) + .requestFocus(focusedComponent, true)); } @Override @@ -492,7 +499,7 @@ public class InstancesWindow extends DialogWrapper { if (children.size() > 0) { totalChildren.addAndGet(children.size()); SwingUtilities.invokeLater(() -> { - if(MyFilteringWorker.this == myFilteringTask) { + if (MyFilteringWorker.this == myFilteringTask) { addChildrenToTree(children, false); } }); From 5e982277057f4c70e131038ebf109b8e3080ad45 Mon Sep 17 00:00:00 2001 From: roenke Date: Mon, 15 Aug 2016 20:26:32 +0300 Subject: [PATCH 024/151] IDEA-159835 Add basic implementation for "Jump to source" context menu action (arrays not working) --- action/JumpToTypeSourceAction.java | 41 +++++++++++++++++++ ...ff.java => ShowClassesWithDiffAction.java} | 2 +- 2 files changed, 42 insertions(+), 1 deletion(-) create mode 100644 action/JumpToTypeSourceAction.java rename action/{ShowClassesWithDiff.java => ShowClassesWithDiffAction.java} (90%) diff --git a/action/JumpToTypeSourceAction.java b/action/JumpToTypeSourceAction.java new file mode 100644 index 000000000000..909ba5bc0112 --- /dev/null +++ b/action/JumpToTypeSourceAction.java @@ -0,0 +1,41 @@ +package org.jetbrains.debugger.memory.action; + +import com.intellij.codeInsight.navigation.NavigationUtil; +import com.intellij.debugger.engine.DebuggerUtils; +import com.intellij.openapi.actionSystem.AnActionEvent; +import com.intellij.openapi.project.Project; +import com.intellij.psi.PsiClass; +import com.intellij.psi.search.GlobalSearchScope; +import com.intellij.xdebugger.XDebugSession; +import com.sun.jdi.ReferenceType; +import org.jetbrains.annotations.Nullable; + +public class JumpToTypeSourceAction extends ClassesActionBase { + + @Override + protected boolean isEnabled(AnActionEvent e) { + final PsiClass psiClass = getPsiClass(e); + + return super.isEnabled(e) && psiClass != null && psiClass.isPhysical(); + } + + @Override + protected void perform(AnActionEvent e) { + final PsiClass psiClass = getPsiClass(e); + if(psiClass != null) { + NavigationUtil.openFileWithPsiElement(psiClass, true, true); + } + } + + @Nullable + private PsiClass getPsiClass(AnActionEvent e) { + ReferenceType selectedClass = getSelectedClass(e); + XDebugSession session = getDebugSession(e); + if(selectedClass == null || session == null) { + return null; + } + + Project project = session.getProject(); + return DebuggerUtils.findClass(selectedClass.name(), project, GlobalSearchScope.allScope(project)); + } +} diff --git a/action/ShowClassesWithDiff.java b/action/ShowClassesWithDiffAction.java similarity index 90% rename from action/ShowClassesWithDiff.java rename to action/ShowClassesWithDiffAction.java index 8ca30ec247b8..f12c349e0593 100644 --- a/action/ShowClassesWithDiff.java +++ b/action/ShowClassesWithDiffAction.java @@ -5,7 +5,7 @@ import com.intellij.openapi.actionSystem.ToggleAction; import com.intellij.openapi.project.Project; import org.jetbrains.debugger.memory.component.MemoryViewManager; -public class ShowClassesWithDiff extends ToggleAction { +public class ShowClassesWithDiffAction extends ToggleAction { @Override public boolean isSelected(AnActionEvent e) { return MemoryViewManager.getInstance().isNeedShowDiffOnly(); From 6b38a494908ac1352cccbf03156fa4b52e97d2d1 Mon Sep 17 00:00:00 2001 From: roenke Date: Mon, 15 Aug 2016 23:29:13 +0300 Subject: [PATCH 025/151] IDEA-159835 Add ability to jump to source code of type of array element --- action/JumpToTypeSourceAction.java | 36 +++++++++++++++++++++++++----- 1 file changed, 30 insertions(+), 6 deletions(-) diff --git a/action/JumpToTypeSourceAction.java b/action/JumpToTypeSourceAction.java index 909ba5bc0112..01632ac1a70a 100644 --- a/action/JumpToTypeSourceAction.java +++ b/action/JumpToTypeSourceAction.java @@ -7,7 +7,8 @@ import com.intellij.openapi.project.Project; import com.intellij.psi.PsiClass; import com.intellij.psi.search.GlobalSearchScope; import com.intellij.xdebugger.XDebugSession; -import com.sun.jdi.ReferenceType; +import com.sun.jdi.*; +import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; public class JumpToTypeSourceAction extends ClassesActionBase { @@ -22,8 +23,8 @@ public class JumpToTypeSourceAction extends ClassesActionBase { @Override protected void perform(AnActionEvent e) { final PsiClass psiClass = getPsiClass(e); - if(psiClass != null) { - NavigationUtil.openFileWithPsiElement(psiClass, true, true); + if (psiClass != null) { + NavigationUtil.activateFileWithPsiElement(psiClass); } } @@ -31,11 +32,34 @@ public class JumpToTypeSourceAction extends ClassesActionBase { private PsiClass getPsiClass(AnActionEvent e) { ReferenceType selectedClass = getSelectedClass(e); XDebugSession session = getDebugSession(e); - if(selectedClass == null || session == null) { + if (selectedClass == null || session == null) { return null; } - Project project = session.getProject(); - return DebuggerUtils.findClass(selectedClass.name(), project, GlobalSearchScope.allScope(project)); + ReferenceType targetClass = getObjectType(selectedClass); + if (targetClass != null) { + Project project = session.getProject(); + return DebuggerUtils + .findClass(targetClass.name(), project, GlobalSearchScope.allScope(project)); + } + + return null; + } + + @Nullable + private ReferenceType getObjectType(@NotNull ReferenceType ref) { + if (!(ref instanceof ArrayType)) { + return ref; + } + + try { + Type elementType = ((ArrayType) ref).componentType(); + if (elementType instanceof ReferenceType) { + return getObjectType((ReferenceType) elementType); + } + } catch (ClassNotLoadedException ignored) { + } + + return null; } } From 70ddce0c14c669f80c6a9cea9bb9c61ae132c2db Mon Sep 17 00:00:00 2001 From: roenke Date: Tue, 16 Aug 2016 11:25:54 +0300 Subject: [PATCH 026/151] IDEA-159835 Jump to type of arrays element (for multidimensional arrays) --- action/JumpToTypeSourceAction.java | 13 +++++++------ 1 file changed, 7 insertions(+), 6 deletions(-) diff --git a/action/JumpToTypeSourceAction.java b/action/JumpToTypeSourceAction.java index 01632ac1a70a..6e72daadb9ff 100644 --- a/action/JumpToTypeSourceAction.java +++ b/action/JumpToTypeSourceAction.java @@ -11,6 +11,8 @@ import com.sun.jdi.*; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; +import java.util.List; + public class JumpToTypeSourceAction extends ClassesActionBase { @Override @@ -52,12 +54,11 @@ public class JumpToTypeSourceAction extends ClassesActionBase { return ref; } - try { - Type elementType = ((ArrayType) ref).componentType(); - if (elementType instanceof ReferenceType) { - return getObjectType((ReferenceType) elementType); - } - } catch (ClassNotLoadedException ignored) { + String elementTypeName = ref.name().replace("[]", ""); + VirtualMachine vm = ref.virtualMachine(); + final List referenceTypes = vm.classesByName(elementTypeName); + if (referenceTypes.size() == 1) { + return referenceTypes.get(0); } return null; From 815829aadd8e9d1936ecca209a822c3a79c0e760 Mon Sep 17 00:00:00 2001 From: Vitaliy Bibaev Date: Wed, 17 Aug 2016 13:31:38 +0300 Subject: [PATCH 027/151] Simplify information node when session has been resumed in instances window --- view/InstancesWindow.java | 34 ++++++---------------------------- 1 file changed, 6 insertions(+), 28 deletions(-) diff --git a/view/InstancesWindow.java b/view/InstancesWindow.java index 77e55c68ffbd..855310e06a0e 100644 --- a/view/InstancesWindow.java +++ b/view/InstancesWindow.java @@ -30,6 +30,7 @@ import com.intellij.psi.PsiClass; import com.intellij.psi.search.GlobalSearchScope; import com.intellij.ui.JBColor; import com.intellij.ui.JBProgressBar; +import com.intellij.ui.SimpleTextAttributes; import com.intellij.ui.components.JBLabel; import com.intellij.ui.components.JBPanel; import com.intellij.ui.components.JBScrollPane; @@ -41,7 +42,6 @@ import com.intellij.xdebugger.XDebugSession; import com.intellij.xdebugger.XDebugSessionListener; import com.intellij.xdebugger.XExpression; import com.intellij.xdebugger.frame.*; -import com.intellij.xdebugger.frame.presentation.XValuePresentation; import com.intellij.xdebugger.impl.XDebugSessionImpl; import com.intellij.xdebugger.impl.XSourcePositionImpl; import com.intellij.xdebugger.impl.actions.XDebuggerActionBase; @@ -348,41 +348,19 @@ public class InstancesWindow extends DialogWrapper { } private class MySessionListener implements XDebugSessionListener { - private final XValueChildrenList myRunningAppChildNode = new XValueChildrenList(); - private volatile XDebuggerTreeState myTreeState = null; - { - myRunningAppChildNode.add(new XNamedValue("") { - @Override - public void computePresentation(@NotNull XValueNode node, @NotNull XValuePlace place) { - node.setPresentation(XDebuggerUIConstants.INFORMATION_MESSAGE_ICON, new XValuePresentation() { - @NotNull - @Override - public String getSeparator() { - return ""; - } - - @Override - public void renderValue(@NotNull XValueTextRenderer renderer) { - renderer.renderValue("The application is running"); - } - }, false); - } - }); - } - @Override public void sessionResumed() { SwingUtilities.invokeLater(() -> { myTreeState = XDebuggerTreeState.saveState(myInstancesTree); cancelFilteringTask(); - XDebuggerTreeNode root = myInstancesTree.getRoot(); - if (root != null) { - root.clearChildren(); - addChildrenToTree(myRunningAppChildNode, true); - } + myInstancesTree.getRoot().clearChildren(); + XCompositeNode root = (XCompositeNode) myInstancesTree.getRoot(); + + root.setMessage("The application is running", XDebuggerUIConstants.INFORMATION_MESSAGE_ICON, + SimpleTextAttributes.REGULAR_ATTRIBUTES, null); }); } From 34126d2fff5f8a34223d3088f8cf661f5ff7790a Mon Sep 17 00:00:00 2001 From: Vitaliy Bibaev Date: Wed, 17 Aug 2016 15:43:02 +0300 Subject: [PATCH 028/151] Disable button "Filter" during filtration --- view/InstancesWindow.java | 3 +++ 1 file changed, 3 insertions(+) diff --git a/view/InstancesWindow.java b/view/InstancesWindow.java index 855310e06a0e..3bea4064c631 100644 --- a/view/InstancesWindow.java +++ b/view/InstancesWindow.java @@ -214,6 +214,8 @@ public class InstancesWindow extends DialogWrapper { if (!expression.isEmpty()) { myFilterConditionEditor.saveTextInHistory(); } + + myFilterButton.setEnabled(false); myInstancesTree.rebuildAndRestore(XDebuggerTreeState.saveState(myInstancesTree)); }); @@ -439,6 +441,7 @@ public class InstancesWindow extends DialogWrapper { @Override protected void done() { addChildrenToTree(XValueChildrenList.EMPTY, true); + myFilterButton.setEnabled(true); hideProgressPane(); } From 52dd2b5e85c90c667d6e6a0ba22a6622e18ec297 Mon Sep 17 00:00:00 2001 From: Vitaliy Bibaev Date: Wed, 17 Aug 2016 19:48:01 +0300 Subject: [PATCH 029/151] IDEA-159836 Add statistics for nodes/error amount to instances view --- view/FilteringProgressView.java | 117 ++++++++++++++++++++++++++++++++ view/InstancesWindow.java | 86 ++++++++++++----------- 2 files changed, 159 insertions(+), 44 deletions(-) create mode 100644 view/FilteringProgressView.java diff --git a/view/FilteringProgressView.java b/view/FilteringProgressView.java new file mode 100644 index 000000000000..30cd478a7e81 --- /dev/null +++ b/view/FilteringProgressView.java @@ -0,0 +1,117 @@ +package org.jetbrains.debugger.memory.view; + +import com.intellij.icons.AllIcons; +import com.intellij.ui.JBProgressBar; +import com.intellij.ui.components.JBLabel; +import com.intellij.util.ui.UIUtil; +import com.intellij.util.ui.components.BorderLayoutPanel; +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; +import org.jetbrains.debugger.memory.view.InstancesWindow.FilteringCompletionReason; + +import java.awt.event.MouseAdapter; +import java.awt.event.MouseEvent; + +class FilteringProgressView extends BorderLayoutPanel { + private final static String LIMIT_REACHED_LABEL = "Limit has been reached"; + private final static String INTERRUPTED_LABEL = "Filtering has been interrupted"; + + private final JBProgressBar myProgressBar = new JBProgressBar(); + private final BorderLayoutPanel myProgressPanel = new BorderLayoutPanel(); + private final JBLabel myStopButton = new JBLabel(UIUtil.isUnderDarcula() + ? AllIcons.Actions.Clean : AllIcons.Actions.CleanLight); + private final JBLabel myProgressText = new JBLabel(); + + private int myProceedCount = 0; + private int myErrorCount = 0; + private int myMatchedCount = 0; + + private boolean myIsInProcess = false; + + @Nullable + private FilteringCompletionReason myCompletionReason = null; + + FilteringProgressView() { + myStopButton.setOpaque(true); + myProgressPanel.addToCenter(myProgressBar); + myProgressPanel.addToRight(myStopButton); + addToTop(myProgressText); + addToCenter(myProgressPanel); + } + + void addStopActionListener(@NotNull Runnable listener) { + myStopButton.addMouseListener(new MouseAdapter() { + @Override + public void mouseClicked(MouseEvent e) { + listener.run(); + } + }); + } + + void updateProgress(int proceedCount, int matchedCount, int errorCount) { + if (myIsInProcess) { + myProceedCount += proceedCount; + myMatchedCount += matchedCount; + myErrorCount += errorCount; + myProgressBar.setValue(myProceedCount); + myProgressText.setText(getDescription()); + } + } + + void start(int totalCount) { + reset(); + myIsInProcess = true; + myProgressBar.setMinimum(0); + myProgressBar.setMaximum(totalCount); + setProgressBarVisible(true); + } + + void complete(@NotNull FilteringCompletionReason reason) { + if (!myIsInProcess) { + throw new IllegalStateException("First you need to start progress"); + } + + myIsInProcess = false; + myCompletionReason = reason; + myProgressText.setText(getDescription()); + setProgressBarVisible(false); + } + + private void reset() { + myIsInProcess = false; + myErrorCount = 0; + myProceedCount = 0; + myMatchedCount = 0; + myCompletionReason = null; + } + + private void setProgressBarVisible(boolean value) { + myProgressPanel.setVisible(value); + } + + private String getDescription() { + int total = myProgressBar.getMaximum(); + String itemsInfo = String.format("Items: %d / %d", myMatchedCount, total); + if (myIsInProcess || myCompletionReason == null) { + return itemsInfo; + } + + switch (myCompletionReason) { + case ALL_CHECKED: + break; + case INTERRUPTED: + itemsInfo = String.format("%s (%s)", itemsInfo, INTERRUPTED_LABEL); + break; + case LIMIT_REACHED: + itemsInfo = String.format("%s (%s)", itemsInfo, LIMIT_REACHED_LABEL); + break; + } + + if (!myIsInProcess && myErrorCount != 0) { + String errors = String.format("Errors: %d", myErrorCount); + return String.format("%s
%s", itemsInfo, errors); + } + + return itemsInfo; + } +} diff --git a/view/InstancesWindow.java b/view/InstancesWindow.java index 3bea4064c631..8d21bbdbf92c 100644 --- a/view/InstancesWindow.java +++ b/view/InstancesWindow.java @@ -16,7 +16,6 @@ import com.intellij.debugger.ui.impl.watch.MessageDescriptor; import com.intellij.debugger.ui.impl.watch.NodeManagerImpl; import com.intellij.debugger.ui.tree.NodeDescriptor; import com.intellij.debugger.ui.tree.render.CachedEvaluator; -import com.intellij.icons.AllIcons; import com.intellij.openapi.Disposable; import com.intellij.openapi.actionSystem.*; import com.intellij.openapi.actionSystem.ex.AnActionListener; @@ -29,14 +28,12 @@ import com.intellij.openapi.wm.IdeFocusManager; import com.intellij.psi.PsiClass; import com.intellij.psi.search.GlobalSearchScope; import com.intellij.ui.JBColor; -import com.intellij.ui.JBProgressBar; import com.intellij.ui.SimpleTextAttributes; import com.intellij.ui.components.JBLabel; import com.intellij.ui.components.JBPanel; import com.intellij.ui.components.JBScrollPane; import com.intellij.util.ui.JBDimension; import com.intellij.util.ui.JBUI; -import com.intellij.util.ui.UIUtil; import com.intellij.util.ui.update.UiNotifyConnector; import com.intellij.xdebugger.XDebugSession; import com.intellij.xdebugger.XDebugSessionListener; @@ -68,8 +65,6 @@ import org.jetbrains.java.debugger.JavaDebuggerEditorsProvider; import javax.swing.*; import javax.swing.tree.TreeNode; import java.awt.*; -import java.awt.event.MouseAdapter; -import java.awt.event.MouseEvent; import java.util.List; import java.util.concurrent.atomic.AtomicInteger; @@ -106,6 +101,10 @@ public class InstancesWindow extends DialogWrapper { root.setDefaultButton(myInstancesView.myFilterButton); } + enum FilteringCompletionReason { + ALL_CHECKED, INTERRUPTED, LIMIT_REACHED + } + @NotNull @Override protected String getDimensionServiceKey() { @@ -126,7 +125,7 @@ public class InstancesWindow extends DialogWrapper { protected JComponent createSouthPanel() { JComponent comp = super.createSouthPanel(); if (comp != null) { - comp.add(myInstancesView.myProgressPanel, BorderLayout.WEST); + comp.add(myInstancesView.myProgress, BorderLayout.WEST); } return comp; @@ -156,9 +155,8 @@ public class InstancesWindow extends DialogWrapper { private final MyCachedEvaluator myEvaluator = new MyCachedEvaluator(); private final MyNodeManager myNodeManager = new MyNodeManager(myProject); - private final JBProgressBar myFilteringProgressBar = new JBProgressBar(); private final JButton myFilterButton = new JButton("Filter"); - private final JComponent myProgressPanel = new JBPanel<>(new BorderLayout()); + private final FilteringProgressView myProgress = new FilteringProgressView(); private final AnActionListener.Adapter myActionListener = new MyActionListener(); private final Object myFilteringTaskLock = new Object(); @@ -189,17 +187,7 @@ public class InstancesWindow extends DialogWrapper { filteringPane.add(myFilterConditionEditor.getComponent(), BorderLayout.CENTER); filteringPane.add(myFilterButton, BorderLayout.EAST); - JLabel stopFilteringButton = - new JLabel(UIUtil.isUnderDarcula() ? AllIcons.Actions.Clean : AllIcons.Actions.CleanLight); - stopFilteringButton.setOpaque(true); - stopFilteringButton.addMouseListener(new MouseAdapter() { - public void mousePressed(MouseEvent e) { - cancelFilteringTask(); - } - }); - - myProgressPanel.add(myFilteringProgressBar, BorderLayout.CENTER); - myProgressPanel.add(stopFilteringButton, BorderLayout.EAST); + myProgress.addStopActionListener(this::cancelFilteringTask); XDebuggerTreeCreator treeCreator = new XDebuggerTreeCreator(myProject, editorsProvider, null, markers); @@ -259,6 +247,10 @@ public class InstancesWindow extends DialogWrapper { if (evaluationContext != null) { synchronized (myFilteringTaskLock) { myFilteringTask = new MyFilteringWorker(instances, evaluationContext, createEvaluator()); + SwingUtilities.invokeLater(() -> { + myProgress.start(instances.size()); + myFilteringTask.execute(); + }); myFilteringTask.execute(); } } @@ -266,18 +258,6 @@ public class InstancesWindow extends DialogWrapper { }); } - private void showProgressPane(int progressBarMaximum) { - myFilteringProgressBar.setMinimum(0); - myFilteringProgressBar.setMaximum(progressBarMaximum); - myProgressPanel.setVisible(true); - myProgressPanel.repaint(); - } - - private void hideProgressPane() { - myProgressPanel.setVisible(false); - myProgressPanel.repaint(); - } - @Nullable private ExpressionEvaluator createEvaluator() { ExpressionEvaluator evaluator = null; @@ -430,6 +410,9 @@ public class InstancesWindow extends DialogWrapper { private volatile boolean myDebuggerTaskCompleted = false; + @NotNull + private volatile FilteringCompletionReason myCompletionReason = FilteringCompletionReason.INTERRUPTED; + MyFilteringWorker(@NotNull List refs, @NotNull EvaluationContextImpl evaluationContext, @Nullable ExpressionEvaluator evaluator) { @@ -440,9 +423,11 @@ public class InstancesWindow extends DialogWrapper { @Override protected void done() { - addChildrenToTree(XValueChildrenList.EMPTY, true); - myFilterButton.setEnabled(true); - hideProgressPane(); + SwingUtilities.invokeLater(() -> { + addChildrenToTree(XValueChildrenList.EMPTY, true); + myFilterButton.setEnabled(true); + myProgress.complete(myCompletionReason); + }); } @Override @@ -450,10 +435,10 @@ public class InstancesWindow extends DialogWrapper { DebugProcessImpl debugProcess = (DebugProcessImpl) DebuggerManager.getInstance(myProject) .getDebugProcess(myDebugSession.getDebugProcess().getProcessHandler()); - SwingUtilities.invokeLater(() -> showProgressPane(myReferences.size())); - AtomicInteger totalChildren = new AtomicInteger(0); - for (int i = 0, size = myReferences.size(); i < size; i += FILTERING_CHUNK_SIZE) { + AtomicInteger totalViewed = new AtomicInteger(0); + for (int i = 0, size = myReferences.size(); i < size && totalChildren.get() < MAX_TREE_NODE_COUNT; + i += FILTERING_CHUNK_SIZE) { myDebuggerTaskCompleted = false; final int chunkBegin = i; debugProcess.getManagerThread().schedule(new DebuggerContextCommandImpl(debugProcess.getDebuggerContext()) { @@ -466,19 +451,28 @@ public class InstancesWindow extends DialogWrapper { public void threadAction(@NotNull SuspendContextImpl suspendContext) { XValueChildrenList children = new XValueChildrenList(); int endOfChunk = min(chunkBegin + FILTERING_CHUNK_SIZE, size); + int errorCount = 0; for (int j = chunkBegin; j < endOfChunk && totalChildren.get() < MAX_TREE_NODE_COUNT; j++) { ObjectReference ref = myReferences.get(j); - if (myExpressionEvaluator != null && isSatisfy(myExpressionEvaluator, ref) != MyFilteringResult.MATCH) { - continue; + totalViewed.incrementAndGet(); + if (myExpressionEvaluator != null) { + MyFilteringResult comparison = isSatisfy(myExpressionEvaluator, ref); + if(comparison == MyFilteringResult.EVAL_ERROR) { + errorCount++; + } + + if(comparison != MyFilteringResult.MATCH) { + continue; + } } JavaValue val = new InstanceJavaValue(null, new InstanceValueDescriptor(myProject, ref), myEvaluationContext, myNodeManager, true); children.add(val); + totalChildren.incrementAndGet(); } if (children.size() > 0) { - totalChildren.addAndGet(children.size()); SwingUtilities.invokeLater(() -> { if (MyFilteringWorker.this == myFilteringTask) { addChildrenToTree(children, false); @@ -486,7 +480,10 @@ public class InstancesWindow extends DialogWrapper { }); } - SwingUtilities.invokeLater(() -> myFilteringProgressBar.setValue(endOfChunk)); + final int childrenSize = children.size(); + final int finalErrorsCount = errorCount; + SwingUtilities.invokeLater(() -> + myProgress.updateProgress(endOfChunk - chunkBegin, childrenSize, finalErrorsCount)); synchronized (MyFilteringWorker.this) { myDebuggerTaskCompleted = true; @@ -500,11 +497,12 @@ public class InstancesWindow extends DialogWrapper { MyFilteringWorker.this.wait(); } } - if (totalChildren.get() >= MAX_TREE_NODE_COUNT) { - break; - } } + myCompletionReason = totalViewed.get() == myReferences.size() + ? FilteringCompletionReason.ALL_CHECKED + : FilteringCompletionReason.LIMIT_REACHED; + return null; } From 2a1d8b9e477bb90cae1d8a9781bbe7ddec76b3f6 Mon Sep 17 00:00:00 2001 From: Vitaliy Bibaev Date: Wed, 17 Aug 2016 20:47:26 +0300 Subject: [PATCH 030/151] Show errors after successfully filtered items --- view/ErrorsValueGroup.java | 60 ++++++++++++++++++++++++++++++++++++++ view/InstancesWindow.java | 36 ++++++++++++++--------- 2 files changed, 82 insertions(+), 14 deletions(-) create mode 100644 view/ErrorsValueGroup.java diff --git a/view/ErrorsValueGroup.java b/view/ErrorsValueGroup.java new file mode 100644 index 000000000000..6d5a368318bc --- /dev/null +++ b/view/ErrorsValueGroup.java @@ -0,0 +1,60 @@ +package org.jetbrains.debugger.memory.view; + +import com.intellij.icons.AllIcons; +import com.intellij.util.containers.HashMap; +import com.intellij.xdebugger.frame.XCompositeNode; +import com.intellij.xdebugger.frame.XNamedValue; +import com.intellij.xdebugger.frame.XValueChildrenList; +import com.intellij.xdebugger.frame.XValueGroup; +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; + +import javax.swing.*; +import java.util.ArrayList; +import java.util.List; +import java.util.Map; + +class ErrorsValueGroup extends XValueGroup { + private final Map> myErrorMessage2ValueMap = new HashMap<>(); + ErrorsValueGroup(@NotNull String name) { + super(name); + } + + void addErrorValue(@NotNull String message, @NotNull XNamedValue value) { + List lst; + if(!myErrorMessage2ValueMap.containsKey(message)) { + myErrorMessage2ValueMap.put(message, new ArrayList<>()); + } + + lst = myErrorMessage2ValueMap.get(message); + lst.add(value); + } + + @Nullable + @Override + public Icon getIcon() { + return AllIcons.General.Error; + } + + @Override + public void computeChildren(@NotNull XCompositeNode node) { + XValueChildrenList lst = new XValueChildrenList(); + myErrorMessage2ValueMap.keySet().forEach(s -> lst.addTopGroup(new MyErrorsValueGroup(s))); + node.addChildren(lst, true); + } + + private class MyErrorsValueGroup extends XValueGroup { + + @Override + public void computeChildren(@NotNull XCompositeNode node) { + XValueChildrenList lst = new XValueChildrenList(); + String name = getName(); + myErrorMessage2ValueMap.get(name).forEach(lst::add); + node.addChildren(lst, true); + } + + MyErrorsValueGroup(@NotNull String name) { + super(name); + } + } +} diff --git a/view/InstancesWindow.java b/view/InstancesWindow.java index 8d21bbdbf92c..3ae81705ec5b 100644 --- a/view/InstancesWindow.java +++ b/view/InstancesWindow.java @@ -145,7 +145,7 @@ public class InstancesWindow extends DialogWrapper { private class MyInstancesView extends JBPanel implements Disposable { private static final String HISTORY_ID_PREFIX = "filtering"; - private static final int MAX_TREE_NODE_COUNT = 1000; + private static final int MAX_TREE_NODE_COUNT = 2000; private static final int FILTERING_CHUNK_SIZE = 30; private final XDebuggerTree myInstancesTree; @@ -407,6 +407,7 @@ public class InstancesWindow extends DialogWrapper { private final List myReferences; private final EvaluationContextImpl myEvaluationContext; private final ExpressionEvaluator myExpressionEvaluator; + private final ErrorsValueGroup myErrorsGroup = new ErrorsValueGroup("Errors"); private volatile boolean myDebuggerTaskCompleted = false; @@ -423,11 +424,11 @@ public class InstancesWindow extends DialogWrapper { @Override protected void done() { - SwingUtilities.invokeLater(() -> { - addChildrenToTree(XValueChildrenList.EMPTY, true); - myFilterButton.setEnabled(true); - myProgress.complete(myCompletionReason); - }); + XValueChildrenList lst = new XValueChildrenList(); + lst.addBottomGroup(myErrorsGroup); + addChildrenToTree(lst, true); + myFilterButton.setEnabled(true); + myProgress.complete(myCompletionReason); } @Override @@ -449,26 +450,33 @@ public class InstancesWindow extends DialogWrapper { @Override public void threadAction(@NotNull SuspendContextImpl suspendContext) { + // TODO: need to rewrite this XValueChildrenList children = new XValueChildrenList(); int endOfChunk = min(chunkBegin + FILTERING_CHUNK_SIZE, size); int errorCount = 0; for (int j = chunkBegin; j < endOfChunk && totalChildren.get() < MAX_TREE_NODE_COUNT; j++) { ObjectReference ref = myReferences.get(j); totalViewed.incrementAndGet(); + Pair comparison = null; if (myExpressionEvaluator != null) { - MyFilteringResult comparison = isSatisfy(myExpressionEvaluator, ref); - if(comparison == MyFilteringResult.EVAL_ERROR) { + comparison = isSatisfy(myExpressionEvaluator, ref); + if (comparison.first == MyFilteringResult.EVAL_ERROR) { errorCount++; } - if(comparison != MyFilteringResult.MATCH) { + if (comparison.first == MyFilteringResult.NO_MATCH) { continue; } } JavaValue val = new InstanceJavaValue(null, new InstanceValueDescriptor(myProject, ref), myEvaluationContext, myNodeManager, true); - children.add(val); + if (comparison == null || comparison.first == MyFilteringResult.MATCH) { + children.add(val); + } else { + myErrorsGroup.addErrorValue(comparison.second, val); + } + totalChildren.incrementAndGet(); } @@ -506,17 +514,17 @@ public class InstancesWindow extends DialogWrapper { return null; } - private MyFilteringResult isSatisfy(@NotNull ExpressionEvaluator evaluator, @NotNull Value value) { + private Pair isSatisfy(@NotNull ExpressionEvaluator evaluator, @NotNull Value value) { try { Value result = evaluator.evaluate(myEvaluationContext.createEvaluationContext(value)); if (result instanceof BooleanValue && ((BooleanValue) result).value()) { - return MyFilteringResult.MATCH; + return Pair.create(MyFilteringResult.MATCH, ""); } } catch (EvaluateException e) { - return MyFilteringResult.EVAL_ERROR; + return Pair.create(MyFilteringResult.EVAL_ERROR, e.getMessage()); } - return MyFilteringResult.NO_MATCH; + return Pair.create(MyFilteringResult.NO_MATCH, ""); } } } From 29c91c11795d443a40b36a919b2958e3371c31f4 Mon Sep 17 00:00:00 2001 From: Vitaliy Bibaev Date: Wed, 17 Aug 2016 21:08:32 +0300 Subject: [PATCH 031/151] Show "Errors" node only if errors occurred --- view/ErrorsValueGroup.java | 7 ++++++- view/InstancesWindow.java | 11 ++++++++--- 2 files changed, 14 insertions(+), 4 deletions(-) diff --git a/view/ErrorsValueGroup.java b/view/ErrorsValueGroup.java index 6d5a368318bc..598b66e91e2a 100644 --- a/view/ErrorsValueGroup.java +++ b/view/ErrorsValueGroup.java @@ -16,13 +16,14 @@ import java.util.Map; class ErrorsValueGroup extends XValueGroup { private final Map> myErrorMessage2ValueMap = new HashMap<>(); + ErrorsValueGroup(@NotNull String name) { super(name); } void addErrorValue(@NotNull String message, @NotNull XNamedValue value) { List lst; - if(!myErrorMessage2ValueMap.containsKey(message)) { + if (!myErrorMessage2ValueMap.containsKey(message)) { myErrorMessage2ValueMap.put(message, new ArrayList<>()); } @@ -43,6 +44,10 @@ class ErrorsValueGroup extends XValueGroup { node.addChildren(lst, true); } + boolean isEmpty() { + return myErrorMessage2ValueMap.isEmpty(); + } + private class MyErrorsValueGroup extends XValueGroup { @Override diff --git a/view/InstancesWindow.java b/view/InstancesWindow.java index 3ae81705ec5b..0bb1c79dee2d 100644 --- a/view/InstancesWindow.java +++ b/view/InstancesWindow.java @@ -424,9 +424,14 @@ public class InstancesWindow extends DialogWrapper { @Override protected void done() { - XValueChildrenList lst = new XValueChildrenList(); - lst.addBottomGroup(myErrorsGroup); - addChildrenToTree(lst, true); + if (!myErrorsGroup.isEmpty()) { + XValueChildrenList lst = new XValueChildrenList(); + lst.addBottomGroup(myErrorsGroup); + addChildrenToTree(lst, true); + } else { + addChildrenToTree(XValueChildrenList.EMPTY, true); + } + myFilterButton.setEnabled(true); myProgress.complete(myCompletionReason); } From c7e4450e89beabf39e6516247a4c08dfa21f3b17 Mon Sep 17 00:00:00 2001 From: Vitaliy Bibaev Date: Wed, 17 Aug 2016 21:12:27 +0300 Subject: [PATCH 032/151] Move ErrorsValueGroup to util package --- {view => utils}/ErrorsValueGroup.java | 16 ++++++++-------- view/InstancesWindow.java | 1 + 2 files changed, 9 insertions(+), 8 deletions(-) rename {view => utils}/ErrorsValueGroup.java (86%) diff --git a/view/ErrorsValueGroup.java b/utils/ErrorsValueGroup.java similarity index 86% rename from view/ErrorsValueGroup.java rename to utils/ErrorsValueGroup.java index 598b66e91e2a..922a70cf9af3 100644 --- a/view/ErrorsValueGroup.java +++ b/utils/ErrorsValueGroup.java @@ -1,4 +1,4 @@ -package org.jetbrains.debugger.memory.view; +package org.jetbrains.debugger.memory.utils; import com.intellij.icons.AllIcons; import com.intellij.util.containers.HashMap; @@ -14,14 +14,14 @@ import java.util.ArrayList; import java.util.List; import java.util.Map; -class ErrorsValueGroup extends XValueGroup { +public class ErrorsValueGroup extends XValueGroup { private final Map> myErrorMessage2ValueMap = new HashMap<>(); - ErrorsValueGroup(@NotNull String name) { + public ErrorsValueGroup(@NotNull String name) { super(name); } - void addErrorValue(@NotNull String message, @NotNull XNamedValue value) { + public void addErrorValue(@NotNull String message, @NotNull XNamedValue value) { List lst; if (!myErrorMessage2ValueMap.containsKey(message)) { myErrorMessage2ValueMap.put(message, new ArrayList<>()); @@ -31,6 +31,10 @@ class ErrorsValueGroup extends XValueGroup { lst.add(value); } + public boolean isEmpty() { + return myErrorMessage2ValueMap.isEmpty(); + } + @Nullable @Override public Icon getIcon() { @@ -44,10 +48,6 @@ class ErrorsValueGroup extends XValueGroup { node.addChildren(lst, true); } - boolean isEmpty() { - return myErrorMessage2ValueMap.isEmpty(); - } - private class MyErrorsValueGroup extends XValueGroup { @Override diff --git a/view/InstancesWindow.java b/view/InstancesWindow.java index 0bb1c79dee2d..7dcfe0aace8e 100644 --- a/view/InstancesWindow.java +++ b/view/InstancesWindow.java @@ -58,6 +58,7 @@ import com.sun.jdi.ReferenceType; import com.sun.jdi.Value; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; +import org.jetbrains.debugger.memory.utils.ErrorsValueGroup; import org.jetbrains.debugger.memory.utils.InstanceJavaValue; import org.jetbrains.debugger.memory.utils.InstanceValueDescriptor; import org.jetbrains.java.debugger.JavaDebuggerEditorsProvider; From 02dfd7d3a4d40a25d5b17a6dc59017cc18fdd909 Mon Sep 17 00:00:00 2001 From: Vitaliy Bibaev Date: Thu, 18 Aug 2016 12:16:28 +0300 Subject: [PATCH 033/151] Rename dimension service key for instances window --- view/InstancesWindow.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/view/InstancesWindow.java b/view/InstancesWindow.java index 7dcfe0aace8e..3b1aedf5272f 100644 --- a/view/InstancesWindow.java +++ b/view/InstancesWindow.java @@ -109,7 +109,7 @@ public class InstancesWindow extends DialogWrapper { @NotNull @Override protected String getDimensionServiceKey() { - return "#org.jetbrains.plugin.view.InstancesWindow"; + return "#org.jetbrains.debugger.memory.view.InstancesWindow"; } @Nullable From 1ea174714599a4f2a68110fe48ae18e85656f02c Mon Sep 17 00:00:00 2001 From: Vitaliy Bibaev Date: Thu, 18 Aug 2016 13:31:36 +0300 Subject: [PATCH 034/151] Move logic with expression editor to ExpressionEditorWithHistory --- view/ExpressionEditorWithHistory.java | 38 ++++++++++++++++++++------- view/InstancesWindow.java | 26 ++---------------- 2 files changed, 31 insertions(+), 33 deletions(-) diff --git a/view/ExpressionEditorWithHistory.java b/view/ExpressionEditorWithHistory.java index 7894d61ec5e2..d7b78130b8c6 100644 --- a/view/ExpressionEditorWithHistory.java +++ b/view/ExpressionEditorWithHistory.java @@ -1,22 +1,26 @@ package org.jetbrains.debugger.memory.view; import com.intellij.codeInsight.lookup.LookupManager; +import com.intellij.debugger.engine.DebuggerUtils; import com.intellij.openapi.Disposable; import com.intellij.openapi.actionSystem.AnAction; import com.intellij.openapi.actionSystem.AnActionEvent; import com.intellij.openapi.actionSystem.CustomShortcutSet; +import com.intellij.openapi.application.ApplicationManager; import com.intellij.openapi.editor.ex.util.EditorUtil; import com.intellij.openapi.project.Project; import com.intellij.openapi.ui.popup.PopupStep; import com.intellij.openapi.ui.popup.util.BaseListPopupStep; +import com.intellij.psi.PsiClass; +import com.intellij.psi.search.GlobalSearchScope; import com.intellij.ui.ColoredListCellRenderer; import com.intellij.ui.popup.list.ListPopupImpl; import com.intellij.xdebugger.XExpression; -import com.intellij.xdebugger.XSourcePosition; import com.intellij.xdebugger.evaluation.XDebuggerEditorsProvider; +import com.intellij.xdebugger.impl.XSourcePositionImpl; import com.intellij.xdebugger.impl.breakpoints.XExpressionImpl; import com.intellij.xdebugger.impl.ui.XDebuggerExpressionEditor; -import org.jetbrains.annotations.NonNls; +import com.sun.jdi.ReferenceType; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; @@ -24,13 +28,16 @@ import javax.swing.*; import java.util.List; class ExpressionEditorWithHistory extends XDebuggerExpressionEditor { - ExpressionEditorWithHistory(@NotNull Project project, - @NotNull XDebuggerEditorsProvider debuggerEditorsProvider, - @Nullable @NonNls String historyId, - @Nullable XSourcePosition sourcePosition, - @Nullable Disposable parentDisposable) { - super(project, debuggerEditorsProvider, historyId, sourcePosition, XExpressionImpl.EMPTY_EXPRESSION, - false, true, true); + private static final String HISTORY_ID_PREFIX = "filtering"; + + ExpressionEditorWithHistory(final @NotNull Project project, + final @NotNull ReferenceType referenceType, + final @NotNull XDebuggerEditorsProvider debuggerEditorsProvider, + final @Nullable Disposable parentDisposable) { + super(project, debuggerEditorsProvider, HISTORY_ID_PREFIX + referenceType.name(), null, + XExpressionImpl.EMPTY_EXPRESSION, false, true, true); + + new AnAction("InstancesWindow.ShowHistory") { @Override @@ -43,6 +50,19 @@ class ExpressionEditorWithHistory extends XDebuggerExpressionEditor { e.getPresentation().setEnabled(LookupManager.getActiveLookup(getEditor()) == null); } }.registerCustomShortcutSet(CustomShortcutSet.fromString("DOWN"), getComponent(), parentDisposable); + + new SwingWorker() { + @Override + protected Void doInBackground() throws Exception { + ApplicationManager.getApplication().runReadAction(() -> { + final PsiClass psiClass = DebuggerUtils.findClass(referenceType.name(), + project, GlobalSearchScope.allScope(project)); + XSourcePositionImpl position = XSourcePositionImpl.createByElement(psiClass); + SwingUtilities.invokeLater(() -> setSourcePosition(position)); + }); + return null; + } + }.execute(); } private void showHistory() { diff --git a/view/InstancesWindow.java b/view/InstancesWindow.java index 3b1aedf5272f..8255f8ece80b 100644 --- a/view/InstancesWindow.java +++ b/view/InstancesWindow.java @@ -2,7 +2,6 @@ package org.jetbrains.debugger.memory.view; import com.intellij.debugger.DebuggerManager; import com.intellij.debugger.engine.DebugProcessImpl; -import com.intellij.debugger.engine.DebuggerUtils; import com.intellij.debugger.engine.JavaValue; import com.intellij.debugger.engine.SuspendContextImpl; import com.intellij.debugger.engine.evaluation.EvaluateException; @@ -19,14 +18,11 @@ import com.intellij.debugger.ui.tree.render.CachedEvaluator; import com.intellij.openapi.Disposable; import com.intellij.openapi.actionSystem.*; import com.intellij.openapi.actionSystem.ex.AnActionListener; -import com.intellij.openapi.application.ApplicationManager; import com.intellij.openapi.project.Project; import com.intellij.openapi.ui.DialogWrapper; import com.intellij.openapi.util.Disposer; import com.intellij.openapi.util.Pair; import com.intellij.openapi.wm.IdeFocusManager; -import com.intellij.psi.PsiClass; -import com.intellij.psi.search.GlobalSearchScope; import com.intellij.ui.JBColor; import com.intellij.ui.SimpleTextAttributes; import com.intellij.ui.components.JBLabel; @@ -40,7 +36,6 @@ import com.intellij.xdebugger.XDebugSessionListener; import com.intellij.xdebugger.XExpression; import com.intellij.xdebugger.frame.*; import com.intellij.xdebugger.impl.XDebugSessionImpl; -import com.intellij.xdebugger.impl.XSourcePositionImpl; import com.intellij.xdebugger.impl.actions.XDebuggerActionBase; import com.intellij.xdebugger.impl.evaluate.quick.XDebuggerTreeCreator; import com.intellij.xdebugger.impl.frame.XValueMarkers; @@ -145,7 +140,6 @@ public class InstancesWindow extends DialogWrapper { } private class MyInstancesView extends JBPanel implements Disposable { - private static final String HISTORY_ID_PREFIX = "filtering"; private static final int MAX_TREE_NODE_COUNT = 2000; private static final int FILTERING_CHUNK_SIZE = 30; @@ -172,9 +166,8 @@ public class InstancesWindow extends DialogWrapper { myDebugSession.addSessionListener(myDebugSessionListener, InstancesWindow.this.myDisposable); JavaDebuggerEditorsProvider editorsProvider = new JavaDebuggerEditorsProvider(); - myFilterConditionEditor = new ExpressionEditorWithHistory(myProject, editorsProvider, - HISTORY_ID_PREFIX + myReferenceType.name(), null, InstancesWindow.this.myDisposable); - setSourcePositionForEditor(); + myFilterConditionEditor = new ExpressionEditorWithHistory(myProject, myReferenceType, + editorsProvider, InstancesWindow.this.myDisposable); myFilterButton.setBorder(BorderFactory.createEmptyBorder()); Dimension filteringButtonSize = myFilterConditionEditor.getEditorComponent().getPreferredSize(); @@ -300,21 +293,6 @@ public class InstancesWindow extends DialogWrapper { } } - private void setSourcePositionForEditor() { - new SwingWorker() { - @Override - protected Void doInBackground() throws Exception { - ApplicationManager.getApplication().runReadAction(() -> { - final PsiClass psiClass = DebuggerUtils.findClass(myReferenceType.name(), - myProject, GlobalSearchScope.allScope(myProject)); - XSourcePositionImpl position = XSourcePositionImpl.createByElement(psiClass); - SwingUtilities.invokeLater(() -> myFilterConditionEditor.setSourcePosition(position)); - }); - return null; - } - }.execute(); - } - @NotNull private Pair getTreeRootDescriptor() { return Pair.pair(new XValue() { From 856d7de4a2fd17bb61364d0190d947d45b3db6a0 Mon Sep 17 00:00:00 2001 From: Vitaliy Bibaev Date: Thu, 18 Aug 2016 13:45:36 +0300 Subject: [PATCH 035/151] Fix bug in actions: "Evaluate expression" and "Add to watches" --- view/InstancesWindow.java | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/view/InstancesWindow.java b/view/InstancesWindow.java index 8255f8ece80b..794597e190d4 100644 --- a/view/InstancesWindow.java +++ b/view/InstancesWindow.java @@ -354,7 +354,8 @@ public class InstancesWindow extends DialogWrapper { new ValueMarkup(expression.replace("@", ""), new JBColor(0, 0), null)); } - myInstancesTree.rebuildAndRestore(XDebuggerTreeState.saveState(myInstancesTree)); + SwingUtilities.invokeLater(() -> + myInstancesTree.rebuildAndRestore(XDebuggerTreeState.saveState(myInstancesTree))); } } } From b65f2864b6593056195ff79ae080114e3985daf5 Mon Sep 17 00:00:00 2001 From: Vitaliy Bibaev Date: Thu, 18 Aug 2016 14:07:25 +0300 Subject: [PATCH 036/151] IDEA-159447 Add warning that filtering may lead to side effects --- view/InstancesWindow.java | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/view/InstancesWindow.java b/view/InstancesWindow.java index 794597e190d4..8f62e098de9a 100644 --- a/view/InstancesWindow.java +++ b/view/InstancesWindow.java @@ -30,6 +30,7 @@ import com.intellij.ui.components.JBPanel; import com.intellij.ui.components.JBScrollPane; import com.intellij.util.ui.JBDimension; import com.intellij.util.ui.JBUI; +import com.intellij.util.ui.UIUtil; import com.intellij.util.ui.update.UiNotifyConnector; import com.intellij.xdebugger.XDebugSession; import com.intellij.xdebugger.XDebugSessionListener; @@ -176,10 +177,15 @@ public class InstancesWindow extends DialogWrapper { myFilterButton.setPreferredSize(filteringButtonSize); JBPanel filteringPane = new JBPanel(new BorderLayout(JBUI.scale(BORDER_LAYOUT_DEFAULT_GAP), 0)); + JBLabel sideEffectsWarning = new JBLabel("Warning: filtering may have side effects", SwingConstants.RIGHT); + sideEffectsWarning.setBorder(JBUI.Borders.empty(1, 0, 0, 0)); + sideEffectsWarning.setComponentStyle(UIUtil.ComponentStyle.SMALL); + sideEffectsWarning.setFontColor(UIUtil.FontColor.BRIGHTER); filteringPane.add(new JBLabel("Condition:"), BorderLayout.WEST); filteringPane.add(myFilterConditionEditor.getComponent(), BorderLayout.CENTER); filteringPane.add(myFilterButton, BorderLayout.EAST); + filteringPane.add(sideEffectsWarning, BorderLayout.SOUTH); myProgress.addStopActionListener(this::cancelFilteringTask); From 7cd8fba0c707d12b83f552df045a3fd98d88a292 Mon Sep 17 00:00:00 2001 From: Vitaliy Bibaev Date: Thu, 18 Aug 2016 16:12:48 +0300 Subject: [PATCH 037/151] Fix race condition when "done" executed earlier then all progress update tasks --- view/InstancesWindow.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/view/InstancesWindow.java b/view/InstancesWindow.java index 8f62e098de9a..d51667f4f8b4 100644 --- a/view/InstancesWindow.java +++ b/view/InstancesWindow.java @@ -419,7 +419,7 @@ public class InstancesWindow extends DialogWrapper { } myFilterButton.setEnabled(true); - myProgress.complete(myCompletionReason); + SwingUtilities.invokeLater(() -> myProgress.complete(myCompletionReason)); } @Override From 16d739b89a900e784dabe1bc8cf81143950d7de1 Mon Sep 17 00:00:00 2001 From: Vitaliy Bibaev Date: Thu, 18 Aug 2016 16:13:14 +0300 Subject: [PATCH 038/151] Replace "Items" label to "Shown" --- view/FilteringProgressView.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/view/FilteringProgressView.java b/view/FilteringProgressView.java index 30cd478a7e81..7f17f6b9a8d7 100644 --- a/view/FilteringProgressView.java +++ b/view/FilteringProgressView.java @@ -91,7 +91,7 @@ class FilteringProgressView extends BorderLayoutPanel { private String getDescription() { int total = myProgressBar.getMaximum(); - String itemsInfo = String.format("Items: %d / %d", myMatchedCount, total); + String itemsInfo = String.format("Shown: %d / %d", myMatchedCount, total); if (myIsInProcess || myCompletionReason == null) { return itemsInfo; } From ddb098cfc624d167c26a57e1a10a369eaeb45903 Mon Sep 17 00:00:00 2001 From: Vitaliy Bibaev Date: Thu, 18 Aug 2016 16:49:21 +0300 Subject: [PATCH 039/151] Add array length to node label in instances tree --- utils/InstanceValueDescriptor.java | 9 +++++++-- utils/NamesUtils.java | 22 +++++++++++++++++++--- 2 files changed, 26 insertions(+), 5 deletions(-) diff --git a/utils/InstanceValueDescriptor.java b/utils/InstanceValueDescriptor.java index 2c1b65f4f7c9..101d30714643 100644 --- a/utils/InstanceValueDescriptor.java +++ b/utils/InstanceValueDescriptor.java @@ -9,6 +9,7 @@ import com.intellij.openapi.project.Project; import com.intellij.psi.JavaPsiFacade; import com.intellij.psi.PsiElementFactory; import com.intellij.psi.PsiExpression; +import com.sun.jdi.ArrayReference; import com.sun.jdi.ObjectReference; import com.sun.jdi.Value; @@ -20,7 +21,11 @@ public class InstanceValueDescriptor extends ValueDescriptorImpl { @Override public String calcValueName() { - ObjectReference ref = ((ObjectReference)getValue()); + ObjectReference ref = ((ObjectReference) getValue()); + if (ref instanceof ArrayReference) { + ArrayReference arrayReference = (ArrayReference) ref; + return NamesUtils.getArrayUniqueName(arrayReference); + } return NamesUtils.getUniqueName(ref); } @@ -37,7 +42,7 @@ public class InstanceValueDescriptor extends ValueDescriptorImpl { @Override public PsiExpression getDescriptorEvaluation(DebuggerContext debuggerContext) throws EvaluateException { PsiElementFactory elementFactory = JavaPsiFacade.getInstance(myProject).getElementFactory(); - ObjectReference ref = ((ObjectReference)getValue()); + ObjectReference ref = ((ObjectReference) getValue()); String name = NamesUtils.getUniqueName(ref).replace("@", ""); String presentation = String.format("%s_DebugLabel", name); diff --git a/utils/NamesUtils.java b/utils/NamesUtils.java index 8b4363ae1cd1..d7fdd334bf7d 100644 --- a/utils/NamesUtils.java +++ b/utils/NamesUtils.java @@ -1,10 +1,26 @@ package org.jetbrains.debugger.memory.utils; +import com.intellij.openapi.util.text.StringUtil; +import com.sun.jdi.ArrayReference; import com.sun.jdi.ObjectReference; +import org.jetbrains.annotations.NotNull; + +import java.util.regex.Pattern; class NamesUtils { - static String getUniqueName(ObjectReference ref) { - String name = ref.referenceType().name().replace("[]", "Array"); - return String.format("%s@%d", name.substring(name.lastIndexOf('.') + 1), ref.uniqueID()); + @NotNull + static String getUniqueName(@NotNull ObjectReference ref) { + String shortName = StringUtil.getShortName(ref.referenceType().name()); + String name = shortName.replace("[]", "Array"); + return String.format("%s@%d", name, ref.uniqueID()); + } + + @NotNull + static String getArrayUniqueName(@NotNull ArrayReference ref) { + String shortName = StringUtil.getShortName(ref.referenceType().name()); + int length = ref.length(); + + String name = shortName.replaceFirst(Pattern.quote("[]"), String.format("[%d]", length)); + return String.format("%s@%d", name, ref.uniqueID()); } } From 6f1c5f237780c9c64abce5973778801ff8305f29 Mon Sep 17 00:00:00 2001 From: Vitaliy Bibaev Date: Thu, 18 Aug 2016 17:39:58 +0300 Subject: [PATCH 040/151] Fix ArrayIndexOutOfBoundsException in classes table --- view/ClassesTable.java | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/view/ClassesTable.java b/view/ClassesTable.java index 51420e56de68..ec2dd8a944a4 100644 --- a/view/ClassesTable.java +++ b/view/ClassesTable.java @@ -170,7 +170,8 @@ public class ClassesTable extends JBTable implements DataProvider { showContent(); if (newSelectedIndex != -1 && !myModel.isHided()) { - changeSelection(convertRowIndexToView(newSelectedIndex), + int ix = convertRowIndexToView(newSelectedIndex); + changeSelection(ix, DiffViewTableModel.CLASSNAME_COLUMN_INDEX, false, false); } } @@ -251,7 +252,7 @@ public class ClassesTable extends JBTable implements DataProvider { mySelectedClassWhenHided = getSelectedClass(); myIsWithContent = false; clearSelection(); - getRowSorter().allRowsChanged(); + fireTableDataChanged(); } } From 8d25d88e49eb22ed00d36e837f54e07226d4b1f7 Mon Sep 17 00:00:00 2001 From: Vitaliy Bibaev Date: Thu, 18 Aug 2016 19:47:33 +0300 Subject: [PATCH 041/151] Update version: 0.9 --- view/ExpressionEditorWithHistory.java | 2 -- 1 file changed, 2 deletions(-) diff --git a/view/ExpressionEditorWithHistory.java b/view/ExpressionEditorWithHistory.java index d7b78130b8c6..ab34a0972d00 100644 --- a/view/ExpressionEditorWithHistory.java +++ b/view/ExpressionEditorWithHistory.java @@ -37,8 +37,6 @@ class ExpressionEditorWithHistory extends XDebuggerExpressionEditor { super(project, debuggerEditorsProvider, HISTORY_ID_PREFIX + referenceType.name(), null, XExpressionImpl.EMPTY_EXPRESSION, false, true, true); - - new AnAction("InstancesWindow.ShowHistory") { @Override public void actionPerformed(AnActionEvent e) { From 3acee07a6990af8f8f12def9c4cd5dc5552a6a29 Mon Sep 17 00:00:00 2001 From: Vitaliy Bibaev Date: Fri, 19 Aug 2016 12:57:01 +0300 Subject: [PATCH 042/151] Hide progress panel while session is running --- view/InstancesWindow.java | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/view/InstancesWindow.java b/view/InstancesWindow.java index d51667f4f8b4..fa02e715f144 100644 --- a/view/InstancesWindow.java +++ b/view/InstancesWindow.java @@ -328,12 +328,16 @@ public class InstancesWindow extends DialogWrapper { root.setMessage("The application is running", XDebuggerUIConstants.INFORMATION_MESSAGE_ICON, SimpleTextAttributes.REGULAR_ATTRIBUTES, null); + myProgress.setVisible(false); }); } @Override public void sessionPaused() { - SwingUtilities.invokeLater(() -> myInstancesTree.rebuildAndRestore(myTreeState)); + SwingUtilities.invokeLater(() -> { + myProgress.setVisible(true); + myInstancesTree.rebuildAndRestore(myTreeState); + }); } } From c782ee412f187201b503a4cf0d43650614aeec8d Mon Sep 17 00:00:00 2001 From: Vitaliy Bibaev Date: Mon, 22 Aug 2016 11:59:42 +0300 Subject: [PATCH 043/151] Fix filtering condition for classes --- view/ClassesTable.java | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/view/ClassesTable.java b/view/ClassesTable.java index ec2dd8a944a4..a3d6f37c4392 100644 --- a/view/ClassesTable.java +++ b/view/ClassesTable.java @@ -107,8 +107,8 @@ public class ClassesTable extends JBTable implements DataProvider { } String name = ref.name().toLowerCase(); - String pattern = myFilteringPattern; - return name.contains(pattern) || match(pattern.toLowerCase(), name.substring(name.lastIndexOf('.') + 1)); + String pattern = myFilteringPattern.toLowerCase(); + return name.contains(pattern) || match(pattern, name.substring(name.lastIndexOf('.') + 1)); } }); From aa1e91aa1979a5fddc203131e23c78d08dd068f4 Mon Sep 17 00:00:00 2001 From: Vitaliy Bibaev Date: Mon, 22 Aug 2016 13:15:59 +0300 Subject: [PATCH 044/151] Use jb matcher, instead of custom. Highlight matched text fragments --- view/ClassesTable.java | 45 +++++++++++++++++++++--------------------- 1 file changed, 23 insertions(+), 22 deletions(-) diff --git a/view/ClassesTable.java b/view/ClassesTable.java index a3d6f37c4392..b1f9ed46766b 100644 --- a/view/ClassesTable.java +++ b/view/ClassesTable.java @@ -2,9 +2,14 @@ package org.jetbrains.debugger.memory.view; import com.intellij.openapi.actionSystem.DataKey; import com.intellij.openapi.actionSystem.DataProvider; +import com.intellij.openapi.util.TextRange; +import com.intellij.psi.codeStyle.MinusculeMatcher; +import com.intellij.psi.codeStyle.NameUtil; import com.intellij.ui.ColoredTableCellRenderer; import com.intellij.ui.SimpleTextAttributes; +import com.intellij.ui.speedSearch.SpeedSearchUtil; import com.intellij.ui.table.JBTable; +import com.intellij.util.containers.FList; import com.intellij.util.ui.JBUI; import com.intellij.xdebugger.XDebugSession; import com.sun.jdi.ReferenceType; @@ -41,6 +46,7 @@ public class ClassesTable extends JBTable implements DataProvider { private boolean myOnlyWithDiff; private boolean myOnlyWithInstances; + private MinusculeMatcher myMatcher = NameUtil.buildMatcher("*").build(); private String myFilteringPattern = ""; private ConcurrentHashMap myCounts = new ConcurrentHashMap<>(); @@ -78,7 +84,20 @@ public class ClassesTable extends JBTable implements DataProvider { protected void customizeCellRenderer(JTable table, @Nullable Object value, boolean isSelected, boolean hasFocus, int row, int column) { String presentation = value == null ? "null" : ((ReferenceType) value).name(); - append(String.format(" %s", presentation), SimpleTextAttributes.REGULAR_ATTRIBUTES); + + append(" "); + if (value != null && isSelected) { + FList textRanges = myMatcher.matchingFragments(presentation); + if(textRanges != null) { + SimpleTextAttributes attributes = new SimpleTextAttributes(getBackground(), getForeground(), null, + SimpleTextAttributes.STYLE_SEARCH_MATCH); + SpeedSearchUtil.appendColoredFragments(this, presentation, textRanges, + SimpleTextAttributes.REGULAR_ATTRIBUTES, attributes); + return; + } + } + + append(String.format("%s", presentation), SimpleTextAttributes.REGULAR_ATTRIBUTES); } }); @@ -101,14 +120,9 @@ public class ClassesTable extends JBTable implements DataProvider { int ix = entry.getIdentifier(); ReferenceType ref = myElems.get(ix); DiffValue diff = myCounts.getOrDefault(ref, myUnknownValue); - if (myOnlyWithDiff && diff.diff() == 0 || - myOnlyWithInstances && !diff.hasInstance()) { - return false; - } + return !(myOnlyWithDiff && diff.diff() == 0 || myOnlyWithInstances && !diff.hasInstance()) + && myMatcher.matches(ref.name()); - String name = ref.name().toLowerCase(); - String pattern = myFilteringPattern.toLowerCase(); - return name.contains(pattern) || match(pattern, name.substring(name.lastIndexOf('.') + 1)); } }); @@ -140,6 +154,7 @@ public class ClassesTable extends JBTable implements DataProvider { void setFilterPattern(String pattern) { if (!myFilteringPattern.equals(pattern)) { myFilteringPattern = pattern; + myMatcher = NameUtil.buildMatcher("*" + pattern).build(); getRowSorter().allRowsChanged(); } } @@ -184,20 +199,6 @@ public class ClassesTable extends JBTable implements DataProvider { myModel.show(); } - // TODO: use jb filter - private boolean match(String pattern, String value) { - int i = 0; - int j = 0; - while (j < value.length()) { - if (i < pattern.length() && value.charAt(j) == pattern.charAt(i)) { - i++; - } - j++; - } - - return i == pattern.length(); - } - @Nullable @Override public Object getData(@NonNls String dataId) { From 0f7f09f0fc7b2e9c7ec740bdcf03140ac1f12276 Mon Sep 17 00:00:00 2001 From: Vitaliy Bibaev Date: Mon, 22 Aug 2016 19:09:02 +0300 Subject: [PATCH 045/151] Support android plugin for IDEA Ultimate. Make DiffValue mutable --- utils/AndroidUtil.java | 12 +++ view/ClassesFilteredView.java | 40 +++++++--- view/ClassesTable.java | 146 ++++++++++++++++++++++++---------- 3 files changed, 145 insertions(+), 53 deletions(-) create mode 100644 utils/AndroidUtil.java diff --git a/utils/AndroidUtil.java b/utils/AndroidUtil.java new file mode 100644 index 000000000000..e74911112527 --- /dev/null +++ b/utils/AndroidUtil.java @@ -0,0 +1,12 @@ +package org.jetbrains.debugger.memory.utils; + +import com.sun.jdi.VirtualMachine; +import org.jetbrains.annotations.NotNull; + +public class AndroidUtil { + public static final int ANDROID_INSTANCES_COUNT_BATCH_SIZE = 500; + + public static boolean isAndroidVM(@NotNull VirtualMachine vm) { + return vm.name().toLowerCase().contains("dalvik"); + } +} diff --git a/view/ClassesFilteredView.java b/view/ClassesFilteredView.java index 6652553c6d51..434bd8ee7366 100644 --- a/view/ClassesFilteredView.java +++ b/view/ClassesFilteredView.java @@ -12,6 +12,7 @@ import com.intellij.openapi.actionSystem.ActionPopupMenu; import com.intellij.openapi.diagnostic.Logger; import com.intellij.openapi.project.Project; import com.intellij.ui.*; +import com.intellij.util.SmartList; import com.intellij.util.ui.components.BorderLayoutPanel; import com.intellij.xdebugger.XDebugSession; import com.intellij.xdebugger.XDebugSessionListener; @@ -22,6 +23,7 @@ import org.jetbrains.annotations.Nullable; import org.jetbrains.debugger.memory.component.MemoryViewManager; import org.jetbrains.debugger.memory.component.MemoryViewManagerState; import org.jetbrains.debugger.memory.event.MemoryViewManagerListener; +import org.jetbrains.debugger.memory.utils.AndroidUtil; import org.jetbrains.debugger.memory.utils.KeyboardUtils; import org.jetbrains.debugger.memory.utils.SingleAlarmWithMutableDelay; @@ -31,12 +33,15 @@ import java.awt.*; import java.awt.event.KeyAdapter; import java.awt.event.KeyEvent; import java.awt.event.MouseEvent; +import java.util.Arrays; import java.util.List; import java.util.concurrent.TimeUnit; +import java.util.stream.IntStream; public class ClassesFilteredView extends BorderLayoutPanel { private static final Logger LOG = Logger.getInstance(ClassesFilteredView.class); private final static double DELAY_BEFORE_INSTANCES_QUERY_COEFFICIENT = 0.5; + private final static int DEFAULT_BATCH_SIZE = Integer.MAX_VALUE; private final Project myProject; private final XDebugSession myDebugSession; @@ -199,17 +204,34 @@ public class ClassesFilteredView extends BorderLayoutPanel { } VirtualMachine vm = classes.get(0).virtualMachine(); - long start = System.nanoTime(); - long[] counts = vm.instanceCounts(classes); - long delay = TimeUnit.NANOSECONDS.toMillis(System.nanoTime() - start); - mySingleAlarm.setDelay((int) (DELAY_BEFORE_INSTANCES_QUERY_COEFFICIENT * delay)); + SwingUtilities.invokeAndWait(() -> myTable.setClasses(classes)); + int batchSize = AndroidUtil.isAndroidVM(vm) + ? AndroidUtil.ANDROID_INSTANCES_COUNT_BATCH_SIZE + : DEFAULT_BATCH_SIZE; + + List chunks = new SmartList<>(); + int size = classes.size(); + for (int begin = 0, end = Math.min(batchSize, size); + begin != size && contextIsValid(); + begin = end, end = Math.min(end + batchSize, size)) { + final List batch = classes.subList(begin, end); + + long start = System.nanoTime(); + final long[] counts = vm.instanceCounts(batch); + long delay = TimeUnit.NANOSECONDS.toMillis(System.nanoTime() - start); + + chunks.add(counts); + + mySingleAlarm.setDelay((int) (DELAY_BEFORE_INSTANCES_QUERY_COEFFICIENT * delay)); + LOG.info(String.format("Instances query time = %d ms. Count = %d", delay, batch.size())); + } + + long[] counts = chunks.size() == 1 ? chunks.get(0) : IntStream.range(0, chunks.size()).boxed() + .flatMapToLong(integer -> Arrays.stream(chunks.get(integer))) + .toArray(); - LOG.info(String.format("Instances query time = %d ms. Count = %d", delay, classes.size())); SwingUtilities.invokeLater(() -> { - if (contextIsValid()) { - myTable.setClassesAndCounts(classes, counts); - } - + myTable.updateInstanceCounts(classes, counts); myTable.setBusy(false); }); } diff --git a/view/ClassesTable.java b/view/ClassesTable.java index b1f9ed46766b..d6a85043b638 100644 --- a/view/ClassesTable.java +++ b/view/ClassesTable.java @@ -79,39 +79,9 @@ public class ClassesTable extends JBTable implements DataProvider { setShowGrid(false); - setDefaultRenderer(ReferenceType.class, new ColoredTableCellRenderer() { - @Override - protected void customizeCellRenderer(JTable table, @Nullable Object value, - boolean isSelected, boolean hasFocus, int row, int column) { - String presentation = value == null ? "null" : ((ReferenceType) value).name(); + setDefaultRenderer(ReferenceType.class, new MyClassTableCellRenderer()); - append(" "); - if (value != null && isSelected) { - FList textRanges = myMatcher.matchingFragments(presentation); - if(textRanges != null) { - SimpleTextAttributes attributes = new SimpleTextAttributes(getBackground(), getForeground(), null, - SimpleTextAttributes.STYLE_SEARCH_MATCH); - SpeedSearchUtil.appendColoredFragments(this, presentation, textRanges, - SimpleTextAttributes.REGULAR_ATTRIBUTES, attributes); - return; - } - } - - append(String.format("%s", presentation), SimpleTextAttributes.REGULAR_ATTRIBUTES); - } - }); - - setDefaultRenderer(Integer.class, new ColoredTableCellRenderer() { - @Override - protected void customizeCellRenderer(JTable table, @Nullable Object value, - boolean selected, boolean hasFocus, int row, int column) { - if (value != null) { - int val = (Integer) value; - append(String.format("%s%d", val > 0 ? "+" : "", val)); - setTextAlign(SwingConstants.RIGHT); - } - } - }); + setDefaultRenderer(Long.class, new MyNumberTableCellRenderer()); TableRowSorter sorter = new TableRowSorter<>(myModel); sorter.setRowFilter(new RowFilter() { @@ -173,14 +143,11 @@ public class ClassesTable extends JBTable implements DataProvider { } } - void setClassesAndCounts(List classes, long[] counts) { + void setClasses(@NotNull List classes) { ReferenceType selectedClass = myModel.getSelectedClassBeforeHided(); final int newSelectedIndex = classes.indexOf(selectedClass); myElems = Collections.unmodifiableList(new ArrayList<>(classes)); - for (int i = 0, size = myElems.size(); i < size; i++) { - ReferenceType ref = myElems.get(i); - myCounts.put(ref, myCounts.getOrDefault(ref, myUnknownValue).update(counts[i])); - } + myCounts.values().forEach(DiffValue::makeObsolete); showContent(); @@ -191,6 +158,15 @@ public class ClassesTable extends JBTable implements DataProvider { } } + void updateInstanceCounts(@NotNull List refs, @NotNull long[] counts) { + for (int i = 0, size = refs.size(); i < size; i++) { + ReferenceType ref = refs.get(i); + myCounts.put(ref, myCounts.getOrDefault(ref, myUnknownValue).update(counts[i])); + } + + getRowSorter().allRowsChanged(); + } + void hideContent() { myModel.hide(); } @@ -235,7 +211,7 @@ public class ClassesTable extends JBTable implements DataProvider { return myCounts.getOrDefault(myElems.get(ix), myUnknownValue).myCurrentCount; } }, - new AbstractTableColumnDescriptor("Diff", Integer.class) { + new AbstractTableColumnDescriptor("Diff", Long.class) { @Override public Object getValue(int ix) { return myCounts.getOrDefault(myElems.get(ix), myUnknownValue).diff(); @@ -274,6 +250,18 @@ public class ClassesTable extends JBTable implements DataProvider { } } + /** + * State transmissions for DiffValue and UnknownDiffValue + * unknown -> diff + * diff -> diff + * diff -> obsolete + * obsolete -> diff + * + * State descriptions: + * Unknown - instances count never executed + * Diff - actual value + * Obsolete - query for updating value is executing right now + */ private static class UnknownDiffValue extends DiffValue { UnknownDiffValue() { super(0); @@ -284,15 +272,25 @@ public class ClassesTable extends JBTable implements DataProvider { return true; } + @Override + boolean isObsolete() { + return true; + } + + @Override + void makeObsolete() { + } + @Override DiffValue update(long count) { - return new DiffValue(count, count); + return new DiffValue(count); } } private static class DiffValue { - private final long myOldCount; - private final long myCurrentCount; + private long myOldCount; + private long myCurrentCount; + private boolean myIsObsolete = false; DiffValue(long count) { this(count, count); @@ -304,15 +302,75 @@ public class ClassesTable extends JBTable implements DataProvider { } DiffValue update(long count) { - return new DiffValue(myCurrentCount, count); + myOldCount = myCurrentCount; + myIsObsolete = false; + myCurrentCount = count; + return this; + } + + void makeObsolete() { + myIsObsolete = true; + } + + boolean isObsolete() { + return myIsObsolete; } boolean hasInstance() { return myCurrentCount > 0; } - int diff() { - return (int) (myCurrentCount - myOldCount); + long diff() { + return myCurrentCount - myOldCount; + } + } + + private class MyNumberTableCellRenderer extends ColoredTableCellRenderer { + @Override + protected void customizeCellRenderer(JTable table, @Nullable Object value, + boolean selected, boolean hasFocus, int row, int column) { + int ix = convertRowIndexToModel(row); + if(myCounts.getOrDefault(myElems.get(ix), myUnknownValue).isObsolete() || value == null) { + return; + } + + long val = (long) value; + if (column == DiffViewTableModel.COUNT_COLUMN_INDEX) { + renderCount(val); + } else { + renderDiff(val); + } + } + + private void renderDiff(long diff) { + append(String.format("%s%d", diff > 0 ? "+" : "", diff)); + setTextAlign(SwingConstants.RIGHT); + } + + private void renderCount(long count) { + append(String.valueOf(count)); + } + } + + private class MyClassTableCellRenderer extends ColoredTableCellRenderer { + @Override + protected void customizeCellRenderer(JTable table, @Nullable Object value, + boolean isSelected, boolean hasFocus, int row, int column) { + String presentation = value == null ? "null" : ((ReferenceType) value).name(); + + append(" "); + if (value != null && isSelected) { + FList textRanges = myMatcher.matchingFragments(presentation); + if (textRanges != null) { + SimpleTextAttributes attributes = new SimpleTextAttributes(getBackground(), getForeground(), null, + SimpleTextAttributes.STYLE_SEARCH_MATCH); + SpeedSearchUtil.appendColoredFragments(this, presentation, textRanges, + SimpleTextAttributes.REGULAR_ATTRIBUTES, attributes); + return; + } + } + + append(String.format("%s", presentation), SimpleTextAttributes.REGULAR_ATTRIBUTES); } } } From 6c4a6a8b5211078575466747d537834669d69185 Mon Sep 17 00:00:00 2001 From: Vitaliy Bibaev Date: Tue, 23 Aug 2016 13:12:56 +0300 Subject: [PATCH 046/151] Show list of classes only when loading completed --- view/ClassesFilteredView.java | 12 ++++----- view/ClassesTable.java | 50 +++++++++-------------------------- 2 files changed, 17 insertions(+), 45 deletions(-) diff --git a/view/ClassesFilteredView.java b/view/ClassesFilteredView.java index 434bd8ee7366..d0c70a68b879 100644 --- a/view/ClassesFilteredView.java +++ b/view/ClassesFilteredView.java @@ -196,15 +196,13 @@ public class ClassesFilteredView extends BorderLayoutPanel { @Override public void contextAction(@NotNull SuspendContextImpl suspendContext) throws Exception { - myDebugProcess.getVirtualMachineProxy(); - List classes = myDebugProcess.getVirtualMachineProxy().allClasses(); + final List classes = myDebugProcess.getVirtualMachineProxy().allClasses(); if (classes.isEmpty()) { return; } VirtualMachine vm = classes.get(0).virtualMachine(); - SwingUtilities.invokeAndWait(() -> myTable.setClasses(classes)); int batchSize = AndroidUtil.isAndroidVM(vm) ? AndroidUtil.ANDROID_INSTANCES_COUNT_BATCH_SIZE : DEFAULT_BATCH_SIZE; @@ -214,10 +212,10 @@ public class ClassesFilteredView extends BorderLayoutPanel { for (int begin = 0, end = Math.min(batchSize, size); begin != size && contextIsValid(); begin = end, end = Math.min(end + batchSize, size)) { - final List batch = classes.subList(begin, end); + List batch = classes.subList(begin, end); long start = System.nanoTime(); - final long[] counts = vm.instanceCounts(batch); + long[] counts = vm.instanceCounts(batch); long delay = TimeUnit.NANOSECONDS.toMillis(System.nanoTime() - start); chunks.add(counts); @@ -226,12 +224,12 @@ public class ClassesFilteredView extends BorderLayoutPanel { LOG.info(String.format("Instances query time = %d ms. Count = %d", delay, batch.size())); } - long[] counts = chunks.size() == 1 ? chunks.get(0) : IntStream.range(0, chunks.size()).boxed() + final long[] counts = chunks.size() == 1 ? chunks.get(0) : IntStream.range(0, chunks.size()).boxed() .flatMapToLong(integer -> Arrays.stream(chunks.get(integer))) .toArray(); SwingUtilities.invokeLater(() -> { - myTable.updateInstanceCounts(classes, counts); + myTable.setClassesAndUpdateCounts(classes, counts); myTable.setBusy(false); }); } diff --git a/view/ClassesTable.java b/view/ClassesTable.java index d6a85043b638..1ffac18c9f53 100644 --- a/view/ClassesTable.java +++ b/view/ClassesTable.java @@ -143,11 +143,16 @@ public class ClassesTable extends JBTable implements DataProvider { } } - void setClasses(@NotNull List classes) { + void setClassesAndUpdateCounts(@NotNull List classes, @NotNull long[] counts) { + assert classes.size() == counts.length; ReferenceType selectedClass = myModel.getSelectedClassBeforeHided(); - final int newSelectedIndex = classes.indexOf(selectedClass); + int newSelectedIndex = classes.indexOf(selectedClass); myElems = Collections.unmodifiableList(new ArrayList<>(classes)); - myCounts.values().forEach(DiffValue::makeObsolete); + + for (int i = 0, size = classes.size(); i < size; i++) { + ReferenceType ref = classes.get(i); + myCounts.put(ref, myCounts.getOrDefault(ref, myUnknownValue).update(counts[i])); + } showContent(); @@ -158,15 +163,6 @@ public class ClassesTable extends JBTable implements DataProvider { } } - void updateInstanceCounts(@NotNull List refs, @NotNull long[] counts) { - for (int i = 0, size = refs.size(); i < size; i++) { - ReferenceType ref = refs.get(i); - myCounts.put(ref, myCounts.getOrDefault(ref, myUnknownValue).update(counts[i])); - } - - getRowSorter().allRowsChanged(); - } - void hideContent() { myModel.hide(); } @@ -254,13 +250,10 @@ public class ClassesTable extends JBTable implements DataProvider { * State transmissions for DiffValue and UnknownDiffValue * unknown -> diff * diff -> diff - * diff -> obsolete - * obsolete -> diff - * + *

* State descriptions: * Unknown - instances count never executed * Diff - actual value - * Obsolete - query for updating value is executing right now */ private static class UnknownDiffValue extends DiffValue { UnknownDiffValue() { @@ -272,15 +265,6 @@ public class ClassesTable extends JBTable implements DataProvider { return true; } - @Override - boolean isObsolete() { - return true; - } - - @Override - void makeObsolete() { - } - @Override DiffValue update(long count) { return new DiffValue(count); @@ -290,7 +274,6 @@ public class ClassesTable extends JBTable implements DataProvider { private static class DiffValue { private long myOldCount; private long myCurrentCount; - private boolean myIsObsolete = false; DiffValue(long count) { this(count, count); @@ -303,19 +286,10 @@ public class ClassesTable extends JBTable implements DataProvider { DiffValue update(long count) { myOldCount = myCurrentCount; - myIsObsolete = false; myCurrentCount = count; return this; } - void makeObsolete() { - myIsObsolete = true; - } - - boolean isObsolete() { - return myIsObsolete; - } - boolean hasInstance() { return myCurrentCount > 0; } @@ -329,12 +303,12 @@ public class ClassesTable extends JBTable implements DataProvider { @Override protected void customizeCellRenderer(JTable table, @Nullable Object value, boolean selected, boolean hasFocus, int row, int column) { - int ix = convertRowIndexToModel(row); - if(myCounts.getOrDefault(myElems.get(ix), myUnknownValue).isObsolete() || value == null) { + if (value == null) { return; } long val = (long) value; + setTextAlign(SwingConstants.RIGHT); if (column == DiffViewTableModel.COUNT_COLUMN_INDEX) { renderCount(val); } else { @@ -344,11 +318,11 @@ public class ClassesTable extends JBTable implements DataProvider { private void renderDiff(long diff) { append(String.format("%s%d", diff > 0 ? "+" : "", diff)); - setTextAlign(SwingConstants.RIGHT); } private void renderCount(long count) { append(String.valueOf(count)); + } } From 442ab0aeed0114b6bb3727972ee3b917c548dc77 Mon Sep 17 00:00:00 2001 From: Vitaliy Bibaev Date: Tue, 23 Aug 2016 15:03:37 +0300 Subject: [PATCH 047/151] Set limit for instances count for android --- utils/AndroidUtil.java | 3 ++- view/ClassesFilteredView.java | 2 +- view/InstancesWindow.java | 14 +++++++++++++- 3 files changed, 16 insertions(+), 3 deletions(-) diff --git a/utils/AndroidUtil.java b/utils/AndroidUtil.java index e74911112527..112297fd4a96 100644 --- a/utils/AndroidUtil.java +++ b/utils/AndroidUtil.java @@ -4,7 +4,8 @@ import com.sun.jdi.VirtualMachine; import org.jetbrains.annotations.NotNull; public class AndroidUtil { - public static final int ANDROID_INSTANCES_COUNT_BATCH_SIZE = 500; + public static final int ANDROID_COUNT_BY_CLASSES_BATCH_SIZE = 500; + public static final int ANDROID_INSTANCES_LIMIT = 30000; public static boolean isAndroidVM(@NotNull VirtualMachine vm) { return vm.name().toLowerCase().contains("dalvik"); diff --git a/view/ClassesFilteredView.java b/view/ClassesFilteredView.java index d0c70a68b879..e6736331928c 100644 --- a/view/ClassesFilteredView.java +++ b/view/ClassesFilteredView.java @@ -204,7 +204,7 @@ public class ClassesFilteredView extends BorderLayoutPanel { VirtualMachine vm = classes.get(0).virtualMachine(); int batchSize = AndroidUtil.isAndroidVM(vm) - ? AndroidUtil.ANDROID_INSTANCES_COUNT_BATCH_SIZE + ? AndroidUtil.ANDROID_COUNT_BY_CLASSES_BATCH_SIZE : DEFAULT_BATCH_SIZE; List chunks = new SmartList<>(); diff --git a/view/InstancesWindow.java b/view/InstancesWindow.java index fa02e715f144..0ca5d928230b 100644 --- a/view/InstancesWindow.java +++ b/view/InstancesWindow.java @@ -54,6 +54,7 @@ import com.sun.jdi.ReferenceType; import com.sun.jdi.Value; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; +import org.jetbrains.debugger.memory.utils.AndroidUtil; import org.jetbrains.debugger.memory.utils.ErrorsValueGroup; import org.jetbrains.debugger.memory.utils.InstanceJavaValue; import org.jetbrains.debugger.memory.utils.InstanceValueDescriptor; @@ -72,6 +73,7 @@ public class InstancesWindow extends DialogWrapper { private static final int DEFAULT_WINDOW_HEIGHT = 400; private static final int FILTERING_BUTTON_ADDITIONAL_WIDTH = 30; private static final int BORDER_LAYOUT_DEFAULT_GAP = 5; + private static final int DEFAULT_INSTANCES_COUNT = 0; // All private final Project myProject; private final XDebugSession myDebugSession; @@ -240,10 +242,20 @@ public class InstancesWindow extends DialogWrapper { @Override public void threadAction(@NotNull SuspendContextImpl suspendContext) { - List instances = myReferenceType.instances(0); + int limit = AndroidUtil.isAndroidVM(myReferenceType.virtualMachine()) + ? AndroidUtil.ANDROID_INSTANCES_LIMIT + : DEFAULT_INSTANCES_COUNT; + List instances = myReferenceType.instances(limit); + EvaluationContextImpl evaluationContext = debugProcess .getDebuggerContext().createEvaluationContext(); + //noinspection StatementWithEmptyBody + if (AndroidUtil.isAndroidVM(myReferenceType.virtualMachine()) + && instances.size() == AndroidUtil.ANDROID_INSTANCES_LIMIT) { + // TODO: show warning + } + if (evaluationContext != null) { synchronized (myFilteringTaskLock) { myFilteringTask = new MyFilteringWorker(instances, evaluationContext, createEvaluator()); From 7f2dbc028332939f1922a4904e53c474c19b45ef Mon Sep 17 00:00:00 2001 From: Vitaliy Bibaev Date: Tue, 23 Aug 2016 16:09:59 +0300 Subject: [PATCH 048/151] Show warning if vm is dalvik and limitation for instances has been exceeded --- view/InstancesWindow.java | 11 +++++++---- 1 file changed, 7 insertions(+), 4 deletions(-) diff --git a/view/InstancesWindow.java b/view/InstancesWindow.java index 0ca5d928230b..b7eb2c39a70b 100644 --- a/view/InstancesWindow.java +++ b/view/InstancesWindow.java @@ -87,7 +87,7 @@ public class InstancesWindow extends DialogWrapper { myProject = session.getProject(); myDebugSession = session; myReferenceType = referenceType; - setTitle("Instances of " + referenceType.name()); + addWarningMessage(null); myDebugSession.addSessionListener(new XDebugSessionListener() { @Override public void sessionStopped() { @@ -104,6 +104,11 @@ public class InstancesWindow extends DialogWrapper { ALL_CHECKED, INTERRUPTED, LIMIT_REACHED } + private void addWarningMessage(@Nullable String message) { + String warning = message == null ? "" : String.format(". Warning: %s", message); + setTitle(String.format("Instances of %s%s", myReferenceType.name(), warning)); + } + @NotNull @Override protected String getDimensionServiceKey() { @@ -250,10 +255,9 @@ public class InstancesWindow extends DialogWrapper { EvaluationContextImpl evaluationContext = debugProcess .getDebuggerContext().createEvaluationContext(); - //noinspection StatementWithEmptyBody if (AndroidUtil.isAndroidVM(myReferenceType.virtualMachine()) && instances.size() == AndroidUtil.ANDROID_INSTANCES_LIMIT) { - // TODO: show warning + addWarningMessage("Not all instances will be shown (android restriction)"); } if (evaluationContext != null) { @@ -263,7 +267,6 @@ public class InstancesWindow extends DialogWrapper { myProgress.start(instances.size()); myFilteringTask.execute(); }); - myFilteringTask.execute(); } } } From 15d4e9f73fe0ea265ffcb5bf1efec3286397a21d Mon Sep 17 00:00:00 2001 From: Vitaliy Bibaev Date: Tue, 23 Aug 2016 19:14:22 +0300 Subject: [PATCH 049/151] Fix bug when "Show Instances" action enabled when no selected class --- action/ShowInstancesFromClassesViewAction.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/action/ShowInstancesFromClassesViewAction.java b/action/ShowInstancesFromClassesViewAction.java index 9034d583091a..ebbda9b9435a 100644 --- a/action/ShowInstancesFromClassesViewAction.java +++ b/action/ShowInstancesFromClassesViewAction.java @@ -8,7 +8,7 @@ import org.jetbrains.debugger.memory.view.InstancesWindow; public class ShowInstancesFromClassesViewAction extends ClassesActionBase { @Override protected boolean isEnabled(AnActionEvent e) { - return super.isEnabled(e); + return super.isEnabled(e) && getSelectedClass(e) != null ; } @Override From 99d19c88d23390f1fc9cdf9328288b468952763c Mon Sep 17 00:00:00 2001 From: Vitaliy Bibaev Date: Tue, 23 Aug 2016 19:31:46 +0300 Subject: [PATCH 050/151] Hide grid in selected element of classes table --- view/ClassesTable.java | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/view/ClassesTable.java b/view/ClassesTable.java index 1ffac18c9f53..1c2ced59bf9c 100644 --- a/view/ClassesTable.java +++ b/view/ClassesTable.java @@ -10,6 +10,7 @@ import com.intellij.ui.SimpleTextAttributes; import com.intellij.ui.speedSearch.SpeedSearchUtil; import com.intellij.ui.table.JBTable; import com.intellij.util.containers.FList; +import com.intellij.util.ui.JBDimension; import com.intellij.util.ui.JBUI; import com.intellij.xdebugger.XDebugSession; import com.sun.jdi.ReferenceType; @@ -78,9 +79,9 @@ public class ClassesTable extends JBTable implements DataProvider { diffColumn.setResizable(false); setShowGrid(false); + setIntercellSpacing(new JBDimension(0, 0)); setDefaultRenderer(ReferenceType.class, new MyClassTableCellRenderer()); - setDefaultRenderer(Long.class, new MyNumberTableCellRenderer()); TableRowSorter sorter = new TableRowSorter<>(myModel); From 716d52fe42ca1cd6af460a01c85d2a550897eb59 Mon Sep 17 00:00:00 2001 From: Vitaliy Bibaev Date: Tue, 23 Aug 2016 20:10:36 +0300 Subject: [PATCH 051/151] Add context menu actions for tracking of instances --- action/ClassesActionBase.java | 2 +- action/tracking/HashBasedTrackingAction.java | 11 +++++++++++ action/tracking/IdentityBasedTrackingAction.java | 11 +++++++++++ .../tracking/RetainReferenceBasedTrackingAction.java | 12 ++++++++++++ 4 files changed, 35 insertions(+), 1 deletion(-) create mode 100644 action/tracking/HashBasedTrackingAction.java create mode 100644 action/tracking/IdentityBasedTrackingAction.java create mode 100644 action/tracking/RetainReferenceBasedTrackingAction.java diff --git a/action/ClassesActionBase.java b/action/ClassesActionBase.java index 51e2c3a550a5..cdd3a50dd892 100644 --- a/action/ClassesActionBase.java +++ b/action/ClassesActionBase.java @@ -8,7 +8,7 @@ import com.sun.jdi.ReferenceType; import org.jetbrains.annotations.Nullable; import org.jetbrains.debugger.memory.view.ClassesTable; -abstract class ClassesActionBase extends AnAction { +public abstract class ClassesActionBase extends AnAction { @Override public void update(AnActionEvent e) { e.getPresentation().setEnabled(isEnabled(e)); diff --git a/action/tracking/HashBasedTrackingAction.java b/action/tracking/HashBasedTrackingAction.java new file mode 100644 index 000000000000..f0813c177eb6 --- /dev/null +++ b/action/tracking/HashBasedTrackingAction.java @@ -0,0 +1,11 @@ +package org.jetbrains.debugger.memory.action.tracking; + +import com.intellij.openapi.actionSystem.AnActionEvent; +import org.jetbrains.debugger.memory.action.ClassesActionBase; + +public class HashBasedTrackingAction extends ClassesActionBase { + @Override + protected void perform(AnActionEvent e) { + + } +} diff --git a/action/tracking/IdentityBasedTrackingAction.java b/action/tracking/IdentityBasedTrackingAction.java new file mode 100644 index 000000000000..577f47090bc4 --- /dev/null +++ b/action/tracking/IdentityBasedTrackingAction.java @@ -0,0 +1,11 @@ +package org.jetbrains.debugger.memory.action.tracking; + +import com.intellij.openapi.actionSystem.AnActionEvent; +import org.jetbrains.debugger.memory.action.ClassesActionBase; + +public class IdentityBasedTrackingAction extends ClassesActionBase { + @Override + protected void perform(AnActionEvent e) { + + } +} diff --git a/action/tracking/RetainReferenceBasedTrackingAction.java b/action/tracking/RetainReferenceBasedTrackingAction.java new file mode 100644 index 000000000000..095a79965b6a --- /dev/null +++ b/action/tracking/RetainReferenceBasedTrackingAction.java @@ -0,0 +1,12 @@ +package org.jetbrains.debugger.memory.action.tracking; + +import com.intellij.openapi.actionSystem.AnActionEvent; +import org.jetbrains.debugger.memory.action.ClassesActionBase; + +public class RetainReferenceBasedTrackingAction extends ClassesActionBase { + + @Override + protected void perform(AnActionEvent e) { + + } +} From 90b8f9ea8f226bad0ceb262b771b57ba2e8e474a Mon Sep 17 00:00:00 2001 From: Vitaliy Bibaev Date: Wed, 24 Aug 2016 11:15:58 +0300 Subject: [PATCH 052/151] Add persistent project component, for storing classes which were attached for tracking --- action/ClassesActionBase.java | 2 +- action/tracking/HashBasedTrackingAction.java | 10 ++- .../tracking/IdentityBasedTrackingAction.java | 9 +- .../RetainReferenceBasedTrackingAction.java | 10 ++- component/InstancesTracker.java | 89 +++++++++++++++++++ 5 files changed, 115 insertions(+), 5 deletions(-) create mode 100644 component/InstancesTracker.java diff --git a/action/ClassesActionBase.java b/action/ClassesActionBase.java index cdd3a50dd892..e534614afd83 100644 --- a/action/ClassesActionBase.java +++ b/action/ClassesActionBase.java @@ -27,7 +27,7 @@ public abstract class ClassesActionBase extends AnAction { protected abstract void perform(AnActionEvent e); @Nullable - ReferenceType getSelectedClass(AnActionEvent e) { + protected ReferenceType getSelectedClass(AnActionEvent e) { return e.getData(ClassesTable.SELECTED_CLASS_KEY); } diff --git a/action/tracking/HashBasedTrackingAction.java b/action/tracking/HashBasedTrackingAction.java index f0813c177eb6..fa53a12e63ff 100644 --- a/action/tracking/HashBasedTrackingAction.java +++ b/action/tracking/HashBasedTrackingAction.java @@ -1,11 +1,19 @@ package org.jetbrains.debugger.memory.action.tracking; import com.intellij.openapi.actionSystem.AnActionEvent; +import com.intellij.openapi.project.Project; +import com.sun.jdi.ReferenceType; import org.jetbrains.debugger.memory.action.ClassesActionBase; +import org.jetbrains.debugger.memory.component.InstancesTracker; +import org.jetbrains.debugger.memory.component.InstancesTracker.TrackingType; public class HashBasedTrackingAction extends ClassesActionBase { @Override protected void perform(AnActionEvent e) { - + Project project = e.getProject(); + ReferenceType selectedClass = getSelectedClass(e); + if (project != null && selectedClass != null) { + InstancesTracker.getInstance(project).add(selectedClass, TrackingType.HASH); + } } } diff --git a/action/tracking/IdentityBasedTrackingAction.java b/action/tracking/IdentityBasedTrackingAction.java index 577f47090bc4..cbed9a55ee5d 100644 --- a/action/tracking/IdentityBasedTrackingAction.java +++ b/action/tracking/IdentityBasedTrackingAction.java @@ -1,11 +1,18 @@ package org.jetbrains.debugger.memory.action.tracking; import com.intellij.openapi.actionSystem.AnActionEvent; +import com.intellij.openapi.project.Project; +import com.sun.jdi.ReferenceType; import org.jetbrains.debugger.memory.action.ClassesActionBase; +import org.jetbrains.debugger.memory.component.InstancesTracker; public class IdentityBasedTrackingAction extends ClassesActionBase { @Override protected void perform(AnActionEvent e) { - + Project project = e.getProject(); + ReferenceType selectedClass = getSelectedClass(e); + if (project != null && selectedClass != null) { + InstancesTracker.getInstance(project).add(selectedClass, InstancesTracker.TrackingType.IDENTITY); + } } } diff --git a/action/tracking/RetainReferenceBasedTrackingAction.java b/action/tracking/RetainReferenceBasedTrackingAction.java index 095a79965b6a..ad7441506ed2 100644 --- a/action/tracking/RetainReferenceBasedTrackingAction.java +++ b/action/tracking/RetainReferenceBasedTrackingAction.java @@ -1,12 +1,18 @@ package org.jetbrains.debugger.memory.action.tracking; import com.intellij.openapi.actionSystem.AnActionEvent; +import com.intellij.openapi.project.Project; +import com.sun.jdi.ReferenceType; import org.jetbrains.debugger.memory.action.ClassesActionBase; +import org.jetbrains.debugger.memory.component.InstancesTracker; public class RetainReferenceBasedTrackingAction extends ClassesActionBase { - @Override protected void perform(AnActionEvent e) { - + Project project = e.getProject(); + ReferenceType selectedClass = getSelectedClass(e); + if (project != null && selectedClass != null) { + InstancesTracker.getInstance(project).add(selectedClass, InstancesTracker.TrackingType.RETAIN); + } } } diff --git a/component/InstancesTracker.java b/component/InstancesTracker.java new file mode 100644 index 000000000000..5a8c932a5af0 --- /dev/null +++ b/component/InstancesTracker.java @@ -0,0 +1,89 @@ +package org.jetbrains.debugger.memory.component; + +import com.intellij.openapi.components.*; +import com.intellij.openapi.project.Project; +import com.intellij.util.xmlb.annotations.AbstractCollection; +import com.intellij.util.xmlb.annotations.Attribute; +import com.intellij.util.xmlb.annotations.Tag; +import com.sun.jdi.ReferenceType; +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; + +import java.util.List; +import java.util.Vector; + +@State(name = "InstancesTracker", storages = @Storage(StoragePathMacros.WORKSPACE_FILE)) +public class InstancesTracker extends AbstractProjectComponent + implements PersistentStateComponent { + private MyState myState = new MyState(); + + public InstancesTracker(Project project) { + super(project); + } + + public static InstancesTracker getInstance(@NotNull Project project) { + return project.getComponent(InstancesTracker.class); + } + + public enum TrackingType { + IDENTITY, HASH, RETAIN + } + + public void add(@NotNull ReferenceType ref, @NotNull TrackingType type) { + myState.classes.add(new TrackingClassState(ref.name(), type)); + } + + @SuppressWarnings("unused") + public boolean remove(@NotNull ReferenceType ref, @NotNull TrackingType type) { + return myState.classes.removeIf(state -> state.className.equals(ref.name()) && type.equals(state.trackingType)); + } + + @Nullable + @Override + public MyState getState() { + return new MyState(myState); + } + + @Override + public void loadState(MyState state) { + myState = new MyState(state); + } + + static class MyState { + @NotNull + @AbstractCollection(surroundWithTag = false, elementTypes = {TrackingClassState.class}) + final List classes = new Vector<>(); + + MyState() { + } + + MyState(@NotNull MyState state) { + for (TrackingClassState classState : state.classes) { + TrackingClassState newState = new TrackingClassState(); + newState.className = classState.className; + newState.trackingType = classState.trackingType; + classes.add(newState); + } + } + } + + @Tag("class-state") + private static class TrackingClassState { + @NotNull + @Attribute("name") + String className =""; + + @Nullable + @Attribute("type") + TrackingType trackingType; + + @SuppressWarnings("WeakerAccess") + public TrackingClassState() { + } + + TrackingClassState(@NotNull String name, @NotNull TrackingType type) { + className = name; + trackingType = type; + } + } +} From 027a81370264e37e20a715dae7ea3ccbefd56383 Mon Sep 17 00:00:00 2001 From: Vitaliy Bibaev Date: Wed, 24 Aug 2016 11:44:33 +0300 Subject: [PATCH 053/151] Allow use only single method for tracking of instances of some class --- component/InstancesTracker.java | 70 ++++++++++++++--------------- event/InstancesTrackerListener.java | 14 ++++++ 2 files changed, 48 insertions(+), 36 deletions(-) create mode 100644 event/InstancesTrackerListener.java diff --git a/component/InstancesTracker.java b/component/InstancesTracker.java index 5a8c932a5af0..8546d9ca954b 100644 --- a/component/InstancesTracker.java +++ b/component/InstancesTracker.java @@ -2,19 +2,23 @@ package org.jetbrains.debugger.memory.component; import com.intellij.openapi.components.*; import com.intellij.openapi.project.Project; +import com.intellij.util.EventDispatcher; +import com.intellij.util.containers.HashMap; import com.intellij.util.xmlb.annotations.AbstractCollection; -import com.intellij.util.xmlb.annotations.Attribute; -import com.intellij.util.xmlb.annotations.Tag; import com.sun.jdi.ReferenceType; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; +import org.jetbrains.debugger.memory.event.InstancesTrackerListener; -import java.util.List; -import java.util.Vector; +import java.util.Collections; +import java.util.Map; +import java.util.concurrent.ConcurrentHashMap; @State(name = "InstancesTracker", storages = @Storage(StoragePathMacros.WORKSPACE_FILE)) public class InstancesTracker extends AbstractProjectComponent implements PersistentStateComponent { + private final EventDispatcher myDispatcher = + EventDispatcher.create(InstancesTrackerListener.class); private MyState myState = new MyState(); public InstancesTracker(Project project) { @@ -29,13 +33,30 @@ public class InstancesTracker extends AbstractProjectComponent IDENTITY, HASH, RETAIN } - public void add(@NotNull ReferenceType ref, @NotNull TrackingType type) { - myState.classes.add(new TrackingClassState(ref.name(), type)); + public Map getTrackingClasses() { + Map copy = new HashMap<>(myState.classes); + return Collections.unmodifiableMap(copy); } - @SuppressWarnings("unused") - public boolean remove(@NotNull ReferenceType ref, @NotNull TrackingType type) { - return myState.classes.removeIf(state -> state.className.equals(ref.name()) && type.equals(state.trackingType)); + public void add(@NotNull ReferenceType ref, @NotNull TrackingType type) { + String name = ref.name(); + if(type.equals(myState.classes.getOrDefault(name, null))) { + return; + } + + myState.classes.put(ref.name(), type); + myDispatcher.getMulticaster().classAdded(name, type); + } + + public boolean remove(@NotNull ReferenceType ref) { + String name = ref.name(); + TrackingType removed = myState.classes.remove(name); + if(removed != null) { + myDispatcher.getMulticaster().classRemoved(name); + return true; + } + + return false; } @Nullable @@ -51,39 +72,16 @@ public class InstancesTracker extends AbstractProjectComponent static class MyState { @NotNull - @AbstractCollection(surroundWithTag = false, elementTypes = {TrackingClassState.class}) - final List classes = new Vector<>(); + @AbstractCollection(surroundWithTag = false, elementTypes = {Map.Entry.class}) + final Map classes = new ConcurrentHashMap<>(); MyState() { } MyState(@NotNull MyState state) { - for (TrackingClassState classState : state.classes) { - TrackingClassState newState = new TrackingClassState(); - newState.className = classState.className; - newState.trackingType = classState.trackingType; - classes.add(newState); + for (Map.Entry classState : state.classes.entrySet()) { + classes.put(classState.getKey(), classState.getValue()); } } } - - @Tag("class-state") - private static class TrackingClassState { - @NotNull - @Attribute("name") - String className =""; - - @Nullable - @Attribute("type") - TrackingType trackingType; - - @SuppressWarnings("WeakerAccess") - public TrackingClassState() { - } - - TrackingClassState(@NotNull String name, @NotNull TrackingType type) { - className = name; - trackingType = type; - } - } } diff --git a/event/InstancesTrackerListener.java b/event/InstancesTrackerListener.java new file mode 100644 index 000000000000..bc0fd49f301d --- /dev/null +++ b/event/InstancesTrackerListener.java @@ -0,0 +1,14 @@ +package org.jetbrains.debugger.memory.event; + +import org.jetbrains.annotations.NotNull; +import org.jetbrains.debugger.memory.component.InstancesTracker; + +import java.util.EventListener; + +public interface InstancesTrackerListener extends EventListener{ + default void classAdded(@NotNull String name, @NotNull InstancesTracker.TrackingType type) { + } + + default void classRemoved(@NotNull String name) { + } +} From 85f958daf550823a3a715e9a3660a79113bcc0e2 Mon Sep 17 00:00:00 2001 From: Vitaliy Bibaev Date: Wed, 24 Aug 2016 13:09:03 +0300 Subject: [PATCH 054/151] Make classes view disposable, and highlight classes for which tracking enabled --- component/InstancesTracker.java | 6 +++ component/MemoryViewManager.java | 9 ++--- toolwindow/MemoryViewToolWindowFactory.java | 6 ++- utils/SingleAlarmWithMutableDelay.java | 6 ++- view/ClassesFilteredView.java | 16 +++++--- view/ClassesTable.java | 41 ++++++++++++++++++--- 6 files changed, 66 insertions(+), 18 deletions(-) diff --git a/component/InstancesTracker.java b/component/InstancesTracker.java index 8546d9ca954b..6b1c84a540bf 100644 --- a/component/InstancesTracker.java +++ b/component/InstancesTracker.java @@ -1,5 +1,6 @@ package org.jetbrains.debugger.memory.component; +import com.intellij.openapi.Disposable; import com.intellij.openapi.components.*; import com.intellij.openapi.project.Project; import com.intellij.util.EventDispatcher; @@ -48,6 +49,7 @@ public class InstancesTracker extends AbstractProjectComponent myDispatcher.getMulticaster().classAdded(name, type); } + @SuppressWarnings("unused") public boolean remove(@NotNull ReferenceType ref) { String name = ref.name(); TrackingType removed = myState.classes.remove(name); @@ -59,6 +61,10 @@ public class InstancesTracker extends AbstractProjectComponent return false; } + public void addTrackerListener(@NotNull InstancesTrackerListener listener, @NotNull Disposable parentDisposable) { + myDispatcher.addListener(listener, parentDisposable); + } + @Nullable @Override public MyState getState() { diff --git a/component/MemoryViewManager.java b/component/MemoryViewManager.java index 42949b2bc59c..1af153b035a6 100644 --- a/component/MemoryViewManager.java +++ b/component/MemoryViewManager.java @@ -1,5 +1,6 @@ package org.jetbrains.debugger.memory.component; +import com.intellij.openapi.Disposable; import com.intellij.openapi.application.ApplicationManager; import com.intellij.openapi.components.*; import com.intellij.openapi.project.Project; @@ -60,12 +61,8 @@ public class MemoryViewManager extends ApplicationComponent.Adapter return myState.isShowWithInstancesOnly; } - public void addMemoryViewManagerListener(MemoryViewManagerListener listener) { - myDispatcher.addListener(listener); - } - - public void removeMemoryViewManagerListener(MemoryViewManagerListener listener) { - myDispatcher.removeListener(listener); + public void addMemoryViewManagerListener(MemoryViewManagerListener listener, @NotNull Disposable parentDisposable) { + myDispatcher.addListener(listener, parentDisposable); } @Nullable diff --git a/toolwindow/MemoryViewToolWindowFactory.java b/toolwindow/MemoryViewToolWindowFactory.java index 88f425b3d312..2b8680786822 100644 --- a/toolwindow/MemoryViewToolWindowFactory.java +++ b/toolwindow/MemoryViewToolWindowFactory.java @@ -9,6 +9,7 @@ import com.intellij.openapi.actionSystem.ActionManager; import com.intellij.openapi.actionSystem.DefaultActionGroup; import com.intellij.openapi.project.DumbAware; import com.intellij.openapi.project.Project; +import com.intellij.openapi.util.Disposer; import com.intellij.openapi.wm.ToolWindow; import com.intellij.openapi.wm.ToolWindowFactory; import com.intellij.openapi.wm.ex.ToolWindowEx; @@ -93,7 +94,10 @@ public class MemoryViewToolWindowFactory implements ToolWindowFactory, DumbAware } private void removeSession(@NotNull XDebugSession session) { - myMemoryViews.remove(session); + ClassesFilteredView removed = myMemoryViews.remove(session); + if(removed != null) { + Disposer.dispose(removed); + } } private void updateCurrentMemoryView(@NotNull Project project, @NotNull ToolWindow toolWindow) { diff --git a/utils/SingleAlarmWithMutableDelay.java b/utils/SingleAlarmWithMutableDelay.java index 43687ce17139..75cb937c187c 100644 --- a/utils/SingleAlarmWithMutableDelay.java +++ b/utils/SingleAlarmWithMutableDelay.java @@ -1,12 +1,16 @@ package org.jetbrains.debugger.memory.utils; +import com.intellij.openapi.Disposable; import com.intellij.util.Alarm; +import com.intellij.util.config.ToggleBooleanProperty; import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; public class SingleAlarmWithMutableDelay extends Alarm { private final Runnable myTask; private volatile int myDelay; - public SingleAlarmWithMutableDelay(@NotNull Runnable task) { + public SingleAlarmWithMutableDelay(@NotNull Runnable task, @NotNull Disposable parentDisposable) { + super(parentDisposable); myTask = task; } diff --git a/view/ClassesFilteredView.java b/view/ClassesFilteredView.java index e6736331928c..e32f299b437f 100644 --- a/view/ClassesFilteredView.java +++ b/view/ClassesFilteredView.java @@ -6,11 +6,13 @@ import com.intellij.debugger.engine.DebuggerManagerThreadImpl; import com.intellij.debugger.engine.SuspendContextImpl; import com.intellij.debugger.engine.events.SuspendContextCommandImpl; import com.intellij.debugger.impl.DebuggerManagerImpl; +import com.intellij.openapi.Disposable; import com.intellij.openapi.actionSystem.ActionGroup; import com.intellij.openapi.actionSystem.ActionManager; import com.intellij.openapi.actionSystem.ActionPopupMenu; import com.intellij.openapi.diagnostic.Logger; import com.intellij.openapi.project.Project; +import com.intellij.openapi.util.Disposer; import com.intellij.ui.*; import com.intellij.util.SmartList; import com.intellij.util.ui.components.BorderLayoutPanel; @@ -38,7 +40,7 @@ import java.util.List; import java.util.concurrent.TimeUnit; import java.util.stream.IntStream; -public class ClassesFilteredView extends BorderLayoutPanel { +public class ClassesFilteredView extends BorderLayoutPanel implements Disposable { private static final Logger LOG = Logger.getInstance(ClassesFilteredView.class); private final static double DELAY_BEFORE_INSTANCES_QUERY_COEFFICIENT = 0.5; private final static int DEFAULT_BATCH_SIZE = Integer.MAX_VALUE; @@ -66,6 +68,7 @@ public class ClassesFilteredView extends BorderLayoutPanel { myTable = new ClassesTable(myDebugSession, memoryViewManagerState.isShowWithDiffOnly, memoryViewManagerState.isShowWithInstancesOnly); + Disposer.register(this, myTable); myTable.addKeyListener(new KeyAdapter() { @Override @@ -94,7 +97,7 @@ public class ClassesFilteredView extends BorderLayoutPanel { myTable.setFilteringByInstanceExists(state.isShowWithInstancesOnly); }; - MemoryViewManager.getInstance().addMemoryViewManagerListener(memoryViewManagerListener); + MemoryViewManager.getInstance().addMemoryViewManagerListener(memoryViewManagerListener, this); myDebugSession.addSessionListener(new XDebugSessionListener() { @Override @@ -106,7 +109,6 @@ public class ClassesFilteredView extends BorderLayoutPanel { @Override public void sessionStopped() { debugSession.removeSessionListener(this); - MemoryViewManager.getInstance().removeMemoryViewManagerListener(memoryViewManagerListener); } @Override @@ -115,7 +117,7 @@ public class ClassesFilteredView extends BorderLayoutPanel { updateClassesAndCounts(); } } - }); + }, this); myFilterTextField.addDocumentListener(new DocumentAdapter() { @Override @@ -133,7 +135,7 @@ public class ClassesFilteredView extends BorderLayoutPanel { .schedule(new MyUpdateClassesCommand(myLastSuspendContext)); }); } - }); + }, this); myTable.addMouseListener(new PopupHandler() { @Override @@ -183,6 +185,10 @@ public class ClassesFilteredView extends BorderLayoutPanel { return ActionManager.getInstance().createActionPopupMenu("ClassesView.PopupActionGroup", group); } + @Override + public void dispose() { + } + private final class MyUpdateClassesCommand extends SuspendContextCommandImpl { MyUpdateClassesCommand(@Nullable SuspendContextImpl suspendContext) { diff --git a/view/ClassesTable.java b/view/ClassesTable.java index 1c2ced59bf9c..983f812ec7f2 100644 --- a/view/ClassesTable.java +++ b/view/ClassesTable.java @@ -1,35 +1,38 @@ package org.jetbrains.debugger.memory.view; +import com.intellij.openapi.Disposable; import com.intellij.openapi.actionSystem.DataKey; import com.intellij.openapi.actionSystem.DataProvider; import com.intellij.openapi.util.TextRange; import com.intellij.psi.codeStyle.MinusculeMatcher; import com.intellij.psi.codeStyle.NameUtil; import com.intellij.ui.ColoredTableCellRenderer; +import com.intellij.ui.JBColor; import com.intellij.ui.SimpleTextAttributes; import com.intellij.ui.speedSearch.SpeedSearchUtil; import com.intellij.ui.table.JBTable; import com.intellij.util.containers.FList; +import com.intellij.util.containers.HashMap; import com.intellij.util.ui.JBDimension; import com.intellij.util.ui.JBUI; +import com.intellij.util.ui.UIUtil; import com.intellij.xdebugger.XDebugSession; import com.sun.jdi.ReferenceType; import org.jetbrains.annotations.NonNls; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; +import org.jetbrains.debugger.memory.component.InstancesTracker; +import org.jetbrains.debugger.memory.event.InstancesTrackerListener; import org.jetbrains.debugger.memory.utils.AbstractTableColumnDescriptor; import org.jetbrains.debugger.memory.utils.AbstractTableModelWithColumns; import javax.swing.*; import javax.swing.table.TableColumn; import javax.swing.table.TableRowSorter; -import java.util.ArrayList; -import java.util.Arrays; -import java.util.Collections; -import java.util.List; +import java.util.*; import java.util.concurrent.ConcurrentHashMap; -public class ClassesTable extends JBTable implements DataProvider { +public class ClassesTable extends JBTable implements DataProvider, Disposable { public static final DataKey SELECTED_CLASS_KEY = DataKey.create("ClassesTable.SelectedClass"); public static final DataKey DEBUG_SESSION_KEY = DataKey.create("ClassesTable.DebugSession"); private static final int CLASSES_COLUMN_PREFERRED_WIDTH = 250; @@ -43,6 +46,7 @@ public class ClassesTable extends JBTable implements DataProvider { private final DiffViewTableModel myModel = new DiffViewTableModel(); private final UnknownDiffValue myUnknownValue = new UnknownDiffValue(); private final XDebugSession myDebugSession; + private final Map myTrackedClasses; private boolean myOnlyWithDiff; private boolean myOnlyWithInstances; @@ -62,6 +66,22 @@ public class ClassesTable extends JBTable implements DataProvider { getEmptyText().setText(EMPTY_TABLE_CONTENT); + InstancesTracker tracker = InstancesTracker.getInstance(myDebugSession.getProject()); + tracker.addTrackerListener(new InstancesTrackerListener() { + @Override + public void classAdded(@NotNull String name, @NotNull InstancesTracker.TrackingType type) { + myTrackedClasses.put(name, type); + getRowSorter().allRowsChanged(); + } + + @Override + public void classRemoved(@NotNull String name) { + myTrackedClasses.remove(name); + getRowSorter().allRowsChanged(); + } + }, this); + myTrackedClasses = new HashMap<>(tracker.getTrackingClasses()); + TableColumn classesColumn = getColumnModel().getColumn(DiffViewTableModel.CLASSNAME_COLUMN_INDEX); TableColumn countColumn = getColumnModel().getColumn(DiffViewTableModel.COUNT_COLUMN_INDEX); TableColumn diffColumn = getColumnModel().getColumn(DiffViewTableModel.DIFF_COLUMN_INDEX); @@ -185,6 +205,10 @@ public class ClassesTable extends JBTable implements DataProvider { return null; } + @Override + public void dispose() { + } + private class DiffViewTableModel extends AbstractTableModelWithColumns { private final static int CLASSNAME_COLUMN_INDEX = 0; private final static int COUNT_COLUMN_INDEX = 1; @@ -308,6 +332,10 @@ public class ClassesTable extends JBTable implements DataProvider { return; } + String className = ((ReferenceType) getValueAt(row, DiffViewTableModel.CLASSNAME_COLUMN_INDEX)).name(); + if (!selected && myTrackedClasses.containsKey(className)) { + setBackground(UIUtil.toAlpha(JBColor.yellow, 20)); + } long val = (long) value; setTextAlign(SwingConstants.RIGHT); if (column == DiffViewTableModel.COUNT_COLUMN_INDEX) { @@ -332,6 +360,9 @@ public class ClassesTable extends JBTable implements DataProvider { protected void customizeCellRenderer(JTable table, @Nullable Object value, boolean isSelected, boolean hasFocus, int row, int column) { String presentation = value == null ? "null" : ((ReferenceType) value).name(); + if (value != null && !isSelected && myTrackedClasses.containsKey(presentation)) { + setBackground(UIUtil.toAlpha(JBColor.yellow, 20)); + } append(" "); if (value != null && isSelected) { From e95da174d13b8c505cd9d5e9dc3f775e0b5e8114 Mon Sep 17 00:00:00 2001 From: Vitaliy Bibaev Date: Wed, 24 Aug 2016 13:43:39 +0300 Subject: [PATCH 055/151] Add action for disabling of instance tracking, hide tracking action when class not selected --- action/tracking/DisableTrackingAction.java | 26 ++++++++++++++++++++++ action/tracking/TrackingActionGroup.java | 13 +++++++++++ component/InstancesTracker.java | 5 ++++- 3 files changed, 43 insertions(+), 1 deletion(-) create mode 100644 action/tracking/DisableTrackingAction.java create mode 100644 action/tracking/TrackingActionGroup.java diff --git a/action/tracking/DisableTrackingAction.java b/action/tracking/DisableTrackingAction.java new file mode 100644 index 000000000000..a492115e8d5b --- /dev/null +++ b/action/tracking/DisableTrackingAction.java @@ -0,0 +1,26 @@ +package org.jetbrains.debugger.memory.action.tracking; + +import com.intellij.openapi.actionSystem.AnActionEvent; +import com.intellij.openapi.project.Project; +import com.sun.jdi.ReferenceType; +import org.jetbrains.debugger.memory.action.ClassesActionBase; +import org.jetbrains.debugger.memory.component.InstancesTracker; + +public class DisableTrackingAction extends ClassesActionBase { + @Override + protected boolean isEnabled(AnActionEvent e) { + Project project = e.getProject(); + ReferenceType selectedClass = getSelectedClass(e); + return super.isEnabled(e) && selectedClass != null && + project != null && InstancesTracker.getInstance(project).isTracked(selectedClass.name()); + } + + @Override + protected void perform(AnActionEvent e) { + Project project = e.getProject(); + ReferenceType selectedClass = getSelectedClass(e); + if(project != null && selectedClass != null) { + InstancesTracker.getInstance(project).remove(selectedClass); + } + } +} diff --git a/action/tracking/TrackingActionGroup.java b/action/tracking/TrackingActionGroup.java new file mode 100644 index 000000000000..fd586f3125dc --- /dev/null +++ b/action/tracking/TrackingActionGroup.java @@ -0,0 +1,13 @@ +package org.jetbrains.debugger.memory.action.tracking; + +import com.intellij.openapi.actionSystem.AnActionEvent; +import com.intellij.openapi.actionSystem.DefaultActionGroup; +import com.intellij.openapi.project.DumbAware; +import org.jetbrains.debugger.memory.view.ClassesTable; + +public class TrackingActionGroup extends DefaultActionGroup implements DumbAware{ + @Override + public void update(AnActionEvent e) { + e.getPresentation().setVisible(e.getData(ClassesTable.SELECTED_CLASS_KEY) != null); + } +} diff --git a/component/InstancesTracker.java b/component/InstancesTracker.java index 6b1c84a540bf..879a4c2bc9fc 100644 --- a/component/InstancesTracker.java +++ b/component/InstancesTracker.java @@ -34,6 +34,10 @@ public class InstancesTracker extends AbstractProjectComponent IDENTITY, HASH, RETAIN } + public boolean isTracked(@NotNull String className) { + return myState.classes.containsKey(className); + } + public Map getTrackingClasses() { Map copy = new HashMap<>(myState.classes); return Collections.unmodifiableMap(copy); @@ -49,7 +53,6 @@ public class InstancesTracker extends AbstractProjectComponent myDispatcher.getMulticaster().classAdded(name, type); } - @SuppressWarnings("unused") public boolean remove(@NotNull ReferenceType ref) { String name = ref.name(); TrackingType removed = myState.classes.remove(name); From 2c09a549a862c22b8a20c4f4737592e943c9facd Mon Sep 17 00:00:00 2001 From: Vitaliy Bibaev Date: Wed, 24 Aug 2016 15:00:13 +0300 Subject: [PATCH 056/151] Add basic implementation for algorithms for tracking of instances --- tracking/HashBasedTracking.java | 20 ++++++++++++++++++++ tracking/IdBasedTracking.java | 20 ++++++++++++++++++++ tracking/InstanceTrackingStrategy.java | 25 +++++++++++++++++++++++++ tracking/RetainReferencesTracking.java | 24 ++++++++++++++++++++++++ 4 files changed, 89 insertions(+) create mode 100644 tracking/HashBasedTracking.java create mode 100644 tracking/IdBasedTracking.java create mode 100644 tracking/InstanceTrackingStrategy.java create mode 100644 tracking/RetainReferencesTracking.java diff --git a/tracking/HashBasedTracking.java b/tracking/HashBasedTracking.java new file mode 100644 index 000000000000..0394202d4664 --- /dev/null +++ b/tracking/HashBasedTracking.java @@ -0,0 +1,20 @@ +package org.jetbrains.debugger.memory.tracking; + +import com.sun.jdi.ObjectReference; +import org.apache.commons.lang.NotImplementedException; +import org.jetbrains.annotations.NotNull; + +import java.util.List; + +public class HashBasedTracking extends InstanceTrackingStrategy { + + public HashBasedTracking(@NotNull List initialInstances) { + + } + + @NotNull + @Override + protected List updateImpl(@NotNull List references) { + throw new NotImplementedException(); + } +} diff --git a/tracking/IdBasedTracking.java b/tracking/IdBasedTracking.java new file mode 100644 index 000000000000..070aad0f2e2c --- /dev/null +++ b/tracking/IdBasedTracking.java @@ -0,0 +1,20 @@ +package org.jetbrains.debugger.memory.tracking; + +import com.sun.jdi.ObjectReference; +import org.jetbrains.annotations.NotNull; + +import java.util.List; +import java.util.stream.Collectors; + +public class IdBasedTracking extends InstanceTrackingStrategy { + private long myLastId = -1; + public IdBasedTracking(@NotNull List initialInstances) { + myLastId = initialInstances.stream().map(ObjectReference::uniqueID).max(Long::compare).orElse(-1L); + } + + @NotNull + @Override + protected List updateImpl(@NotNull List references) { + return references.stream().filter(reference -> reference.uniqueID() > myLastId).collect(Collectors.toList()); + } +} diff --git a/tracking/InstanceTrackingStrategy.java b/tracking/InstanceTrackingStrategy.java new file mode 100644 index 000000000000..0a9451d57cd0 --- /dev/null +++ b/tracking/InstanceTrackingStrategy.java @@ -0,0 +1,25 @@ +package org.jetbrains.debugger.memory.tracking; + +import com.sun.jdi.ObjectReference; +import org.jetbrains.annotations.NotNull; + +import java.util.ArrayList; +import java.util.Collections; +import java.util.List; + +public abstract class InstanceTrackingStrategy { + @NotNull + private List myNewInstances = new ArrayList<>(); + + @NotNull + public final List getNewInstances() { + return Collections.unmodifiableList(myNewInstances); + } + + public final void update(@NotNull List references) { + myNewInstances = updateImpl(references); + } + + @NotNull + protected abstract List updateImpl(@NotNull List references); +} diff --git a/tracking/RetainReferencesTracking.java b/tracking/RetainReferencesTracking.java new file mode 100644 index 000000000000..d213e8a2417a --- /dev/null +++ b/tracking/RetainReferencesTracking.java @@ -0,0 +1,24 @@ +package org.jetbrains.debugger.memory.tracking; + +import com.sun.jdi.ObjectReference; +import org.jetbrains.annotations.NotNull; + +import java.util.List; + +public class RetainReferencesTracking extends InstanceTrackingStrategy { + private final IdBasedTracking myIdTracker; + @SuppressWarnings("unused") + private List myHardRefs; + public RetainReferencesTracking(@NotNull List initialInstances) { + myHardRefs = initialInstances; + myIdTracker = new IdBasedTracking(initialInstances); + } + + @NotNull + @Override + protected List updateImpl(@NotNull List references) { + myIdTracker.update(references); + myHardRefs = references; + return myIdTracker.getNewInstances(); + } +} From 5d4010f769b55c4db3bde9038f0c733352c1ec3a Mon Sep 17 00:00:00 2001 From: Vitaliy Bibaev Date: Wed, 24 Aug 2016 16:02:08 +0300 Subject: [PATCH 057/151] Fix error in id-based tracking --- tracking/IdBasedTracking.java | 12 ++++++++++-- 1 file changed, 10 insertions(+), 2 deletions(-) diff --git a/tracking/IdBasedTracking.java b/tracking/IdBasedTracking.java index 070aad0f2e2c..4394e54d6bc7 100644 --- a/tracking/IdBasedTracking.java +++ b/tracking/IdBasedTracking.java @@ -9,12 +9,20 @@ import java.util.stream.Collectors; public class IdBasedTracking extends InstanceTrackingStrategy { private long myLastId = -1; public IdBasedTracking(@NotNull List initialInstances) { - myLastId = initialInstances.stream().map(ObjectReference::uniqueID).max(Long::compare).orElse(-1L); + myLastId = getMaxId(initialInstances); } @NotNull @Override protected List updateImpl(@NotNull List references) { - return references.stream().filter(reference -> reference.uniqueID() > myLastId).collect(Collectors.toList()); + List result = references.stream() + .filter(reference -> reference.uniqueID() > myLastId) + .collect(Collectors.toList()); + myLastId = Math.max(myLastId, getMaxId(result)); + return result; + } + + private long getMaxId(@NotNull List references) { + return references.stream().map(ObjectReference::uniqueID).max(Long::compare).orElse(-1L); } } From 68f75b23ae0009c92a30cdfacdac359b726bb11b Mon Sep 17 00:00:00 2001 From: Vitaliy Bibaev Date: Wed, 24 Aug 2016 16:40:34 +0300 Subject: [PATCH 058/151] Add toggle ability for instances tracking actions --- action/tracking/HashBasedTrackingAction.java | 18 +++---- .../tracking/IdentityBasedTrackingAction.java | 16 ++----- .../tracking/InstancesTrackingActionBase.java | 47 +++++++++++++++++++ .../RetainReferenceBasedTrackingAction.java | 16 ++----- component/InstancesTracker.java | 8 +++- event/InstancesTrackerListener.java | 2 +- view/ClassesTable.java | 2 +- 7 files changed, 72 insertions(+), 37 deletions(-) create mode 100644 action/tracking/InstancesTrackingActionBase.java diff --git a/action/tracking/HashBasedTrackingAction.java b/action/tracking/HashBasedTrackingAction.java index fa53a12e63ff..afac0f214808 100644 --- a/action/tracking/HashBasedTrackingAction.java +++ b/action/tracking/HashBasedTrackingAction.java @@ -1,19 +1,13 @@ package org.jetbrains.debugger.memory.action.tracking; -import com.intellij.openapi.actionSystem.AnActionEvent; -import com.intellij.openapi.project.Project; -import com.sun.jdi.ReferenceType; -import org.jetbrains.debugger.memory.action.ClassesActionBase; -import org.jetbrains.debugger.memory.component.InstancesTracker; +import org.jetbrains.annotations.NotNull; import org.jetbrains.debugger.memory.component.InstancesTracker.TrackingType; -public class HashBasedTrackingAction extends ClassesActionBase { +public class HashBasedTrackingAction extends InstancesTrackingActionBase { + @NotNull @Override - protected void perform(AnActionEvent e) { - Project project = e.getProject(); - ReferenceType selectedClass = getSelectedClass(e); - if (project != null && selectedClass != null) { - InstancesTracker.getInstance(project).add(selectedClass, TrackingType.HASH); - } + protected TrackingType getType() { + return TrackingType.HASH; } } + diff --git a/action/tracking/IdentityBasedTrackingAction.java b/action/tracking/IdentityBasedTrackingAction.java index cbed9a55ee5d..6b04df12181d 100644 --- a/action/tracking/IdentityBasedTrackingAction.java +++ b/action/tracking/IdentityBasedTrackingAction.java @@ -1,18 +1,12 @@ package org.jetbrains.debugger.memory.action.tracking; -import com.intellij.openapi.actionSystem.AnActionEvent; -import com.intellij.openapi.project.Project; -import com.sun.jdi.ReferenceType; -import org.jetbrains.debugger.memory.action.ClassesActionBase; +import org.jetbrains.annotations.NotNull; import org.jetbrains.debugger.memory.component.InstancesTracker; -public class IdentityBasedTrackingAction extends ClassesActionBase { +public class IdentityBasedTrackingAction extends InstancesTrackingActionBase { + @NotNull @Override - protected void perform(AnActionEvent e) { - Project project = e.getProject(); - ReferenceType selectedClass = getSelectedClass(e); - if (project != null && selectedClass != null) { - InstancesTracker.getInstance(project).add(selectedClass, InstancesTracker.TrackingType.IDENTITY); - } + protected InstancesTracker.TrackingType getType() { + return InstancesTracker.TrackingType.IDENTITY; } } diff --git a/action/tracking/InstancesTrackingActionBase.java b/action/tracking/InstancesTrackingActionBase.java new file mode 100644 index 000000000000..e7c904db694d --- /dev/null +++ b/action/tracking/InstancesTrackingActionBase.java @@ -0,0 +1,47 @@ +package org.jetbrains.debugger.memory.action.tracking; + +import com.intellij.openapi.actionSystem.AnActionEvent; +import com.intellij.openapi.actionSystem.ToggleAction; +import com.intellij.openapi.project.Project; +import com.sun.jdi.ReferenceType; +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; +import org.jetbrains.debugger.memory.component.InstancesTracker; +import org.jetbrains.debugger.memory.view.ClassesTable; + +import static org.jetbrains.debugger.memory.component.InstancesTracker.TrackingType; + +abstract class InstancesTrackingActionBase extends ToggleAction { + @Override + public void setSelected(AnActionEvent e, boolean state) { + TrackingType type = getType(); + ReferenceType selectedClass = getSelectedClass(e); + Project project = e.getProject(); + if (selectedClass != null && project != null) { + InstancesTracker tracker = InstancesTracker.getInstance(project); + TrackingType currentType = tracker.getTrackingType(selectedClass.name()); + if (state) { + tracker.add(selectedClass, type); + } else if(type.equals(currentType)) { + tracker.remove(selectedClass); + } + } + } + + @Override + public boolean isSelected(AnActionEvent e) { + TrackingType type = getType(); + ReferenceType selectedClass = getSelectedClass(e); + Project project = e.getProject(); + return selectedClass != null && project != null && + type.equals(InstancesTracker.getInstance(project).getTrackingType(selectedClass.name())); + } + + @NotNull + protected abstract TrackingType getType(); + + @Nullable + private ReferenceType getSelectedClass(AnActionEvent e) { + return e.getData(ClassesTable.SELECTED_CLASS_KEY); + } +} diff --git a/action/tracking/RetainReferenceBasedTrackingAction.java b/action/tracking/RetainReferenceBasedTrackingAction.java index ad7441506ed2..b0d118470227 100644 --- a/action/tracking/RetainReferenceBasedTrackingAction.java +++ b/action/tracking/RetainReferenceBasedTrackingAction.java @@ -1,18 +1,12 @@ package org.jetbrains.debugger.memory.action.tracking; -import com.intellij.openapi.actionSystem.AnActionEvent; -import com.intellij.openapi.project.Project; -import com.sun.jdi.ReferenceType; -import org.jetbrains.debugger.memory.action.ClassesActionBase; +import org.jetbrains.annotations.NotNull; import org.jetbrains.debugger.memory.component.InstancesTracker; -public class RetainReferenceBasedTrackingAction extends ClassesActionBase { +public class RetainReferenceBasedTrackingAction extends InstancesTrackingActionBase { + @NotNull @Override - protected void perform(AnActionEvent e) { - Project project = e.getProject(); - ReferenceType selectedClass = getSelectedClass(e); - if (project != null && selectedClass != null) { - InstancesTracker.getInstance(project).add(selectedClass, InstancesTracker.TrackingType.RETAIN); - } + protected InstancesTracker.TrackingType getType() { + return InstancesTracker.TrackingType.RETAIN; } } diff --git a/component/InstancesTracker.java b/component/InstancesTracker.java index 879a4c2bc9fc..29594c125a78 100644 --- a/component/InstancesTracker.java +++ b/component/InstancesTracker.java @@ -38,6 +38,12 @@ public class InstancesTracker extends AbstractProjectComponent return myState.classes.containsKey(className); } + @Nullable + public TrackingType getTrackingType(@NotNull String className) { + return myState.classes.getOrDefault(className, null); + } + + public Map getTrackingClasses() { Map copy = new HashMap<>(myState.classes); return Collections.unmodifiableMap(copy); @@ -50,7 +56,7 @@ public class InstancesTracker extends AbstractProjectComponent } myState.classes.put(ref.name(), type); - myDispatcher.getMulticaster().classAdded(name, type); + myDispatcher.getMulticaster().classChanged(name, type); } public boolean remove(@NotNull ReferenceType ref) { diff --git a/event/InstancesTrackerListener.java b/event/InstancesTrackerListener.java index bc0fd49f301d..ed03afbc4173 100644 --- a/event/InstancesTrackerListener.java +++ b/event/InstancesTrackerListener.java @@ -6,7 +6,7 @@ import org.jetbrains.debugger.memory.component.InstancesTracker; import java.util.EventListener; public interface InstancesTrackerListener extends EventListener{ - default void classAdded(@NotNull String name, @NotNull InstancesTracker.TrackingType type) { + default void classChanged(@NotNull String name, @NotNull InstancesTracker.TrackingType type) { } default void classRemoved(@NotNull String name) { diff --git a/view/ClassesTable.java b/view/ClassesTable.java index 983f812ec7f2..830803917450 100644 --- a/view/ClassesTable.java +++ b/view/ClassesTable.java @@ -69,7 +69,7 @@ public class ClassesTable extends JBTable implements DataProvider, Disposable { InstancesTracker tracker = InstancesTracker.getInstance(myDebugSession.getProject()); tracker.addTrackerListener(new InstancesTrackerListener() { @Override - public void classAdded(@NotNull String name, @NotNull InstancesTracker.TrackingType type) { + public void classChanged(@NotNull String name, @NotNull InstancesTracker.TrackingType type) { myTrackedClasses.put(name, type); getRowSorter().allRowsChanged(); } From 45fc3d16385fa10c98aed6d7cf31631c48f8ae71 Mon Sep 17 00:00:00 2001 From: Vitaliy Bibaev Date: Wed, 24 Aug 2016 18:47:39 +0300 Subject: [PATCH 059/151] Add static creator method for strategy classes --- tracking/HashBasedTracking.java | 4 ++-- ...acking.java => IdentityBasedTracking.java} | 4 ++-- tracking/InstanceTrackingStrategy.java | 21 ++++++++++++++++--- tracking/RetainReferencesTracking.java | 8 +++---- 4 files changed, 26 insertions(+), 11 deletions(-) rename tracking/{IdBasedTracking.java => IdentityBasedTracking.java} (84%) diff --git a/tracking/HashBasedTracking.java b/tracking/HashBasedTracking.java index 0394202d4664..b3c02ce934d1 100644 --- a/tracking/HashBasedTracking.java +++ b/tracking/HashBasedTracking.java @@ -6,9 +6,9 @@ import org.jetbrains.annotations.NotNull; import java.util.List; -public class HashBasedTracking extends InstanceTrackingStrategy { +class HashBasedTracking extends InstanceTrackingStrategy { - public HashBasedTracking(@NotNull List initialInstances) { + HashBasedTracking(@NotNull List initialInstances) { } diff --git a/tracking/IdBasedTracking.java b/tracking/IdentityBasedTracking.java similarity index 84% rename from tracking/IdBasedTracking.java rename to tracking/IdentityBasedTracking.java index 4394e54d6bc7..78ef6670576a 100644 --- a/tracking/IdBasedTracking.java +++ b/tracking/IdentityBasedTracking.java @@ -6,9 +6,9 @@ import org.jetbrains.annotations.NotNull; import java.util.List; import java.util.stream.Collectors; -public class IdBasedTracking extends InstanceTrackingStrategy { +class IdentityBasedTracking extends InstanceTrackingStrategy { private long myLastId = -1; - public IdBasedTracking(@NotNull List initialInstances) { + IdentityBasedTracking(@NotNull List initialInstances) { myLastId = getMaxId(initialInstances); } diff --git a/tracking/InstanceTrackingStrategy.java b/tracking/InstanceTrackingStrategy.java index 0a9451d57cd0..558ed9dc3943 100644 --- a/tracking/InstanceTrackingStrategy.java +++ b/tracking/InstanceTrackingStrategy.java @@ -7,16 +7,31 @@ import java.util.ArrayList; import java.util.Collections; import java.util.List; -public abstract class InstanceTrackingStrategy { +import static org.jetbrains.debugger.memory.component.InstancesTracker.*; + +abstract class InstanceTrackingStrategy { @NotNull private List myNewInstances = new ArrayList<>(); + public static InstanceTrackingStrategy create(@NotNull TrackingType type, @NotNull List init) { + switch (type) { + case IDENTITY: + return new IdentityBasedTracking(init); + case HASH: + return new HashBasedTracking(init); + case RETAIN: + return new RetainReferencesTracking(init); + } + + throw new UnsupportedOperationException("Such TrackingType not found"); + } + @NotNull - public final List getNewInstances() { + final List getNewInstances() { return Collections.unmodifiableList(myNewInstances); } - public final void update(@NotNull List references) { + final void update(@NotNull List references) { myNewInstances = updateImpl(references); } diff --git a/tracking/RetainReferencesTracking.java b/tracking/RetainReferencesTracking.java index d213e8a2417a..1830dd4d58de 100644 --- a/tracking/RetainReferencesTracking.java +++ b/tracking/RetainReferencesTracking.java @@ -5,13 +5,13 @@ import org.jetbrains.annotations.NotNull; import java.util.List; -public class RetainReferencesTracking extends InstanceTrackingStrategy { - private final IdBasedTracking myIdTracker; +class RetainReferencesTracking extends InstanceTrackingStrategy { + private final IdentityBasedTracking myIdTracker; @SuppressWarnings("unused") private List myHardRefs; - public RetainReferencesTracking(@NotNull List initialInstances) { + RetainReferencesTracking(@NotNull List initialInstances) { myHardRefs = initialInstances; - myIdTracker = new IdBasedTracking(initialInstances); + myIdTracker = new IdentityBasedTracking(initialInstances); } @NotNull From 86e3ba494cc24aa51c7545a3eff29f83862761d0 Mon Sep 17 00:00:00 2001 From: Vitaliy Bibaev Date: Wed, 24 Aug 2016 19:58:55 +0300 Subject: [PATCH 060/151] Change InstancesWindow structure, use InstanceProvider and class name instead of ReferenceType --- action/ShowInstancesByClassAction.java | 4 ++- .../ShowInstancesFromClassesViewAction.java | 2 +- utils/InstancesProvider.java | 11 +++++++ view/ClassesFilteredView.java | 2 +- view/ClassesTable.java | 28 +++++++++------- view/ExpressionEditorWithHistory.java | 7 ++-- view/InstancesWindow.java | 32 ++++++++++--------- 7 files changed, 53 insertions(+), 33 deletions(-) create mode 100644 utils/InstancesProvider.java diff --git a/action/ShowInstancesByClassAction.java b/action/ShowInstancesByClassAction.java index 7bc2b3c19a8f..451af6f9b1f4 100644 --- a/action/ShowInstancesByClassAction.java +++ b/action/ShowInstancesByClassAction.java @@ -12,6 +12,7 @@ import com.intellij.xdebugger.frame.XValue; import com.intellij.xdebugger.impl.ui.tree.actions.XDebuggerTreeActionBase; import com.intellij.xdebugger.impl.ui.tree.nodes.XValueNodeImpl; import com.sun.jdi.ObjectReference; +import com.sun.jdi.ReferenceType; import com.sun.jdi.Value; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; @@ -36,7 +37,8 @@ public class ShowInstancesByClassAction extends XDebuggerTreeActionBase { XDebugSession debugSession = XDebuggerManager.getInstance(project).getCurrentSession(); ObjectReference ref = getObjectReference(node); if(debugSession != null && ref != null){ - new InstancesWindow(debugSession, ref.referenceType()).show(); + ReferenceType referenceType = ref.referenceType(); + new InstancesWindow(debugSession, referenceType::instances, referenceType.name()).show(); } } } diff --git a/action/ShowInstancesFromClassesViewAction.java b/action/ShowInstancesFromClassesViewAction.java index ebbda9b9435a..a03c5ec1f288 100644 --- a/action/ShowInstancesFromClassesViewAction.java +++ b/action/ShowInstancesFromClassesViewAction.java @@ -16,7 +16,7 @@ public class ShowInstancesFromClassesViewAction extends ClassesActionBase { XDebugSession debugSession = getDebugSession(e); ReferenceType selectedClass = getSelectedClass(e); if (debugSession != null && selectedClass != null) { - new InstancesWindow(debugSession, selectedClass).show(); + new InstancesWindow(debugSession, selectedClass::instances, selectedClass.name()).show(); } } } diff --git a/utils/InstancesProvider.java b/utils/InstancesProvider.java new file mode 100644 index 000000000000..1a717474b1b8 --- /dev/null +++ b/utils/InstancesProvider.java @@ -0,0 +1,11 @@ +package org.jetbrains.debugger.memory.utils; + +import com.sun.jdi.ObjectReference; +import org.jetbrains.annotations.NotNull; + +import java.util.List; + +public interface InstancesProvider { + @NotNull + List getInstances(int limit); +} diff --git a/view/ClassesFilteredView.java b/view/ClassesFilteredView.java index e32f299b437f..820405f3449f 100644 --- a/view/ClassesFilteredView.java +++ b/view/ClassesFilteredView.java @@ -166,7 +166,7 @@ public class ClassesFilteredView extends BorderLayoutPanel implements Disposable private void handleClassSelection(@Nullable ReferenceType ref) { if (ref != null && myDebugSession.isSuspended()) { - new InstancesWindow(myDebugSession, ref).show(); + new InstancesWindow(myDebugSession, ref::instances, ref.name()).show(); } } diff --git a/view/ClassesTable.java b/view/ClassesTable.java index 830803917450..249d4c64106c 100644 --- a/view/ClassesTable.java +++ b/view/ClassesTable.java @@ -12,7 +12,6 @@ import com.intellij.ui.SimpleTextAttributes; import com.intellij.ui.speedSearch.SpeedSearchUtil; import com.intellij.ui.table.JBTable; import com.intellij.util.containers.FList; -import com.intellij.util.containers.HashMap; import com.intellij.util.ui.JBDimension; import com.intellij.util.ui.JBUI; import com.intellij.util.ui.UIUtil; @@ -46,14 +45,14 @@ public class ClassesTable extends JBTable implements DataProvider, Disposable { private final DiffViewTableModel myModel = new DiffViewTableModel(); private final UnknownDiffValue myUnknownValue = new UnknownDiffValue(); private final XDebugSession myDebugSession; - private final Map myTrackedClasses; + private final Map myCounts = new ConcurrentHashMap<>(); + private final InstancesTracker myInstancesTracker; private boolean myOnlyWithDiff; - private boolean myOnlyWithInstances; + private boolean myOnlyWithInstances; private MinusculeMatcher myMatcher = NameUtil.buildMatcher("*").build(); private String myFilteringPattern = ""; - private ConcurrentHashMap myCounts = new ConcurrentHashMap<>(); private volatile List myElems = Collections.unmodifiableList(new ArrayList<>()); @@ -63,6 +62,7 @@ public class ClassesTable extends JBTable implements DataProvider, Disposable { myDebugSession = session; myOnlyWithDiff = onlyWithDiff; myOnlyWithInstances = onlyWithInstances; + myInstancesTracker = InstancesTracker.getInstance(myDebugSession.getProject()); getEmptyText().setText(EMPTY_TABLE_CONTENT); @@ -70,17 +70,14 @@ public class ClassesTable extends JBTable implements DataProvider, Disposable { tracker.addTrackerListener(new InstancesTrackerListener() { @Override public void classChanged(@NotNull String name, @NotNull InstancesTracker.TrackingType type) { - myTrackedClasses.put(name, type); getRowSorter().allRowsChanged(); } @Override public void classRemoved(@NotNull String name) { - myTrackedClasses.remove(name); getRowSorter().allRowsChanged(); } }, this); - myTrackedClasses = new HashMap<>(tracker.getTrackingClasses()); TableColumn classesColumn = getColumnModel().getColumn(DiffViewTableModel.CLASSNAME_COLUMN_INDEX); TableColumn countColumn = getColumnModel().getColumn(DiffViewTableModel.COUNT_COLUMN_INDEX); @@ -209,6 +206,15 @@ public class ClassesTable extends JBTable implements DataProvider, Disposable { public void dispose() { } + private boolean needHighlightAsTracked(boolean isSelected, int row) { + if(isSelected){ + return false; + } + + ReferenceType ref = (ReferenceType) getValueAt(row, DiffViewTableModel.CLASSNAME_COLUMN_INDEX); + return myInstancesTracker.isTracked(ref.name()); + } + private class DiffViewTableModel extends AbstractTableModelWithColumns { private final static int CLASSNAME_COLUMN_INDEX = 0; private final static int COUNT_COLUMN_INDEX = 1; @@ -327,15 +333,15 @@ public class ClassesTable extends JBTable implements DataProvider, Disposable { private class MyNumberTableCellRenderer extends ColoredTableCellRenderer { @Override protected void customizeCellRenderer(JTable table, @Nullable Object value, - boolean selected, boolean hasFocus, int row, int column) { + boolean isSelected, boolean hasFocus, int row, int column) { if (value == null) { return; } - String className = ((ReferenceType) getValueAt(row, DiffViewTableModel.CLASSNAME_COLUMN_INDEX)).name(); - if (!selected && myTrackedClasses.containsKey(className)) { + if (needHighlightAsTracked(isSelected, row)) { setBackground(UIUtil.toAlpha(JBColor.yellow, 20)); } + long val = (long) value; setTextAlign(SwingConstants.RIGHT); if (column == DiffViewTableModel.COUNT_COLUMN_INDEX) { @@ -360,7 +366,7 @@ public class ClassesTable extends JBTable implements DataProvider, Disposable { protected void customizeCellRenderer(JTable table, @Nullable Object value, boolean isSelected, boolean hasFocus, int row, int column) { String presentation = value == null ? "null" : ((ReferenceType) value).name(); - if (value != null && !isSelected && myTrackedClasses.containsKey(presentation)) { + if (needHighlightAsTracked(isSelected, row)) { setBackground(UIUtil.toAlpha(JBColor.yellow, 20)); } diff --git a/view/ExpressionEditorWithHistory.java b/view/ExpressionEditorWithHistory.java index ab34a0972d00..d11e0e100873 100644 --- a/view/ExpressionEditorWithHistory.java +++ b/view/ExpressionEditorWithHistory.java @@ -20,7 +20,6 @@ import com.intellij.xdebugger.evaluation.XDebuggerEditorsProvider; import com.intellij.xdebugger.impl.XSourcePositionImpl; import com.intellij.xdebugger.impl.breakpoints.XExpressionImpl; import com.intellij.xdebugger.impl.ui.XDebuggerExpressionEditor; -import com.sun.jdi.ReferenceType; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; @@ -31,10 +30,10 @@ class ExpressionEditorWithHistory extends XDebuggerExpressionEditor { private static final String HISTORY_ID_PREFIX = "filtering"; ExpressionEditorWithHistory(final @NotNull Project project, - final @NotNull ReferenceType referenceType, + final @NotNull String className, final @NotNull XDebuggerEditorsProvider debuggerEditorsProvider, final @Nullable Disposable parentDisposable) { - super(project, debuggerEditorsProvider, HISTORY_ID_PREFIX + referenceType.name(), null, + super(project, debuggerEditorsProvider, HISTORY_ID_PREFIX + className, null, XExpressionImpl.EMPTY_EXPRESSION, false, true, true); new AnAction("InstancesWindow.ShowHistory") { @@ -53,7 +52,7 @@ class ExpressionEditorWithHistory extends XDebuggerExpressionEditor { @Override protected Void doInBackground() throws Exception { ApplicationManager.getApplication().runReadAction(() -> { - final PsiClass psiClass = DebuggerUtils.findClass(referenceType.name(), + final PsiClass psiClass = DebuggerUtils.findClass(className, project, GlobalSearchScope.allScope(project)); XSourcePositionImpl position = XSourcePositionImpl.createByElement(psiClass); SwingUtilities.invokeLater(() -> setSourcePosition(position)); diff --git a/view/InstancesWindow.java b/view/InstancesWindow.java index b7eb2c39a70b..bbf13da7a553 100644 --- a/view/InstancesWindow.java +++ b/view/InstancesWindow.java @@ -50,14 +50,10 @@ import com.intellij.xdebugger.impl.ui.tree.nodes.XDebuggerTreeNode; import com.intellij.xdebugger.impl.ui.tree.nodes.XValueNodeImpl; import com.sun.jdi.BooleanValue; import com.sun.jdi.ObjectReference; -import com.sun.jdi.ReferenceType; import com.sun.jdi.Value; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; -import org.jetbrains.debugger.memory.utils.AndroidUtil; -import org.jetbrains.debugger.memory.utils.ErrorsValueGroup; -import org.jetbrains.debugger.memory.utils.InstanceJavaValue; -import org.jetbrains.debugger.memory.utils.InstanceValueDescriptor; +import org.jetbrains.debugger.memory.utils.*; import org.jetbrains.java.debugger.JavaDebuggerEditorsProvider; import javax.swing.*; @@ -77,16 +73,20 @@ public class InstancesWindow extends DialogWrapper { private final Project myProject; private final XDebugSession myDebugSession; - private final ReferenceType myReferenceType; + private final InstancesProvider myInstancesProvider; + private final String myClassName; private MyInstancesView myInstancesView; public InstancesWindow(@NotNull XDebugSession session, - @NotNull ReferenceType referenceType) { + @NotNull InstancesProvider provider, + @NotNull String className) { super(session.getProject(), false); myProject = session.getProject(); myDebugSession = session; - myReferenceType = referenceType; + myInstancesProvider = provider; + myClassName = className; + addWarningMessage(null); myDebugSession.addSessionListener(new XDebugSessionListener() { @Override @@ -106,7 +106,7 @@ public class InstancesWindow extends DialogWrapper { private void addWarningMessage(@Nullable String message) { String warning = message == null ? "" : String.format(". Warning: %s", message); - setTitle(String.format("Instances of %s%s", myReferenceType.name(), warning)); + setTitle(String.format("Instances of %s%s", myClassName, warning)); } @NotNull @@ -164,6 +164,8 @@ public class InstancesWindow extends DialogWrapper { private final AnActionListener.Adapter myActionListener = new MyActionListener(); private final Object myFilteringTaskLock = new Object(); + private boolean myIsAndroidVM = false; + private volatile MyFilteringWorker myFilteringTask = null; MyInstancesView() { @@ -174,7 +176,7 @@ public class InstancesWindow extends DialogWrapper { myDebugSession.addSessionListener(myDebugSessionListener, InstancesWindow.this.myDisposable); JavaDebuggerEditorsProvider editorsProvider = new JavaDebuggerEditorsProvider(); - myFilterConditionEditor = new ExpressionEditorWithHistory(myProject, myReferenceType, + myFilterConditionEditor = new ExpressionEditorWithHistory(myProject, myClassName, editorsProvider, InstancesWindow.this.myDisposable); myFilterButton.setBorder(BorderFactory.createEmptyBorder()); @@ -247,16 +249,16 @@ public class InstancesWindow extends DialogWrapper { @Override public void threadAction(@NotNull SuspendContextImpl suspendContext) { - int limit = AndroidUtil.isAndroidVM(myReferenceType.virtualMachine()) + myIsAndroidVM = AndroidUtil.isAndroidVM(debugProcess.getVirtualMachineProxy().getVirtualMachine()); + int limit = myIsAndroidVM ? AndroidUtil.ANDROID_INSTANCES_LIMIT : DEFAULT_INSTANCES_COUNT; - List instances = myReferenceType.instances(limit); + List instances = myInstancesProvider.getInstances(limit); EvaluationContextImpl evaluationContext = debugProcess .getDebuggerContext().createEvaluationContext(); - if (AndroidUtil.isAndroidVM(myReferenceType.virtualMachine()) - && instances.size() == AndroidUtil.ANDROID_INSTANCES_LIMIT) { + if (myIsAndroidVM && instances.size() == AndroidUtil.ANDROID_INSTANCES_LIMIT) { addWarningMessage("Not all instances will be shown (android restriction)"); } @@ -400,7 +402,7 @@ public class InstancesWindow extends DialogWrapper { @Override @NotNull protected String getClassName() { - return myReferenceType.name(); + return myClassName; } ExpressionEvaluator getEvaluator() throws EvaluateException { From 6a9e73f17d1258188291f8f4b548f32c4b950272 Mon Sep 17 00:00:00 2001 From: Vitaliy Bibaev Date: Wed, 24 Aug 2016 20:00:06 +0300 Subject: [PATCH 061/151] Add "Show New Instances" action to context menu of Classes view --- action/ShowNewInstancesAction.java | 21 +++++++++++++++++++++ 1 file changed, 21 insertions(+) create mode 100644 action/ShowNewInstancesAction.java diff --git a/action/ShowNewInstancesAction.java b/action/ShowNewInstancesAction.java new file mode 100644 index 000000000000..c4cbbe8a283e --- /dev/null +++ b/action/ShowNewInstancesAction.java @@ -0,0 +1,21 @@ +package org.jetbrains.debugger.memory.action; + +import com.intellij.openapi.actionSystem.AnActionEvent; +import com.intellij.openapi.project.Project; +import com.sun.jdi.ReferenceType; +import org.jetbrains.debugger.memory.component.InstancesTracker; + +public class ShowNewInstancesAction extends ClassesActionBase { + @Override + protected boolean isEnabled(AnActionEvent e) { + Project project = e.getProject(); + ReferenceType selectedClass = getSelectedClass(e); + return super.isEnabled(e) && selectedClass != null && + project != null && InstancesTracker.getInstance(project).isTracked(selectedClass.name()); + } + + @Override + protected void perform(AnActionEvent e) { + + } +} From 8ff814ac61dc88278e1864f8b5dffeb1a80fd370 Mon Sep 17 00:00:00 2001 From: Vitaliy Bibaev Date: Wed, 24 Aug 2016 20:44:42 +0300 Subject: [PATCH 062/151] Add basic implementation for new instances tracking --- action/ShowNewInstancesAction.java | 11 +++++++++- tracking/InstanceTrackingStrategy.java | 6 +++--- view/ClassesFilteredView.java | 28 +++++++++++++++++++++++++- view/ClassesTable.java | 22 +++++++++++++++++++- 4 files changed, 61 insertions(+), 6 deletions(-) diff --git a/action/ShowNewInstancesAction.java b/action/ShowNewInstancesAction.java index c4cbbe8a283e..7261022a63ca 100644 --- a/action/ShowNewInstancesAction.java +++ b/action/ShowNewInstancesAction.java @@ -2,8 +2,12 @@ package org.jetbrains.debugger.memory.action; import com.intellij.openapi.actionSystem.AnActionEvent; import com.intellij.openapi.project.Project; +import com.intellij.xdebugger.XDebugSession; import com.sun.jdi.ReferenceType; import org.jetbrains.debugger.memory.component.InstancesTracker; +import org.jetbrains.debugger.memory.utils.InstancesProvider; +import org.jetbrains.debugger.memory.view.ClassesTable; +import org.jetbrains.debugger.memory.view.InstancesWindow; public class ShowNewInstancesAction extends ClassesActionBase { @Override @@ -16,6 +20,11 @@ public class ShowNewInstancesAction extends ClassesActionBase { @Override protected void perform(AnActionEvent e) { - + XDebugSession session = getDebugSession(e); + ReferenceType selectedClass = getSelectedClass(e); + InstancesProvider provider = e.getData(ClassesTable.NEW_INSTANCES_PROVIDER_KEY); + if(selectedClass != null && provider != null && session != null) { + new InstancesWindow(session, provider, selectedClass.name()).show(); + } } } diff --git a/tracking/InstanceTrackingStrategy.java b/tracking/InstanceTrackingStrategy.java index 558ed9dc3943..3a37afc3cee6 100644 --- a/tracking/InstanceTrackingStrategy.java +++ b/tracking/InstanceTrackingStrategy.java @@ -9,7 +9,7 @@ import java.util.List; import static org.jetbrains.debugger.memory.component.InstancesTracker.*; -abstract class InstanceTrackingStrategy { +public abstract class InstanceTrackingStrategy { @NotNull private List myNewInstances = new ArrayList<>(); @@ -27,11 +27,11 @@ abstract class InstanceTrackingStrategy { } @NotNull - final List getNewInstances() { + public final List getNewInstances() { return Collections.unmodifiableList(myNewInstances); } - final void update(@NotNull List references) { + public final void update(@NotNull List references) { myNewInstances = updateImpl(references); } diff --git a/view/ClassesFilteredView.java b/view/ClassesFilteredView.java index 820405f3449f..3be9ce3ba2b2 100644 --- a/view/ClassesFilteredView.java +++ b/view/ClassesFilteredView.java @@ -18,13 +18,16 @@ import com.intellij.util.SmartList; import com.intellij.util.ui.components.BorderLayoutPanel; import com.intellij.xdebugger.XDebugSession; import com.intellij.xdebugger.XDebugSessionListener; +import com.sun.jdi.ObjectReference; import com.sun.jdi.ReferenceType; import com.sun.jdi.VirtualMachine; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; +import org.jetbrains.debugger.memory.component.InstancesTracker; import org.jetbrains.debugger.memory.component.MemoryViewManager; import org.jetbrains.debugger.memory.component.MemoryViewManagerState; import org.jetbrains.debugger.memory.event.MemoryViewManagerListener; +import org.jetbrains.debugger.memory.tracking.InstanceTrackingStrategy; import org.jetbrains.debugger.memory.utils.AndroidUtil; import org.jetbrains.debugger.memory.utils.KeyboardUtils; import org.jetbrains.debugger.memory.utils.SingleAlarmWithMutableDelay; @@ -37,6 +40,8 @@ import java.awt.event.KeyEvent; import java.awt.event.MouseEvent; import java.util.Arrays; import java.util.List; +import java.util.Map; +import java.util.concurrent.ConcurrentHashMap; import java.util.concurrent.TimeUnit; import java.util.stream.IntStream; @@ -52,10 +57,14 @@ public class ClassesFilteredView extends BorderLayoutPanel implements Disposable private final SearchTextField myFilterTextField = new SearchTextField(false); private final ClassesTable myTable; + private final InstancesTracker myInstancesTracker; + private final Map myTrackedClasses = new ConcurrentHashMap<>(); + private volatile SuspendContextImpl myLastSuspendContext; private volatile boolean myNeedReloadClasses = false; + public ClassesFilteredView(@NotNull XDebugSession debugSession) { super(); @@ -63,11 +72,12 @@ public class ClassesFilteredView extends BorderLayoutPanel implements Disposable myDebugSession = debugSession; myDebugProcess = DebuggerManager.getInstance(myProject) .getDebugProcess(myDebugSession.getDebugProcess().getProcessHandler()); + myInstancesTracker = InstancesTracker.getInstance(myProject); MemoryViewManagerState memoryViewManagerState = MemoryViewManager.getInstance().getState(); myTable = new ClassesTable(myDebugSession, memoryViewManagerState.isShowWithDiffOnly, - memoryViewManagerState.isShowWithInstancesOnly); + memoryViewManagerState.isShowWithInstancesOnly, this); Disposer.register(this, myTable); myTable.addKeyListener(new KeyAdapter() { @@ -152,6 +162,11 @@ public class ClassesFilteredView extends BorderLayoutPanel implements Disposable addToCenter(scroll); } + @Nullable + InstanceTrackingStrategy getStrategy(@NotNull ReferenceType ref) { + return myTrackedClasses.getOrDefault(ref, null); + } + public void setNeedReloadClasses(boolean value) { if (myNeedReloadClasses != value) { myNeedReloadClasses = value; @@ -203,6 +218,17 @@ public class ClassesFilteredView extends BorderLayoutPanel implements Disposable @Override public void contextAction(@NotNull SuspendContextImpl suspendContext) throws Exception { final List classes = myDebugProcess.getVirtualMachineProxy().allClasses(); + for(ReferenceType ref : classes) { + InstancesTracker.TrackingType type = myInstancesTracker.getTrackingType(ref.name()); + if(type != null && !myTrackedClasses.containsKey(ref)) { + List instances = ref.instances(0); + myTrackedClasses.put(ref, InstanceTrackingStrategy.create(type, instances)); + } + } + + for(Map.Entry entry : myTrackedClasses.entrySet()) { + entry.getValue().update(entry.getKey().instances(0)); + } if (classes.isEmpty()) { return; diff --git a/view/ClassesTable.java b/view/ClassesTable.java index 249d4c64106c..d5ee323f2ffc 100644 --- a/view/ClassesTable.java +++ b/view/ClassesTable.java @@ -16,14 +16,17 @@ import com.intellij.util.ui.JBDimension; import com.intellij.util.ui.JBUI; import com.intellij.util.ui.UIUtil; import com.intellij.xdebugger.XDebugSession; +import com.sun.jdi.ObjectReference; import com.sun.jdi.ReferenceType; import org.jetbrains.annotations.NonNls; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; import org.jetbrains.debugger.memory.component.InstancesTracker; import org.jetbrains.debugger.memory.event.InstancesTrackerListener; +import org.jetbrains.debugger.memory.tracking.InstanceTrackingStrategy; import org.jetbrains.debugger.memory.utils.AbstractTableColumnDescriptor; import org.jetbrains.debugger.memory.utils.AbstractTableModelWithColumns; +import org.jetbrains.debugger.memory.utils.InstancesProvider; import javax.swing.*; import javax.swing.table.TableColumn; @@ -34,6 +37,8 @@ import java.util.concurrent.ConcurrentHashMap; public class ClassesTable extends JBTable implements DataProvider, Disposable { public static final DataKey SELECTED_CLASS_KEY = DataKey.create("ClassesTable.SelectedClass"); public static final DataKey DEBUG_SESSION_KEY = DataKey.create("ClassesTable.DebugSession"); + public static final DataKey NEW_INSTANCES_PROVIDER_KEY = + DataKey.create("ClassesTable.NewInstances"); private static final int CLASSES_COLUMN_PREFERRED_WIDTH = 250; private static final int COUNT_COLUMN_MIN_WIDTH = 80; private static final int COUNT_COLUMN_MAX_WIDTH = 100; @@ -47,6 +52,7 @@ public class ClassesTable extends JBTable implements DataProvider, Disposable { private final XDebugSession myDebugSession; private final Map myCounts = new ConcurrentHashMap<>(); private final InstancesTracker myInstancesTracker; + private final ClassesFilteredView myParent; private boolean myOnlyWithDiff; @@ -56,13 +62,16 @@ public class ClassesTable extends JBTable implements DataProvider, Disposable { private volatile List myElems = Collections.unmodifiableList(new ArrayList<>()); - ClassesTable(@NotNull XDebugSession session, boolean onlyWithDiff, boolean onlyWithInstances) { + // TODO: parent must be unknown in this context + ClassesTable(@NotNull XDebugSession session, boolean onlyWithDiff, boolean onlyWithInstances, + @NotNull ClassesFilteredView parent) { setModel(myModel); myDebugSession = session; myOnlyWithDiff = onlyWithDiff; myOnlyWithInstances = onlyWithInstances; myInstancesTracker = InstancesTracker.getInstance(myDebugSession.getProject()); + myParent = parent; getEmptyText().setText(EMPTY_TABLE_CONTENT); @@ -198,6 +207,17 @@ public class ClassesTable extends JBTable implements DataProvider, Disposable { if (DEBUG_SESSION_KEY.is(dataId)) { return myDebugSession; } + if(NEW_INSTANCES_PROVIDER_KEY.is(dataId)) { + ReferenceType selectedClass = getSelectedClass(); + if(selectedClass != null) { + InstanceTrackingStrategy strategy = myParent.getStrategy(selectedClass); + if(strategy != null) { + List newInstances = strategy.getNewInstances(); + return (InstancesProvider) limit -> newInstances; + } + + } + } return null; } From b380268d909819fc9554a272f4248e1edb585c8d Mon Sep 17 00:00:00 2001 From: Vitaliy Bibaev Date: Fri, 26 Aug 2016 13:03:08 +0300 Subject: [PATCH 063/151] Add ability to navigate in classes table when writing filter expression --- view/ClassesFilteredView.java | 49 +++++++++++++++++++++++++++++------ 1 file changed, 41 insertions(+), 8 deletions(-) diff --git a/view/ClassesFilteredView.java b/view/ClassesFilteredView.java index e6736331928c..d8d70341c13c 100644 --- a/view/ClassesFilteredView.java +++ b/view/ClassesFilteredView.java @@ -48,7 +48,7 @@ public class ClassesFilteredView extends BorderLayoutPanel { private final DebugProcess myDebugProcess; private final SingleAlarmWithMutableDelay mySingleAlarm; - private final SearchTextField myFilterTextField = new SearchTextField(false); + private final SearchTextField myFilterTextField = new FilterTextField(); private final ClassesTable myTable; private volatile SuspendContextImpl myLastSuspendContext; @@ -81,6 +81,31 @@ public class ClassesFilteredView extends BorderLayoutPanel { } }); + myFilterTextField.addKeyboardListener(new KeyAdapter() { + @Override + public void keyPressed(KeyEvent e) { + dispatch(e); + } + + @Override + public void keyReleased(KeyEvent e) { + dispatch(e); + } + + private void dispatch(KeyEvent e) { + if (KeyboardUtils.isArrowKey(e.getKeyCode())) { + myTable.dispatchEvent(e); + } + } + }); + + myFilterTextField.addDocumentListener(new DocumentAdapter() { + @Override + protected void textChanged(DocumentEvent e) { + myTable.setFilterPattern(myFilterTextField.getText()); + } + }); + new DoubleClickListener() { @Override protected boolean onDoubleClick(MouseEvent event) { @@ -117,13 +142,6 @@ public class ClassesFilteredView extends BorderLayoutPanel { } }); - myFilterTextField.addDocumentListener(new DocumentAdapter() { - @Override - protected void textChanged(DocumentEvent e) { - myTable.setFilterPattern(myFilterTextField.getText()); - } - }); - mySingleAlarm = new SingleAlarmWithMutableDelay(() -> { myLastSuspendContext = getSuspendContext(); if (myLastSuspendContext != null) { @@ -242,4 +260,19 @@ public class ClassesFilteredView extends BorderLayoutPanel { return ClassesFilteredView.this.getSuspendContext() == getSuspendContext(); } } + + private class FilterTextField extends SearchTextField { + FilterTextField() { + super(false); + } + + @Override + protected void showPopup() { + } + + @Override + protected boolean hasIconsOutsideOfTextField() { + return false; + } + } } From 0fb33f883cddd1c770fa97368e3844edbc4bdb51 Mon Sep 17 00:00:00 2001 From: Vitaliy Bibaev Date: Fri, 26 Aug 2016 13:31:27 +0300 Subject: [PATCH 064/151] Support backspace and uppercase characters in table with classes --- utils/KeyboardUtils.java | 4 ++++ view/ClassesFilteredView.java | 9 ++++++--- 2 files changed, 10 insertions(+), 3 deletions(-) diff --git a/utils/KeyboardUtils.java b/utils/KeyboardUtils.java index d7818f4bccf5..367c8f567261 100644 --- a/utils/KeyboardUtils.java +++ b/utils/KeyboardUtils.java @@ -12,6 +12,10 @@ public class KeyboardUtils { keyCode == KeyEvent.VK_DOWN || keyCode == KeyEvent.VK_RIGHT; } + public static boolean isBackSpace(int keyCode) { + return keyCode == KeyEvent.VK_BACK_SPACE; + } + public static boolean isCharacter(int keyCode) { return KeyEvent.getKeyText(keyCode).length() == 1; } diff --git a/view/ClassesFilteredView.java b/view/ClassesFilteredView.java index d8d70341c13c..f39b26261bbd 100644 --- a/view/ClassesFilteredView.java +++ b/view/ClassesFilteredView.java @@ -73,10 +73,13 @@ public class ClassesFilteredView extends BorderLayoutPanel { int keyCode = e.getKeyCode(); if (KeyboardUtils.isEnterKey(keyCode)) { handleClassSelection(myTable.getSelectedClass()); - } else if (!KeyboardUtils.isArrowKey(keyCode) && KeyboardUtils.isCharacter(keyCode)) { - SwingUtilities.invokeLater(myFilterTextField::requestFocusInWindow); + } else if (KeyboardUtils.isCharacter(keyCode) || KeyboardUtils.isBackSpace(keyCode)) { String text = myFilterTextField.getText(); - myFilterTextField.setText(text + KeyEvent.getKeyText(keyCode).toLowerCase()); + String newText = KeyboardUtils.isBackSpace(keyCode) + ? text.substring(0, text.length() - 1) + : text + e.getKeyChar(); + myFilterTextField.setText(newText); + FocusManager.getCurrentManager().focusNextComponent(myFilterTextField); } } }); From 7e706e667341546454daf7165d73dbe955f5938d Mon Sep 17 00:00:00 2001 From: Vitaliy Bibaev Date: Fri, 26 Aug 2016 13:36:29 +0300 Subject: [PATCH 065/151] Dispatch to classes table only up/down/enter keys for keyboard events --- utils/KeyboardUtils.java | 5 ++--- view/ClassesFilteredView.java | 2 +- 2 files changed, 3 insertions(+), 4 deletions(-) diff --git a/utils/KeyboardUtils.java b/utils/KeyboardUtils.java index 367c8f567261..57899d96122e 100644 --- a/utils/KeyboardUtils.java +++ b/utils/KeyboardUtils.java @@ -7,9 +7,8 @@ public class KeyboardUtils { return keyCode == KeyEvent.VK_ENTER; } - public static boolean isArrowKey(int keyCode) { - return keyCode == KeyEvent.VK_UP || keyCode == KeyEvent.VK_LEFT || - keyCode == KeyEvent.VK_DOWN || keyCode == KeyEvent.VK_RIGHT; + public static boolean isUpDownKey(int keyCode) { + return keyCode == KeyEvent.VK_UP || keyCode == KeyEvent.VK_DOWN; } public static boolean isBackSpace(int keyCode) { diff --git a/view/ClassesFilteredView.java b/view/ClassesFilteredView.java index f39b26261bbd..193bd4160f77 100644 --- a/view/ClassesFilteredView.java +++ b/view/ClassesFilteredView.java @@ -96,7 +96,7 @@ public class ClassesFilteredView extends BorderLayoutPanel { } private void dispatch(KeyEvent e) { - if (KeyboardUtils.isArrowKey(e.getKeyCode())) { + if (KeyboardUtils.isUpDownKey(e.getKeyCode()) || KeyboardUtils.isEnterKey(e.getKeyCode())) { myTable.dispatchEvent(e); } } From 27cba431be75e4f14ec0abf358c172430e87ecf1 Mon Sep 17 00:00:00 2001 From: Vitaliy Bibaev Date: Fri, 26 Aug 2016 15:56:14 +0300 Subject: [PATCH 066/151] Implement tracking for new instances trough hash codes on target vm --- tracking/HashBasedTracking.java | 78 +++++++++++++++++++++++--- tracking/IdentityBasedTracking.java | 4 +- tracking/InstanceTrackingStrategy.java | 16 ++++-- tracking/RetainReferencesTracking.java | 6 +- view/ClassesFilteredView.java | 4 +- 5 files changed, 91 insertions(+), 17 deletions(-) diff --git a/tracking/HashBasedTracking.java b/tracking/HashBasedTracking.java index b3c02ce934d1..4fa1c6863cbf 100644 --- a/tracking/HashBasedTracking.java +++ b/tracking/HashBasedTracking.java @@ -1,20 +1,84 @@ package org.jetbrains.debugger.memory.tracking; -import com.sun.jdi.ObjectReference; -import org.apache.commons.lang.NotImplementedException; +import com.intellij.debugger.engine.SuspendContextImpl; +import com.intellij.debugger.engine.jdi.ThreadReferenceProxy; +import com.sun.jdi.*; import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; -import java.util.List; +import java.util.*; +import java.util.stream.Collectors; class HashBasedTracking extends InstanceTrackingStrategy { + private final ReferenceType myClassType; + private Set myHashesSet; - HashBasedTracking(@NotNull List initialInstances) { - + HashBasedTracking(@NotNull SuspendContextImpl suspendContext, + @NotNull ReferenceType classType, + @NotNull List initialInstances) { + myClassType = classType; + myHashesSet = toSetOfHashes(evalHashCodes(suspendContext, initialInstances)); } @NotNull @Override - protected List updateImpl(@NotNull List references) { - throw new NotImplementedException(); + protected List updateImpl(@NotNull SuspendContextImpl suspendContext, + @NotNull List references) { + Map> ref2hash = evalHashCodes(suspendContext, references); + List newInstances = new ArrayList<>(); + + for(ObjectReference ref : references) { + Optional hash = ref2hash.get(ref); + if(hash.isPresent() && myHashesSet.contains(hash.get())) { + newInstances.add(ref); + } + } + + return newInstances; + } + + @NotNull + private Map> evalHashCodes(@NotNull SuspendContextImpl suspendContext, + @NotNull List references) { + Method hashCodeMethod = getHashCodeMethod(myClassType); + Map> result = new HashMap<>(); + ThreadReferenceProxy threadProxy = suspendContext.getThread(); + ThreadReference thread = threadProxy != null ? threadProxy.getThreadReference() : null; + if(thread == null) { + return result; + } + + for (ObjectReference ref : references) { + Optional hash = Optional.empty(); + try { + Value value = ref.invokeMethod(thread, hashCodeMethod, Collections.emptyList(), 0); + hash = Optional.of(((IntegerValue)value).value()); + } catch (InvalidTypeException | ClassNotLoadedException | + IncompatibleThreadStateException | InvocationException ignored) { + } + + result.put(ref, hash); + } + + return result; + } + + private Set toSetOfHashes(@NotNull Map> ref2hashCode) { + return ref2hashCode.values().stream() + .filter(Optional::isPresent) + .map(Optional::get) + .collect(Collectors.toCollection(TreeSet::new)); + } + + @Nullable + private Method getHashCodeMethod(@Nullable ReferenceType referenceType) { + if (referenceType == null) { + return null; + } + + return referenceType.methodsByName("hashCode").stream() + .filter(method -> method.argumentTypeNames().isEmpty()) + .findFirst() + .orElseGet(null); } } diff --git a/tracking/IdentityBasedTracking.java b/tracking/IdentityBasedTracking.java index 78ef6670576a..d3d84615a505 100644 --- a/tracking/IdentityBasedTracking.java +++ b/tracking/IdentityBasedTracking.java @@ -1,5 +1,6 @@ package org.jetbrains.debugger.memory.tracking; +import com.intellij.debugger.engine.SuspendContextImpl; import com.sun.jdi.ObjectReference; import org.jetbrains.annotations.NotNull; @@ -14,7 +15,8 @@ class IdentityBasedTracking extends InstanceTrackingStrategy { @NotNull @Override - protected List updateImpl(@NotNull List references) { + protected List updateImpl(@NotNull SuspendContextImpl suspendContext, + @NotNull List references) { List result = references.stream() .filter(reference -> reference.uniqueID() > myLastId) .collect(Collectors.toList()); diff --git a/tracking/InstanceTrackingStrategy.java b/tracking/InstanceTrackingStrategy.java index 3a37afc3cee6..86a1f4a4f2e2 100644 --- a/tracking/InstanceTrackingStrategy.java +++ b/tracking/InstanceTrackingStrategy.java @@ -1,6 +1,8 @@ package org.jetbrains.debugger.memory.tracking; +import com.intellij.debugger.engine.SuspendContextImpl; import com.sun.jdi.ObjectReference; +import com.sun.jdi.ReferenceType; import org.jetbrains.annotations.NotNull; import java.util.ArrayList; @@ -13,12 +15,15 @@ public abstract class InstanceTrackingStrategy { @NotNull private List myNewInstances = new ArrayList<>(); - public static InstanceTrackingStrategy create(@NotNull TrackingType type, @NotNull List init) { + public static InstanceTrackingStrategy create(@NotNull ReferenceType referenceType, + @NotNull SuspendContextImpl suspendContext, + @NotNull TrackingType type, + @NotNull List init) { switch (type) { case IDENTITY: return new IdentityBasedTracking(init); case HASH: - return new HashBasedTracking(init); + return new HashBasedTracking(suspendContext, referenceType, init); case RETAIN: return new RetainReferencesTracking(init); } @@ -31,10 +36,11 @@ public abstract class InstanceTrackingStrategy { return Collections.unmodifiableList(myNewInstances); } - public final void update(@NotNull List references) { - myNewInstances = updateImpl(references); + public final void update(@NotNull SuspendContextImpl suspendContext, @NotNull List references) { + myNewInstances = updateImpl(suspendContext, references); } @NotNull - protected abstract List updateImpl(@NotNull List references); + protected abstract List updateImpl(@NotNull SuspendContextImpl suspendContext, + @NotNull List references); } diff --git a/tracking/RetainReferencesTracking.java b/tracking/RetainReferencesTracking.java index 1830dd4d58de..f5a20d72ce63 100644 --- a/tracking/RetainReferencesTracking.java +++ b/tracking/RetainReferencesTracking.java @@ -1,5 +1,6 @@ package org.jetbrains.debugger.memory.tracking; +import com.intellij.debugger.engine.SuspendContextImpl; import com.sun.jdi.ObjectReference; import org.jetbrains.annotations.NotNull; @@ -16,8 +17,9 @@ class RetainReferencesTracking extends InstanceTrackingStrategy { @NotNull @Override - protected List updateImpl(@NotNull List references) { - myIdTracker.update(references); + protected List updateImpl(@NotNull SuspendContextImpl suspendContext, + @NotNull List references) { + myIdTracker.update(suspendContext, references); myHardRefs = references; return myIdTracker.getNewInstances(); } diff --git a/view/ClassesFilteredView.java b/view/ClassesFilteredView.java index 3be9ce3ba2b2..5bbdcc659169 100644 --- a/view/ClassesFilteredView.java +++ b/view/ClassesFilteredView.java @@ -222,12 +222,12 @@ public class ClassesFilteredView extends BorderLayoutPanel implements Disposable InstancesTracker.TrackingType type = myInstancesTracker.getTrackingType(ref.name()); if(type != null && !myTrackedClasses.containsKey(ref)) { List instances = ref.instances(0); - myTrackedClasses.put(ref, InstanceTrackingStrategy.create(type, instances)); + myTrackedClasses.put(ref, InstanceTrackingStrategy.create(ref, suspendContext, type, instances)); } } for(Map.Entry entry : myTrackedClasses.entrySet()) { - entry.getValue().update(entry.getKey().instances(0)); + entry.getValue().update(suspendContext, entry.getKey().instances(0)); } if (classes.isEmpty()) { From 51a0ddf223dfe783e6b81198d3dfb523fda266cd Mon Sep 17 00:00:00 2001 From: Vitaliy Bibaev Date: Fri, 26 Aug 2016 16:26:51 +0300 Subject: [PATCH 067/151] Fix bugs in hashCode-based instances tracker --- tracking/HashBasedTracking.java | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/tracking/HashBasedTracking.java b/tracking/HashBasedTracking.java index 4fa1c6863cbf..f21014e818fb 100644 --- a/tracking/HashBasedTracking.java +++ b/tracking/HashBasedTracking.java @@ -29,11 +29,12 @@ class HashBasedTracking extends InstanceTrackingStrategy { for(ObjectReference ref : references) { Optional hash = ref2hash.get(ref); - if(hash.isPresent() && myHashesSet.contains(hash.get())) { + if(hash.isPresent() && !myHashesSet.contains(hash.get())) { newInstances.add(ref); } } + myHashesSet = toSetOfHashes(ref2hash); return newInstances; } @@ -67,7 +68,7 @@ class HashBasedTracking extends InstanceTrackingStrategy { return ref2hashCode.values().stream() .filter(Optional::isPresent) .map(Optional::get) - .collect(Collectors.toCollection(TreeSet::new)); + .collect(Collectors.toCollection(HashSet::new)); } @Nullable From f932c38e1d2fc7d50945ff6ff29b56465fc76bc8 Mon Sep 17 00:00:00 2001 From: Vitaliy Bibaev Date: Fri, 26 Aug 2016 17:16:39 +0300 Subject: [PATCH 068/151] Add UI support for tracking of constructors calls --- action/tracking/HashBasedTrackingAction.java | 2 +- .../tracking/IdentityBasedTrackingAction.java | 6 +-- .../tracking/InstancesTrackingActionBase.java | 2 +- .../RetainReferenceBasedTrackingAction.java | 6 +-- .../tracking/TrackInstanceCreationAction.java | 12 ++++++ component/InstancesTracker.java | 13 +----- event/InstancesTrackerListener.java | 4 +- tracking/InstanceTrackingStrategy.java | 2 - tracking/TrackingType.java | 29 +++++++++++++ view/ClassesFilteredView.java | 17 +++++--- view/ClassesTable.java | 41 +++++++++++-------- 11 files changed, 87 insertions(+), 47 deletions(-) create mode 100644 action/tracking/TrackInstanceCreationAction.java create mode 100644 tracking/TrackingType.java diff --git a/action/tracking/HashBasedTrackingAction.java b/action/tracking/HashBasedTrackingAction.java index afac0f214808..8a8c29c2444d 100644 --- a/action/tracking/HashBasedTrackingAction.java +++ b/action/tracking/HashBasedTrackingAction.java @@ -1,7 +1,7 @@ package org.jetbrains.debugger.memory.action.tracking; import org.jetbrains.annotations.NotNull; -import org.jetbrains.debugger.memory.component.InstancesTracker.TrackingType; +import org.jetbrains.debugger.memory.tracking.TrackingType; public class HashBasedTrackingAction extends InstancesTrackingActionBase { @NotNull diff --git a/action/tracking/IdentityBasedTrackingAction.java b/action/tracking/IdentityBasedTrackingAction.java index 6b04df12181d..f43f65952cf1 100644 --- a/action/tracking/IdentityBasedTrackingAction.java +++ b/action/tracking/IdentityBasedTrackingAction.java @@ -1,12 +1,12 @@ package org.jetbrains.debugger.memory.action.tracking; import org.jetbrains.annotations.NotNull; -import org.jetbrains.debugger.memory.component.InstancesTracker; +import org.jetbrains.debugger.memory.tracking.TrackingType; public class IdentityBasedTrackingAction extends InstancesTrackingActionBase { @NotNull @Override - protected InstancesTracker.TrackingType getType() { - return InstancesTracker.TrackingType.IDENTITY; + protected TrackingType getType() { + return TrackingType.IDENTITY; } } diff --git a/action/tracking/InstancesTrackingActionBase.java b/action/tracking/InstancesTrackingActionBase.java index e7c904db694d..aacd946e7beb 100644 --- a/action/tracking/InstancesTrackingActionBase.java +++ b/action/tracking/InstancesTrackingActionBase.java @@ -9,7 +9,7 @@ import org.jetbrains.annotations.Nullable; import org.jetbrains.debugger.memory.component.InstancesTracker; import org.jetbrains.debugger.memory.view.ClassesTable; -import static org.jetbrains.debugger.memory.component.InstancesTracker.TrackingType; +import org.jetbrains.debugger.memory.tracking.TrackingType; abstract class InstancesTrackingActionBase extends ToggleAction { @Override diff --git a/action/tracking/RetainReferenceBasedTrackingAction.java b/action/tracking/RetainReferenceBasedTrackingAction.java index b0d118470227..bf86f08209f2 100644 --- a/action/tracking/RetainReferenceBasedTrackingAction.java +++ b/action/tracking/RetainReferenceBasedTrackingAction.java @@ -1,12 +1,12 @@ package org.jetbrains.debugger.memory.action.tracking; import org.jetbrains.annotations.NotNull; -import org.jetbrains.debugger.memory.component.InstancesTracker; +import org.jetbrains.debugger.memory.tracking.TrackingType; public class RetainReferenceBasedTrackingAction extends InstancesTrackingActionBase { @NotNull @Override - protected InstancesTracker.TrackingType getType() { - return InstancesTracker.TrackingType.RETAIN; + protected TrackingType getType() { + return TrackingType.RETAIN; } } diff --git a/action/tracking/TrackInstanceCreationAction.java b/action/tracking/TrackInstanceCreationAction.java new file mode 100644 index 000000000000..e8cfc3f9c9d8 --- /dev/null +++ b/action/tracking/TrackInstanceCreationAction.java @@ -0,0 +1,12 @@ +package org.jetbrains.debugger.memory.action.tracking; + +import org.jetbrains.annotations.NotNull; +import org.jetbrains.debugger.memory.tracking.TrackingType; + +public class TrackInstanceCreationAction extends InstancesTrackingActionBase{ + @NotNull + @Override + protected TrackingType getType() { + return TrackingType.CREATION; + } +} diff --git a/component/InstancesTracker.java b/component/InstancesTracker.java index 29594c125a78..77145325508f 100644 --- a/component/InstancesTracker.java +++ b/component/InstancesTracker.java @@ -4,14 +4,13 @@ import com.intellij.openapi.Disposable; import com.intellij.openapi.components.*; import com.intellij.openapi.project.Project; import com.intellij.util.EventDispatcher; -import com.intellij.util.containers.HashMap; import com.intellij.util.xmlb.annotations.AbstractCollection; import com.sun.jdi.ReferenceType; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; import org.jetbrains.debugger.memory.event.InstancesTrackerListener; +import org.jetbrains.debugger.memory.tracking.TrackingType; -import java.util.Collections; import java.util.Map; import java.util.concurrent.ConcurrentHashMap; @@ -30,10 +29,6 @@ public class InstancesTracker extends AbstractProjectComponent return project.getComponent(InstancesTracker.class); } - public enum TrackingType { - IDENTITY, HASH, RETAIN - } - public boolean isTracked(@NotNull String className) { return myState.classes.containsKey(className); } @@ -43,12 +38,6 @@ public class InstancesTracker extends AbstractProjectComponent return myState.classes.getOrDefault(className, null); } - - public Map getTrackingClasses() { - Map copy = new HashMap<>(myState.classes); - return Collections.unmodifiableMap(copy); - } - public void add(@NotNull ReferenceType ref, @NotNull TrackingType type) { String name = ref.name(); if(type.equals(myState.classes.getOrDefault(name, null))) { diff --git a/event/InstancesTrackerListener.java b/event/InstancesTrackerListener.java index ed03afbc4173..0e8924e17657 100644 --- a/event/InstancesTrackerListener.java +++ b/event/InstancesTrackerListener.java @@ -1,12 +1,12 @@ package org.jetbrains.debugger.memory.event; import org.jetbrains.annotations.NotNull; -import org.jetbrains.debugger.memory.component.InstancesTracker; +import org.jetbrains.debugger.memory.tracking.TrackingType; import java.util.EventListener; public interface InstancesTrackerListener extends EventListener{ - default void classChanged(@NotNull String name, @NotNull InstancesTracker.TrackingType type) { + default void classChanged(@NotNull String name, @NotNull TrackingType type) { } default void classRemoved(@NotNull String name) { diff --git a/tracking/InstanceTrackingStrategy.java b/tracking/InstanceTrackingStrategy.java index 86a1f4a4f2e2..1fc0543fad2e 100644 --- a/tracking/InstanceTrackingStrategy.java +++ b/tracking/InstanceTrackingStrategy.java @@ -9,8 +9,6 @@ import java.util.ArrayList; import java.util.Collections; import java.util.List; -import static org.jetbrains.debugger.memory.component.InstancesTracker.*; - public abstract class InstanceTrackingStrategy { @NotNull private List myNewInstances = new ArrayList<>(); diff --git a/tracking/TrackingType.java b/tracking/TrackingType.java new file mode 100644 index 000000000000..bbdb16794b3e --- /dev/null +++ b/tracking/TrackingType.java @@ -0,0 +1,29 @@ +package org.jetbrains.debugger.memory.tracking; + +import com.intellij.ui.JBColor; +import org.jetbrains.annotations.NotNull; + +public enum TrackingType { + IDENTITY("Id Based Tracking", JBColor.yellow), + HASH("Hash Based Tracking", JBColor.yellow), + RETAIN("Retaining Reference Object Tracking", JBColor.yellow), + CREATION("Track Constructors", JBColor.orange); + + private final String myDescription; + private final JBColor myHighlighting; + + TrackingType(@NotNull String description, @NotNull JBColor highlighting) { + myDescription = description; + myHighlighting = highlighting; + } + + @NotNull + public JBColor color() { + return myHighlighting; + } + + @NotNull + public String description() { + return myDescription; + } +} diff --git a/view/ClassesFilteredView.java b/view/ClassesFilteredView.java index 5bbdcc659169..241d134c9bd3 100644 --- a/view/ClassesFilteredView.java +++ b/view/ClassesFilteredView.java @@ -28,6 +28,7 @@ import org.jetbrains.debugger.memory.component.MemoryViewManager; import org.jetbrains.debugger.memory.component.MemoryViewManagerState; import org.jetbrains.debugger.memory.event.MemoryViewManagerListener; import org.jetbrains.debugger.memory.tracking.InstanceTrackingStrategy; +import org.jetbrains.debugger.memory.tracking.TrackingType; import org.jetbrains.debugger.memory.utils.AndroidUtil; import org.jetbrains.debugger.memory.utils.KeyboardUtils; import org.jetbrains.debugger.memory.utils.SingleAlarmWithMutableDelay; @@ -218,15 +219,19 @@ public class ClassesFilteredView extends BorderLayoutPanel implements Disposable @Override public void contextAction(@NotNull SuspendContextImpl suspendContext) throws Exception { final List classes = myDebugProcess.getVirtualMachineProxy().allClasses(); - for(ReferenceType ref : classes) { - InstancesTracker.TrackingType type = myInstancesTracker.getTrackingType(ref.name()); - if(type != null && !myTrackedClasses.containsKey(ref)) { - List instances = ref.instances(0); - myTrackedClasses.put(ref, InstanceTrackingStrategy.create(ref, suspendContext, type, instances)); + for (ReferenceType ref : classes) { + TrackingType type = myInstancesTracker.getTrackingType(ref.name()); + if (type != null && !myTrackedClasses.containsKey(ref)) { + if (type == TrackingType.CREATION) { + + } else { + List instances = ref.instances(0); + myTrackedClasses.put(ref, InstanceTrackingStrategy.create(ref, suspendContext, type, instances)); + } } } - for(Map.Entry entry : myTrackedClasses.entrySet()) { + for (Map.Entry entry : myTrackedClasses.entrySet()) { entry.getValue().update(suspendContext, entry.getKey().instances(0)); } diff --git a/view/ClassesTable.java b/view/ClassesTable.java index d5ee323f2ffc..baccc02d1d05 100644 --- a/view/ClassesTable.java +++ b/view/ClassesTable.java @@ -7,7 +7,6 @@ import com.intellij.openapi.util.TextRange; import com.intellij.psi.codeStyle.MinusculeMatcher; import com.intellij.psi.codeStyle.NameUtil; import com.intellij.ui.ColoredTableCellRenderer; -import com.intellij.ui.JBColor; import com.intellij.ui.SimpleTextAttributes; import com.intellij.ui.speedSearch.SpeedSearchUtil; import com.intellij.ui.table.JBTable; @@ -24,6 +23,7 @@ import org.jetbrains.annotations.Nullable; import org.jetbrains.debugger.memory.component.InstancesTracker; import org.jetbrains.debugger.memory.event.InstancesTrackerListener; import org.jetbrains.debugger.memory.tracking.InstanceTrackingStrategy; +import org.jetbrains.debugger.memory.tracking.TrackingType; import org.jetbrains.debugger.memory.utils.AbstractTableColumnDescriptor; import org.jetbrains.debugger.memory.utils.AbstractTableModelWithColumns; import org.jetbrains.debugger.memory.utils.InstancesProvider; @@ -78,7 +78,7 @@ public class ClassesTable extends JBTable implements DataProvider, Disposable { InstancesTracker tracker = InstancesTracker.getInstance(myDebugSession.getProject()); tracker.addTrackerListener(new InstancesTrackerListener() { @Override - public void classChanged(@NotNull String name, @NotNull InstancesTracker.TrackingType type) { + public void classChanged(@NotNull String name, @NotNull TrackingType type) { getRowSorter().allRowsChanged(); } @@ -207,11 +207,11 @@ public class ClassesTable extends JBTable implements DataProvider, Disposable { if (DEBUG_SESSION_KEY.is(dataId)) { return myDebugSession; } - if(NEW_INSTANCES_PROVIDER_KEY.is(dataId)) { + if (NEW_INSTANCES_PROVIDER_KEY.is(dataId)) { ReferenceType selectedClass = getSelectedClass(); - if(selectedClass != null) { + if (selectedClass != null) { InstanceTrackingStrategy strategy = myParent.getStrategy(selectedClass); - if(strategy != null) { + if (strategy != null) { List newInstances = strategy.getNewInstances(); return (InstancesProvider) limit -> newInstances; } @@ -226,13 +226,10 @@ public class ClassesTable extends JBTable implements DataProvider, Disposable { public void dispose() { } - private boolean needHighlightAsTracked(boolean isSelected, int row) { - if(isSelected){ - return false; - } - + @Nullable + private TrackingType getTrackingType(int row) { ReferenceType ref = (ReferenceType) getValueAt(row, DiffViewTableModel.CLASSNAME_COLUMN_INDEX); - return myInstancesTracker.isTracked(ref.name()); + return myInstancesTracker.getTrackingType(ref.name()); } private class DiffViewTableModel extends AbstractTableModelWithColumns { @@ -324,6 +321,7 @@ public class ClassesTable extends JBTable implements DataProvider, Disposable { private static class DiffValue { private long myOldCount; + private long myCurrentCount; DiffValue(long count) { @@ -348,9 +346,11 @@ public class ClassesTable extends JBTable implements DataProvider, Disposable { long diff() { return myCurrentCount - myOldCount; } + } private class MyNumberTableCellRenderer extends ColoredTableCellRenderer { + @Override protected void customizeCellRenderer(JTable table, @Nullable Object value, boolean isSelected, boolean hasFocus, int row, int column) { @@ -358,8 +358,9 @@ public class ClassesTable extends JBTable implements DataProvider, Disposable { return; } - if (needHighlightAsTracked(isSelected, row)) { - setBackground(UIUtil.toAlpha(JBColor.yellow, 20)); + TrackingType trackingType = getTrackingType(row); + if (!isSelected && trackingType != null) { + setBackground(UIUtil.toAlpha(trackingType.color(), 20)); } long val = (long) value; @@ -379,15 +380,18 @@ public class ClassesTable extends JBTable implements DataProvider, Disposable { append(String.valueOf(count)); } + } private class MyClassTableCellRenderer extends ColoredTableCellRenderer { + @Override protected void customizeCellRenderer(JTable table, @Nullable Object value, boolean isSelected, boolean hasFocus, int row, int column) { String presentation = value == null ? "null" : ((ReferenceType) value).name(); - if (needHighlightAsTracked(isSelected, row)) { - setBackground(UIUtil.toAlpha(JBColor.yellow, 20)); + TrackingType trackingType = getTrackingType(row); + if (trackingType != null && !isSelected) { + setBackground(UIUtil.toAlpha(trackingType.color(), 20)); } append(" "); @@ -398,11 +402,14 @@ public class ClassesTable extends JBTable implements DataProvider, Disposable { SimpleTextAttributes.STYLE_SEARCH_MATCH); SpeedSearchUtil.appendColoredFragments(this, presentation, textRanges, SimpleTextAttributes.REGULAR_ATTRIBUTES, attributes); - return; } + } else { + append(String.format("%s", presentation), SimpleTextAttributes.REGULAR_ATTRIBUTES); } - append(String.format("%s", presentation), SimpleTextAttributes.REGULAR_ATTRIBUTES); + if (trackingType != null) { + append(String.format(" (%s)", trackingType.description())); + } } } } From db2a1a6e8baf4600dc25a08fa357e0fb00ae2040 Mon Sep 17 00:00:00 2001 From: Vitaliy Bibaev Date: Mon, 29 Aug 2016 17:23:00 +0300 Subject: [PATCH 069/151] Add basic implementation for tracking instances through constructors calls --- tracking/ConstructorInstancesTracker.java | 96 +++++++++++++++++++++++ tracking/InstanceTrackingStrategy.java | 2 +- tracking/TrackerForNewInstances.java | 9 +++ view/ClassesFilteredView.java | 20 +++-- view/ClassesTable.java | 5 +- 5 files changed, 122 insertions(+), 10 deletions(-) create mode 100644 tracking/ConstructorInstancesTracker.java create mode 100644 tracking/TrackerForNewInstances.java diff --git a/tracking/ConstructorInstancesTracker.java b/tracking/ConstructorInstancesTracker.java new file mode 100644 index 000000000000..801df0caaf29 --- /dev/null +++ b/tracking/ConstructorInstancesTracker.java @@ -0,0 +1,96 @@ +package org.jetbrains.debugger.memory.tracking; + +import com.intellij.debugger.engine.DebugProcessImpl; +import com.intellij.debugger.engine.SuspendContextImpl; +import com.intellij.debugger.engine.evaluation.EvaluateException; +import com.intellij.debugger.engine.events.SuspendContextCommandImpl; +import com.intellij.debugger.ui.breakpoints.JavaLineBreakpointType; +import com.intellij.debugger.ui.breakpoints.LineBreakpoint; +import com.intellij.openapi.project.Project; +import com.intellij.xdebugger.breakpoints.XBreakpoint; +import com.intellij.xdebugger.impl.XDebuggerManagerImpl; +import com.intellij.xdebugger.impl.breakpoints.LineBreakpointState; +import com.intellij.xdebugger.impl.breakpoints.XLineBreakpointImpl; +import com.sun.jdi.Location; +import com.sun.jdi.Method; +import com.sun.jdi.ObjectReference; +import com.sun.jdi.ReferenceType; +import com.sun.jdi.event.LocatableEvent; +import com.sun.jdi.request.BreakpointRequest; +import com.sun.jdi.request.EventRequest; +import org.jetbrains.annotations.NotNull; +import org.jetbrains.java.debugger.breakpoints.properties.JavaLineBreakpointProperties; + +import java.util.ArrayList; +import java.util.HashSet; +import java.util.List; + +public class ConstructorInstancesTracker implements TrackerForNewInstances { + private final HashSet myNewObjects = new HashSet<>(); + + public ConstructorInstancesTracker(@NotNull ReferenceType ref, + @NotNull DebugProcessImpl debugProcess) { + Project project = debugProcess.getProject(); + JavaLineBreakpointType breakPointType = new JavaLineBreakpointType(); + + XBreakpoint bpn = new XLineBreakpointImpl<>(breakPointType, + ((XDebuggerManagerImpl) XDebuggerManagerImpl.getInstance(project)).getBreakpointManager(), + new JavaLineBreakpointProperties(), + new LineBreakpointState<>()); + MyConstructorBreakpoints myLineBreakpoint = new MyConstructorBreakpoints(project, bpn); + myLineBreakpoint.createRequestForPreparedClass(debugProcess, ref); + } + + public void obsolete() { + myNewObjects.clear(); + } + + @Override + public List getNewInstances() { + return new ArrayList<>(myNewObjects); + } + + private final class MyConstructorBreakpoints extends LineBreakpoint { + private final List myRequests = new ArrayList<>(); + + MyConstructorBreakpoints(Project project, XBreakpoint xBreakpoint) { + super(project, xBreakpoint); + } + + @Override + protected void createRequestForPreparedClass(DebugProcessImpl debugProcess, ReferenceType classType) { + for (Method cons : classType.methodsByName("")) { + Location loc = cons.location(); + BreakpointRequest breakpointRequest = debugProcess.getRequestsManager().createBreakpointRequest(this, loc); + breakpointRequest.enable(); + myRequests.add(breakpointRequest); + } + } + + void disable(@NotNull DebugProcessImpl debugProcess) { + myRequests.forEach(EventRequest::disable); + myRequests.clear(); + debugProcess.getRequestsManager().deleteRequest(this); + } + + @Override + public void reload() { + } + + @Override + public boolean processLocatableEvent(SuspendContextCommandImpl action, LocatableEvent event) + throws EventProcessingException { + try { + SuspendContextImpl suspendContext = action.getSuspendContext(); + if (suspendContext != null) { + ObjectReference thisRef = getThisObject(suspendContext, event); + System.out.println("new object " + thisRef.uniqueID() + " added."); + myNewObjects.add(thisRef); + } + } catch (EvaluateException e) { + e.printStackTrace(); + } + return false; + } + } +} diff --git a/tracking/InstanceTrackingStrategy.java b/tracking/InstanceTrackingStrategy.java index 1fc0543fad2e..48497b16dc43 100644 --- a/tracking/InstanceTrackingStrategy.java +++ b/tracking/InstanceTrackingStrategy.java @@ -9,7 +9,7 @@ import java.util.ArrayList; import java.util.Collections; import java.util.List; -public abstract class InstanceTrackingStrategy { +public abstract class InstanceTrackingStrategy implements TrackerForNewInstances { @NotNull private List myNewInstances = new ArrayList<>(); diff --git a/tracking/TrackerForNewInstances.java b/tracking/TrackerForNewInstances.java new file mode 100644 index 000000000000..4ea69620c6a8 --- /dev/null +++ b/tracking/TrackerForNewInstances.java @@ -0,0 +1,9 @@ +package org.jetbrains.debugger.memory.tracking; + +import com.sun.jdi.ObjectReference; + +import java.util.List; + +public interface TrackerForNewInstances { + List getNewInstances(); +} diff --git a/view/ClassesFilteredView.java b/view/ClassesFilteredView.java index 241d134c9bd3..9b65325c8d69 100644 --- a/view/ClassesFilteredView.java +++ b/view/ClassesFilteredView.java @@ -2,6 +2,7 @@ package org.jetbrains.debugger.memory.view; import com.intellij.debugger.DebuggerManager; import com.intellij.debugger.engine.DebugProcess; +import com.intellij.debugger.engine.DebugProcessImpl; import com.intellij.debugger.engine.DebuggerManagerThreadImpl; import com.intellij.debugger.engine.SuspendContextImpl; import com.intellij.debugger.engine.events.SuspendContextCommandImpl; @@ -27,7 +28,9 @@ import org.jetbrains.debugger.memory.component.InstancesTracker; import org.jetbrains.debugger.memory.component.MemoryViewManager; import org.jetbrains.debugger.memory.component.MemoryViewManagerState; import org.jetbrains.debugger.memory.event.MemoryViewManagerListener; +import org.jetbrains.debugger.memory.tracking.ConstructorInstancesTracker; import org.jetbrains.debugger.memory.tracking.InstanceTrackingStrategy; +import org.jetbrains.debugger.memory.tracking.TrackerForNewInstances; import org.jetbrains.debugger.memory.tracking.TrackingType; import org.jetbrains.debugger.memory.utils.AndroidUtil; import org.jetbrains.debugger.memory.utils.KeyboardUtils; @@ -60,12 +63,11 @@ public class ClassesFilteredView extends BorderLayoutPanel implements Disposable private final ClassesTable myTable; private final InstancesTracker myInstancesTracker; private final Map myTrackedClasses = new ConcurrentHashMap<>(); - + private final Map myConstructorTrackedClasses = new ConcurrentHashMap<>(); private volatile SuspendContextImpl myLastSuspendContext; private volatile boolean myNeedReloadClasses = false; - public ClassesFilteredView(@NotNull XDebugSession debugSession) { super(); @@ -113,6 +115,7 @@ public class ClassesFilteredView extends BorderLayoutPanel implements Disposable myDebugSession.addSessionListener(new XDebugSessionListener() { @Override public void sessionResumed() { + myConstructorTrackedClasses.values().forEach(ConstructorInstancesTracker::obsolete); SwingUtilities.invokeLater(myTable::hideContent); mySingleAlarm.cancelAllRequests(); } @@ -164,8 +167,12 @@ public class ClassesFilteredView extends BorderLayoutPanel implements Disposable } @Nullable - InstanceTrackingStrategy getStrategy(@NotNull ReferenceType ref) { - return myTrackedClasses.getOrDefault(ref, null); + TrackerForNewInstances getStrategy(@NotNull ReferenceType ref) { + if(myTrackedClasses.containsKey(ref)) { + return myTrackedClasses.get(ref); + } + + return myConstructorTrackedClasses.getOrDefault(ref, null); } public void setNeedReloadClasses(boolean value) { @@ -221,9 +228,10 @@ public class ClassesFilteredView extends BorderLayoutPanel implements Disposable final List classes = myDebugProcess.getVirtualMachineProxy().allClasses(); for (ReferenceType ref : classes) { TrackingType type = myInstancesTracker.getTrackingType(ref.name()); - if (type != null && !myTrackedClasses.containsKey(ref)) { + if (type != null && !myTrackedClasses.containsKey(ref) && !myConstructorTrackedClasses.containsKey(ref)) { if (type == TrackingType.CREATION) { - + myConstructorTrackedClasses.put(ref, + new ConstructorInstancesTracker(ref, (DebugProcessImpl) myDebugProcess)); } else { List instances = ref.instances(0); myTrackedClasses.put(ref, InstanceTrackingStrategy.create(ref, suspendContext, type, instances)); diff --git a/view/ClassesTable.java b/view/ClassesTable.java index baccc02d1d05..513138c25be1 100644 --- a/view/ClassesTable.java +++ b/view/ClassesTable.java @@ -22,7 +22,7 @@ import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; import org.jetbrains.debugger.memory.component.InstancesTracker; import org.jetbrains.debugger.memory.event.InstancesTrackerListener; -import org.jetbrains.debugger.memory.tracking.InstanceTrackingStrategy; +import org.jetbrains.debugger.memory.tracking.TrackerForNewInstances; import org.jetbrains.debugger.memory.tracking.TrackingType; import org.jetbrains.debugger.memory.utils.AbstractTableColumnDescriptor; import org.jetbrains.debugger.memory.utils.AbstractTableModelWithColumns; @@ -210,12 +210,11 @@ public class ClassesTable extends JBTable implements DataProvider, Disposable { if (NEW_INSTANCES_PROVIDER_KEY.is(dataId)) { ReferenceType selectedClass = getSelectedClass(); if (selectedClass != null) { - InstanceTrackingStrategy strategy = myParent.getStrategy(selectedClass); + TrackerForNewInstances strategy = myParent.getStrategy(selectedClass); if (strategy != null) { List newInstances = strategy.getNewInstances(); return (InstancesProvider) limit -> newInstances; } - } } From cf329ee9a42e9e26a1140b3535731d1f28ac8113 Mon Sep 17 00:00:00 2001 From: Vitaliy Bibaev Date: Fri, 19 Aug 2016 15:23:49 +0300 Subject: [PATCH 070/151] Fix empty text in classes table. When session suspended, need to change empty text for "Nothing to show" --- view/ClassesFilteredView.java | 9 ++++++++- view/ClassesTable.java | 4 ---- 2 files changed, 8 insertions(+), 5 deletions(-) diff --git a/view/ClassesFilteredView.java b/view/ClassesFilteredView.java index 193bd4160f77..357b4312d292 100644 --- a/view/ClassesFilteredView.java +++ b/view/ClassesFilteredView.java @@ -42,6 +42,9 @@ public class ClassesFilteredView extends BorderLayoutPanel { private static final Logger LOG = Logger.getInstance(ClassesFilteredView.class); private final static double DELAY_BEFORE_INSTANCES_QUERY_COEFFICIENT = 0.5; private final static int DEFAULT_BATCH_SIZE = Integer.MAX_VALUE; + private static final String EMPTY_TABLE_CONTENT_WHEN_RUNNED = "The application is running"; + private static final String EMPTY_TABLE_CONTENT_WHEN_SUSPENDED = "Nothing to show"; + private final Project myProject; private final XDebugSession myDebugSession; @@ -127,7 +130,10 @@ public class ClassesFilteredView extends BorderLayoutPanel { myDebugSession.addSessionListener(new XDebugSessionListener() { @Override public void sessionResumed() { - SwingUtilities.invokeLater(myTable::hideContent); + SwingUtilities.invokeLater(() -> { + myTable.getEmptyText().setText(EMPTY_TABLE_CONTENT_WHEN_RUNNED); + myTable.hideContent(); + }); mySingleAlarm.cancelAllRequests(); } @@ -139,6 +145,7 @@ public class ClassesFilteredView extends BorderLayoutPanel { @Override public void sessionPaused() { + SwingUtilities.invokeLater(() -> myTable.getEmptyText().setText(EMPTY_TABLE_CONTENT_WHEN_SUSPENDED)); if (myNeedReloadClasses) { updateClassesAndCounts(); } diff --git a/view/ClassesTable.java b/view/ClassesTable.java index 1c2ced59bf9c..8fae7b9020ca 100644 --- a/view/ClassesTable.java +++ b/view/ClassesTable.java @@ -38,8 +38,6 @@ public class ClassesTable extends JBTable implements DataProvider { private static final int DIFF_COLUMN_MIN_WIDTH = 80; private static final int DIFF_COLUMN_MAX_WIDTH = 100; - private static final String EMPTY_TABLE_CONTENT = "The application is running"; - private final DiffViewTableModel myModel = new DiffViewTableModel(); private final UnknownDiffValue myUnknownValue = new UnknownDiffValue(); private final XDebugSession myDebugSession; @@ -60,8 +58,6 @@ public class ClassesTable extends JBTable implements DataProvider { myOnlyWithDiff = onlyWithDiff; myOnlyWithInstances = onlyWithInstances; - getEmptyText().setText(EMPTY_TABLE_CONTENT); - TableColumn classesColumn = getColumnModel().getColumn(DiffViewTableModel.CLASSNAME_COLUMN_INDEX); TableColumn countColumn = getColumnModel().getColumn(DiffViewTableModel.COUNT_COLUMN_INDEX); TableColumn diffColumn = getColumnModel().getColumn(DiffViewTableModel.DIFF_COLUMN_INDEX); From b135d8c9e8a2dac3d04a4b539a2a7e746a6551ce Mon Sep 17 00:00:00 2001 From: Vitaliy Bibaev Date: Mon, 29 Aug 2016 19:05:39 +0300 Subject: [PATCH 071/151] IDEA-160276 Fix diff column value when class loaded during last step --- view/ClassesTable.java | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/view/ClassesTable.java b/view/ClassesTable.java index 8fae7b9020ca..b209f2004d48 100644 --- a/view/ClassesTable.java +++ b/view/ClassesTable.java @@ -144,11 +144,15 @@ public class ClassesTable extends JBTable implements DataProvider { assert classes.size() == counts.length; ReferenceType selectedClass = myModel.getSelectedClassBeforeHided(); int newSelectedIndex = classes.indexOf(selectedClass); + boolean isInitialized = !myElems.isEmpty(); myElems = Collections.unmodifiableList(new ArrayList<>(classes)); for (int i = 0, size = classes.size(); i < size; i++) { ReferenceType ref = classes.get(i); - myCounts.put(ref, myCounts.getOrDefault(ref, myUnknownValue).update(counts[i])); + DiffValue oldValue = isInitialized && !myCounts.containsKey(ref) + ? new DiffValue(0, 0) + : myCounts.getOrDefault(ref, myUnknownValue); + myCounts.put(ref, oldValue.update(counts[i])); } showContent(); From 5913bbd203277a9c2f521cb9d2dbeee5b7030c13 Mon Sep 17 00:00:00 2001 From: Vitaliy Bibaev Date: Tue, 30 Aug 2016 13:41:28 +0300 Subject: [PATCH 072/151] Dont track subclasses --- tracking/ConstructorInstancesTracker.java | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/tracking/ConstructorInstancesTracker.java b/tracking/ConstructorInstancesTracker.java index 801df0caaf29..faf6eecca577 100644 --- a/tracking/ConstructorInstancesTracker.java +++ b/tracking/ConstructorInstancesTracker.java @@ -27,9 +27,11 @@ import java.util.List; public class ConstructorInstancesTracker implements TrackerForNewInstances { private final HashSet myNewObjects = new HashSet<>(); + private final ReferenceType myReference; public ConstructorInstancesTracker(@NotNull ReferenceType ref, @NotNull DebugProcessImpl debugProcess) { + myReference = ref; Project project = debugProcess.getProject(); JavaLineBreakpointType breakPointType = new JavaLineBreakpointType(); @@ -84,8 +86,9 @@ public class ConstructorInstancesTracker implements TrackerForNewInstances { SuspendContextImpl suspendContext = action.getSuspendContext(); if (suspendContext != null) { ObjectReference thisRef = getThisObject(suspendContext, event); - System.out.println("new object " + thisRef.uniqueID() + " added."); - myNewObjects.add(thisRef); + if (myReference.equals(thisRef.referenceType())) { + myNewObjects.add(thisRef); + } } } catch (EvaluateException e) { e.printStackTrace(); From b95c15b8724d290708cbc3eae4f8558457ac4372 Mon Sep 17 00:00:00 2001 From: Vitaliy Bibaev Date: Tue, 30 Aug 2016 15:20:53 +0300 Subject: [PATCH 073/151] Check that object is not collected before hash calculation --- tracking/HashBasedTracking.java | 13 ++++++++++--- 1 file changed, 10 insertions(+), 3 deletions(-) diff --git a/tracking/HashBasedTracking.java b/tracking/HashBasedTracking.java index f21014e818fb..83955ce354d9 100644 --- a/tracking/HashBasedTracking.java +++ b/tracking/HashBasedTracking.java @@ -45,15 +45,22 @@ class HashBasedTracking extends InstanceTrackingStrategy { Map> result = new HashMap<>(); ThreadReferenceProxy threadProxy = suspendContext.getThread(); ThreadReference thread = threadProxy != null ? threadProxy.getThreadReference() : null; - if(thread == null) { + if(thread == null || hashCodeMethod == null) { return result; } for (ObjectReference ref : references) { Optional hash = Optional.empty(); try { - Value value = ref.invokeMethod(thread, hashCodeMethod, Collections.emptyList(), 0); - hash = Optional.of(((IntegerValue)value).value()); + if(!ref.isCollected()) { + ref.disableCollection(); + if(!ref.isCollected()) { + Value value = ref.invokeMethod(thread, hashCodeMethod, Collections.emptyList(), + ObjectReference.INVOKE_NONVIRTUAL); + hash = Optional.of(((IntegerValue)value).value()); + } + ref.enableCollection(); + } } catch (InvalidTypeException | ClassNotLoadedException | IncompatibleThreadStateException | InvocationException ignored) { } From 63bf235d073b726cbb7e4d4b60643a4fbb9e8581 Mon Sep 17 00:00:00 2001 From: Vitaliy Bibaev Date: Wed, 31 Aug 2016 15:22:31 +0300 Subject: [PATCH 074/151] Don't update classes counts if context changed --- view/ClassesFilteredView.java | 18 +++++++++--------- 1 file changed, 9 insertions(+), 9 deletions(-) diff --git a/view/ClassesFilteredView.java b/view/ClassesFilteredView.java index 357b4312d292..b47def68e716 100644 --- a/view/ClassesFilteredView.java +++ b/view/ClassesFilteredView.java @@ -238,7 +238,7 @@ public class ClassesFilteredView extends BorderLayoutPanel { List chunks = new SmartList<>(); int size = classes.size(); for (int begin = 0, end = Math.min(batchSize, size); - begin != size && contextIsValid(); + begin != size && isContextValid(); begin = end, end = Math.min(end + batchSize, size)) { List batch = classes.subList(begin, end); @@ -252,21 +252,21 @@ public class ClassesFilteredView extends BorderLayoutPanel { LOG.info(String.format("Instances query time = %d ms. Count = %d", delay, batch.size())); } - final long[] counts = chunks.size() == 1 ? chunks.get(0) : IntStream.range(0, chunks.size()).boxed() - .flatMapToLong(integer -> Arrays.stream(chunks.get(integer))) - .toArray(); + if(isContextValid()) { + final long[] counts = chunks.size() == 1 ? chunks.get(0) : IntStream.range(0, chunks.size()).boxed() + .flatMapToLong(integer -> Arrays.stream(chunks.get(integer))) + .toArray(); + SwingUtilities.invokeLater(() -> myTable.setClassesAndUpdateCounts(classes, counts)); + } - SwingUtilities.invokeLater(() -> { - myTable.setClassesAndUpdateCounts(classes, counts); - myTable.setBusy(false); - }); + SwingUtilities.invokeLater(() -> myTable.setBusy(false)); } @Override public void commandCancelled() { } - private boolean contextIsValid() { + private boolean isContextValid() { return ClassesFilteredView.this.getSuspendContext() == getSuspendContext(); } } From 1b1e74e1e51bb212f78a987850c99832a9e38d9f Mon Sep 17 00:00:00 2001 From: Vitaliy Bibaev Date: Wed, 31 Aug 2016 17:53:12 +0300 Subject: [PATCH 075/151] Select first class in table if selection has been lost after filtering --- view/ClassesTable.java | 3 +++ 1 file changed, 3 insertions(+) diff --git a/view/ClassesTable.java b/view/ClassesTable.java index b209f2004d48..4f44f5e32a36 100644 --- a/view/ClassesTable.java +++ b/view/ClassesTable.java @@ -123,6 +123,9 @@ public class ClassesTable extends JBTable implements DataProvider { myFilteringPattern = pattern; myMatcher = NameUtil.buildMatcher("*" + pattern).build(); getRowSorter().allRowsChanged(); + if(getSelectedClass() == null && getRowCount() > 0) { + getSelectionModel().setSelectionInterval(0, 0); + } } } From 4816300443ac17530a0897c2bf8e17000b65f0c7 Mon Sep 17 00:00:00 2001 From: Vitaliy Bibaev Date: Thu, 1 Sep 2016 13:43:57 +0300 Subject: [PATCH 076/151] Save execution stack for tracked instances --- component/CreationPositionTracker.java | 57 +++++++++++++++++++++++ tracking/ConstructorInstancesTracker.java | 21 ++++++++- view/ClassesFilteredView.java | 3 +- 3 files changed, 77 insertions(+), 4 deletions(-) create mode 100644 component/CreationPositionTracker.java diff --git a/component/CreationPositionTracker.java b/component/CreationPositionTracker.java new file mode 100644 index 000000000000..2fc414e18a93 --- /dev/null +++ b/component/CreationPositionTracker.java @@ -0,0 +1,57 @@ +package org.jetbrains.debugger.memory.component; + +import com.intellij.debugger.jdi.StackFrameProxyImpl; +import com.intellij.openapi.components.AbstractProjectComponent; +import com.intellij.openapi.project.Project; +import com.intellij.xdebugger.XDebugSession; +import com.intellij.xdebugger.XDebugSessionListener; +import com.sun.jdi.ObjectReference; +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; + +import java.util.Collections; +import java.util.List; +import java.util.Map; +import java.util.concurrent.ConcurrentHashMap; + +public class CreationPositionTracker extends AbstractProjectComponent { + private final Map>> mySession2Reference2Stack + = new ConcurrentHashMap<>(); + + public CreationPositionTracker(Project project) { + super(project); + } + + public static CreationPositionTracker getInstance(@NotNull Project project) { + return project.getComponent(CreationPositionTracker.class); + } + + @Nullable + public List getStack(@NotNull XDebugSession session, @NotNull ObjectReference ref) { + Map> ref2Stack = mySession2Reference2Stack.getOrDefault(session, null); + List stack = ref2Stack != null ? ref2Stack.getOrDefault(ref, null) : null; + return stack != null ? Collections.unmodifiableList(stack) : null; + } + + public void addStack(@NotNull XDebugSession session, @NotNull ObjectReference ref, @NotNull List stack) { + if (!mySession2Reference2Stack.containsKey(session)) { + mySession2Reference2Stack.put(session, new ConcurrentHashMap<>()); + session.addSessionListener(new XDebugSessionListener() { + @Override + public void sessionStopped() { + mySession2Reference2Stack.remove(session); + } + }); + } + + mySession2Reference2Stack.get(session).put(ref, stack); + } + + public void releaseBySession(@NotNull XDebugSession session) { + if(!mySession2Reference2Stack.containsKey(session)) { + return; + } + + mySession2Reference2Stack.put(session, new ConcurrentHashMap<>()); + } +} diff --git a/tracking/ConstructorInstancesTracker.java b/tracking/ConstructorInstancesTracker.java index faf6eecca577..d85bf0b2ebd6 100644 --- a/tracking/ConstructorInstancesTracker.java +++ b/tracking/ConstructorInstancesTracker.java @@ -1,12 +1,16 @@ package org.jetbrains.debugger.memory.tracking; +import com.intellij.debugger.DebuggerManager; import com.intellij.debugger.engine.DebugProcessImpl; import com.intellij.debugger.engine.SuspendContextImpl; import com.intellij.debugger.engine.evaluation.EvaluateException; import com.intellij.debugger.engine.events.SuspendContextCommandImpl; +import com.intellij.debugger.jdi.StackFrameProxyImpl; +import com.intellij.debugger.jdi.ThreadReferenceProxyImpl; import com.intellij.debugger.ui.breakpoints.JavaLineBreakpointType; import com.intellij.debugger.ui.breakpoints.LineBreakpoint; import com.intellij.openapi.project.Project; +import com.intellij.xdebugger.XDebugSession; import com.intellij.xdebugger.breakpoints.XBreakpoint; import com.intellij.xdebugger.impl.XDebuggerManagerImpl; import com.intellij.xdebugger.impl.breakpoints.LineBreakpointState; @@ -19,6 +23,7 @@ import com.sun.jdi.event.LocatableEvent; import com.sun.jdi.request.BreakpointRequest; import com.sun.jdi.request.EventRequest; import org.jetbrains.annotations.NotNull; +import org.jetbrains.debugger.memory.component.CreationPositionTracker; import org.jetbrains.java.debugger.breakpoints.properties.JavaLineBreakpointProperties; import java.util.ArrayList; @@ -28,11 +33,15 @@ import java.util.List; public class ConstructorInstancesTracker implements TrackerForNewInstances { private final HashSet myNewObjects = new HashSet<>(); private final ReferenceType myReference; + private final XDebugSession myDebugSession; + private final CreationPositionTracker myPositionTracker; public ConstructorInstancesTracker(@NotNull ReferenceType ref, - @NotNull DebugProcessImpl debugProcess) { + @NotNull XDebugSession debugSession) { myReference = ref; - Project project = debugProcess.getProject(); + myDebugSession = debugSession; + myPositionTracker = CreationPositionTracker.getInstance(debugSession.getProject()); + Project project = debugSession.getProject(); JavaLineBreakpointType breakPointType = new JavaLineBreakpointType(); XBreakpoint bpn = new XLineBreakpointImpl<>(breakPointType, @@ -40,11 +49,14 @@ public class ConstructorInstancesTracker implements TrackerForNewInstances { new JavaLineBreakpointProperties(), new LineBreakpointState<>()); MyConstructorBreakpoints myLineBreakpoint = new MyConstructorBreakpoints(project, bpn); + DebugProcessImpl debugProcess = (DebugProcessImpl) DebuggerManager.getInstance(project) + .getDebugProcess(debugSession.getDebugProcess().getProcessHandler()); myLineBreakpoint.createRequestForPreparedClass(debugProcess, ref); } public void obsolete() { myNewObjects.clear(); + myPositionTracker.releaseBySession(myDebugSession); } @Override @@ -88,6 +100,11 @@ public class ConstructorInstancesTracker implements TrackerForNewInstances { ObjectReference thisRef = getThisObject(suspendContext, event); if (myReference.equals(thisRef.referenceType())) { myNewObjects.add(thisRef); + ThreadReferenceProxyImpl threadReferenceProxy = suspendContext.getThread(); + List stack = threadReferenceProxy == null ? null : threadReferenceProxy.frames(); + if(stack != null) { + myPositionTracker.addStack(myDebugSession, thisRef, stack); + } } } } catch (EvaluateException e) { diff --git a/view/ClassesFilteredView.java b/view/ClassesFilteredView.java index 9b65325c8d69..813450c818d6 100644 --- a/view/ClassesFilteredView.java +++ b/view/ClassesFilteredView.java @@ -2,7 +2,6 @@ package org.jetbrains.debugger.memory.view; import com.intellij.debugger.DebuggerManager; import com.intellij.debugger.engine.DebugProcess; -import com.intellij.debugger.engine.DebugProcessImpl; import com.intellij.debugger.engine.DebuggerManagerThreadImpl; import com.intellij.debugger.engine.SuspendContextImpl; import com.intellij.debugger.engine.events.SuspendContextCommandImpl; @@ -231,7 +230,7 @@ public class ClassesFilteredView extends BorderLayoutPanel implements Disposable if (type != null && !myTrackedClasses.containsKey(ref) && !myConstructorTrackedClasses.containsKey(ref)) { if (type == TrackingType.CREATION) { myConstructorTrackedClasses.put(ref, - new ConstructorInstancesTracker(ref, (DebugProcessImpl) myDebugProcess)); + new ConstructorInstancesTracker(ref, myDebugSession)); } else { List instances = ref.instances(0); myTrackedClasses.put(ref, InstanceTrackingStrategy.create(ref, suspendContext, type, instances)); From 2b3bc73a0db59d1fe021a4ab52e44734b8677901 Mon Sep 17 00:00:00 2001 From: Vitaliy Bibaev Date: Thu, 1 Sep 2016 13:45:23 +0300 Subject: [PATCH 077/151] Add action for showing a stacktrace for specific instance --- action/DebuggerTreeAction.java | 28 ++++++++++++ action/ShowInstancesByClassAction.java | 23 +--------- .../tracking/GetAllocationSourceAction.java | 43 +++++++++++++++++++ 3 files changed, 72 insertions(+), 22 deletions(-) create mode 100644 action/DebuggerTreeAction.java create mode 100644 action/tracking/GetAllocationSourceAction.java diff --git a/action/DebuggerTreeAction.java b/action/DebuggerTreeAction.java new file mode 100644 index 000000000000..c46d72c5ff97 --- /dev/null +++ b/action/DebuggerTreeAction.java @@ -0,0 +1,28 @@ +package org.jetbrains.debugger.memory.action; + +import com.intellij.debugger.ui.impl.watch.NodeDescriptorProvider; +import com.intellij.debugger.ui.tree.NodeDescriptor; +import com.intellij.debugger.ui.tree.ValueDescriptor; +import com.intellij.xdebugger.frame.XValue; +import com.intellij.xdebugger.impl.ui.tree.actions.XDebuggerTreeActionBase; +import com.intellij.xdebugger.impl.ui.tree.nodes.XValueNodeImpl; +import com.sun.jdi.ObjectReference; +import com.sun.jdi.Value; +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; + +public abstract class DebuggerTreeAction extends XDebuggerTreeActionBase { + @Nullable + protected ObjectReference getObjectReference(@NotNull XValueNodeImpl node) { + XValue valueContainer = node.getValueContainer(); + if (valueContainer instanceof NodeDescriptorProvider) { + NodeDescriptor descriptor = ((NodeDescriptorProvider) valueContainer).getDescriptor(); + if (descriptor instanceof ValueDescriptor) { + Value value = ((ValueDescriptor)descriptor).getValue(); + return value instanceof ObjectReference ? (ObjectReference) value : null; + } + } + + return null; + } +} diff --git a/action/ShowInstancesByClassAction.java b/action/ShowInstancesByClassAction.java index 451af6f9b1f4..f4ccf9271eea 100644 --- a/action/ShowInstancesByClassAction.java +++ b/action/ShowInstancesByClassAction.java @@ -1,24 +1,17 @@ package org.jetbrains.debugger.memory.action; -import com.intellij.debugger.ui.impl.watch.NodeDescriptorProvider; -import com.intellij.debugger.ui.tree.NodeDescriptor; -import com.intellij.debugger.ui.tree.ValueDescriptor; import com.intellij.openapi.actionSystem.AnActionEvent; import com.intellij.openapi.project.Project; import com.intellij.openapi.util.text.StringUtil; import com.intellij.xdebugger.XDebugSession; import com.intellij.xdebugger.XDebuggerManager; -import com.intellij.xdebugger.frame.XValue; -import com.intellij.xdebugger.impl.ui.tree.actions.XDebuggerTreeActionBase; import com.intellij.xdebugger.impl.ui.tree.nodes.XValueNodeImpl; import com.sun.jdi.ObjectReference; import com.sun.jdi.ReferenceType; -import com.sun.jdi.Value; import org.jetbrains.annotations.NotNull; -import org.jetbrains.annotations.Nullable; import org.jetbrains.debugger.memory.view.InstancesWindow; -public class ShowInstancesByClassAction extends XDebuggerTreeActionBase { +public class ShowInstancesByClassAction extends DebuggerTreeAction { @Override protected boolean isEnabled(@NotNull XValueNodeImpl node, @NotNull AnActionEvent e) { ObjectReference ref = getObjectReference(node); @@ -42,18 +35,4 @@ public class ShowInstancesByClassAction extends XDebuggerTreeActionBase { } } } - - @Nullable - private ObjectReference getObjectReference(@NotNull XValueNodeImpl node) { - XValue valueContainer = node.getValueContainer(); - if (valueContainer instanceof NodeDescriptorProvider) { - NodeDescriptor descriptor = ((NodeDescriptorProvider) valueContainer).getDescriptor(); - if (descriptor instanceof ValueDescriptor) { - Value value = ((ValueDescriptor)descriptor).getValue(); - return value instanceof ObjectReference ? (ObjectReference) value : null; - } - } - - return null; - } } diff --git a/action/tracking/GetAllocationSourceAction.java b/action/tracking/GetAllocationSourceAction.java new file mode 100644 index 000000000000..9ec6531eebb6 --- /dev/null +++ b/action/tracking/GetAllocationSourceAction.java @@ -0,0 +1,43 @@ +package org.jetbrains.debugger.memory.action.tracking; + +import com.intellij.debugger.jdi.StackFrameProxyImpl; +import com.intellij.openapi.actionSystem.AnActionEvent; +import com.intellij.openapi.project.Project; +import com.intellij.xdebugger.XDebugSession; +import com.intellij.xdebugger.XDebuggerManager; +import com.intellij.xdebugger.frame.XExecutionStack; +import com.intellij.xdebugger.impl.ui.tree.nodes.XValueNodeImpl; +import com.sun.jdi.ObjectReference; +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; +import org.jetbrains.debugger.memory.action.DebuggerTreeAction; +import org.jetbrains.debugger.memory.component.CreationPositionTracker; + +import java.util.List; + +public class GetAllocationSourceAction extends DebuggerTreeAction { + @Override + public void update(AnActionEvent e) { + e.getPresentation().setVisible(getStack(e) != null); + } + + @Override + protected void perform(XValueNodeImpl node, @NotNull String nodeName, AnActionEvent e) { + List stack = getStack(e); + System.out.println(stack != null); + } + + @Nullable + private List getStack(AnActionEvent e) { + Project project = e.getProject(); + XValueNodeImpl selectedNode = getSelectedNode(e.getDataContext()); + ObjectReference ref = selectedNode != null ? getObjectReference(selectedNode) : null; + if(project == null || ref == null) { + return null; + } + + // TODO: Wrong way: session may be not current + XDebugSession session = XDebuggerManager.getInstance(project).getCurrentSession(); + return session == null ? null : CreationPositionTracker.getInstance(project).getStack(session, ref); + } +} From 959890afb5814529bd3f91f826777a8e41b3bd90 Mon Sep 17 00:00:00 2001 From: Vitaliy Bibaev Date: Thu, 1 Sep 2016 16:32:14 +0300 Subject: [PATCH 078/151] Add basic implementation for stack frame selector --- .../tracking/GetAllocationSourceAction.java | 16 ++-- component/CreationPositionTracker.java | 12 +-- tracking/ConstructorInstancesTracker.java | 22 +++-- utils/StackFrameDescriptor.java | 29 ++++++ view/StackFrameSelector.java | 88 +++++++++++++++++++ 5 files changed, 150 insertions(+), 17 deletions(-) create mode 100644 utils/StackFrameDescriptor.java create mode 100644 view/StackFrameSelector.java diff --git a/action/tracking/GetAllocationSourceAction.java b/action/tracking/GetAllocationSourceAction.java index 9ec6531eebb6..cfc3cd9d89b0 100644 --- a/action/tracking/GetAllocationSourceAction.java +++ b/action/tracking/GetAllocationSourceAction.java @@ -1,17 +1,19 @@ package org.jetbrains.debugger.memory.action.tracking; -import com.intellij.debugger.jdi.StackFrameProxyImpl; import com.intellij.openapi.actionSystem.AnActionEvent; +import com.intellij.openapi.editor.Editor; +import com.intellij.openapi.fileEditor.FileEditorManager; import com.intellij.openapi.project.Project; import com.intellij.xdebugger.XDebugSession; import com.intellij.xdebugger.XDebuggerManager; -import com.intellij.xdebugger.frame.XExecutionStack; import com.intellij.xdebugger.impl.ui.tree.nodes.XValueNodeImpl; import com.sun.jdi.ObjectReference; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; import org.jetbrains.debugger.memory.action.DebuggerTreeAction; import org.jetbrains.debugger.memory.component.CreationPositionTracker; +import org.jetbrains.debugger.memory.utils.StackFrameDescriptor; +import org.jetbrains.debugger.memory.view.StackFrameSelector; import java.util.List; @@ -23,12 +25,16 @@ public class GetAllocationSourceAction extends DebuggerTreeAction { @Override protected void perform(XValueNodeImpl node, @NotNull String nodeName, AnActionEvent e) { - List stack = getStack(e); - System.out.println(stack != null); + Project project = e.getProject(); + List stack = getStack(e); + Editor editor = project == null ? null : FileEditorManager.getInstance(project).getSelectedTextEditor(); + if(project != null && stack != null && editor != null) { + new StackFrameSelector(project, stack, editor).show(); + } } @Nullable - private List getStack(AnActionEvent e) { + private List getStack(AnActionEvent e) { Project project = e.getProject(); XValueNodeImpl selectedNode = getSelectedNode(e.getDataContext()); ObjectReference ref = selectedNode != null ? getObjectReference(selectedNode) : null; diff --git a/component/CreationPositionTracker.java b/component/CreationPositionTracker.java index 2fc414e18a93..cb16fcd98a3a 100644 --- a/component/CreationPositionTracker.java +++ b/component/CreationPositionTracker.java @@ -1,6 +1,5 @@ package org.jetbrains.debugger.memory.component; -import com.intellij.debugger.jdi.StackFrameProxyImpl; import com.intellij.openapi.components.AbstractProjectComponent; import com.intellij.openapi.project.Project; import com.intellij.xdebugger.XDebugSession; @@ -8,6 +7,7 @@ import com.intellij.xdebugger.XDebugSessionListener; import com.sun.jdi.ObjectReference; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; +import org.jetbrains.debugger.memory.utils.StackFrameDescriptor; import java.util.Collections; import java.util.List; @@ -15,7 +15,7 @@ import java.util.Map; import java.util.concurrent.ConcurrentHashMap; public class CreationPositionTracker extends AbstractProjectComponent { - private final Map>> mySession2Reference2Stack + private final Map>> mySession2Reference2Stack = new ConcurrentHashMap<>(); public CreationPositionTracker(Project project) { @@ -27,13 +27,13 @@ public class CreationPositionTracker extends AbstractProjectComponent { } @Nullable - public List getStack(@NotNull XDebugSession session, @NotNull ObjectReference ref) { - Map> ref2Stack = mySession2Reference2Stack.getOrDefault(session, null); - List stack = ref2Stack != null ? ref2Stack.getOrDefault(ref, null) : null; + public List getStack(@NotNull XDebugSession session, @NotNull ObjectReference ref) { + Map> ref2Stack = mySession2Reference2Stack.getOrDefault(session, null); + List stack = ref2Stack != null ? ref2Stack.getOrDefault(ref, null) : null; return stack != null ? Collections.unmodifiableList(stack) : null; } - public void addStack(@NotNull XDebugSession session, @NotNull ObjectReference ref, @NotNull List stack) { + public void addStack(@NotNull XDebugSession session, @NotNull ObjectReference ref, @NotNull List stack) { if (!mySession2Reference2Stack.containsKey(session)) { mySession2Reference2Stack.put(session, new ConcurrentHashMap<>()); session.addSessionListener(new XDebugSessionListener() { diff --git a/tracking/ConstructorInstancesTracker.java b/tracking/ConstructorInstancesTracker.java index d85bf0b2ebd6..7c1dee6fd3e2 100644 --- a/tracking/ConstructorInstancesTracker.java +++ b/tracking/ConstructorInstancesTracker.java @@ -15,20 +15,20 @@ import com.intellij.xdebugger.breakpoints.XBreakpoint; import com.intellij.xdebugger.impl.XDebuggerManagerImpl; import com.intellij.xdebugger.impl.breakpoints.LineBreakpointState; import com.intellij.xdebugger.impl.breakpoints.XLineBreakpointImpl; -import com.sun.jdi.Location; -import com.sun.jdi.Method; -import com.sun.jdi.ObjectReference; -import com.sun.jdi.ReferenceType; +import com.sun.jdi.*; import com.sun.jdi.event.LocatableEvent; import com.sun.jdi.request.BreakpointRequest; import com.sun.jdi.request.EventRequest; import org.jetbrains.annotations.NotNull; import org.jetbrains.debugger.memory.component.CreationPositionTracker; +import org.jetbrains.debugger.memory.utils.StackFrameDescriptor; import org.jetbrains.java.debugger.breakpoints.properties.JavaLineBreakpointProperties; import java.util.ArrayList; import java.util.HashSet; import java.util.List; +import java.util.Objects; +import java.util.stream.Collectors; public class ConstructorInstancesTracker implements TrackerForNewInstances { private final HashSet myNewObjects = new HashSet<>(); @@ -102,8 +102,18 @@ public class ConstructorInstancesTracker implements TrackerForNewInstances { myNewObjects.add(thisRef); ThreadReferenceProxyImpl threadReferenceProxy = suspendContext.getThread(); List stack = threadReferenceProxy == null ? null : threadReferenceProxy.frames(); - if(stack != null) { - myPositionTracker.addStack(myDebugSession, thisRef, stack); + + + if (stack != null) { + List stackFrameDescriptors = stack.stream().map(frame -> { + try { + Location loc = frame.location(); + return new StackFrameDescriptor(loc.declaringType().name(), frame.getIndexFromBottom(), loc.lineNumber()); + } catch (EvaluateException e) { + return null; + } + }).filter(Objects::nonNull).collect(Collectors.toList()); + myPositionTracker.addStack(myDebugSession, thisRef, stackFrameDescriptors); } } } diff --git a/utils/StackFrameDescriptor.java b/utils/StackFrameDescriptor.java new file mode 100644 index 000000000000..866edf73161c --- /dev/null +++ b/utils/StackFrameDescriptor.java @@ -0,0 +1,29 @@ +package org.jetbrains.debugger.memory.utils; + +import org.jetbrains.annotations.NotNull; + +public class StackFrameDescriptor { + private final String myFilePath; + private int myFrameLevel; + private int myLineNumber; + + public StackFrameDescriptor(@NotNull String path, int frameLevel, int line) { + myFilePath = path.replace('\\', '.'); + myFrameLevel = frameLevel; + myLineNumber = line; + } + + @NotNull + public String path() { + return myFilePath; + } + + public int line() { + return myLineNumber; + } + + @Override + public String toString() { + return String.format("%d. %s:%d", myFrameLevel, myFilePath, myLineNumber); + } +} diff --git a/view/StackFrameSelector.java b/view/StackFrameSelector.java new file mode 100644 index 000000000000..6f80becbdc56 --- /dev/null +++ b/view/StackFrameSelector.java @@ -0,0 +1,88 @@ +package org.jetbrains.debugger.memory.view; + +import com.intellij.codeInsight.navigation.NavigationUtil; +import com.intellij.debugger.engine.DebuggerUtils; +import com.intellij.openapi.editor.Editor; +import com.intellij.openapi.editor.LogicalPosition; +import com.intellij.openapi.editor.ScrollType; +import com.intellij.openapi.project.Project; +import com.intellij.openapi.ui.DialogWrapper; +import com.intellij.openapi.wm.IdeFocusManager; +import com.intellij.psi.PsiClass; +import com.intellij.psi.search.GlobalSearchScope; +import com.intellij.ui.DoubleClickListener; +import com.intellij.ui.components.JBList; +import com.intellij.ui.components.JBScrollPane; +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; +import org.jetbrains.debugger.memory.utils.StackFrameDescriptor; + +import javax.swing.*; +import java.awt.event.MouseEvent; +import java.util.List; + +public class StackFrameSelector extends DialogWrapper { + private final Project myProject; + private final List myStackFrame; + private final Editor myEditor; + + public StackFrameSelector(@NotNull Project project, + @NotNull List stack, + @NotNull Editor editor) { + super(project, false); + myStackFrame = stack; + myProject = project; + myEditor = editor; + setModal(false); + init(); + } + + @Nullable + @Override + protected JComponent createNorthPanel() { + return null; + } + + @Nullable + @Override + protected JComponent createSouthPanel() { + return null; + } + + @Nullable + @Override + protected JComponent createCenterPanel() { + JBList frameList = new JBList(new AbstractListModel() { + @Override + public int getSize() { + return myStackFrame.size(); + } + + @Override + public Object getElementAt(int index) { + return myStackFrame.get(index); + } + }); + + new DoubleClickListener() { + @Override + protected boolean onDoubleClick(MouseEvent event) { + StackFrameDescriptor selectedValue = (StackFrameDescriptor) frameList.getSelectedValue(); + if (selectedValue != null) { + PsiClass psiClass = DebuggerUtils.findClass(selectedValue.path(), myProject, GlobalSearchScope.allScope(myProject)); + if (psiClass != null) { + NavigationUtil.openFileWithPsiElement(psiClass, true, false); + myEditor.getCaretModel().removeSecondaryCarets(); + myEditor.getCaretModel().moveToLogicalPosition(new LogicalPosition(selectedValue.line() - 1, 0, false)); + myEditor.getScrollingModel().scrollToCaret(ScrollType.CENTER); + myEditor.getSelectionModel().removeSelection(); + IdeFocusManager.getGlobalInstance().requestFocus(myEditor.getContentComponent(), true); + } + } + return false; + } + }.installOn(frameList); + + return new JBScrollPane(frameList); + } +} From cf71634697e689e6166c59f1c2e48ca8085c25e2 Mon Sep 17 00:00:00 2001 From: Vitaliy Bibaev Date: Thu, 1 Sep 2016 17:09:48 +0300 Subject: [PATCH 079/151] IDEA-160618 Set 500000 as limitation for instances loading --- view/InstancesWindow.java | 15 ++++++++------- 1 file changed, 8 insertions(+), 7 deletions(-) diff --git a/view/InstancesWindow.java b/view/InstancesWindow.java index b7eb2c39a70b..466a5639de1c 100644 --- a/view/InstancesWindow.java +++ b/view/InstancesWindow.java @@ -73,7 +73,7 @@ public class InstancesWindow extends DialogWrapper { private static final int DEFAULT_WINDOW_HEIGHT = 400; private static final int FILTERING_BUTTON_ADDITIONAL_WIDTH = 30; private static final int BORDER_LAYOUT_DEFAULT_GAP = 5; - private static final int DEFAULT_INSTANCES_COUNT = 0; // All + private static final int DEFAULT_INSTANCES_LIMIT = 500000; // All private final Project myProject; private final XDebugSession myDebugSession; @@ -249,22 +249,23 @@ public class InstancesWindow extends DialogWrapper { public void threadAction(@NotNull SuspendContextImpl suspendContext) { int limit = AndroidUtil.isAndroidVM(myReferenceType.virtualMachine()) ? AndroidUtil.ANDROID_INSTANCES_LIMIT - : DEFAULT_INSTANCES_COUNT; - List instances = myReferenceType.instances(limit); + : DEFAULT_INSTANCES_LIMIT; + List instances = myReferenceType.instances(limit + 1); EvaluationContextImpl evaluationContext = debugProcess .getDebuggerContext().createEvaluationContext(); - if (AndroidUtil.isAndroidVM(myReferenceType.virtualMachine()) - && instances.size() == AndroidUtil.ANDROID_INSTANCES_LIMIT) { - addWarningMessage("Not all instances will be shown (android restriction)"); + if (instances.size() > limit) { + addWarningMessage(String.format("Not all instances will be loaded (only %d)", limit)); + instances = instances.subList(0, limit); } + final int progressSize = instances.size(); if (evaluationContext != null) { synchronized (myFilteringTaskLock) { myFilteringTask = new MyFilteringWorker(instances, evaluationContext, createEvaluator()); SwingUtilities.invokeLater(() -> { - myProgress.start(instances.size()); + myProgress.start(progressSize); myFilteringTask.execute(); }); } From fe91e3b0b893932b6f6a4f32b3a228cc487d5432 Mon Sep 17 00:00:00 2001 From: Vitaliy Bibaev Date: Thu, 1 Sep 2016 20:30:48 +0300 Subject: [PATCH 080/151] Improve UI and logic for instances tracking --- tracking/ConstructorInstancesTracker.java | 68 ++++++++++++++-------- tracking/HashBasedTracking.java | 36 +++++++++++- tracking/InstanceTrackingStrategy.java | 10 +++- tracking/TrackerForNewInstances.java | 3 + tracking/TrackingType.java | 5 +- utils/LowestPriorityCommand.java | 20 +++++++ view/ClassesFilteredView.java | 70 +++++++++++++++++------ view/ClassesTable.java | 38 ++++++------ 8 files changed, 186 insertions(+), 64 deletions(-) create mode 100644 utils/LowestPriorityCommand.java diff --git a/tracking/ConstructorInstancesTracker.java b/tracking/ConstructorInstancesTracker.java index 7c1dee6fd3e2..5d8e4ad9169d 100644 --- a/tracking/ConstructorInstancesTracker.java +++ b/tracking/ConstructorInstancesTracker.java @@ -9,32 +9,39 @@ import com.intellij.debugger.jdi.StackFrameProxyImpl; import com.intellij.debugger.jdi.ThreadReferenceProxyImpl; import com.intellij.debugger.ui.breakpoints.JavaLineBreakpointType; import com.intellij.debugger.ui.breakpoints.LineBreakpoint; +import com.intellij.openapi.Disposable; import com.intellij.openapi.project.Project; import com.intellij.xdebugger.XDebugSession; import com.intellij.xdebugger.breakpoints.XBreakpoint; import com.intellij.xdebugger.impl.XDebuggerManagerImpl; import com.intellij.xdebugger.impl.breakpoints.LineBreakpointState; import com.intellij.xdebugger.impl.breakpoints.XLineBreakpointImpl; -import com.sun.jdi.*; +import com.sun.jdi.Location; +import com.sun.jdi.Method; +import com.sun.jdi.ObjectReference; +import com.sun.jdi.ReferenceType; import com.sun.jdi.event.LocatableEvent; import com.sun.jdi.request.BreakpointRequest; import com.sun.jdi.request.EventRequest; import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; import org.jetbrains.debugger.memory.component.CreationPositionTracker; import org.jetbrains.debugger.memory.utils.StackFrameDescriptor; import org.jetbrains.java.debugger.breakpoints.properties.JavaLineBreakpointProperties; -import java.util.ArrayList; -import java.util.HashSet; -import java.util.List; -import java.util.Objects; +import java.util.*; import java.util.stream.Collectors; -public class ConstructorInstancesTracker implements TrackerForNewInstances { - private final HashSet myNewObjects = new HashSet<>(); +public class ConstructorInstancesTracker implements TrackerForNewInstances, Disposable { private final ReferenceType myReference; private final XDebugSession myDebugSession; private final CreationPositionTracker myPositionTracker; + private final DebugProcessImpl myDebugProcess; + private final MyConstructorBreakpoints myBreakpoint; + @Nullable + private HashSet myNewObjects = null; + @NotNull + private HashSet myTrackedObjects = new HashSet<>(); public ConstructorInstancesTracker(@NotNull ReferenceType ref, @NotNull XDebugSession debugSession) { @@ -42,29 +49,46 @@ public class ConstructorInstancesTracker implements TrackerForNewInstances { myDebugSession = debugSession; myPositionTracker = CreationPositionTracker.getInstance(debugSession.getProject()); Project project = debugSession.getProject(); + myDebugProcess = (DebugProcessImpl) DebuggerManager.getInstance(project) + .getDebugProcess(debugSession.getDebugProcess().getProcessHandler()); JavaLineBreakpointType breakPointType = new JavaLineBreakpointType(); XBreakpoint bpn = new XLineBreakpointImpl<>(breakPointType, ((XDebuggerManagerImpl) XDebuggerManagerImpl.getInstance(project)).getBreakpointManager(), new JavaLineBreakpointProperties(), new LineBreakpointState<>()); - MyConstructorBreakpoints myLineBreakpoint = new MyConstructorBreakpoints(project, bpn); - DebugProcessImpl debugProcess = (DebugProcessImpl) DebuggerManager.getInstance(project) - .getDebugProcess(debugSession.getDebugProcess().getProcessHandler()); - myLineBreakpoint.createRequestForPreparedClass(debugProcess, ref); + myBreakpoint= new MyConstructorBreakpoints(project, bpn); + myBreakpoint.createRequestForPreparedClass(myDebugProcess, ref); } public void obsolete() { - myNewObjects.clear(); + myNewObjects = null; myPositionTracker.releaseBySession(myDebugSession); } - @Override - public List getNewInstances() { - return new ArrayList<>(myNewObjects); + public void commitTracked() { + myNewObjects = myTrackedObjects; + myTrackedObjects = new HashSet<>(); } - private final class MyConstructorBreakpoints extends LineBreakpoint { + @NotNull + @Override + public List getNewInstances() { + return myNewObjects == null ? Collections.EMPTY_LIST : new ArrayList<>(myNewObjects); + } + + @Override + public boolean isReady() { + return myNewObjects != null; + } + + @Override + public void dispose() { + myBreakpoint.dispose(); + } + + private final class MyConstructorBreakpoints extends LineBreakpoint + implements Disposable { private final List myRequests = new ArrayList<>(); MyConstructorBreakpoints(Project project, XBreakpoint xBreakpoint) { @@ -81,14 +105,11 @@ public class ConstructorInstancesTracker implements TrackerForNewInstances { } } - void disable(@NotNull DebugProcessImpl debugProcess) { + @Override + public void dispose() { myRequests.forEach(EventRequest::disable); myRequests.clear(); - debugProcess.getRequestsManager().deleteRequest(this); - } - - @Override - public void reload() { + myDebugProcess.getRequestsManager().deleteRequest(this); } @Override @@ -99,11 +120,10 @@ public class ConstructorInstancesTracker implements TrackerForNewInstances { if (suspendContext != null) { ObjectReference thisRef = getThisObject(suspendContext, event); if (myReference.equals(thisRef.referenceType())) { - myNewObjects.add(thisRef); + myTrackedObjects.add(thisRef); ThreadReferenceProxyImpl threadReferenceProxy = suspendContext.getThread(); List stack = threadReferenceProxy == null ? null : threadReferenceProxy.frames(); - if (stack != null) { List stackFrameDescriptors = stack.stream().map(frame -> { try { diff --git a/tracking/HashBasedTracking.java b/tracking/HashBasedTracking.java index 83955ce354d9..856ee5cfe4f9 100644 --- a/tracking/HashBasedTracking.java +++ b/tracking/HashBasedTracking.java @@ -12,12 +12,16 @@ import java.util.stream.Collectors; class HashBasedTracking extends InstanceTrackingStrategy { private final ReferenceType myClassType; private Set myHashesSet; + private MyState myState = MyState.WAIT_HASHES; - HashBasedTracking(@NotNull SuspendContextImpl suspendContext, + HashBasedTracking(@Nullable SuspendContextImpl suspendContext, @NotNull ReferenceType classType, @NotNull List initialInstances) { myClassType = classType; - myHashesSet = toSetOfHashes(evalHashCodes(suspendContext, initialInstances)); + if(suspendContext != null) { + myHashesSet = toSetOfHashes(evalHashCodes(suspendContext, initialInstances)); + myState = myState.next(); + } } @NotNull @@ -35,9 +39,15 @@ class HashBasedTracking extends InstanceTrackingStrategy { } myHashesSet = toSetOfHashes(ref2hash); + myState = myState.next(); return newInstances; } + @Override + public boolean isReady() { + return myState == MyState.READY; + } + @NotNull private Map> evalHashCodes(@NotNull SuspendContextImpl suspendContext, @NotNull List references) { @@ -89,4 +99,26 @@ class HashBasedTracking extends InstanceTrackingStrategy { .findFirst() .orElseGet(null); } + + // WAIT_HASHES -> WAIT_UPDATE -> READY + enum MyState { + WAIT_HASHES { + @Override + public MyState next() { + return WAIT_UPDATE; + } + }, WAIT_UPDATE { + @Override + public MyState next() { + return READY; + } + }, READY { + @Override + public MyState next() { + return this; + } + }; + + public abstract MyState next(); + } } diff --git a/tracking/InstanceTrackingStrategy.java b/tracking/InstanceTrackingStrategy.java index 48497b16dc43..9385105e4380 100644 --- a/tracking/InstanceTrackingStrategy.java +++ b/tracking/InstanceTrackingStrategy.java @@ -4,6 +4,7 @@ import com.intellij.debugger.engine.SuspendContextImpl; import com.sun.jdi.ObjectReference; import com.sun.jdi.ReferenceType; import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; import java.util.ArrayList; import java.util.Collections; @@ -12,9 +13,10 @@ import java.util.List; public abstract class InstanceTrackingStrategy implements TrackerForNewInstances { @NotNull private List myNewInstances = new ArrayList<>(); + private boolean myIsReady = false; public static InstanceTrackingStrategy create(@NotNull ReferenceType referenceType, - @NotNull SuspendContextImpl suspendContext, + @Nullable SuspendContextImpl suspendContext, @NotNull TrackingType type, @NotNull List init) { switch (type) { @@ -36,6 +38,12 @@ public abstract class InstanceTrackingStrategy implements TrackerForNewInstances public final void update(@NotNull SuspendContextImpl suspendContext, @NotNull List references) { myNewInstances = updateImpl(suspendContext, references); + myIsReady = true; + } + + @Override + public boolean isReady() { + return myIsReady; } @NotNull diff --git a/tracking/TrackerForNewInstances.java b/tracking/TrackerForNewInstances.java index 4ea69620c6a8..d28b930a7b77 100644 --- a/tracking/TrackerForNewInstances.java +++ b/tracking/TrackerForNewInstances.java @@ -1,9 +1,12 @@ package org.jetbrains.debugger.memory.tracking; import com.sun.jdi.ObjectReference; +import org.jetbrains.annotations.NotNull; import java.util.List; public interface TrackerForNewInstances { + @NotNull List getNewInstances(); + boolean isReady(); } diff --git a/tracking/TrackingType.java b/tracking/TrackingType.java index bbdb16794b3e..a000daaade40 100644 --- a/tracking/TrackingType.java +++ b/tracking/TrackingType.java @@ -4,7 +4,8 @@ import com.intellij.ui.JBColor; import org.jetbrains.annotations.NotNull; public enum TrackingType { - IDENTITY("Id Based Tracking", JBColor.yellow), + + IDENTITY("Id Based Tracking",JBColor.yellow), HASH("Hash Based Tracking", JBColor.yellow), RETAIN("Retaining Reference Object Tracking", JBColor.yellow), CREATION("Track Constructors", JBColor.orange); @@ -26,4 +27,6 @@ public enum TrackingType { public String description() { return myDescription; } + + public static JBColor inactiveColor() { return JBColor.GRAY; } } diff --git a/utils/LowestPriorityCommand.java b/utils/LowestPriorityCommand.java new file mode 100644 index 000000000000..214cc5681f66 --- /dev/null +++ b/utils/LowestPriorityCommand.java @@ -0,0 +1,20 @@ +package org.jetbrains.debugger.memory.utils; + +import com.intellij.debugger.engine.SuspendContextImpl; +import com.intellij.debugger.engine.events.SuspendContextCommandImpl; +import org.jetbrains.annotations.Nullable; + +public class LowestPriorityCommand extends SuspendContextCommandImpl { + protected LowestPriorityCommand(@Nullable SuspendContextImpl suspendContext) { + super(suspendContext); + } + + @Override + protected void commandCancelled() { + } + + @Override + public Priority getPriority() { + return Priority.LOWEST; + } +} diff --git a/view/ClassesFilteredView.java b/view/ClassesFilteredView.java index 813450c818d6..af41691039db 100644 --- a/view/ClassesFilteredView.java +++ b/view/ClassesFilteredView.java @@ -4,7 +4,6 @@ import com.intellij.debugger.DebuggerManager; import com.intellij.debugger.engine.DebugProcess; import com.intellij.debugger.engine.DebuggerManagerThreadImpl; import com.intellij.debugger.engine.SuspendContextImpl; -import com.intellij.debugger.engine.events.SuspendContextCommandImpl; import com.intellij.debugger.impl.DebuggerManagerImpl; import com.intellij.openapi.Disposable; import com.intellij.openapi.actionSystem.ActionGroup; @@ -26,6 +25,7 @@ import org.jetbrains.annotations.Nullable; import org.jetbrains.debugger.memory.component.InstancesTracker; import org.jetbrains.debugger.memory.component.MemoryViewManager; import org.jetbrains.debugger.memory.component.MemoryViewManagerState; +import org.jetbrains.debugger.memory.event.InstancesTrackerListener; import org.jetbrains.debugger.memory.event.MemoryViewManagerListener; import org.jetbrains.debugger.memory.tracking.ConstructorInstancesTracker; import org.jetbrains.debugger.memory.tracking.InstanceTrackingStrategy; @@ -33,6 +33,7 @@ import org.jetbrains.debugger.memory.tracking.TrackerForNewInstances; import org.jetbrains.debugger.memory.tracking.TrackingType; import org.jetbrains.debugger.memory.utils.AndroidUtil; import org.jetbrains.debugger.memory.utils.KeyboardUtils; +import org.jetbrains.debugger.memory.utils.LowestPriorityCommand; import org.jetbrains.debugger.memory.utils.SingleAlarmWithMutableDelay; import javax.swing.*; @@ -75,7 +76,27 @@ public class ClassesFilteredView extends BorderLayoutPanel implements Disposable myDebugProcess = DebuggerManager.getInstance(myProject) .getDebugProcess(myDebugSession.getDebugProcess().getProcessHandler()); myInstancesTracker = InstancesTracker.getInstance(myProject); + myInstancesTracker.addTrackerListener(new InstancesTrackerListener() { + @Override + public void classChanged(@NotNull String name, @NotNull TrackingType type) { + ReferenceType ref = myTable.getClassByName(name); + if (ref != null) { + ((DebuggerManagerThreadImpl) myDebugProcess.getManagerThread()) + .schedule(new LowestPriorityCommand(getSuspendContext()) { + @Override + public void contextAction(@NotNull SuspendContextImpl suspendContext) throws Exception { + trackClass(ref, type, suspendContext); + } + }); + } + } + @Override + public void classRemoved(@NotNull String name) { + myTrackedClasses.keySet().removeIf(referenceType -> name.equals(referenceType.name())); + myTable.getRowSorter().allRowsChanged(); + } + }, this); MemoryViewManagerState memoryViewManagerState = MemoryViewManager.getInstance().getState(); myTable = new ClassesTable(myDebugSession, memoryViewManagerState.isShowWithDiffOnly, @@ -127,6 +148,7 @@ public class ClassesFilteredView extends BorderLayoutPanel implements Disposable @Override public void sessionPaused() { if (myNeedReloadClasses) { + myConstructorTrackedClasses.values().forEach(ConstructorInstancesTracker::commitTracked); updateClassesAndCounts(); } } @@ -167,7 +189,7 @@ public class ClassesFilteredView extends BorderLayoutPanel implements Disposable @Nullable TrackerForNewInstances getStrategy(@NotNull ReferenceType ref) { - if(myTrackedClasses.containsKey(ref)) { + if (myTrackedClasses.containsKey(ref)) { return myTrackedClasses.get(ref); } @@ -186,6 +208,20 @@ public class ClassesFilteredView extends BorderLayoutPanel implements Disposable } } + private void trackClass(@NotNull ReferenceType ref, + @NotNull TrackingType type, + @Nullable SuspendContextImpl suspendContext) { + LOG.assertTrue(DebuggerManager.getInstance(myProject).isDebuggerManagerThread()); + if (type == TrackingType.CREATION) { + myConstructorTrackedClasses.put(ref, + new ConstructorInstancesTracker(ref, myDebugSession)); + } else { + List instances = ref.instances(0); + myTrackedClasses.put(ref, InstanceTrackingStrategy.create(ref, suspendContext, type, instances)); + } + } + + private void handleClassSelection(@Nullable ReferenceType ref) { if (ref != null && myDebugSession.isSuspended()) { new InstancesWindow(myDebugSession, ref::instances, ref.name()).show(); @@ -207,34 +243,34 @@ public class ClassesFilteredView extends BorderLayoutPanel implements Disposable return ActionManager.getInstance().createActionPopupMenu("ClassesView.PopupActionGroup", group); } + boolean isTrackingActive(@NotNull ReferenceType ref) { + TrackerForNewInstances strategy = myConstructorTrackedClasses.getOrDefault(ref, null); + if (strategy != null) { + return strategy.isReady(); + } + + strategy = myTrackedClasses.getOrDefault(ref, null); + return strategy != null && strategy.isReady(); + + } + @Override public void dispose() { } - private final class MyUpdateClassesCommand extends SuspendContextCommandImpl { + private final class MyUpdateClassesCommand extends LowestPriorityCommand { MyUpdateClassesCommand(@Nullable SuspendContextImpl suspendContext) { super(suspendContext); } - @Override - public Priority getPriority() { - return Priority.LOWEST; - } - @Override public void contextAction(@NotNull SuspendContextImpl suspendContext) throws Exception { final List classes = myDebugProcess.getVirtualMachineProxy().allClasses(); for (ReferenceType ref : classes) { TrackingType type = myInstancesTracker.getTrackingType(ref.name()); if (type != null && !myTrackedClasses.containsKey(ref) && !myConstructorTrackedClasses.containsKey(ref)) { - if (type == TrackingType.CREATION) { - myConstructorTrackedClasses.put(ref, - new ConstructorInstancesTracker(ref, myDebugSession)); - } else { - List instances = ref.instances(0); - myTrackedClasses.put(ref, InstanceTrackingStrategy.create(ref, suspendContext, type, instances)); - } + trackClass(ref, type, suspendContext); } } @@ -278,10 +314,6 @@ public class ClassesFilteredView extends BorderLayoutPanel implements Disposable }); } - @Override - public void commandCancelled() { - } - private boolean contextIsValid() { return ClassesFilteredView.this.getSuspendContext() == getSuspendContext(); } diff --git a/view/ClassesTable.java b/view/ClassesTable.java index 513138c25be1..664584ad6bb5 100644 --- a/view/ClassesTable.java +++ b/view/ClassesTable.java @@ -7,6 +7,7 @@ import com.intellij.openapi.util.TextRange; import com.intellij.psi.codeStyle.MinusculeMatcher; import com.intellij.psi.codeStyle.NameUtil; import com.intellij.ui.ColoredTableCellRenderer; +import com.intellij.ui.JBColor; import com.intellij.ui.SimpleTextAttributes; import com.intellij.ui.speedSearch.SpeedSearchUtil; import com.intellij.ui.table.JBTable; @@ -21,7 +22,6 @@ import org.jetbrains.annotations.NonNls; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; import org.jetbrains.debugger.memory.component.InstancesTracker; -import org.jetbrains.debugger.memory.event.InstancesTrackerListener; import org.jetbrains.debugger.memory.tracking.TrackerForNewInstances; import org.jetbrains.debugger.memory.tracking.TrackingType; import org.jetbrains.debugger.memory.utils.AbstractTableColumnDescriptor; @@ -75,19 +75,6 @@ public class ClassesTable extends JBTable implements DataProvider, Disposable { getEmptyText().setText(EMPTY_TABLE_CONTENT); - InstancesTracker tracker = InstancesTracker.getInstance(myDebugSession.getProject()); - tracker.addTrackerListener(new InstancesTrackerListener() { - @Override - public void classChanged(@NotNull String name, @NotNull TrackingType type) { - getRowSorter().allRowsChanged(); - } - - @Override - public void classRemoved(@NotNull String name) { - getRowSorter().allRowsChanged(); - } - }, this); - TableColumn classesColumn = getColumnModel().getColumn(DiffViewTableModel.CLASSNAME_COLUMN_INDEX); TableColumn countColumn = getColumnModel().getColumn(DiffViewTableModel.COUNT_COLUMN_INDEX); TableColumn diffColumn = getColumnModel().getColumn(DiffViewTableModel.DIFF_COLUMN_INDEX); @@ -144,6 +131,17 @@ public class ClassesTable extends JBTable implements DataProvider, Disposable { return null; } + @Nullable + ReferenceType getClassByName(@NotNull String name) { + for(ReferenceType ref : myElems) { + if(name.equals(ref.name())) { + return ref; + } + } + + return null; + } + void setBusy(boolean value) { setPaintBusy(value); } @@ -211,7 +209,7 @@ public class ClassesTable extends JBTable implements DataProvider, Disposable { ReferenceType selectedClass = getSelectedClass(); if (selectedClass != null) { TrackerForNewInstances strategy = myParent.getStrategy(selectedClass); - if (strategy != null) { + if (strategy != null && strategy.isReady()) { List newInstances = strategy.getNewInstances(); return (InstancesProvider) limit -> newInstances; } @@ -359,7 +357,10 @@ public class ClassesTable extends JBTable implements DataProvider, Disposable { TrackingType trackingType = getTrackingType(row); if (!isSelected && trackingType != null) { - setBackground(UIUtil.toAlpha(trackingType.color(), 20)); + JBColor color = myParent.isTrackingActive(myElems.get(convertRowIndexToModel(row))) + ? trackingType.color() + : TrackingType.inactiveColor(); + setBackground(UIUtil.toAlpha(color, 20)); } long val = (long) value; @@ -390,7 +391,10 @@ public class ClassesTable extends JBTable implements DataProvider, Disposable { String presentation = value == null ? "null" : ((ReferenceType) value).name(); TrackingType trackingType = getTrackingType(row); if (trackingType != null && !isSelected) { - setBackground(UIUtil.toAlpha(trackingType.color(), 20)); + JBColor color = myParent.isTrackingActive(myElems.get(convertRowIndexToModel(row))) + ? trackingType.color() + : TrackingType.inactiveColor(); + setBackground(UIUtil.toAlpha(color, 20)); } append(" "); From 6ce7e15e60310ed49a75a13a660e559092ea7569 Mon Sep 17 00:00:00 2001 From: Vitaliy Bibaev Date: Thu, 1 Sep 2016 23:45:07 +0300 Subject: [PATCH 081/151] Fix order of updating for new trackers --- tracking/ConstructorInstancesTracker.java | 5 +++++ view/ClassesFilteredView.java | 9 +++++---- 2 files changed, 10 insertions(+), 4 deletions(-) diff --git a/tracking/ConstructorInstancesTracker.java b/tracking/ConstructorInstancesTracker.java index 5d8e4ad9169d..5cc111eadfbd 100644 --- a/tracking/ConstructorInstancesTracker.java +++ b/tracking/ConstructorInstancesTracker.java @@ -57,6 +57,7 @@ public class ConstructorInstancesTracker implements TrackerForNewInstances, Disp ((XDebuggerManagerImpl) XDebuggerManagerImpl.getInstance(project)).getBreakpointManager(), new JavaLineBreakpointProperties(), new LineBreakpointState<>()); + myBreakpoint= new MyConstructorBreakpoints(project, bpn); myBreakpoint.createRequestForPreparedClass(myDebugProcess, ref); } @@ -105,6 +106,10 @@ public class ConstructorInstancesTracker implements TrackerForNewInstances, Disp } } + @Override + public void reload() { + } + @Override public void dispose() { myRequests.forEach(EventRequest::disable); diff --git a/view/ClassesFilteredView.java b/view/ClassesFilteredView.java index af41691039db..c29fd1d083a7 100644 --- a/view/ClassesFilteredView.java +++ b/view/ClassesFilteredView.java @@ -267,6 +267,11 @@ public class ClassesFilteredView extends BorderLayoutPanel implements Disposable @Override public void contextAction(@NotNull SuspendContextImpl suspendContext) throws Exception { final List classes = myDebugProcess.getVirtualMachineProxy().allClasses(); + + for (Map.Entry entry : myTrackedClasses.entrySet()) { + entry.getValue().update(suspendContext, entry.getKey().instances(0)); + } + for (ReferenceType ref : classes) { TrackingType type = myInstancesTracker.getTrackingType(ref.name()); if (type != null && !myTrackedClasses.containsKey(ref) && !myConstructorTrackedClasses.containsKey(ref)) { @@ -274,10 +279,6 @@ public class ClassesFilteredView extends BorderLayoutPanel implements Disposable } } - for (Map.Entry entry : myTrackedClasses.entrySet()) { - entry.getValue().update(suspendContext, entry.getKey().instances(0)); - } - if (classes.isEmpty()) { return; } From a76d9cba063fbaa78baa8049ad4f95d459c35f2d Mon Sep 17 00:00:00 2001 From: Vitaliy Bibaev Date: Fri, 2 Sep 2016 12:06:28 +0300 Subject: [PATCH 082/151] Dispose constructor trackers correctly --- tracking/ConstructorInstancesTracker.java | 24 ++++++++++++----------- view/ClassesFilteredView.java | 11 ++++++++--- 2 files changed, 21 insertions(+), 14 deletions(-) diff --git a/tracking/ConstructorInstancesTracker.java b/tracking/ConstructorInstancesTracker.java index 5cc111eadfbd..eaf5a4511e1e 100644 --- a/tracking/ConstructorInstancesTracker.java +++ b/tracking/ConstructorInstancesTracker.java @@ -4,6 +4,7 @@ import com.intellij.debugger.DebuggerManager; import com.intellij.debugger.engine.DebugProcessImpl; import com.intellij.debugger.engine.SuspendContextImpl; import com.intellij.debugger.engine.evaluation.EvaluateException; +import com.intellij.debugger.engine.events.DebuggerCommandImpl; import com.intellij.debugger.engine.events.SuspendContextCommandImpl; import com.intellij.debugger.jdi.StackFrameProxyImpl; import com.intellij.debugger.jdi.ThreadReferenceProxyImpl; @@ -11,6 +12,7 @@ import com.intellij.debugger.ui.breakpoints.JavaLineBreakpointType; import com.intellij.debugger.ui.breakpoints.LineBreakpoint; import com.intellij.openapi.Disposable; import com.intellij.openapi.project.Project; +import com.intellij.openapi.util.Disposer; import com.intellij.xdebugger.XDebugSession; import com.intellij.xdebugger.breakpoints.XBreakpoint; import com.intellij.xdebugger.impl.XDebuggerManagerImpl; @@ -22,7 +24,6 @@ import com.sun.jdi.ObjectReference; import com.sun.jdi.ReferenceType; import com.sun.jdi.event.LocatableEvent; import com.sun.jdi.request.BreakpointRequest; -import com.sun.jdi.request.EventRequest; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; import org.jetbrains.debugger.memory.component.CreationPositionTracker; @@ -37,7 +38,6 @@ public class ConstructorInstancesTracker implements TrackerForNewInstances, Disp private final XDebugSession myDebugSession; private final CreationPositionTracker myPositionTracker; private final DebugProcessImpl myDebugProcess; - private final MyConstructorBreakpoints myBreakpoint; @Nullable private HashSet myNewObjects = null; @NotNull @@ -58,8 +58,9 @@ public class ConstructorInstancesTracker implements TrackerForNewInstances, Disp new JavaLineBreakpointProperties(), new LineBreakpointState<>()); - myBreakpoint= new MyConstructorBreakpoints(project, bpn); - myBreakpoint.createRequestForPreparedClass(myDebugProcess, ref); + MyConstructorBreakpoints breakpoint = new MyConstructorBreakpoints(project, bpn); + breakpoint.createRequestForPreparedClass(myDebugProcess, ref); + Disposer.register(this, breakpoint); } public void obsolete() { @@ -85,12 +86,10 @@ public class ConstructorInstancesTracker implements TrackerForNewInstances, Disp @Override public void dispose() { - myBreakpoint.dispose(); } private final class MyConstructorBreakpoints extends LineBreakpoint implements Disposable { - private final List myRequests = new ArrayList<>(); MyConstructorBreakpoints(Project project, XBreakpoint xBreakpoint) { super(project, xBreakpoint); @@ -102,7 +101,6 @@ public class ConstructorInstancesTracker implements TrackerForNewInstances, Disp Location loc = cons.location(); BreakpointRequest breakpointRequest = debugProcess.getRequestsManager().createBreakpointRequest(this, loc); breakpointRequest.enable(); - myRequests.add(breakpointRequest); } } @@ -112,9 +110,12 @@ public class ConstructorInstancesTracker implements TrackerForNewInstances, Disp @Override public void dispose() { - myRequests.forEach(EventRequest::disable); - myRequests.clear(); - myDebugProcess.getRequestsManager().deleteRequest(this); + myDebugProcess.getManagerThread().schedule(new DebuggerCommandImpl() { + @Override + protected void action() throws Exception { + myDebugProcess.getRequestsManager().deleteRequest(MyConstructorBreakpoints.this); + } + }); } @Override @@ -133,7 +134,8 @@ public class ConstructorInstancesTracker implements TrackerForNewInstances, Disp List stackFrameDescriptors = stack.stream().map(frame -> { try { Location loc = frame.location(); - return new StackFrameDescriptor(loc.declaringType().name(), frame.getIndexFromBottom(), loc.lineNumber()); + return new StackFrameDescriptor(loc.declaringType().name(), + frame.getIndexFromBottom(), loc.lineNumber()); } catch (EvaluateException e) { return null; } diff --git a/view/ClassesFilteredView.java b/view/ClassesFilteredView.java index c29fd1d083a7..43c1bb4fbea5 100644 --- a/view/ClassesFilteredView.java +++ b/view/ClassesFilteredView.java @@ -213,15 +213,20 @@ public class ClassesFilteredView extends BorderLayoutPanel implements Disposable @Nullable SuspendContextImpl suspendContext) { LOG.assertTrue(DebuggerManager.getInstance(myProject).isDebuggerManagerThread()); if (type == TrackingType.CREATION) { - myConstructorTrackedClasses.put(ref, - new ConstructorInstancesTracker(ref, myDebugSession)); + ConstructorInstancesTracker old = myConstructorTrackedClasses.getOrDefault(ref, null); + if (old != null) { + Disposer.dispose(old); + } + + ConstructorInstancesTracker tracker = new ConstructorInstancesTracker(ref, myDebugSession); + Disposer.register(ClassesFilteredView.this, tracker); + myConstructorTrackedClasses.put(ref, tracker); } else { List instances = ref.instances(0); myTrackedClasses.put(ref, InstanceTrackingStrategy.create(ref, suspendContext, type, instances)); } } - private void handleClassSelection(@Nullable ReferenceType ref) { if (ref != null && myDebugSession.isSuspended()) { new InstancesWindow(myDebugSession, ref::instances, ref.name()).show(); From b6a24d0379976f0c6083ea56444f61dbc499a8da Mon Sep 17 00:00:00 2001 From: Vitaliy Bibaev Date: Fri, 2 Sep 2016 19:13:47 +0300 Subject: [PATCH 083/151] Add navigation for stack frames for new instances --- .../tracking/GetAllocationSourceAction.java | 14 ++++++--- view/StackFrameSelector.java | 31 +++++++++---------- 2 files changed, 23 insertions(+), 22 deletions(-) diff --git a/action/tracking/GetAllocationSourceAction.java b/action/tracking/GetAllocationSourceAction.java index cfc3cd9d89b0..555d6309d3e5 100644 --- a/action/tracking/GetAllocationSourceAction.java +++ b/action/tracking/GetAllocationSourceAction.java @@ -1,9 +1,9 @@ package org.jetbrains.debugger.memory.action.tracking; +import com.intellij.debugger.DebuggerManager; import com.intellij.openapi.actionSystem.AnActionEvent; -import com.intellij.openapi.editor.Editor; -import com.intellij.openapi.fileEditor.FileEditorManager; import com.intellij.openapi.project.Project; +import com.intellij.psi.search.GlobalSearchScope; import com.intellij.xdebugger.XDebugSession; import com.intellij.xdebugger.XDebuggerManager; import com.intellij.xdebugger.impl.ui.tree.nodes.XValueNodeImpl; @@ -27,9 +27,13 @@ public class GetAllocationSourceAction extends DebuggerTreeAction { protected void perform(XValueNodeImpl node, @NotNull String nodeName, AnActionEvent e) { Project project = e.getProject(); List stack = getStack(e); - Editor editor = project == null ? null : FileEditorManager.getInstance(project).getSelectedTextEditor(); - if(project != null && stack != null && editor != null) { - new StackFrameSelector(project, stack, editor).show(); + if(project != null && stack != null) { + XDebugSession session = XDebuggerManager.getInstance(project).getCurrentSession(); + if(session != null) { + GlobalSearchScope searchScope = DebuggerManager.getInstance(project) + .getDebugProcess(session.getDebugProcess().getProcessHandler()).getSearchScope(); + new StackFrameSelector(project, stack, searchScope).show(); + } } } diff --git a/view/StackFrameSelector.java b/view/StackFrameSelector.java index 6f80becbdc56..52df1c30baeb 100644 --- a/view/StackFrameSelector.java +++ b/view/StackFrameSelector.java @@ -1,13 +1,10 @@ package org.jetbrains.debugger.memory.view; -import com.intellij.codeInsight.navigation.NavigationUtil; import com.intellij.debugger.engine.DebuggerUtils; -import com.intellij.openapi.editor.Editor; -import com.intellij.openapi.editor.LogicalPosition; -import com.intellij.openapi.editor.ScrollType; +import com.intellij.execution.filters.OpenFileHyperlinkInfo; +import com.intellij.openapi.application.ApplicationManager; import com.intellij.openapi.project.Project; import com.intellij.openapi.ui.DialogWrapper; -import com.intellij.openapi.wm.IdeFocusManager; import com.intellij.psi.PsiClass; import com.intellij.psi.search.GlobalSearchScope; import com.intellij.ui.DoubleClickListener; @@ -24,15 +21,15 @@ import java.util.List; public class StackFrameSelector extends DialogWrapper { private final Project myProject; private final List myStackFrame; - private final Editor myEditor; + private final GlobalSearchScope mySearchScope; public StackFrameSelector(@NotNull Project project, @NotNull List stack, - @NotNull Editor editor) { + @NotNull GlobalSearchScope searchScope) { super(project, false); - myStackFrame = stack; myProject = project; - myEditor = editor; + myStackFrame = stack; + mySearchScope = searchScope; setModal(false); init(); } @@ -69,14 +66,14 @@ public class StackFrameSelector extends DialogWrapper { protected boolean onDoubleClick(MouseEvent event) { StackFrameDescriptor selectedValue = (StackFrameDescriptor) frameList.getSelectedValue(); if (selectedValue != null) { - PsiClass psiClass = DebuggerUtils.findClass(selectedValue.path(), myProject, GlobalSearchScope.allScope(myProject)); - if (psiClass != null) { - NavigationUtil.openFileWithPsiElement(psiClass, true, false); - myEditor.getCaretModel().removeSecondaryCarets(); - myEditor.getCaretModel().moveToLogicalPosition(new LogicalPosition(selectedValue.line() - 1, 0, false)); - myEditor.getScrollingModel().scrollToCaret(ScrollType.CENTER); - myEditor.getSelectionModel().removeSelection(); - IdeFocusManager.getGlobalInstance().requestFocus(myEditor.getContentComponent(), true); + PsiClass psiClass = DebuggerUtils.findClass(selectedValue.path(), myProject, mySearchScope); + if(psiClass != null) { + ApplicationManager.getApplication().runReadAction(() -> { + OpenFileHyperlinkInfo info = + new OpenFileHyperlinkInfo(myProject, + psiClass.getContainingFile().getVirtualFile(), selectedValue.line() - 1); + info.navigate(myProject); + }); } } return false; From 186ba9408c9145a9b63ad36d0c9070408e1750d4 Mon Sep 17 00:00:00 2001 From: Vitaliy Bibaev Date: Fri, 2 Sep 2016 20:05:26 +0300 Subject: [PATCH 084/151] Add basic implementation for tracking unloaded classes --- component/InstancesTracker.java | 14 ++++++++-- tracking/ConstructorInstancesTracker.java | 9 ++++++- view/ClassesFilteredView.java | 33 +++++++++++++++++------ 3 files changed, 45 insertions(+), 11 deletions(-) diff --git a/component/InstancesTracker.java b/component/InstancesTracker.java index 77145325508f..13e1d5705883 100644 --- a/component/InstancesTracker.java +++ b/component/InstancesTracker.java @@ -11,8 +11,10 @@ import org.jetbrains.annotations.Nullable; import org.jetbrains.debugger.memory.event.InstancesTrackerListener; import org.jetbrains.debugger.memory.tracking.TrackingType; +import java.util.List; import java.util.Map; import java.util.concurrent.ConcurrentHashMap; +import java.util.stream.Collectors; @State(name = "InstancesTracker", storages = @Storage(StoragePathMacros.WORKSPACE_FILE)) public class InstancesTracker extends AbstractProjectComponent @@ -38,9 +40,17 @@ public class InstancesTracker extends AbstractProjectComponent return myState.classes.getOrDefault(className, null); } + @NotNull + public List getClassesByTrackingType(@NotNull TrackingType type) { + return myState.classes.entrySet().stream() + .filter(entry -> entry.getValue().equals(type)) + .map(Map.Entry::getKey) + .collect(Collectors.toList()); + } + public void add(@NotNull ReferenceType ref, @NotNull TrackingType type) { String name = ref.name(); - if(type.equals(myState.classes.getOrDefault(name, null))) { + if (type.equals(myState.classes.getOrDefault(name, null))) { return; } @@ -51,7 +61,7 @@ public class InstancesTracker extends AbstractProjectComponent public boolean remove(@NotNull ReferenceType ref) { String name = ref.name(); TrackingType removed = myState.classes.remove(name); - if(removed != null) { + if (removed != null) { myDispatcher.getMulticaster().classRemoved(name); return true; } diff --git a/tracking/ConstructorInstancesTracker.java b/tracking/ConstructorInstancesTracker.java index eaf5a4511e1e..777936f8114e 100644 --- a/tracking/ConstructorInstancesTracker.java +++ b/tracking/ConstructorInstancesTracker.java @@ -1,6 +1,7 @@ package org.jetbrains.debugger.memory.tracking; import com.intellij.debugger.DebuggerManager; +import com.intellij.debugger.engine.DebugProcess; import com.intellij.debugger.engine.DebugProcessImpl; import com.intellij.debugger.engine.SuspendContextImpl; import com.intellij.debugger.engine.evaluation.EvaluateException; @@ -59,7 +60,8 @@ public class ConstructorInstancesTracker implements TrackerForNewInstances, Disp new LineBreakpointState<>()); MyConstructorBreakpoints breakpoint = new MyConstructorBreakpoints(project, bpn); - breakpoint.createRequestForPreparedClass(myDebugProcess, ref); + myDebugProcess.getRequestsManager().callbackOnPrepareClasses(breakpoint, myReference.name()); + Disposer.register(this, breakpoint); } @@ -95,6 +97,11 @@ public class ConstructorInstancesTracker implements TrackerForNewInstances, Disp super(project, xBreakpoint); } + @Override + public void processClassPrepare(DebugProcess debugProcess, ReferenceType classType) { + createRequestForPreparedClass(myDebugProcess, classType); + } + @Override protected void createRequestForPreparedClass(DebugProcessImpl debugProcess, ReferenceType classType) { for (Method cons : classType.methodsByName("")) { diff --git a/view/ClassesFilteredView.java b/view/ClassesFilteredView.java index 43c1bb4fbea5..a492ab0d54e9 100644 --- a/view/ClassesFilteredView.java +++ b/view/ClassesFilteredView.java @@ -1,9 +1,9 @@ package org.jetbrains.debugger.memory.view; import com.intellij.debugger.DebuggerManager; -import com.intellij.debugger.engine.DebugProcess; -import com.intellij.debugger.engine.DebuggerManagerThreadImpl; +import com.intellij.debugger.engine.DebugProcessImpl; import com.intellij.debugger.engine.SuspendContextImpl; +import com.intellij.debugger.engine.events.DebuggerCommandImpl; import com.intellij.debugger.impl.DebuggerManagerImpl; import com.intellij.openapi.Disposable; import com.intellij.openapi.actionSystem.ActionGroup; @@ -56,7 +56,7 @@ public class ClassesFilteredView extends BorderLayoutPanel implements Disposable private final Project myProject; private final XDebugSession myDebugSession; - private final DebugProcess myDebugProcess; + private final DebugProcessImpl myDebugProcess; private final SingleAlarmWithMutableDelay mySingleAlarm; private final SearchTextField myFilterTextField = new SearchTextField(false); @@ -73,15 +73,15 @@ public class ClassesFilteredView extends BorderLayoutPanel implements Disposable myProject = debugSession.getProject(); myDebugSession = debugSession; - myDebugProcess = DebuggerManager.getInstance(myProject) + myDebugProcess = (DebugProcessImpl) DebuggerManager.getInstance(myProject) .getDebugProcess(myDebugSession.getDebugProcess().getProcessHandler()); myInstancesTracker = InstancesTracker.getInstance(myProject); - myInstancesTracker.addTrackerListener(new InstancesTrackerListener() { + InstancesTrackerListener instancesTrackerListener = new InstancesTrackerListener() { @Override public void classChanged(@NotNull String name, @NotNull TrackingType type) { ReferenceType ref = myTable.getClassByName(name); if (ref != null) { - ((DebuggerManagerThreadImpl) myDebugProcess.getManagerThread()) + myDebugProcess.getManagerThread() .schedule(new LowestPriorityCommand(getSuspendContext()) { @Override public void contextAction(@NotNull SuspendContextImpl suspendContext) throws Exception { @@ -96,7 +96,24 @@ public class ClassesFilteredView extends BorderLayoutPanel implements Disposable myTrackedClasses.keySet().removeIf(referenceType -> name.equals(referenceType.name())); myTable.getRowSorter().allRowsChanged(); } - }, this); + }; + + myDebugProcess.getManagerThread().schedule(new DebuggerCommandImpl() { + @Override + protected void action() throws Exception { + List constructorTrackerClasses = myInstancesTracker.getClassesByTrackingType(TrackingType.CREATION); + myInstancesTracker.addTrackerListener(instancesTrackerListener, ClassesFilteredView.this); + for (String className : constructorTrackerClasses) { + List classes = myDebugProcess.getVirtualMachineProxy().classesByName(className); + if (classes.isEmpty()) { + // TODO: track class preparation + } else { + // TODO: foreach add breakpoint + } + } + } + }); + MemoryViewManagerState memoryViewManagerState = MemoryViewManager.getInstance().getState(); myTable = new ClassesTable(myDebugSession, memoryViewManagerState.isShowWithDiffOnly, @@ -166,7 +183,7 @@ public class ClassesFilteredView extends BorderLayoutPanel implements Disposable if (myLastSuspendContext != null) { SwingUtilities.invokeLater(() -> { myTable.setBusy(true); - ((DebuggerManagerThreadImpl) myDebugProcess.getManagerThread()) + myDebugProcess.getManagerThread() .schedule(new MyUpdateClassesCommand(myLastSuspendContext)); }); } From 5b9a24adc7814de1071d041232f9e36909591666 Mon Sep 17 00:00:00 2001 From: Vitaliy Bibaev Date: Mon, 5 Sep 2016 15:16:01 +0300 Subject: [PATCH 085/151] Start tracking new instances when class loaded instead of first breakpoint after loading --- component/InstancesTracker.java | 6 ++++ tracking/ClassPreparedListener.java | 44 +++++++++++++++++++++++ tracking/ConstructorInstancesTracker.java | 8 +---- view/ClassesFilteredView.java | 36 ++++++++++--------- 4 files changed, 71 insertions(+), 23 deletions(-) create mode 100644 tracking/ClassPreparedListener.java diff --git a/component/InstancesTracker.java b/component/InstancesTracker.java index 13e1d5705883..74c725a98815 100644 --- a/component/InstancesTracker.java +++ b/component/InstancesTracker.java @@ -4,6 +4,7 @@ import com.intellij.openapi.Disposable; import com.intellij.openapi.components.*; import com.intellij.openapi.project.Project; import com.intellij.util.EventDispatcher; +import com.intellij.util.containers.HashMap; import com.intellij.util.xmlb.annotations.AbstractCollection; import com.sun.jdi.ReferenceType; import org.jetbrains.annotations.NotNull; @@ -40,6 +41,11 @@ public class InstancesTracker extends AbstractProjectComponent return myState.classes.getOrDefault(className, null); } + @NotNull + public Map getTrackedClasses() { + return new HashMap<>(myState.classes); + } + @NotNull public List getClassesByTrackingType(@NotNull TrackingType type) { return myState.classes.entrySet().stream() diff --git a/tracking/ClassPreparedListener.java b/tracking/ClassPreparedListener.java new file mode 100644 index 000000000000..f102d4905e30 --- /dev/null +++ b/tracking/ClassPreparedListener.java @@ -0,0 +1,44 @@ +package org.jetbrains.debugger.memory.tracking; + +import com.intellij.debugger.DebuggerManager; +import com.intellij.debugger.engine.DebugProcess; +import com.intellij.debugger.engine.DebugProcessImpl; +import com.intellij.debugger.requests.ClassPrepareRequestor; +import com.intellij.openapi.project.Project; +import com.intellij.xdebugger.XDebugSession; +import com.sun.jdi.ReferenceType; +import com.sun.jdi.request.ClassPrepareRequest; +import org.jetbrains.annotations.NotNull; + +public abstract class ClassPreparedListener { + private final XDebugSession myDebugSession; + private final Project myProject; + + protected ClassPreparedListener(@NotNull String className, + @NotNull XDebugSession debugSession) { + myProject = debugSession.getProject(); + myDebugSession = debugSession; + DebugProcessImpl debugProcess = getDebugProcess(); + ClassPrepareRequestor request = new MyClassPreparedRequest(); + ClassPrepareRequest classPrepareRequest = debugProcess.getRequestsManager() + .createClassPrepareRequest(request, className); + if (classPrepareRequest != null) { + classPrepareRequest.enable(); + } + } + + public abstract void onClassPrepared(@NotNull ReferenceType referenceType); + + private DebugProcessImpl getDebugProcess() { + return (DebugProcessImpl) DebuggerManager.getInstance(myProject) + .getDebugProcess(myDebugSession.getDebugProcess().getProcessHandler()); + } + + private final class MyClassPreparedRequest implements ClassPrepareRequestor { + @Override + public void processClassPrepare(DebugProcess debuggerProcess, ReferenceType referenceType) { + getDebugProcess().getRequestsManager().deleteRequest(this); + onClassPrepared(referenceType); + } + } +} diff --git a/tracking/ConstructorInstancesTracker.java b/tracking/ConstructorInstancesTracker.java index 777936f8114e..123e7db18a9d 100644 --- a/tracking/ConstructorInstancesTracker.java +++ b/tracking/ConstructorInstancesTracker.java @@ -1,7 +1,6 @@ package org.jetbrains.debugger.memory.tracking; import com.intellij.debugger.DebuggerManager; -import com.intellij.debugger.engine.DebugProcess; import com.intellij.debugger.engine.DebugProcessImpl; import com.intellij.debugger.engine.SuspendContextImpl; import com.intellij.debugger.engine.evaluation.EvaluateException; @@ -60,7 +59,7 @@ public class ConstructorInstancesTracker implements TrackerForNewInstances, Disp new LineBreakpointState<>()); MyConstructorBreakpoints breakpoint = new MyConstructorBreakpoints(project, bpn); - myDebugProcess.getRequestsManager().callbackOnPrepareClasses(breakpoint, myReference.name()); + breakpoint.createRequestForPreparedClass(myDebugProcess, myReference); Disposer.register(this, breakpoint); } @@ -97,11 +96,6 @@ public class ConstructorInstancesTracker implements TrackerForNewInstances, Disp super(project, xBreakpoint); } - @Override - public void processClassPrepare(DebugProcess debugProcess, ReferenceType classType) { - createRequestForPreparedClass(myDebugProcess, classType); - } - @Override protected void createRequestForPreparedClass(DebugProcessImpl debugProcess, ReferenceType classType) { for (Method cons : classType.methodsByName("")) { diff --git a/view/ClassesFilteredView.java b/view/ClassesFilteredView.java index a492ab0d54e9..3f6937ed15cd 100644 --- a/view/ClassesFilteredView.java +++ b/view/ClassesFilteredView.java @@ -27,10 +27,7 @@ import org.jetbrains.debugger.memory.component.MemoryViewManager; import org.jetbrains.debugger.memory.component.MemoryViewManagerState; import org.jetbrains.debugger.memory.event.InstancesTrackerListener; import org.jetbrains.debugger.memory.event.MemoryViewManagerListener; -import org.jetbrains.debugger.memory.tracking.ConstructorInstancesTracker; -import org.jetbrains.debugger.memory.tracking.InstanceTrackingStrategy; -import org.jetbrains.debugger.memory.tracking.TrackerForNewInstances; -import org.jetbrains.debugger.memory.tracking.TrackingType; +import org.jetbrains.debugger.memory.tracking.*; import org.jetbrains.debugger.memory.utils.AndroidUtil; import org.jetbrains.debugger.memory.utils.KeyboardUtils; import org.jetbrains.debugger.memory.utils.LowestPriorityCommand; @@ -99,18 +96,32 @@ public class ClassesFilteredView extends BorderLayoutPanel implements Disposable }; myDebugProcess.getManagerThread().schedule(new DebuggerCommandImpl() { + @Override + public Priority getPriority() { + return Priority.LOWEST; + } + @Override protected void action() throws Exception { - List constructorTrackerClasses = myInstancesTracker.getClassesByTrackingType(TrackingType.CREATION); - myInstancesTracker.addTrackerListener(instancesTrackerListener, ClassesFilteredView.this); - for (String className : constructorTrackerClasses) { + for(Map.Entry entry : myInstancesTracker.getTrackedClasses().entrySet()) { + TrackingType type = entry.getValue(); + String className = entry.getKey(); List classes = myDebugProcess.getVirtualMachineProxy().classesByName(className); if (classes.isEmpty()) { - // TODO: track class preparation + new ClassPreparedListener(className, myDebugSession) { + @Override + public void onClassPrepared(@NotNull ReferenceType referenceType) { + trackClass(referenceType, type, getSuspendContext()); + } + }; } else { - // TODO: foreach add breakpoint + for(ReferenceType ref : classes) { + trackClass(ref, type, getSuspendContext()); + } } } + + myInstancesTracker.addTrackerListener(instancesTrackerListener, ClassesFilteredView.this); } }); @@ -294,13 +305,6 @@ public class ClassesFilteredView extends BorderLayoutPanel implements Disposable entry.getValue().update(suspendContext, entry.getKey().instances(0)); } - for (ReferenceType ref : classes) { - TrackingType type = myInstancesTracker.getTrackingType(ref.name()); - if (type != null && !myTrackedClasses.containsKey(ref) && !myConstructorTrackedClasses.containsKey(ref)) { - trackClass(ref, type, suspendContext); - } - } - if (classes.isEmpty()) { return; } From 3b4a97b8b398ba0472c357dab27391e74113ba6e Mon Sep 17 00:00:00 2001 From: Vitaliy Bibaev Date: Mon, 5 Sep 2016 22:55:31 +0300 Subject: [PATCH 086/151] Support hashCode based tracking for arrays too --- tracking/HashBasedTracking.java | 74 +++++++++++++++++++++++++-------- 1 file changed, 56 insertions(+), 18 deletions(-) diff --git a/tracking/HashBasedTracking.java b/tracking/HashBasedTracking.java index 856ee5cfe4f9..80723075b04e 100644 --- a/tracking/HashBasedTracking.java +++ b/tracking/HashBasedTracking.java @@ -2,15 +2,20 @@ package org.jetbrains.debugger.memory.tracking; import com.intellij.debugger.engine.SuspendContextImpl; import com.intellij.debugger.engine.jdi.ThreadReferenceProxy; +import com.intellij.openapi.diagnostic.Logger; import com.sun.jdi.*; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; import java.util.*; +import java.util.concurrent.TimeUnit; import java.util.stream.Collectors; class HashBasedTracking extends InstanceTrackingStrategy { + private static final Logger LOG = Logger.getInstance(HashBasedTracking.class); + private final ReferenceType myClassType; + private MyHashCodeMethodWrapper myHashCodeWrapper; private Set myHashesSet; private MyState myState = MyState.WAIT_HASHES; @@ -18,7 +23,7 @@ class HashBasedTracking extends InstanceTrackingStrategy { @NotNull ReferenceType classType, @NotNull List initialInstances) { myClassType = classType; - if(suspendContext != null) { + if (suspendContext != null) { myHashesSet = toSetOfHashes(evalHashCodes(suspendContext, initialInstances)); myState = myState.next(); } @@ -31,9 +36,9 @@ class HashBasedTracking extends InstanceTrackingStrategy { Map> ref2hash = evalHashCodes(suspendContext, references); List newInstances = new ArrayList<>(); - for(ObjectReference ref : references) { + for (ObjectReference ref : references) { Optional hash = ref2hash.get(ref); - if(hash.isPresent() && !myHashesSet.contains(hash.get())) { + if (hash.isPresent() && !myState.equals(MyState.WAIT_HASHES) && !myHashesSet.contains(hash.get())) { newInstances.add(ref); } } @@ -51,33 +56,37 @@ class HashBasedTracking extends InstanceTrackingStrategy { @NotNull private Map> evalHashCodes(@NotNull SuspendContextImpl suspendContext, @NotNull List references) { - Method hashCodeMethod = getHashCodeMethod(myClassType); + MyHashCodeMethodWrapper hashCodeCaller = getHashCodeMethod(myClassType); Map> result = new HashMap<>(); ThreadReferenceProxy threadProxy = suspendContext.getThread(); ThreadReference thread = threadProxy != null ? threadProxy.getThreadReference() : null; - if(thread == null || hashCodeMethod == null) { + if (thread == null || hashCodeCaller == null) { + LOG.warn(thread == null ? "Thread is null" : "hashCode not found"); return result; } + long start = System.nanoTime(); for (ObjectReference ref : references) { Optional hash = Optional.empty(); try { - if(!ref.isCollected()) { + if (!ref.isCollected()) { ref.disableCollection(); - if(!ref.isCollected()) { - Value value = ref.invokeMethod(thread, hashCodeMethod, Collections.emptyList(), - ObjectReference.INVOKE_NONVIRTUAL); - hash = Optional.of(((IntegerValue)value).value()); + if (!ref.isCollected()) { + hash = hashCodeCaller.eval(ref, suspendContext, thread); } ref.enableCollection(); } } catch (InvalidTypeException | ClassNotLoadedException | - IncompatibleThreadStateException | InvocationException ignored) { + IncompatibleThreadStateException | InvocationException e) { + LOG.warn("Hash code evaluation failed", e); } result.put(ref, hash); } + long duration = TimeUnit.NANOSECONDS.toMillis(System.nanoTime() - start); + LOG.info(String.format("Hash code evaluation: %d ms. Count = %d", duration, references.size())); + return result; } @@ -89,15 +98,38 @@ class HashBasedTracking extends InstanceTrackingStrategy { } @Nullable - private Method getHashCodeMethod(@Nullable ReferenceType referenceType) { - if (referenceType == null) { - return null; + private MyHashCodeMethodWrapper getHashCodeMethod(@NotNull ReferenceType referenceType) { + if (myHashCodeWrapper != null) { + return myHashCodeWrapper; } - return referenceType.methodsByName("hashCode").stream() - .filter(method -> method.argumentTypeNames().isEmpty()) - .findFirst() - .orElseGet(null); + final List referenceTypes = referenceType.virtualMachine().classesByName("java.util.Objects"); + if (!referenceTypes.isEmpty()) { + ClassType objectsClass = (ClassType) referenceTypes.get(0); + Method hashCodeFromObjects = objectsClass.methodsByName("hashCode").stream() + .filter(method -> method.argumentTypeNames().size() == 1 + && "java.lang.Object".equals(method.argumentTypeNames().get(0))) + .findFirst() + .orElse(null); + + myHashCodeWrapper = (ref, suspendContext, thread) -> { + final Value value = objectsClass.invokeMethod(thread, hashCodeFromObjects, + Collections.singletonList(ref), ClassType.INVOKE_SINGLE_THREADED); + return Optional.of(((IntegerValue) value).value()); + }; + } else { + final Method hashCode = referenceType.methodsByName("hashCode").stream() + .filter(method -> method.argumentTypeNames().isEmpty()) + .findFirst() + .orElse(null); + + myHashCodeWrapper = hashCode == null ? null : (ref, suspendContext, thread) -> { + final Value value = ref.invokeMethod(thread, hashCode, Collections.emptyList(), ClassType.INVOKE_SINGLE_THREADED); + return Optional.of(((IntegerValue) value).value()); + }; + } + + return myHashCodeWrapper; } // WAIT_HASHES -> WAIT_UPDATE -> READY @@ -121,4 +153,10 @@ class HashBasedTracking extends InstanceTrackingStrategy { public abstract MyState next(); } + + private interface MyHashCodeMethodWrapper { + Optional eval(@NotNull ObjectReference ref, @NotNull SuspendContextImpl suspendContext, + @NotNull ThreadReference thread) throws InvocationException, InvalidTypeException, + ClassNotLoadedException, IncompatibleThreadStateException; + } } From 2a2401046dd2185adce917ae0329a7f94ff3ff95 Mon Sep 17 00:00:00 2001 From: Vitaliy Bibaev Date: Tue, 6 Sep 2016 13:17:56 +0300 Subject: [PATCH 087/151] Replace dialog window with popup window when "Jump To Allocation" action performed --- .../tracking/GetAllocationSourceAction.java | 4 +- component/CreationPositionTracker.java | 3 +- view/StackFramePopup.java | 90 +++++++++++++++++++ 3 files changed, 94 insertions(+), 3 deletions(-) create mode 100644 view/StackFramePopup.java diff --git a/action/tracking/GetAllocationSourceAction.java b/action/tracking/GetAllocationSourceAction.java index 555d6309d3e5..e396d989ae6e 100644 --- a/action/tracking/GetAllocationSourceAction.java +++ b/action/tracking/GetAllocationSourceAction.java @@ -13,7 +13,7 @@ import org.jetbrains.annotations.Nullable; import org.jetbrains.debugger.memory.action.DebuggerTreeAction; import org.jetbrains.debugger.memory.component.CreationPositionTracker; import org.jetbrains.debugger.memory.utils.StackFrameDescriptor; -import org.jetbrains.debugger.memory.view.StackFrameSelector; +import org.jetbrains.debugger.memory.view.StackFramePopup; import java.util.List; @@ -32,7 +32,7 @@ public class GetAllocationSourceAction extends DebuggerTreeAction { if(session != null) { GlobalSearchScope searchScope = DebuggerManager.getInstance(project) .getDebugProcess(session.getDebugProcess().getProcessHandler()).getSearchScope(); - new StackFrameSelector(project, stack, searchScope).show(); + new StackFramePopup(project, stack, searchScope).show(); } } } diff --git a/component/CreationPositionTracker.java b/component/CreationPositionTracker.java index cb16fcd98a3a..6045d9089b41 100644 --- a/component/CreationPositionTracker.java +++ b/component/CreationPositionTracker.java @@ -33,7 +33,8 @@ public class CreationPositionTracker extends AbstractProjectComponent { return stack != null ? Collections.unmodifiableList(stack) : null; } - public void addStack(@NotNull XDebugSession session, @NotNull ObjectReference ref, @NotNull List stack) { + public void addStack(@NotNull XDebugSession session, @NotNull ObjectReference ref, + @NotNull List stack) { if (!mySession2Reference2Stack.containsKey(session)) { mySession2Reference2Stack.put(session, new ConcurrentHashMap<>()); session.addSessionListener(new XDebugSessionListener() { diff --git a/view/StackFramePopup.java b/view/StackFramePopup.java new file mode 100644 index 000000000000..8e517c469756 --- /dev/null +++ b/view/StackFramePopup.java @@ -0,0 +1,90 @@ +package org.jetbrains.debugger.memory.view; + +import com.intellij.debugger.engine.DebuggerUtils; +import com.intellij.execution.filters.OpenFileHyperlinkInfo; +import com.intellij.openapi.application.ApplicationManager; +import com.intellij.openapi.fileEditor.FileEditorManager; +import com.intellij.openapi.fileEditor.OpenFileDescriptor; +import com.intellij.openapi.project.Project; +import com.intellij.openapi.ui.popup.JBPopup; +import com.intellij.openapi.ui.popup.JBPopupFactory; +import com.intellij.psi.PsiClass; +import com.intellij.psi.search.GlobalSearchScope; +import com.intellij.ui.components.JBList; +import org.jetbrains.annotations.NotNull; +import org.jetbrains.debugger.memory.utils.KeyboardUtils; +import org.jetbrains.debugger.memory.utils.StackFrameDescriptor; + +import javax.swing.*; +import java.awt.event.KeyAdapter; +import java.awt.event.KeyEvent; +import java.util.List; + +public class StackFramePopup { + private final Project myProject; + private final List myStackFrame; + private final GlobalSearchScope myScope; + + public StackFramePopup(@NotNull Project project, + @NotNull List stack, + @NotNull GlobalSearchScope searchScope) { + myProject = project; + myStackFrame = stack; + myScope = searchScope; + } + + public void show() { + JBList list = createStackList(); + JBPopup popup = JBPopupFactory.getInstance().createListPopupBuilder(list) + .setTitle("Select stack frame") + .setAutoSelectIfEmpty(true) + .setResizable(false) + .createPopup(); + popup.showInFocusCenter(); + } + + private JBList createStackList() { + JBList frameList = new JBList(new AbstractListModel() { + @Override + public int getSize() { + return myStackFrame.size(); + } + + @Override + public Object getElementAt(int index) { + return myStackFrame.get(index); + } + }); + + frameList.addKeyListener(new KeyAdapter() { + @Override + public void keyReleased(KeyEvent e) { + if (KeyboardUtils.isEnterKey(e.getKeyCode())) { + navigateToSelectedFrame(frameList, true); + } + } + }); + + frameList.addListSelectionListener(e -> navigateToSelectedFrame(frameList, false)); + + return frameList; + } + + private void navigateToSelectedFrame(@NotNull JBList frameList, boolean focusEditor) { + StackFrameDescriptor frame = (StackFrameDescriptor) frameList.getSelectedValue(); + if (frame != null) { + PsiClass psiClass = DebuggerUtils.findClass(frame.path(), myProject, myScope); + if (psiClass != null) { + ApplicationManager.getApplication().runReadAction(() -> { + OpenFileHyperlinkInfo info = + new OpenFileHyperlinkInfo(myProject, psiClass.getContainingFile().getVirtualFile(), + frame.line() - 1); + OpenFileDescriptor descriptor = info.getDescriptor(); + if (descriptor != null) { + FileEditorManager.getInstance(myProject).openTextEditor(descriptor, focusEditor); + } + }); + } + } + } +} From b077d49315746e2534c14efca854ed7c32049943 Mon Sep 17 00:00:00 2001 From: Vitaliy Bibaev Date: Tue, 6 Sep 2016 13:55:29 +0300 Subject: [PATCH 088/151] New renderer for list with stack frame --- tracking/ConstructorInstancesTracker.java | 5 ++-- utils/StackFrameDescriptor.java | 29 ++++++++++++++++------- view/StackFramePopup.java | 11 +++++++++ 3 files changed, 34 insertions(+), 11 deletions(-) diff --git a/tracking/ConstructorInstancesTracker.java b/tracking/ConstructorInstancesTracker.java index 123e7db18a9d..1fd7fa93dc64 100644 --- a/tracking/ConstructorInstancesTracker.java +++ b/tracking/ConstructorInstancesTracker.java @@ -135,8 +135,9 @@ public class ConstructorInstancesTracker implements TrackerForNewInstances, Disp List stackFrameDescriptors = stack.stream().map(frame -> { try { Location loc = frame.location(); - return new StackFrameDescriptor(loc.declaringType().name(), - frame.getIndexFromBottom(), loc.lineNumber()); + String typeName = loc.declaringType().name(); + String methodName = loc.method().name(); + return new StackFrameDescriptor(typeName, methodName, loc.lineNumber()); } catch (EvaluateException e) { return null; } diff --git a/utils/StackFrameDescriptor.java b/utils/StackFrameDescriptor.java index 866edf73161c..7d1ca9d6ebef 100644 --- a/utils/StackFrameDescriptor.java +++ b/utils/StackFrameDescriptor.java @@ -1,15 +1,16 @@ package org.jetbrains.debugger.memory.utils; +import com.intellij.openapi.util.text.StringUtil; import org.jetbrains.annotations.NotNull; public class StackFrameDescriptor { private final String myFilePath; - private int myFrameLevel; - private int myLineNumber; + private final String myMethodName; + private final int myLineNumber; - public StackFrameDescriptor(@NotNull String path, int frameLevel, int line) { + public StackFrameDescriptor(@NotNull String path, @NotNull String methodName, int line) { myFilePath = path.replace('\\', '.'); - myFrameLevel = frameLevel; + myMethodName = methodName; myLineNumber = line; } @@ -18,12 +19,22 @@ public class StackFrameDescriptor { return myFilePath; } + @NotNull + public String methodName() { + return myMethodName; + } + + @NotNull + public String className() { + return StringUtil.getShortName(myFilePath); + } + + @NotNull + public String packageName() { + return StringUtil.getPackageName(myFilePath); + } + public int line() { return myLineNumber; } - - @Override - public String toString() { - return String.format("%d. %s:%d", myFrameLevel, myFilePath, myLineNumber); - } } diff --git a/view/StackFramePopup.java b/view/StackFramePopup.java index 8e517c469756..45f9d6df09da 100644 --- a/view/StackFramePopup.java +++ b/view/StackFramePopup.java @@ -10,6 +10,8 @@ import com.intellij.openapi.ui.popup.JBPopup; import com.intellij.openapi.ui.popup.JBPopupFactory; import com.intellij.psi.PsiClass; import com.intellij.psi.search.GlobalSearchScope; +import com.intellij.ui.ColoredListCellRenderer; +import com.intellij.ui.SimpleTextAttributes; import com.intellij.ui.components.JBList; import org.jetbrains.annotations.NotNull; import org.jetbrains.debugger.memory.utils.KeyboardUtils; @@ -56,6 +58,15 @@ public class StackFramePopup { } }); + frameList.setCellRenderer(new ColoredListCellRenderer() { + @Override + protected void customizeCellRenderer(@NotNull JList list, + StackFrameDescriptor value, int index, boolean isSelected, boolean hasFocus) { + append(String.format("%s:%d, %s", value.methodName(), value.line(), value.className())); + append(String.format(" (%s)", value.packageName()), SimpleTextAttributes.GRAYED_ITALIC_ATTRIBUTES); + } + }); + frameList.addKeyListener(new KeyAdapter() { @Override public void keyReleased(KeyEvent e) { From e672175654d7356040946596220779adb10e398c Mon Sep 17 00:00:00 2001 From: Vitaliy Bibaev Date: Tue, 6 Sep 2016 16:20:49 +0300 Subject: [PATCH 089/151] Remove redundant code. Not show empty parens if package is empty in stack popup --- component/InstancesTracker.java | 10 ---- view/StackFramePopup.java | 6 ++- view/StackFrameSelector.java | 85 --------------------------------- 3 files changed, 5 insertions(+), 96 deletions(-) delete mode 100644 view/StackFrameSelector.java diff --git a/component/InstancesTracker.java b/component/InstancesTracker.java index 74c725a98815..28c416bec251 100644 --- a/component/InstancesTracker.java +++ b/component/InstancesTracker.java @@ -12,10 +12,8 @@ import org.jetbrains.annotations.Nullable; import org.jetbrains.debugger.memory.event.InstancesTrackerListener; import org.jetbrains.debugger.memory.tracking.TrackingType; -import java.util.List; import java.util.Map; import java.util.concurrent.ConcurrentHashMap; -import java.util.stream.Collectors; @State(name = "InstancesTracker", storages = @Storage(StoragePathMacros.WORKSPACE_FILE)) public class InstancesTracker extends AbstractProjectComponent @@ -46,14 +44,6 @@ public class InstancesTracker extends AbstractProjectComponent return new HashMap<>(myState.classes); } - @NotNull - public List getClassesByTrackingType(@NotNull TrackingType type) { - return myState.classes.entrySet().stream() - .filter(entry -> entry.getValue().equals(type)) - .map(Map.Entry::getKey) - .collect(Collectors.toList()); - } - public void add(@NotNull ReferenceType ref, @NotNull TrackingType type) { String name = ref.name(); if (type.equals(myState.classes.getOrDefault(name, null))) { diff --git a/view/StackFramePopup.java b/view/StackFramePopup.java index 45f9d6df09da..196efcd47fd9 100644 --- a/view/StackFramePopup.java +++ b/view/StackFramePopup.java @@ -13,6 +13,7 @@ import com.intellij.psi.search.GlobalSearchScope; import com.intellij.ui.ColoredListCellRenderer; import com.intellij.ui.SimpleTextAttributes; import com.intellij.ui.components.JBList; +import org.apache.commons.lang.StringUtils; import org.jetbrains.annotations.NotNull; import org.jetbrains.debugger.memory.utils.KeyboardUtils; import org.jetbrains.debugger.memory.utils.StackFrameDescriptor; @@ -63,7 +64,10 @@ public class StackFramePopup { protected void customizeCellRenderer(@NotNull JList list, StackFrameDescriptor value, int index, boolean isSelected, boolean hasFocus) { append(String.format("%s:%d, %s", value.methodName(), value.line(), value.className())); - append(String.format(" (%s)", value.packageName()), SimpleTextAttributes.GRAYED_ITALIC_ATTRIBUTES); + String packageName = value.packageName(); + if(!StringUtils.isEmpty(packageName)) { + append(String.format(" (%s)", value.packageName()), SimpleTextAttributes.GRAYED_ITALIC_ATTRIBUTES); + } } }); diff --git a/view/StackFrameSelector.java b/view/StackFrameSelector.java deleted file mode 100644 index 52df1c30baeb..000000000000 --- a/view/StackFrameSelector.java +++ /dev/null @@ -1,85 +0,0 @@ -package org.jetbrains.debugger.memory.view; - -import com.intellij.debugger.engine.DebuggerUtils; -import com.intellij.execution.filters.OpenFileHyperlinkInfo; -import com.intellij.openapi.application.ApplicationManager; -import com.intellij.openapi.project.Project; -import com.intellij.openapi.ui.DialogWrapper; -import com.intellij.psi.PsiClass; -import com.intellij.psi.search.GlobalSearchScope; -import com.intellij.ui.DoubleClickListener; -import com.intellij.ui.components.JBList; -import com.intellij.ui.components.JBScrollPane; -import org.jetbrains.annotations.NotNull; -import org.jetbrains.annotations.Nullable; -import org.jetbrains.debugger.memory.utils.StackFrameDescriptor; - -import javax.swing.*; -import java.awt.event.MouseEvent; -import java.util.List; - -public class StackFrameSelector extends DialogWrapper { - private final Project myProject; - private final List myStackFrame; - private final GlobalSearchScope mySearchScope; - - public StackFrameSelector(@NotNull Project project, - @NotNull List stack, - @NotNull GlobalSearchScope searchScope) { - super(project, false); - myProject = project; - myStackFrame = stack; - mySearchScope = searchScope; - setModal(false); - init(); - } - - @Nullable - @Override - protected JComponent createNorthPanel() { - return null; - } - - @Nullable - @Override - protected JComponent createSouthPanel() { - return null; - } - - @Nullable - @Override - protected JComponent createCenterPanel() { - JBList frameList = new JBList(new AbstractListModel() { - @Override - public int getSize() { - return myStackFrame.size(); - } - - @Override - public Object getElementAt(int index) { - return myStackFrame.get(index); - } - }); - - new DoubleClickListener() { - @Override - protected boolean onDoubleClick(MouseEvent event) { - StackFrameDescriptor selectedValue = (StackFrameDescriptor) frameList.getSelectedValue(); - if (selectedValue != null) { - PsiClass psiClass = DebuggerUtils.findClass(selectedValue.path(), myProject, mySearchScope); - if(psiClass != null) { - ApplicationManager.getApplication().runReadAction(() -> { - OpenFileHyperlinkInfo info = - new OpenFileHyperlinkInfo(myProject, - psiClass.getContainingFile().getVirtualFile(), selectedValue.line() - 1); - info.navigate(myProject); - }); - } - } - return false; - } - }.installOn(frameList); - - return new JBScrollPane(frameList); - } -} From 62e401700f50b136f3a52f63c86b21dc1d0cd4e4 Mon Sep 17 00:00:00 2001 From: Vitaliy Bibaev Date: Tue, 6 Sep 2016 17:34:27 +0300 Subject: [PATCH 090/151] Show label "The application is running" by default in table with classes --- view/ClassesFilteredView.java | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/view/ClassesFilteredView.java b/view/ClassesFilteredView.java index 91094adce788..e6f1b09fde6a 100644 --- a/view/ClassesFilteredView.java +++ b/view/ClassesFilteredView.java @@ -53,7 +53,6 @@ public class ClassesFilteredView extends BorderLayoutPanel implements Disposable private static final String EMPTY_TABLE_CONTENT_WHEN_RUNNED = "The application is running"; private static final String EMPTY_TABLE_CONTENT_WHEN_SUSPENDED = "Nothing to show"; - private final Project myProject; private final XDebugSession myDebugSession; private final DebugProcessImpl myDebugProcess; @@ -132,6 +131,7 @@ public class ClassesFilteredView extends BorderLayoutPanel implements Disposable myTable = new ClassesTable(myDebugSession, memoryViewManagerState.isShowWithDiffOnly, memoryViewManagerState.isShowWithInstancesOnly, this); + myTable.getEmptyText().setText(EMPTY_TABLE_CONTENT_WHEN_RUNNED); Disposer.register(this, myTable); myTable.addKeyListener(new KeyAdapter() { @@ -195,7 +195,10 @@ public class ClassesFilteredView extends BorderLayoutPanel implements Disposable @Override public void sessionResumed() { myConstructorTrackedClasses.values().forEach(ConstructorInstancesTracker::obsolete); - SwingUtilities.invokeLater(myTable::hideContent); + SwingUtilities.invokeLater(() -> { + myTable.getEmptyText().setText(EMPTY_TABLE_CONTENT_WHEN_RUNNED); + myTable.hideContent(); + }); mySingleAlarm.cancelAllRequests(); } From 8f89ed371a4ccd443f835819e1e11d72e5f04c31 Mon Sep 17 00:00:00 2001 From: Vitaliy Bibaev Date: Tue, 6 Sep 2016 18:54:45 +0300 Subject: [PATCH 091/151] Fix bug when breakpoint added into parents' constructors --- tracking/ConstructorInstancesTracker.java | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/tracking/ConstructorInstancesTracker.java b/tracking/ConstructorInstancesTracker.java index 1fd7fa93dc64..923bb64a926a 100644 --- a/tracking/ConstructorInstancesTracker.java +++ b/tracking/ConstructorInstancesTracker.java @@ -98,11 +98,11 @@ public class ConstructorInstancesTracker implements TrackerForNewInstances, Disp @Override protected void createRequestForPreparedClass(DebugProcessImpl debugProcess, ReferenceType classType) { - for (Method cons : classType.methodsByName("")) { + classType.methods().stream().filter(Method::isConstructor).forEach(cons -> { Location loc = cons.location(); BreakpointRequest breakpointRequest = debugProcess.getRequestsManager().createBreakpointRequest(this, loc); breakpointRequest.enable(); - } + }); } @Override From 747fc289533dfe6e06a51dd3c8d389efbebeec5b Mon Sep 17 00:00:00 2001 From: Vitaliy Bibaev Date: Tue, 6 Sep 2016 18:56:01 +0300 Subject: [PATCH 092/151] Fix bug when "Show New Instances" action is enabled, but tracker is not ready --- action/ShowNewInstancesAction.java | 8 +++----- 1 file changed, 3 insertions(+), 5 deletions(-) diff --git a/action/ShowNewInstancesAction.java b/action/ShowNewInstancesAction.java index 7261022a63ca..08ea5b76b6e3 100644 --- a/action/ShowNewInstancesAction.java +++ b/action/ShowNewInstancesAction.java @@ -1,10 +1,8 @@ package org.jetbrains.debugger.memory.action; import com.intellij.openapi.actionSystem.AnActionEvent; -import com.intellij.openapi.project.Project; import com.intellij.xdebugger.XDebugSession; import com.sun.jdi.ReferenceType; -import org.jetbrains.debugger.memory.component.InstancesTracker; import org.jetbrains.debugger.memory.utils.InstancesProvider; import org.jetbrains.debugger.memory.view.ClassesTable; import org.jetbrains.debugger.memory.view.InstancesWindow; @@ -12,10 +10,10 @@ import org.jetbrains.debugger.memory.view.InstancesWindow; public class ShowNewInstancesAction extends ClassesActionBase { @Override protected boolean isEnabled(AnActionEvent e) { - Project project = e.getProject(); + XDebugSession session = getDebugSession(e); ReferenceType selectedClass = getSelectedClass(e); - return super.isEnabled(e) && selectedClass != null && - project != null && InstancesTracker.getInstance(project).isTracked(selectedClass.name()); + InstancesProvider provider = e.getData(ClassesTable.NEW_INSTANCES_PROVIDER_KEY); + return super.isEnabled(e) && session != null && selectedClass != null && provider != null; } @Override From 8db6df92fc0d0a2ec1991edc0466cd56f012bf07 Mon Sep 17 00:00:00 2001 From: Vitaliy Bibaev Date: Tue, 6 Sep 2016 19:01:52 +0300 Subject: [PATCH 093/151] Fix race when item in popup with stack was closed earlier then navigation to source --- view/StackFramePopup.java | 17 ++++------------- 1 file changed, 4 insertions(+), 13 deletions(-) diff --git a/view/StackFramePopup.java b/view/StackFramePopup.java index 196efcd47fd9..64a70330e53d 100644 --- a/view/StackFramePopup.java +++ b/view/StackFramePopup.java @@ -15,12 +15,9 @@ import com.intellij.ui.SimpleTextAttributes; import com.intellij.ui.components.JBList; import org.apache.commons.lang.StringUtils; import org.jetbrains.annotations.NotNull; -import org.jetbrains.debugger.memory.utils.KeyboardUtils; import org.jetbrains.debugger.memory.utils.StackFrameDescriptor; import javax.swing.*; -import java.awt.event.KeyAdapter; -import java.awt.event.KeyEvent; import java.util.List; public class StackFramePopup { @@ -38,11 +35,14 @@ public class StackFramePopup { public void show() { JBList list = createStackList(); + JBPopup popup = JBPopupFactory.getInstance().createListPopupBuilder(list) .setTitle("Select stack frame") .setAutoSelectIfEmpty(true) .setResizable(false) + .setItemChoosenCallback(() -> navigateToSelectedFrame(list, true)) .createPopup(); + popup.showInFocusCenter(); } @@ -65,21 +65,12 @@ public class StackFramePopup { StackFrameDescriptor value, int index, boolean isSelected, boolean hasFocus) { append(String.format("%s:%d, %s", value.methodName(), value.line(), value.className())); String packageName = value.packageName(); - if(!StringUtils.isEmpty(packageName)) { + if (!StringUtils.isEmpty(packageName)) { append(String.format(" (%s)", value.packageName()), SimpleTextAttributes.GRAYED_ITALIC_ATTRIBUTES); } } }); - frameList.addKeyListener(new KeyAdapter() { - @Override - public void keyReleased(KeyEvent e) { - if (KeyboardUtils.isEnterKey(e.getKeyCode())) { - navigateToSelectedFrame(frameList, true); - } - } - }); - frameList.addListSelectionListener(e -> navigateToSelectedFrame(frameList, false)); return frameList; From 8fd5f061727c24aeeafd2ddaf72c646d197f2e2d Mon Sep 17 00:00:00 2001 From: Vitaliy Bibaev Date: Fri, 16 Sep 2016 16:41:43 +0300 Subject: [PATCH 094/151] Disable hash, id, retain trackings --- action/tracking/HashBasedTrackingAction.java | 1 - .../tracking/TrackInstanceCreationAction.java | 2 +- view/ClassesFilteredView.java | 40 ++++++------------- 3 files changed, 13 insertions(+), 30 deletions(-) diff --git a/action/tracking/HashBasedTrackingAction.java b/action/tracking/HashBasedTrackingAction.java index 8a8c29c2444d..c9aa65fa102b 100644 --- a/action/tracking/HashBasedTrackingAction.java +++ b/action/tracking/HashBasedTrackingAction.java @@ -10,4 +10,3 @@ public class HashBasedTrackingAction extends InstancesTrackingActionBase { return TrackingType.HASH; } } - diff --git a/action/tracking/TrackInstanceCreationAction.java b/action/tracking/TrackInstanceCreationAction.java index e8cfc3f9c9d8..810884643cc9 100644 --- a/action/tracking/TrackInstanceCreationAction.java +++ b/action/tracking/TrackInstanceCreationAction.java @@ -3,7 +3,7 @@ package org.jetbrains.debugger.memory.action.tracking; import org.jetbrains.annotations.NotNull; import org.jetbrains.debugger.memory.tracking.TrackingType; -public class TrackInstanceCreationAction extends InstancesTrackingActionBase{ +public class TrackInstanceCreationAction extends InstancesTrackingActionBase { @NotNull @Override protected TrackingType getType() { diff --git a/view/ClassesFilteredView.java b/view/ClassesFilteredView.java index e6f1b09fde6a..ac883146a947 100644 --- a/view/ClassesFilteredView.java +++ b/view/ClassesFilteredView.java @@ -17,7 +17,6 @@ import com.intellij.util.SmartList; import com.intellij.util.ui.components.BorderLayoutPanel; import com.intellij.xdebugger.XDebugSession; import com.intellij.xdebugger.XDebugSessionListener; -import com.sun.jdi.ObjectReference; import com.sun.jdi.ReferenceType; import com.sun.jdi.VirtualMachine; import org.jetbrains.annotations.NotNull; @@ -27,12 +26,16 @@ import org.jetbrains.debugger.memory.component.MemoryViewManager; import org.jetbrains.debugger.memory.component.MemoryViewManagerState; import org.jetbrains.debugger.memory.event.InstancesTrackerListener; import org.jetbrains.debugger.memory.event.MemoryViewManagerListener; -import org.jetbrains.debugger.memory.tracking.*; +import org.jetbrains.debugger.memory.tracking.ClassPreparedListener; +import org.jetbrains.debugger.memory.tracking.ConstructorInstancesTracker; +import org.jetbrains.debugger.memory.tracking.TrackerForNewInstances; +import org.jetbrains.debugger.memory.tracking.TrackingType; import org.jetbrains.debugger.memory.utils.AndroidUtil; import org.jetbrains.debugger.memory.utils.KeyboardUtils; import org.jetbrains.debugger.memory.utils.LowestPriorityCommand; import org.jetbrains.debugger.memory.utils.SingleAlarmWithMutableDelay; +import javax.swing.FocusManager; import javax.swing.*; import javax.swing.event.DocumentEvent; import java.awt.*; @@ -61,7 +64,6 @@ public class ClassesFilteredView extends BorderLayoutPanel implements Disposable private final SearchTextField myFilterTextField = new FilterTextField(); private final ClassesTable myTable; private final InstancesTracker myInstancesTracker; - private final Map myTrackedClasses = new ConcurrentHashMap<>(); private final Map myConstructorTrackedClasses = new ConcurrentHashMap<>(); private volatile SuspendContextImpl myLastSuspendContext; @@ -84,7 +86,7 @@ public class ClassesFilteredView extends BorderLayoutPanel implements Disposable .schedule(new LowestPriorityCommand(getSuspendContext()) { @Override public void contextAction(@NotNull SuspendContextImpl suspendContext) throws Exception { - trackClass(ref, type, suspendContext); + trackClass(ref, type); } }); } @@ -92,7 +94,6 @@ public class ClassesFilteredView extends BorderLayoutPanel implements Disposable @Override public void classRemoved(@NotNull String name) { - myTrackedClasses.keySet().removeIf(referenceType -> name.equals(referenceType.name())); myTable.getRowSorter().allRowsChanged(); } }; @@ -105,7 +106,7 @@ public class ClassesFilteredView extends BorderLayoutPanel implements Disposable @Override protected void action() throws Exception { - for(Map.Entry entry : myInstancesTracker.getTrackedClasses().entrySet()) { + for (Map.Entry entry : myInstancesTracker.getTrackedClasses().entrySet()) { TrackingType type = entry.getValue(); String className = entry.getKey(); List classes = myDebugProcess.getVirtualMachineProxy().classesByName(className); @@ -113,12 +114,12 @@ public class ClassesFilteredView extends BorderLayoutPanel implements Disposable new ClassPreparedListener(className, myDebugSession) { @Override public void onClassPrepared(@NotNull ReferenceType referenceType) { - trackClass(referenceType, type, getSuspendContext()); + trackClass(referenceType, type); } }; } else { - for(ReferenceType ref : classes) { - trackClass(ref, type, getSuspendContext()); + for (ReferenceType ref : classes) { + trackClass(ref, type); } } } @@ -245,10 +246,6 @@ public class ClassesFilteredView extends BorderLayoutPanel implements Disposable @Nullable TrackerForNewInstances getStrategy(@NotNull ReferenceType ref) { - if (myTrackedClasses.containsKey(ref)) { - return myTrackedClasses.get(ref); - } - return myConstructorTrackedClasses.getOrDefault(ref, null); } @@ -265,8 +262,7 @@ public class ClassesFilteredView extends BorderLayoutPanel implements Disposable } private void trackClass(@NotNull ReferenceType ref, - @NotNull TrackingType type, - @Nullable SuspendContextImpl suspendContext) { + @NotNull TrackingType type) { LOG.assertTrue(DebuggerManager.getInstance(myProject).isDebuggerManagerThread()); if (type == TrackingType.CREATION) { ConstructorInstancesTracker old = myConstructorTrackedClasses.getOrDefault(ref, null); @@ -277,9 +273,6 @@ public class ClassesFilteredView extends BorderLayoutPanel implements Disposable ConstructorInstancesTracker tracker = new ConstructorInstancesTracker(ref, myDebugSession); Disposer.register(ClassesFilteredView.this, tracker); myConstructorTrackedClasses.put(ref, tracker); - } else { - List instances = ref.instances(0); - myTrackedClasses.put(ref, InstanceTrackingStrategy.create(ref, suspendContext, type, instances)); } } @@ -306,11 +299,6 @@ public class ClassesFilteredView extends BorderLayoutPanel implements Disposable boolean isTrackingActive(@NotNull ReferenceType ref) { TrackerForNewInstances strategy = myConstructorTrackedClasses.getOrDefault(ref, null); - if (strategy != null) { - return strategy.isReady(); - } - - strategy = myTrackedClasses.getOrDefault(ref, null); return strategy != null && strategy.isReady(); } @@ -329,10 +317,6 @@ public class ClassesFilteredView extends BorderLayoutPanel implements Disposable public void contextAction(@NotNull SuspendContextImpl suspendContext) throws Exception { final List classes = myDebugProcess.getVirtualMachineProxy().allClasses(); - for (Map.Entry entry : myTrackedClasses.entrySet()) { - entry.getValue().update(suspendContext, entry.getKey().instances(0)); - } - if (classes.isEmpty()) { return; } @@ -359,7 +343,7 @@ public class ClassesFilteredView extends BorderLayoutPanel implements Disposable LOG.info(String.format("Instances query time = %d ms. Count = %d", delay, batch.size())); } - if(isContextValid()) { + if (isContextValid()) { final long[] counts = chunks.size() == 1 ? chunks.get(0) : IntStream.range(0, chunks.size()).boxed() .flatMapToLong(integer -> Arrays.stream(chunks.get(integer))) .toArray(); From a74350f5428d0c2ca3d5a7388ea06b863764bcbb Mon Sep 17 00:00:00 2001 From: Vitaliy Bibaev Date: Fri, 16 Sep 2016 18:33:04 +0300 Subject: [PATCH 095/151] Add custom renderer for 'Diff' column --- view/ClassesTable.java | 101 +++++++++++++++++++---------------------- 1 file changed, 47 insertions(+), 54 deletions(-) diff --git a/view/ClassesTable.java b/view/ClassesTable.java index 7cf693f2c32d..00c40ed6bf8b 100644 --- a/view/ClassesTable.java +++ b/view/ClassesTable.java @@ -39,6 +39,7 @@ public class ClassesTable extends JBTable implements DataProvider, Disposable { public static final DataKey DEBUG_SESSION_KEY = DataKey.create("ClassesTable.DebugSession"); public static final DataKey NEW_INSTANCES_PROVIDER_KEY = DataKey.create("ClassesTable.NewInstances"); + private static final int CLASSES_COLUMN_PREFERRED_WIDTH = 250; private static final int COUNT_COLUMN_MIN_WIDTH = 80; private static final int COUNT_COLUMN_MAX_WIDTH = 100; @@ -90,8 +91,9 @@ public class ClassesTable extends JBTable implements DataProvider, Disposable { setShowGrid(false); setIntercellSpacing(new JBDimension(0, 0)); - setDefaultRenderer(ReferenceType.class, new MyClassTableCellRenderer()); - setDefaultRenderer(Long.class, new MyNumberTableCellRenderer()); + setDefaultRenderer(ReferenceType.class, new MyClassColumnRenderer()); + setDefaultRenderer(Long.class, new MyCountColumnRenderer()); + setDefaultRenderer(DiffValue.class, new MyDiffColumnRenderer()); TableRowSorter sorter = new TableRowSorter<>(myModel); sorter.setRowFilter(new RowFilter() { @@ -129,8 +131,8 @@ public class ClassesTable extends JBTable implements DataProvider, Disposable { @Nullable ReferenceType getClassByName(@NotNull String name) { - for(ReferenceType ref : myElems) { - if(name.equals(ref.name())) { + for (ReferenceType ref : myElems) { + if (name.equals(ref.name())) { return ref; } } @@ -147,7 +149,7 @@ public class ClassesTable extends JBTable implements DataProvider, Disposable { myFilteringPattern = pattern; myMatcher = NameUtil.buildMatcher("*" + pattern).build(); getRowSorter().allRowsChanged(); - if(getSelectedClass() == null && getRowCount() > 0) { + if (getSelectedClass() == null && getRowCount() > 0) { getSelectionModel().setSelectionInterval(0, 0); } } @@ -255,10 +257,10 @@ public class ClassesTable extends JBTable implements DataProvider, Disposable { return myCounts.getOrDefault(myElems.get(ix), myUnknownValue).myCurrentCount; } }, - new AbstractTableColumnDescriptor("Diff", Long.class) { + new AbstractTableColumnDescriptor("Diff", DiffValue.class) { @Override public Object getValue(int ix) { - return myCounts.getOrDefault(myElems.get(ix), myUnknownValue).diff(); + return myCounts.getOrDefault(myElems.get(ix), myUnknownValue); } } }); @@ -319,7 +321,7 @@ public class ClassesTable extends JBTable implements DataProvider, Disposable { } } - private static class DiffValue { + private static class DiffValue implements Comparable { private long myOldCount; private long myCurrentCount; @@ -347,51 +349,16 @@ public class ClassesTable extends JBTable implements DataProvider, Disposable { return myCurrentCount - myOldCount; } + @Override + public int compareTo(@NotNull DiffValue o) { + return Long.compare(diff(), o.diff()); + } } - private class MyNumberTableCellRenderer extends ColoredTableCellRenderer { - + private abstract class MyTableCellRenderer extends ColoredTableCellRenderer { @Override - protected void customizeCellRenderer(JTable table, @Nullable Object value, - boolean isSelected, boolean hasFocus, int row, int column) { - if (value == null) { - return; - } - - TrackingType trackingType = getTrackingType(row); - if (!isSelected && trackingType != null) { - JBColor color = myParent.isTrackingActive(myElems.get(convertRowIndexToModel(row))) - ? trackingType.color() - : TrackingType.inactiveColor(); - setBackground(UIUtil.toAlpha(color, 20)); - } - - long val = (long) value; - setTextAlign(SwingConstants.RIGHT); - if (column == DiffViewTableModel.COUNT_COLUMN_INDEX) { - renderCount(val); - } else { - renderDiff(val); - } - } - - private void renderDiff(long diff) { - append(String.format("%s%d", diff > 0 ? "+" : "", diff)); - } - - private void renderCount(long count) { - append(String.valueOf(count)); - - } - - } - - private class MyClassTableCellRenderer extends ColoredTableCellRenderer { - - @Override - protected void customizeCellRenderer(JTable table, @Nullable Object value, - boolean isSelected, boolean hasFocus, int row, int column) { - String presentation = value == null ? "null" : ((ReferenceType) value).name(); + protected void customizeCellRenderer(JTable table, @Nullable Object value, boolean isSelected, + boolean hasFocus, int row, int column) { TrackingType trackingType = getTrackingType(row); if (trackingType != null && !isSelected) { JBColor color = myParent.isTrackingActive(myElems.get(convertRowIndexToModel(row))) @@ -400,8 +367,21 @@ public class ClassesTable extends JBTable implements DataProvider, Disposable { setBackground(UIUtil.toAlpha(color, 20)); } + if (value != null) { + addText(table, value, isSelected, hasFocus, row, column); + } + } + + protected abstract void addText(JTable table, @NotNull Object value, boolean isSelected, + boolean hasFocus, int row, int column); + } + + private class MyClassColumnRenderer extends MyTableCellRenderer { + @Override + protected void addText(JTable table, @NotNull Object value, boolean isSelected, boolean hasFocus, int row, int column) { + String presentation = ((ReferenceType) value).name(); append(" "); - if (value != null && isSelected) { + if (isSelected) { FList textRanges = myMatcher.matchingFragments(presentation); if (textRanges != null) { SimpleTextAttributes attributes = new SimpleTextAttributes(getBackground(), getForeground(), null, @@ -412,10 +392,23 @@ public class ClassesTable extends JBTable implements DataProvider, Disposable { } else { append(String.format("%s", presentation), SimpleTextAttributes.REGULAR_ATTRIBUTES); } + } + } - if (trackingType != null) { - append(String.format(" (%s)", trackingType.description())); - } + private class MyCountColumnRenderer extends MyTableCellRenderer { + @Override + protected void addText(JTable table, @NotNull Object value, boolean isSelected, boolean hasFocus, int row, int column) { + setTextAlign(SwingConstants.RIGHT); + append(value.toString()); + } + } + + private class MyDiffColumnRenderer extends MyTableCellRenderer { + @Override + protected void addText(JTable table, @NotNull Object value, boolean isSelected, boolean hasFocus, int row, int column) { + setTextAlign(SwingConstants.RIGHT); + long diff = ((DiffValue) value).diff(); + append(String.format("%s%d", diff > 0 ? "+" : "", diff)); } } } From e1b3f60d6b369785c901717212e0011ced24cc8a Mon Sep 17 00:00:00 2001 From: Vitaliy Bibaev Date: Sat, 17 Sep 2016 18:35:28 +0300 Subject: [PATCH 096/151] Open new instances on 'diff' column click --- view/ClassesTable.java | 39 +++++++++++++++++++++++++++++++++++---- 1 file changed, 35 insertions(+), 4 deletions(-) diff --git a/view/ClassesTable.java b/view/ClassesTable.java index 00c40ed6bf8b..6b6d58638915 100644 --- a/view/ClassesTable.java +++ b/view/ClassesTable.java @@ -31,7 +31,10 @@ import org.jetbrains.debugger.memory.utils.InstancesProvider; import javax.swing.*; import javax.swing.table.TableColumn; import javax.swing.table.TableRowSorter; +import java.awt.event.MouseAdapter; +import java.awt.event.MouseEvent; import java.util.*; +import java.util.List; import java.util.concurrent.ConcurrentHashMap; public class ClassesTable extends JBTable implements DataProvider, Disposable { @@ -95,6 +98,20 @@ public class ClassesTable extends JBTable implements DataProvider, Disposable { setDefaultRenderer(Long.class, new MyCountColumnRenderer()); setDefaultRenderer(DiffValue.class, new MyDiffColumnRenderer()); + addMouseListener(new MouseAdapter() { + @Override + public void mouseClicked(MouseEvent e) { + if (columnAtPoint(e.getPoint()) != DiffViewTableModel.DIFF_COLUMN_INDEX) { + return; + } + + ReferenceType ref = myElems.get(convertRowIndexToModel(rowAtPoint(e.getPoint()))); + TrackerForNewInstances strategy = myParent.getStrategy(ref); + if (strategy != null) { + new InstancesWindow(myDebugSession, limit -> strategy.getNewInstances(), ref.name()).show(); + } + } + }); TableRowSorter sorter = new TableRowSorter<>(myModel); sorter.setRowFilter(new RowFilter() { @Override @@ -378,7 +395,8 @@ public class ClassesTable extends JBTable implements DataProvider, Disposable { private class MyClassColumnRenderer extends MyTableCellRenderer { @Override - protected void addText(JTable table, @NotNull Object value, boolean isSelected, boolean hasFocus, int row, int column) { + protected void addText(JTable table, @NotNull Object value, boolean isSelected, + boolean hasFocus, int row, int column) { String presentation = ((ReferenceType) value).name(); append(" "); if (isSelected) { @@ -397,18 +415,31 @@ public class ClassesTable extends JBTable implements DataProvider, Disposable { private class MyCountColumnRenderer extends MyTableCellRenderer { @Override - protected void addText(JTable table, @NotNull Object value, boolean isSelected, boolean hasFocus, int row, int column) { + protected void addText(JTable table, @NotNull Object value, boolean isSelected, + boolean hasFocus, int row, int column) { setTextAlign(SwingConstants.RIGHT); append(value.toString()); } } private class MyDiffColumnRenderer extends MyTableCellRenderer { + private final SimpleTextAttributes myClickableCellAttributes = new SimpleTextAttributes(SimpleTextAttributes.STYLE_UNDERLINE, + JBColor.BLUE); + @Override protected void addText(JTable table, @NotNull Object value, boolean isSelected, boolean hasFocus, int row, int column) { - setTextAlign(SwingConstants.RIGHT); long diff = ((DiffValue) value).diff(); - append(String.format("%s%d", diff > 0 ? "+" : "", diff)); + setTextAlign(SwingConstants.RIGHT); + + ReferenceType ref = myElems.get(convertRowIndexToModel(row)); + TrackerForNewInstances strategy = myParent.getStrategy(ref); + SimpleTextAttributes attributes = SimpleTextAttributes.REGULAR_ATTRIBUTES; + if (strategy != null && strategy.isReady()) { + attributes = myClickableCellAttributes; + } + + String text = String.format("%s%d", diff > 0 ? "+" : "", diff); + append(text, attributes); } } } From 9660c9461aa3df56af24b1a3b777fc159034d021 Mon Sep 17 00:00:00 2001 From: Vitaliy Bibaev Date: Wed, 21 Sep 2016 18:09:27 +0300 Subject: [PATCH 097/151] Show count of new/deleted instances in diff column for tracked instances --- tracking/ConstructorInstancesTracker.java | 8 +++++++- tracking/HashBasedTracking.java | 9 ++++++++- tracking/IdentityBasedTracking.java | 7 +++++++ tracking/RetainReferencesTracking.java | 6 +++++- tracking/TrackerForNewInstances.java | 3 +++ view/ClassesTable.java | 24 ++++++++++++++++------- 6 files changed, 47 insertions(+), 10 deletions(-) diff --git a/tracking/ConstructorInstancesTracker.java b/tracking/ConstructorInstancesTracker.java index 923bb64a926a..4eade041d9c4 100644 --- a/tracking/ConstructorInstancesTracker.java +++ b/tracking/ConstructorInstancesTracker.java @@ -38,11 +38,12 @@ public class ConstructorInstancesTracker implements TrackerForNewInstances, Disp private final XDebugSession myDebugSession; private final CreationPositionTracker myPositionTracker; private final DebugProcessImpl myDebugProcess; + @Nullable private HashSet myNewObjects = null; + @NotNull private HashSet myTrackedObjects = new HashSet<>(); - public ConstructorInstancesTracker(@NotNull ReferenceType ref, @NotNull XDebugSession debugSession) { myReference = ref; @@ -80,6 +81,11 @@ public class ConstructorInstancesTracker implements TrackerForNewInstances, Disp return myNewObjects == null ? Collections.EMPTY_LIST : new ArrayList<>(myNewObjects); } + @Override + public int getCount() { + return myNewObjects == null ? 0 : myNewObjects.size(); + } + @Override public boolean isReady() { return myNewObjects != null; diff --git a/tracking/HashBasedTracking.java b/tracking/HashBasedTracking.java index 80723075b04e..110137d11191 100644 --- a/tracking/HashBasedTracking.java +++ b/tracking/HashBasedTracking.java @@ -15,10 +15,11 @@ class HashBasedTracking extends InstanceTrackingStrategy { private static final Logger LOG = Logger.getInstance(HashBasedTracking.class); private final ReferenceType myClassType; + private MyHashCodeMethodWrapper myHashCodeWrapper; private Set myHashesSet; private MyState myState = MyState.WAIT_HASHES; - + private int myCount = 0; HashBasedTracking(@Nullable SuspendContextImpl suspendContext, @NotNull ReferenceType classType, @NotNull List initialInstances) { @@ -45,9 +46,15 @@ class HashBasedTracking extends InstanceTrackingStrategy { myHashesSet = toSetOfHashes(ref2hash); myState = myState.next(); + myCount = newInstances.size(); return newInstances; } + @Override + public int getCount() { + return myCount; + } + @Override public boolean isReady() { return myState == MyState.READY; diff --git a/tracking/IdentityBasedTracking.java b/tracking/IdentityBasedTracking.java index d3d84615a505..32e5170103d5 100644 --- a/tracking/IdentityBasedTracking.java +++ b/tracking/IdentityBasedTracking.java @@ -9,6 +9,7 @@ import java.util.stream.Collectors; class IdentityBasedTracking extends InstanceTrackingStrategy { private long myLastId = -1; + private int myCount = 0; IdentityBasedTracking(@NotNull List initialInstances) { myLastId = getMaxId(initialInstances); } @@ -21,10 +22,16 @@ class IdentityBasedTracking extends InstanceTrackingStrategy { .filter(reference -> reference.uniqueID() > myLastId) .collect(Collectors.toList()); myLastId = Math.max(myLastId, getMaxId(result)); + myCount = result.size(); return result; } private long getMaxId(@NotNull List references) { return references.stream().map(ObjectReference::uniqueID).max(Long::compare).orElse(-1L); } + + @Override + public int getCount() { + return myCount; + } } diff --git a/tracking/RetainReferencesTracking.java b/tracking/RetainReferencesTracking.java index f5a20d72ce63..c35070e95d7d 100644 --- a/tracking/RetainReferencesTracking.java +++ b/tracking/RetainReferencesTracking.java @@ -8,7 +8,6 @@ import java.util.List; class RetainReferencesTracking extends InstanceTrackingStrategy { private final IdentityBasedTracking myIdTracker; - @SuppressWarnings("unused") private List myHardRefs; RetainReferencesTracking(@NotNull List initialInstances) { myHardRefs = initialInstances; @@ -23,4 +22,9 @@ class RetainReferencesTracking extends InstanceTrackingStrategy { myHardRefs = references; return myIdTracker.getNewInstances(); } + + @Override + public int getCount() { + return myHardRefs.size(); + } } diff --git a/tracking/TrackerForNewInstances.java b/tracking/TrackerForNewInstances.java index d28b930a7b77..b6fc329f5d36 100644 --- a/tracking/TrackerForNewInstances.java +++ b/tracking/TrackerForNewInstances.java @@ -8,5 +8,8 @@ import java.util.List; public interface TrackerForNewInstances { @NotNull List getNewInstances(); + + int getCount(); + boolean isReady(); } diff --git a/view/ClassesTable.java b/view/ClassesTable.java index 6b6d58638915..57cc6177fed5 100644 --- a/view/ClassesTable.java +++ b/view/ClassesTable.java @@ -107,7 +107,7 @@ public class ClassesTable extends JBTable implements DataProvider, Disposable { ReferenceType ref = myElems.get(convertRowIndexToModel(rowAtPoint(e.getPoint()))); TrackerForNewInstances strategy = myParent.getStrategy(ref); - if (strategy != null) { + if (strategy != null && strategy.isReady()) { new InstancesWindow(myDebugSession, limit -> strategy.getNewInstances(), ref.name()).show(); } } @@ -121,7 +121,6 @@ public class ClassesTable extends JBTable implements DataProvider, Disposable { DiffValue diff = myCounts.getOrDefault(ref, myUnknownValue); return !(myOnlyWithDiff && diff.diff() == 0 || myOnlyWithInstances && !diff.hasInstance()) && myMatcher.matches(ref.name()); - } }); @@ -433,13 +432,24 @@ public class ClassesTable extends JBTable implements DataProvider, Disposable { ReferenceType ref = myElems.get(convertRowIndexToModel(row)); TrackerForNewInstances strategy = myParent.getStrategy(ref); - SimpleTextAttributes attributes = SimpleTextAttributes.REGULAR_ATTRIBUTES; if (strategy != null && strategy.isReady()) { - attributes = myClickableCellAttributes; + DiffValue diffValue = (DiffValue) value; + long current = diffValue.myCurrentCount; + long old = diffValue.myOldCount; + long trackedNew = strategy.getCount(); + long deleted = Math.max(0, old + trackedNew - current); + if (trackedNew != 0) { + append(String.format("+%d", trackedNew), myClickableCellAttributes); + if (deleted != 0) { + append(String.format(" / %d", -deleted), SimpleTextAttributes.REGULAR_ATTRIBUTES); + } + } else { + append("0", SimpleTextAttributes.REGULAR_ATTRIBUTES); + } + } else { + String text = String.format("%s%d", diff > 0 ? "+" : "", diff); + append(text, SimpleTextAttributes.REGULAR_ATTRIBUTES); } - - String text = String.format("%s%d", diff > 0 ? "+" : "", diff); - append(text, attributes); } } } From bf65fc000590102d130ab6c55f1b36fdc200e090 Mon Sep 17 00:00:00 2001 From: Vitaliy Bibaev Date: Wed, 21 Sep 2016 18:42:11 +0300 Subject: [PATCH 098/151] Fix bug when progress information not updated if a new filtering task has been started --- view/FilteringProgressView.java | 2 ++ 1 file changed, 2 insertions(+) diff --git a/view/FilteringProgressView.java b/view/FilteringProgressView.java index 7f17f6b9a8d7..90c7b1b6091d 100644 --- a/view/FilteringProgressView.java +++ b/view/FilteringProgressView.java @@ -63,6 +63,8 @@ class FilteringProgressView extends BorderLayoutPanel { myIsInProcess = true; myProgressBar.setMinimum(0); myProgressBar.setMaximum(totalCount); + myProgressBar.setValue(0); + myProgressText.setText(getDescription()); setProgressBarVisible(true); } From afc20f1dfd425637fc65099a6a3d19d35b6eedae Mon Sep 17 00:00:00 2001 From: Vitaliy Bibaev Date: Wed, 21 Sep 2016 19:34:22 +0300 Subject: [PATCH 099/151] Remove redundant operations in dispose method of InstancesWindow --- view/InstancesWindow.java | 3 --- 1 file changed, 3 deletions(-) diff --git a/view/InstancesWindow.java b/view/InstancesWindow.java index b9f1059c13e5..455a06790262 100644 --- a/view/InstancesWindow.java +++ b/view/InstancesWindow.java @@ -228,10 +228,7 @@ public class InstancesWindow extends DialogWrapper { @Override public void dispose() { - ActionManager.getInstance().removeAnActionListener(myActionListener); - myDebugSession.removeSessionListener(myDebugSessionListener); cancelFilteringTask(); - Disposer.dispose(myInstancesTree); } From 68af4c67edaec14ec810fa405e673eda92770c0f Mon Sep 17 00:00:00 2001 From: Vitaliy Bibaev Date: Fri, 23 Sep 2016 15:35:06 +0300 Subject: [PATCH 100/151] Do not restart filtering for instances in instances tree after "mark object"/"add to watch"/"evaluate expr" actions --- view/InstancesTree.java | 88 +++++++++++++++++++++++++++++++++++++++ view/InstancesWindow.java | 67 +++++++---------------------- 2 files changed, 102 insertions(+), 53 deletions(-) create mode 100644 view/InstancesTree.java diff --git a/view/InstancesTree.java b/view/InstancesTree.java new file mode 100644 index 000000000000..f5e70d207e40 --- /dev/null +++ b/view/InstancesTree.java @@ -0,0 +1,88 @@ +package org.jetbrains.debugger.memory.view; + +import com.intellij.openapi.project.Project; +import com.intellij.ui.SimpleTextAttributes; +import com.intellij.xdebugger.evaluation.XDebuggerEditorsProvider; +import com.intellij.xdebugger.frame.*; +import com.intellij.xdebugger.impl.actions.XDebuggerActions; +import com.intellij.xdebugger.impl.frame.XValueMarkers; +import com.intellij.xdebugger.impl.ui.tree.XDebuggerTree; +import com.intellij.xdebugger.impl.ui.tree.XDebuggerTreeState; +import com.intellij.xdebugger.impl.ui.tree.nodes.XValueNodeImpl; +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; + +import javax.swing.*; +import java.util.ArrayList; +import java.util.List; + +class InstancesTree extends XDebuggerTree { + private final XValueNodeImpl myRoot; + private final Runnable myOnRootExpandAction; + private List myChildren; + + InstancesTree(@NotNull Project project, + @NotNull XDebuggerEditorsProvider editorsProvider, + @Nullable XValueMarkers valueMarkers, + @NotNull Runnable onRootExpand) { + super(project, editorsProvider, null, XDebuggerActions.INSPECT_TREE_POPUP_GROUP, valueMarkers); + myOnRootExpandAction = onRootExpand; + myRoot = new XValueNodeImpl(this, null, "root", new MyRootValue()); + + myRoot.children(); + setRoot(myRoot, false); + myRoot.setLeaf(false); + setSelectionRow(0); + expandNodesOnLoad(node -> node == myRoot); + } + + void addChildren(@NotNull XValueChildrenList children, boolean last) { + if (myChildren == null) { + myChildren = new ArrayList<>(); + } + + myChildren.add(children); + myRoot.addChildren(children, last); + } + + void rebuildTree(@NotNull RebuildPolicy policy, @NotNull XDebuggerTreeState state) { + if (policy == RebuildPolicy.RELOAD_INSTANCES) { + myChildren = null; + } + + rebuildAndRestore(state); + } + + void rebuildTree(@NotNull RebuildPolicy policy) { + rebuildTree(policy, XDebuggerTreeState.saveState(this)); + } + + void setMessage(@Nullable Icon icon, @NotNull String text, @NotNull SimpleTextAttributes textAttributes) { + myRoot.clearChildren(); + myRoot.setMessage(text, icon, textAttributes, null); + } + + enum RebuildPolicy { + RELOAD_INSTANCES, ONLY_UPDATE_LABELS + } + + private class MyRootValue extends XValue { + @Override + public void computeChildren(@NotNull XCompositeNode node) { + if (myChildren == null) { + myOnRootExpandAction.run(); + } else { + for (XValueChildrenList children : myChildren) { + myRoot.addChildren(children, false); + } + + myRoot.addChildren(XValueChildrenList.EMPTY, true); + } + } + + @Override + public void computePresentation(@NotNull XValueNode node, @NotNull XValuePlace place) { + node.setPresentation(null, "", "", true); + } + } +} diff --git a/view/InstancesWindow.java b/view/InstancesWindow.java index 455a06790262..6bc8ed60e9c8 100644 --- a/view/InstancesWindow.java +++ b/view/InstancesWindow.java @@ -35,18 +35,16 @@ import com.intellij.util.ui.update.UiNotifyConnector; import com.intellij.xdebugger.XDebugSession; import com.intellij.xdebugger.XDebugSessionListener; import com.intellij.xdebugger.XExpression; -import com.intellij.xdebugger.frame.*; +import com.intellij.xdebugger.frame.XValue; +import com.intellij.xdebugger.frame.XValueChildrenList; import com.intellij.xdebugger.impl.XDebugSessionImpl; import com.intellij.xdebugger.impl.actions.XDebuggerActionBase; -import com.intellij.xdebugger.impl.evaluate.quick.XDebuggerTreeCreator; import com.intellij.xdebugger.impl.frame.XValueMarkers; import com.intellij.xdebugger.impl.ui.XDebuggerExpressionEditor; import com.intellij.xdebugger.impl.ui.XDebuggerUIConstants; import com.intellij.xdebugger.impl.ui.tree.ValueMarkup; -import com.intellij.xdebugger.impl.ui.tree.XDebuggerTree; import com.intellij.xdebugger.impl.ui.tree.XDebuggerTreeState; import com.intellij.xdebugger.impl.ui.tree.actions.XDebuggerTreeActionBase; -import com.intellij.xdebugger.impl.ui.tree.nodes.XDebuggerTreeNode; import com.intellij.xdebugger.impl.ui.tree.nodes.XValueNodeImpl; import com.sun.jdi.BooleanValue; import com.sun.jdi.ObjectReference; @@ -135,12 +133,6 @@ public class InstancesWindow extends DialogWrapper { return comp; } - @Override - protected void dispose() { - super.dispose(); - myInstancesView.dispose(); - } - @NotNull @Override protected Action[] createActions() { @@ -151,7 +143,7 @@ public class InstancesWindow extends DialogWrapper { private static final int MAX_TREE_NODE_COUNT = 2000; private static final int FILTERING_CHUNK_SIZE = 30; - private final XDebuggerTree myInstancesTree; + private final InstancesTree myInstancesTree; private final XDebuggerExpressionEditor myFilterConditionEditor; private final XDebugSessionListener myDebugSessionListener = new MySessionListener(); @@ -171,6 +163,7 @@ public class InstancesWindow extends DialogWrapper { MyInstancesView() { super(new BorderLayout(0, JBUI.scale(BORDER_LAYOUT_DEFAULT_GAP))); + Disposer.register(InstancesWindow.this.myDisposable, MyInstancesView.this); XValueMarkers markers = getValueMarkers(); ActionManager.getInstance().addAnActionListener(myActionListener, InstancesWindow.this.myDisposable); myDebugSession.addSessionListener(myDebugSessionListener, InstancesWindow.this.myDisposable); @@ -198,13 +191,7 @@ public class InstancesWindow extends DialogWrapper { myProgress.addStopActionListener(this::cancelFilteringTask); - XDebuggerTreeCreator treeCreator = - new XDebuggerTreeCreator(myProject, editorsProvider, null, markers); - - myInstancesTree = (XDebuggerTree) treeCreator.createTree(getTreeRootDescriptor()); - myInstancesTree.setRootVisible(false); - myInstancesTree.getRoot().setLeaf(false); - myInstancesTree.setExpandableItemsEnabled(true); + myInstancesTree = new InstancesTree(myProject, editorsProvider, markers, this::updateInstances); myFilterButton.addActionListener(e -> { String expression = myFilterConditionEditor.getExpression().getExpression(); @@ -213,7 +200,7 @@ public class InstancesWindow extends DialogWrapper { } myFilterButton.setEnabled(false); - myInstancesTree.rebuildAndRestore(XDebuggerTreeState.saveState(myInstancesTree)); + myInstancesTree.rebuildTree(InstancesTree.RebuildPolicy.RELOAD_INSTANCES); }); JBScrollPane treeScrollPane = new JBScrollPane(myInstancesTree); @@ -308,28 +295,6 @@ public class InstancesWindow extends DialogWrapper { : null; } - private void addChildrenToTree(XValueChildrenList children, boolean last) { - XDebuggerTreeNode root = myInstancesTree.getRoot(); - if (root != null) { - ((XValueNodeImpl) root).addChildren(children, last); - } - } - - @NotNull - private Pair getTreeRootDescriptor() { - return Pair.pair(new XValue() { - @Override - public void computeChildren(@NotNull XCompositeNode node) { - updateInstances(); - } - - @Override - public void computePresentation(@NotNull XValueNode node, @NotNull XValuePlace place) { - node.setPresentation(null, "", "", true); - } - }, "root"); - } - private class MySessionListener implements XDebugSessionListener { private volatile XDebuggerTreeState myTreeState = null; @@ -339,12 +304,8 @@ public class InstancesWindow extends DialogWrapper { myTreeState = XDebuggerTreeState.saveState(myInstancesTree); cancelFilteringTask(); - myInstancesTree.getRoot().clearChildren(); - XCompositeNode root = (XCompositeNode) myInstancesTree.getRoot(); - - root.setMessage("The application is running", XDebuggerUIConstants.INFORMATION_MESSAGE_ICON, - SimpleTextAttributes.REGULAR_ATTRIBUTES, null); - myProgress.setVisible(false); + myInstancesTree.setMessage(XDebuggerUIConstants.INFORMATION_MESSAGE_ICON, + "The application is running", SimpleTextAttributes.REGULAR_ATTRIBUTES); }); } @@ -352,7 +313,7 @@ public class InstancesWindow extends DialogWrapper { public void sessionPaused() { SwingUtilities.invokeLater(() -> { myProgress.setVisible(true); - myInstancesTree.rebuildAndRestore(myTreeState); + myInstancesTree.rebuildTree(InstancesTree.RebuildPolicy.RELOAD_INSTANCES, myTreeState); }); } } @@ -380,8 +341,8 @@ public class InstancesWindow extends DialogWrapper { new ValueMarkup(expression.replace("@", ""), new JBColor(0, 0), null)); } - SwingUtilities.invokeLater(() -> - myInstancesTree.rebuildAndRestore(XDebuggerTreeState.saveState(myInstancesTree))); + SwingUtilities.invokeLater(() -> myInstancesTree + .rebuildTree(InstancesTree.RebuildPolicy.ONLY_UPDATE_LABELS)); } } } @@ -433,9 +394,9 @@ public class InstancesWindow extends DialogWrapper { if (!myErrorsGroup.isEmpty()) { XValueChildrenList lst = new XValueChildrenList(); lst.addBottomGroup(myErrorsGroup); - addChildrenToTree(lst, true); + myInstancesTree.addChildren(lst, true); } else { - addChildrenToTree(XValueChildrenList.EMPTY, true); + myInstancesTree.addChildren(XValueChildrenList.EMPTY, true); } myFilterButton.setEnabled(true); @@ -494,7 +455,7 @@ public class InstancesWindow extends DialogWrapper { if (children.size() > 0) { SwingUtilities.invokeLater(() -> { if (MyFilteringWorker.this == myFilteringTask) { - addChildrenToTree(children, false); + myInstancesTree.addChildren(children, false); } }); } From 3dfd874ee41e132d55319a2a709759309f4b2604 Mon Sep 17 00:00:00 2001 From: Vitaliy Bibaev Date: Fri, 23 Sep 2016 15:54:58 +0300 Subject: [PATCH 101/151] Update only presentation for new instances in tree after step of debugger (instead of instances list) --- view/ClassesTable.java | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/view/ClassesTable.java b/view/ClassesTable.java index 57cc6177fed5..e5331c639f18 100644 --- a/view/ClassesTable.java +++ b/view/ClassesTable.java @@ -107,8 +107,9 @@ public class ClassesTable extends JBTable implements DataProvider, Disposable { ReferenceType ref = myElems.get(convertRowIndexToModel(rowAtPoint(e.getPoint()))); TrackerForNewInstances strategy = myParent.getStrategy(ref); - if (strategy != null && strategy.isReady()) { - new InstancesWindow(myDebugSession, limit -> strategy.getNewInstances(), ref.name()).show(); + if (strategy != null && strategy.isReady() && strategy.getCount() > 0) { + List newInstances = strategy.getNewInstances(); + new InstancesWindow(myDebugSession, limit -> newInstances, ref.name()).show(); } } }); From cdc7d9ab1b7fb942c7585d1606bf138e4d263d2d Mon Sep 17 00:00:00 2001 From: Vitaliy Bibaev Date: Fri, 23 Sep 2016 17:02:22 +0300 Subject: [PATCH 102/151] Track instances creation when tool window is closed --- view/ClassesFilteredView.java | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/view/ClassesFilteredView.java b/view/ClassesFilteredView.java index ac883146a947..a3fe1a720f65 100644 --- a/view/ClassesFilteredView.java +++ b/view/ClassesFilteredView.java @@ -212,7 +212,7 @@ public class ClassesFilteredView extends BorderLayoutPanel implements Disposable public void sessionPaused() { SwingUtilities.invokeLater(() -> myTable.getEmptyText().setText(EMPTY_TABLE_CONTENT_WHEN_SUSPENDED)); if (myNeedReloadClasses) { - myConstructorTrackedClasses.values().forEach(ConstructorInstancesTracker::commitTracked); + commitAllTrackers(); updateClassesAndCounts(); } } @@ -255,6 +255,7 @@ public class ClassesFilteredView extends BorderLayoutPanel implements Disposable if (myNeedReloadClasses) { SuspendContextImpl suspendContext = getSuspendContext(); if (suspendContext != null && !suspendContext.equals(myLastSuspendContext)) { + commitAllTrackers(); updateClassesAndCounts(); } } @@ -282,6 +283,10 @@ public class ClassesFilteredView extends BorderLayoutPanel implements Disposable } } + private void commitAllTrackers() { + myConstructorTrackedClasses.values().forEach(ConstructorInstancesTracker::commitTracked); + } + private SuspendContextImpl getSuspendContext() { return DebuggerManagerImpl.getInstanceEx(myProject).getContext().getSuspendContext(); } From 9a7366a04b74d364f3f9a026011bc82f07b6ff0f Mon Sep 17 00:00:00 2001 From: Vitaliy Bibaev Date: Fri, 23 Sep 2016 18:36:14 +0300 Subject: [PATCH 103/151] Fix bug when instances tree was not empty after session has been resumed --- view/InstancesTree.java | 1 + 1 file changed, 1 insertion(+) diff --git a/view/InstancesTree.java b/view/InstancesTree.java index f5e70d207e40..d2b612ef672b 100644 --- a/view/InstancesTree.java +++ b/view/InstancesTree.java @@ -58,6 +58,7 @@ class InstancesTree extends XDebuggerTree { } void setMessage(@Nullable Icon icon, @NotNull String text, @NotNull SimpleTextAttributes textAttributes) { + myChildren = null; myRoot.clearChildren(); myRoot.setMessage(text, icon, textAttributes, null); } From d6c4f24783b6f61811dcb3331ed18f9a97adff7a Mon Sep 17 00:00:00 2001 From: Vitaliy Bibaev Date: Fri, 23 Sep 2016 19:02:15 +0300 Subject: [PATCH 104/151] Add limit for amount of instances for tracking by constructor calls --- tracking/ConstructorInstancesTracker.java | 38 ++++++++++++++++++++--- 1 file changed, 33 insertions(+), 5 deletions(-) diff --git a/tracking/ConstructorInstancesTracker.java b/tracking/ConstructorInstancesTracker.java index 4eade041d9c4..57f46ade8153 100644 --- a/tracking/ConstructorInstancesTracker.java +++ b/tracking/ConstructorInstancesTracker.java @@ -24,6 +24,7 @@ import com.sun.jdi.ObjectReference; import com.sun.jdi.ReferenceType; import com.sun.jdi.event.LocatableEvent; import com.sun.jdi.request.BreakpointRequest; +import com.sun.jdi.request.EventRequest; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; import org.jetbrains.debugger.memory.component.CreationPositionTracker; @@ -34,6 +35,7 @@ import java.util.*; import java.util.stream.Collectors; public class ConstructorInstancesTracker implements TrackerForNewInstances, Disposable { + private static final int TRACKED_INSTANCES_LIMIT = 2000; private final ReferenceType myReference; private final XDebugSession myDebugSession; private final CreationPositionTracker myPositionTracker; @@ -42,6 +44,8 @@ public class ConstructorInstancesTracker implements TrackerForNewInstances, Disp @Nullable private HashSet myNewObjects = null; + private final MyConstructorBreakpoints myBreakpoint; + @NotNull private HashSet myTrackedObjects = new HashSet<>(); public ConstructorInstancesTracker(@NotNull ReferenceType ref, @@ -59,14 +63,19 @@ public class ConstructorInstancesTracker implements TrackerForNewInstances, Disp new JavaLineBreakpointProperties(), new LineBreakpointState<>()); - MyConstructorBreakpoints breakpoint = new MyConstructorBreakpoints(project, bpn); - breakpoint.createRequestForPreparedClass(myDebugProcess, myReference); + myBreakpoint = new MyConstructorBreakpoints(project, bpn); + myBreakpoint.createRequestForPreparedClass(myDebugProcess, myReference); - Disposer.register(this, breakpoint); + Disposer.register(this, myBreakpoint); } public void obsolete() { + if(myNewObjects != null) { + myNewObjects.forEach(ObjectReference::enableCollection); + } + myNewObjects = null; + myBreakpoint.enable(); myPositionTracker.releaseBySession(myDebugSession); } @@ -98,6 +107,8 @@ public class ConstructorInstancesTracker implements TrackerForNewInstances, Disp private final class MyConstructorBreakpoints extends LineBreakpoint implements Disposable { + private final List myRequests = new ArrayList<>(); + MyConstructorBreakpoints(Project project, XBreakpoint xBreakpoint) { super(project, xBreakpoint); } @@ -107,8 +118,9 @@ public class ConstructorInstancesTracker implements TrackerForNewInstances, Disp classType.methods().stream().filter(Method::isConstructor).forEach(cons -> { Location loc = cons.location(); BreakpointRequest breakpointRequest = debugProcess.getRequestsManager().createBreakpointRequest(this, loc); - breakpointRequest.enable(); + myRequests.add(breakpointRequest); }); + enable(); } @Override @@ -120,6 +132,7 @@ public class ConstructorInstancesTracker implements TrackerForNewInstances, Disp myDebugProcess.getManagerThread().schedule(new DebuggerCommandImpl() { @Override protected void action() throws Exception { + disable(); myDebugProcess.getRequestsManager().deleteRequest(MyConstructorBreakpoints.this); } }); @@ -133,6 +146,7 @@ public class ConstructorInstancesTracker implements TrackerForNewInstances, Disp if (suspendContext != null) { ObjectReference thisRef = getThisObject(suspendContext, event); if (myReference.equals(thisRef.referenceType())) { + thisRef.disableCollection(); myTrackedObjects.add(thisRef); ThreadReferenceProxyImpl threadReferenceProxy = suspendContext.getThread(); List stack = threadReferenceProxy == null ? null : threadReferenceProxy.frames(); @@ -150,12 +164,26 @@ public class ConstructorInstancesTracker implements TrackerForNewInstances, Disp }).filter(Objects::nonNull).collect(Collectors.toList()); myPositionTracker.addStack(myDebugSession, thisRef, stackFrameDescriptors); } + } } } catch (EvaluateException e) { - e.printStackTrace(); + return false; } + + if(myTrackedObjects.size() >= TRACKED_INSTANCES_LIMIT) { + disable(); + } + return false; } + + void enable() { + myRequests.forEach(EventRequest::enable); + } + + void disable() { + myRequests.forEach(EventRequest::disable); + } } } From c5a966c3b1886f9e2473fd680f7a48eee1f482ae Mon Sep 17 00:00:00 2001 From: Vitaliy Bibaev Date: Fri, 23 Sep 2016 19:44:22 +0300 Subject: [PATCH 105/151] Rewrite diff column renderer --- tracking/ConstructorInstancesTracker.java | 3 +-- view/ClassesTable.java | 20 +++++++++----------- 2 files changed, 10 insertions(+), 13 deletions(-) diff --git a/tracking/ConstructorInstancesTracker.java b/tracking/ConstructorInstancesTracker.java index 57f46ade8153..f5e8478e2657 100644 --- a/tracking/ConstructorInstancesTracker.java +++ b/tracking/ConstructorInstancesTracker.java @@ -40,12 +40,11 @@ public class ConstructorInstancesTracker implements TrackerForNewInstances, Disp private final XDebugSession myDebugSession; private final CreationPositionTracker myPositionTracker; private final DebugProcessImpl myDebugProcess; + private final MyConstructorBreakpoints myBreakpoint; @Nullable private HashSet myNewObjects = null; - private final MyConstructorBreakpoints myBreakpoint; - @NotNull private HashSet myTrackedObjects = new HashSet<>(); public ConstructorInstancesTracker(@NotNull ReferenceType ref, diff --git a/view/ClassesTable.java b/view/ClassesTable.java index e5331c639f18..b1d0239ae7f8 100644 --- a/view/ClassesTable.java +++ b/view/ClassesTable.java @@ -433,22 +433,20 @@ public class ClassesTable extends JBTable implements DataProvider, Disposable { ReferenceType ref = myElems.get(convertRowIndexToModel(row)); TrackerForNewInstances strategy = myParent.getStrategy(ref); + + String text = String.format("%s%d", diff > 0 ? "+" : "", diff); + if (strategy != null && strategy.isReady()) { - DiffValue diffValue = (DiffValue) value; - long current = diffValue.myCurrentCount; - long old = diffValue.myOldCount; long trackedNew = strategy.getCount(); - long deleted = Math.max(0, old + trackedNew - current); - if (trackedNew != 0) { - append(String.format("+%d", trackedNew), myClickableCellAttributes); - if (deleted != 0) { - append(String.format(" / %d", -deleted), SimpleTextAttributes.REGULAR_ATTRIBUTES); - } + if (trackedNew == diff) { + append(text, diff == 0 ? SimpleTextAttributes.REGULAR_ATTRIBUTES : myClickableCellAttributes); } else { - append("0", SimpleTextAttributes.REGULAR_ATTRIBUTES); + append(text, SimpleTextAttributes.REGULAR_ATTRIBUTES); + if (trackedNew != 0) { + append(String.format(" (%d)", trackedNew), myClickableCellAttributes); + } } } else { - String text = String.format("%s%d", diff > 0 ? "+" : "", diff); append(text, SimpleTextAttributes.REGULAR_ATTRIBUTES); } } From ae4c4f0227a5858fa4a97986684ce869e94bbaef Mon Sep 17 00:00:00 2001 From: Vitaliy Bibaev Date: Fri, 23 Sep 2016 20:25:58 +0300 Subject: [PATCH 106/151] Navigate to line with operator "new" instead of constructor location --- view/StackFramePopup.java | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/view/StackFramePopup.java b/view/StackFramePopup.java index 64a70330e53d..484e34d25b7a 100644 --- a/view/StackFramePopup.java +++ b/view/StackFramePopup.java @@ -43,6 +43,7 @@ public class StackFramePopup { .setItemChoosenCallback(() -> navigateToSelectedFrame(list, true)) .createPopup(); + list.setSelectedIndex(1); popup.showInFocusCenter(); } @@ -87,7 +88,7 @@ public class StackFramePopup { frame.line() - 1); OpenFileDescriptor descriptor = info.getDescriptor(); if (descriptor != null) { - FileEditorManager.getInstance(myProject).openTextEditor(descriptor, focusEditor); + FileEditorManager.getInstance(myProject).openEditor(descriptor, focusEditor); } }); } From e8f60afae3fde0d5687212e7b1b39992cfe12485 Mon Sep 17 00:00:00 2001 From: Vitaliy Bibaev Date: Fri, 30 Sep 2016 15:48:10 +0300 Subject: [PATCH 107/151] Open java file for library classes (if it available) when action "go to allocation Source" is used --- view/StackFramePopup.java | 21 ++++++++++++++++++--- 1 file changed, 18 insertions(+), 3 deletions(-) diff --git a/view/StackFramePopup.java b/view/StackFramePopup.java index 484e34d25b7a..08c68924676f 100644 --- a/view/StackFramePopup.java +++ b/view/StackFramePopup.java @@ -8,8 +8,11 @@ import com.intellij.openapi.fileEditor.OpenFileDescriptor; import com.intellij.openapi.project.Project; import com.intellij.openapi.ui.popup.JBPopup; import com.intellij.openapi.ui.popup.JBPopupFactory; +import com.intellij.openapi.vfs.VirtualFile; import com.intellij.psi.PsiClass; +import com.intellij.psi.PsiElement; import com.intellij.psi.search.GlobalSearchScope; +import com.intellij.psi.util.PsiUtilCore; import com.intellij.ui.ColoredListCellRenderer; import com.intellij.ui.SimpleTextAttributes; import com.intellij.ui.components.JBList; @@ -21,6 +24,8 @@ import javax.swing.*; import java.util.List; public class StackFramePopup { + private static final char ANONYMOUS_CLASS_DELIMITER = '$'; + private final Project myProject; private final List myStackFrame; private final GlobalSearchScope myScope; @@ -80,14 +85,24 @@ public class StackFramePopup { private void navigateToSelectedFrame(@NotNull JBList frameList, boolean focusEditor) { StackFrameDescriptor frame = (StackFrameDescriptor) frameList.getSelectedValue(); if (frame != null) { - PsiClass psiClass = DebuggerUtils.findClass(frame.path(), myProject, myScope); + String path = frame.path(); + int anonymousClassDelimiterIndex = path.indexOf(ANONYMOUS_CLASS_DELIMITER); + int pathLength = anonymousClassDelimiterIndex > 0 ? anonymousClassDelimiterIndex : path.length(); + path = path.substring(0, pathLength); + PsiClass psiClass = DebuggerUtils.findClass(path, myProject, myScope); if (psiClass != null) { ApplicationManager.getApplication().runReadAction(() -> { + PsiElement navigationElement = psiClass.getNavigationElement(); + VirtualFile file = PsiUtilCore.getVirtualFile(navigationElement); + if (file == null) { + file = psiClass.getContainingFile().getVirtualFile(); + } + OpenFileHyperlinkInfo info = - new OpenFileHyperlinkInfo(myProject, psiClass.getContainingFile().getVirtualFile(), - frame.line() - 1); + new OpenFileHyperlinkInfo(myProject, file, frame.line() - 1); OpenFileDescriptor descriptor = info.getDescriptor(); if (descriptor != null) { + descriptor.setUseCurrentWindow(true); FileEditorManager.getInstance(myProject).openEditor(descriptor, focusEditor); } }); From 962e1ec513c7cf649c9d3cd2fe47886f913f6759 Mon Sep 17 00:00:00 2001 From: Vitaliy Bibaev Date: Fri, 30 Sep 2016 18:36:25 +0300 Subject: [PATCH 108/151] Add ability to select strategy for tracking of instances in background --- action/EnableBackgroundTrackingAction.java | 23 ++++++ component/InstancesTracker.java | 15 ++++ event/InstancesTrackerListener.java | 5 +- toolwindow/MemoryViewToolWindowFactory.java | 19 +++-- tracking/BackgroundTracker.java | 5 ++ tracking/ConstructorInstancesTracker.java | 80 ++++++++++++++++++--- tracking/TrackerForNewInstances.java | 3 +- view/ClassesFilteredView.java | 62 ++++++++++------ 8 files changed, 172 insertions(+), 40 deletions(-) create mode 100644 action/EnableBackgroundTrackingAction.java create mode 100644 tracking/BackgroundTracker.java diff --git a/action/EnableBackgroundTrackingAction.java b/action/EnableBackgroundTrackingAction.java new file mode 100644 index 000000000000..f475aa11e339 --- /dev/null +++ b/action/EnableBackgroundTrackingAction.java @@ -0,0 +1,23 @@ +package org.jetbrains.debugger.memory.action; + +import com.intellij.openapi.actionSystem.AnActionEvent; +import com.intellij.openapi.actionSystem.ToggleAction; +import com.intellij.openapi.project.Project; +import org.jetbrains.debugger.memory.component.InstancesTracker; + +public class EnableBackgroundTrackingAction extends ToggleAction{ + + @Override + public boolean isSelected(AnActionEvent e) { + Project project = e.getProject(); + return project != null && InstancesTracker.getInstance(project).isBackgroundTrackingEnabled(); + } + + @Override + public void setSelected(AnActionEvent e, boolean state) { + Project project = e.getProject(); + if (project != null) { + InstancesTracker.getInstance(project).setBackgroundTackingEnabled(state); + } + } +} diff --git a/component/InstancesTracker.java b/component/InstancesTracker.java index 28c416bec251..390a3a3c62ee 100644 --- a/component/InstancesTracker.java +++ b/component/InstancesTracker.java @@ -34,6 +34,10 @@ public class InstancesTracker extends AbstractProjectComponent return myState.classes.containsKey(className); } + public boolean isBackgroundTrackingEnabled() { + return myState.isBackgroundTrackingEnabled; + } + @Nullable public TrackingType getTrackingType(@NotNull String className) { return myState.classes.getOrDefault(className, null); @@ -69,6 +73,14 @@ public class InstancesTracker extends AbstractProjectComponent myDispatcher.addListener(listener, parentDisposable); } + public void setBackgroundTackingEnabled(boolean state) { + boolean oldState = myState.isBackgroundTrackingEnabled; + if(state != oldState) { + myState.isBackgroundTrackingEnabled = state; + myDispatcher.getMulticaster().backgroundTrackingValueChanged(state); + } + } + @Nullable @Override public MyState getState() { @@ -81,6 +93,8 @@ public class InstancesTracker extends AbstractProjectComponent } static class MyState { + boolean isBackgroundTrackingEnabled = false; + @NotNull @AbstractCollection(surroundWithTag = false, elementTypes = {Map.Entry.class}) final Map classes = new ConcurrentHashMap<>(); @@ -89,6 +103,7 @@ public class InstancesTracker extends AbstractProjectComponent } MyState(@NotNull MyState state) { + isBackgroundTrackingEnabled = state.isBackgroundTrackingEnabled; for (Map.Entry classState : state.classes.entrySet()) { classes.put(classState.getKey(), classState.getValue()); } diff --git a/event/InstancesTrackerListener.java b/event/InstancesTrackerListener.java index 0e8924e17657..e01372032a1c 100644 --- a/event/InstancesTrackerListener.java +++ b/event/InstancesTrackerListener.java @@ -5,10 +5,13 @@ import org.jetbrains.debugger.memory.tracking.TrackingType; import java.util.EventListener; -public interface InstancesTrackerListener extends EventListener{ +public interface InstancesTrackerListener extends EventListener { default void classChanged(@NotNull String name, @NotNull TrackingType type) { } default void classRemoved(@NotNull String name) { } + + default void backgroundTrackingValueChanged(boolean newState) { + } } diff --git a/toolwindow/MemoryViewToolWindowFactory.java b/toolwindow/MemoryViewToolWindowFactory.java index 2b8680786822..497a6926cfee 100644 --- a/toolwindow/MemoryViewToolWindowFactory.java +++ b/toolwindow/MemoryViewToolWindowFactory.java @@ -39,6 +39,9 @@ public class MemoryViewToolWindowFactory implements ToolWindowFactory, DumbAware private final HashMap myMemoryViews = new HashMap<>(); + @Nullable + private ClassesFilteredView myCurrentView = null; + { myEmptyContent = new JBLabel("Run debugging to see loaded classes", SwingConstants.CENTER); } @@ -65,14 +68,15 @@ public class MemoryViewToolWindowFactory implements ToolWindowFactory, DumbAware public void stateChanged() { if (!myMemoryViews.isEmpty() && !toolWindow.isDisposed()) { myMemoryViews.values().forEach(classesFilteredView -> - classesFilteredView.setNeedReloadClasses(toolWindow.isVisible())); + classesFilteredView.setActive(toolWindow.isVisible())); } } }, project); ActionGroup group = new DefaultActionGroup( ActionManager.getInstance().getAction("MemoryView.ShowOnlyWithInstances"), - ActionManager.getInstance().getAction("MemoryView.ShowOnlyWithDiff") + ActionManager.getInstance().getAction("MemoryView.ShowOnlyWithDiff"), + ActionManager.getInstance().getAction("MemoryView.EnableTrackingWithClosedWindow") ); ((ToolWindowEx) toolWindow).setAdditionalGearActions(group); toolWindow.getComponent().setLayout(new BorderLayout()); @@ -88,16 +92,14 @@ public class MemoryViewToolWindowFactory implements ToolWindowFactory, DumbAware ToolWindow toolWindow = getToolWindow(session.getProject()); if (!myMemoryViews.containsKey(session)) { ClassesFilteredView newView = new ClassesFilteredView(session); - newView.setNeedReloadClasses(toolWindow != null && toolWindow.isVisible()); + newView.setActive(toolWindow != null && toolWindow.isVisible()); myMemoryViews.put(session, newView); } } private void removeSession(@NotNull XDebugSession session) { ClassesFilteredView removed = myMemoryViews.remove(session); - if(removed != null) { - Disposer.dispose(removed); - } + Disposer.dispose(removed); } private void updateCurrentMemoryView(@NotNull Project project, @NotNull ToolWindow toolWindow) { @@ -106,6 +108,11 @@ public class MemoryViewToolWindowFactory implements ToolWindowFactory, DumbAware if (session != null && myMemoryViews.containsKey(session)) { ClassesFilteredView view = myMemoryViews.get(session); replaceToolWindowContent(toolWindow, view); + if (myCurrentView != null) { + myCurrentView.setActive(true); + } + + myCurrentView = view; return; } } diff --git a/tracking/BackgroundTracker.java b/tracking/BackgroundTracker.java new file mode 100644 index 000000000000..51ce3955b786 --- /dev/null +++ b/tracking/BackgroundTracker.java @@ -0,0 +1,5 @@ +package org.jetbrains.debugger.memory.tracking; + +interface BackgroundTracker { + void setBackgroundMode(boolean isBackgroundMode); +} diff --git a/tracking/ConstructorInstancesTracker.java b/tracking/ConstructorInstancesTracker.java index f5e8478e2657..c8924f518569 100644 --- a/tracking/ConstructorInstancesTracker.java +++ b/tracking/ConstructorInstancesTracker.java @@ -12,7 +12,6 @@ import com.intellij.debugger.ui.breakpoints.JavaLineBreakpointType; import com.intellij.debugger.ui.breakpoints.LineBreakpoint; import com.intellij.openapi.Disposable; import com.intellij.openapi.project.Project; -import com.intellij.openapi.util.Disposer; import com.intellij.xdebugger.XDebugSession; import com.intellij.xdebugger.breakpoints.XBreakpoint; import com.intellij.xdebugger.impl.XDebuggerManagerImpl; @@ -28,13 +27,14 @@ import com.sun.jdi.request.EventRequest; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; import org.jetbrains.debugger.memory.component.CreationPositionTracker; +import org.jetbrains.debugger.memory.component.InstancesTracker; import org.jetbrains.debugger.memory.utils.StackFrameDescriptor; import org.jetbrains.java.debugger.breakpoints.properties.JavaLineBreakpointProperties; import java.util.*; import java.util.stream.Collectors; -public class ConstructorInstancesTracker implements TrackerForNewInstances, Disposable { +public class ConstructorInstancesTracker implements TrackerForNewInstances, Disposable, BackgroundTracker { private static final int TRACKED_INSTANCES_LIMIT = 2000; private final ReferenceType myReference; private final XDebugSession myDebugSession; @@ -47,12 +47,17 @@ public class ConstructorInstancesTracker implements TrackerForNewInstances, Disp @NotNull private HashSet myTrackedObjects = new HashSet<>(); + + private boolean myIsBackgroundMode; + private boolean myIsBackgroundTrackingEnabled; + public ConstructorInstancesTracker(@NotNull ReferenceType ref, @NotNull XDebugSession debugSession) { myReference = ref; myDebugSession = debugSession; myPositionTracker = CreationPositionTracker.getInstance(debugSession.getProject()); Project project = debugSession.getProject(); + myIsBackgroundTrackingEnabled = InstancesTracker.getInstance(myDebugSession.getProject()).isBackgroundTrackingEnabled(); myDebugProcess = (DebugProcessImpl) DebuggerManager.getInstance(project) .getDebugProcess(debugSession.getDebugProcess().getProcessHandler()); JavaLineBreakpointType breakPointType = new JavaLineBreakpointType(); @@ -64,17 +69,17 @@ public class ConstructorInstancesTracker implements TrackerForNewInstances, Disp myBreakpoint = new MyConstructorBreakpoints(project, bpn); myBreakpoint.createRequestForPreparedClass(myDebugProcess, myReference); - - Disposer.register(this, myBreakpoint); } public void obsolete() { - if(myNewObjects != null) { + if (myNewObjects != null) { myNewObjects.forEach(ObjectReference::enableCollection); } myNewObjects = null; - myBreakpoint.enable(); + if (!myIsBackgroundMode || myIsBackgroundTrackingEnabled) { + myBreakpoint.enable(); + } myPositionTracker.releaseBySession(myDebugSession); } @@ -101,11 +106,54 @@ public class ConstructorInstancesTracker implements TrackerForNewInstances, Disp @Override public void dispose() { + myBreakpoint.dispose(); + } + + @Override + public void backgroundTrackingValueChanged(boolean newValue) { + if (myIsBackgroundTrackingEnabled != newValue) { + myIsBackgroundTrackingEnabled = newValue; + myDebugProcess.getManagerThread().schedule(new DebuggerCommandImpl() { + @Override + protected void action() throws Exception { + if (newValue) { + myBreakpoint.enable(); + } else { + myBreakpoint.disable(); + } + } + }); + } + } + + @Override + public void setBackgroundMode(boolean isBackgroundMode) { + if (myIsBackgroundMode == isBackgroundMode) { + return; + } + + myIsBackgroundMode = isBackgroundMode; + if (isBackgroundMode) { + doEnableBackgroundMode(); + } else { + doDisableBackgroundMode(); + } + } + + private void doDisableBackgroundMode() { + myBreakpoint.enable(); + } + + private void doEnableBackgroundMode() { + if (!myIsBackgroundTrackingEnabled) { + myBreakpoint.disable(); + } } private final class MyConstructorBreakpoints extends LineBreakpoint implements Disposable { + private boolean myIsEnabled = false; private final List myRequests = new ArrayList<>(); MyConstructorBreakpoints(Project project, XBreakpoint xBreakpoint) { @@ -114,12 +162,15 @@ public class ConstructorInstancesTracker implements TrackerForNewInstances, Disp @Override protected void createRequestForPreparedClass(DebugProcessImpl debugProcess, ReferenceType classType) { - classType.methods().stream().filter(Method::isConstructor).forEach(cons -> { + classType.methods().stream().filter(Method::isConstructor).forEach(cons -> { Location loc = cons.location(); BreakpointRequest breakpointRequest = debugProcess.getRequestsManager().createBreakpointRequest(this, loc); myRequests.add(breakpointRequest); }); - enable(); + + if (!myIsBackgroundMode || myIsBackgroundTrackingEnabled) { + enable(); + } } @Override @@ -170,7 +221,7 @@ public class ConstructorInstancesTracker implements TrackerForNewInstances, Disp return false; } - if(myTrackedObjects.size() >= TRACKED_INSTANCES_LIMIT) { + if (myTrackedObjects.size() >= TRACKED_INSTANCES_LIMIT) { disable(); } @@ -178,11 +229,18 @@ public class ConstructorInstancesTracker implements TrackerForNewInstances, Disp } void enable() { - myRequests.forEach(EventRequest::enable); + if (!myIsEnabled) { + myRequests.forEach(EventRequest::enable); + myIsEnabled = true; + } } void disable() { - myRequests.forEach(EventRequest::disable); + if (myIsEnabled) { + myRequests.forEach(EventRequest::disable); + myIsEnabled = false; + } } } } + diff --git a/tracking/TrackerForNewInstances.java b/tracking/TrackerForNewInstances.java index b6fc329f5d36..75a54b0b3985 100644 --- a/tracking/TrackerForNewInstances.java +++ b/tracking/TrackerForNewInstances.java @@ -2,10 +2,11 @@ package org.jetbrains.debugger.memory.tracking; import com.sun.jdi.ObjectReference; import org.jetbrains.annotations.NotNull; +import org.jetbrains.debugger.memory.event.InstancesTrackerListener; import java.util.List; -public interface TrackerForNewInstances { +public interface TrackerForNewInstances extends InstancesTrackerListener { @NotNull List getNewInstances(); diff --git a/view/ClassesFilteredView.java b/view/ClassesFilteredView.java index a3fe1a720f65..2be691d6b076 100644 --- a/view/ClassesFilteredView.java +++ b/view/ClassesFilteredView.java @@ -65,9 +65,11 @@ public class ClassesFilteredView extends BorderLayoutPanel implements Disposable private final ClassesTable myTable; private final InstancesTracker myInstancesTracker; private final Map myConstructorTrackedClasses = new ConcurrentHashMap<>(); + private final XDebugSessionListener myDebugSessionListener; private volatile SuspendContextImpl myLastSuspendContext; - private volatile boolean myNeedReloadClasses = false; + + private boolean myIsActive; public ClassesFilteredView(@NotNull XDebugSession debugSession) { super(); @@ -192,7 +194,7 @@ public class ClassesFilteredView extends BorderLayoutPanel implements Disposable MemoryViewManager.getInstance().addMemoryViewManagerListener(memoryViewManagerListener, this); - myDebugSession.addSessionListener(new XDebugSessionListener() { + myDebugSessionListener = new XDebugSessionListener() { @Override public void sessionResumed() { myConstructorTrackedClasses.values().forEach(ConstructorInstancesTracker::obsolete); @@ -211,12 +213,10 @@ public class ClassesFilteredView extends BorderLayoutPanel implements Disposable @Override public void sessionPaused() { SwingUtilities.invokeLater(() -> myTable.getEmptyText().setText(EMPTY_TABLE_CONTENT_WHEN_SUSPENDED)); - if (myNeedReloadClasses) { - commitAllTrackers(); - updateClassesAndCounts(); - } + commitAllTrackers(); + updateClassesAndCounts(); } - }, this); + }; mySingleAlarm = new SingleAlarmWithMutableDelay(() -> { myLastSuspendContext = getSuspendContext(); @@ -249,19 +249,6 @@ public class ClassesFilteredView extends BorderLayoutPanel implements Disposable return myConstructorTrackedClasses.getOrDefault(ref, null); } - public void setNeedReloadClasses(boolean value) { - if (myNeedReloadClasses != value) { - myNeedReloadClasses = value; - if (myNeedReloadClasses) { - SuspendContextImpl suspendContext = getSuspendContext(); - if (suspendContext != null && !suspendContext.equals(myLastSuspendContext)) { - commitAllTrackers(); - updateClassesAndCounts(); - } - } - } - } - private void trackClass(@NotNull ReferenceType ref, @NotNull TrackingType type) { LOG.assertTrue(DebuggerManager.getInstance(myProject).isDebuggerManagerThread()); @@ -272,6 +259,8 @@ public class ClassesFilteredView extends BorderLayoutPanel implements Disposable } ConstructorInstancesTracker tracker = new ConstructorInstancesTracker(ref, myDebugSession); + tracker.setBackgroundMode(!myIsActive); + Disposer.register(ClassesFilteredView.this, tracker); myConstructorTrackedClasses.put(ref, tracker); } @@ -305,13 +294,44 @@ public class ClassesFilteredView extends BorderLayoutPanel implements Disposable boolean isTrackingActive(@NotNull ReferenceType ref) { TrackerForNewInstances strategy = myConstructorTrackedClasses.getOrDefault(ref, null); return strategy != null && strategy.isReady(); - } @Override public void dispose() { } + public void setActive(boolean active) { + if (myIsActive == active) { + return; + } + + myIsActive = active; + myDebugProcess.getManagerThread().schedule(new DebuggerCommandImpl() { + @Override + protected void action() throws Exception { + if (active) { + doActivate(); + } else { + doPause(); + } + } + }); + } + + private void doActivate() { + myDebugSession.addSessionListener(myDebugSessionListener, ClassesFilteredView.this); + if (getSuspendContext() != null) { + myConstructorTrackedClasses.values().forEach(x -> x.setBackgroundMode(false)); + commitAllTrackers(); + updateClassesAndCounts(); + } + } + + private void doPause() { + myDebugSession.removeSessionListener(myDebugSessionListener); + myConstructorTrackedClasses.values().forEach(x -> x.setBackgroundMode(true)); + } + private final class MyUpdateClassesCommand extends LowestPriorityCommand { MyUpdateClassesCommand(@Nullable SuspendContextImpl suspendContext) { From d25f44cc3c2327f083e9bb136b40a68d01ac16d9 Mon Sep 17 00:00:00 2001 From: Vitaliy Bibaev Date: Fri, 30 Sep 2016 20:04:41 +0300 Subject: [PATCH 109/151] Save stacks for new objects after debugger step --- component/CreationPositionTracker.java | 58 ++++++++++++++++++++++---- view/ClassesTable.java | 17 +++++--- 2 files changed, 61 insertions(+), 14 deletions(-) diff --git a/component/CreationPositionTracker.java b/component/CreationPositionTracker.java index 6045d9089b41..6bf4a4d27f56 100644 --- a/component/CreationPositionTracker.java +++ b/component/CreationPositionTracker.java @@ -5,18 +5,23 @@ import com.intellij.openapi.project.Project; import com.intellij.xdebugger.XDebugSession; import com.intellij.xdebugger.XDebugSessionListener; import com.sun.jdi.ObjectReference; +import com.sun.jdi.ReferenceType; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; import org.jetbrains.debugger.memory.utils.StackFrameDescriptor; -import java.util.Collections; +import java.util.Iterator; import java.util.List; import java.util.Map; import java.util.concurrent.ConcurrentHashMap; +import java.util.stream.Collectors; public class CreationPositionTracker extends AbstractProjectComponent { - private final Map>> mySession2Reference2Stack - = new ConcurrentHashMap<>(); + private final ConcurrentHashMap>> + mySession2Reference2Stack = new ConcurrentHashMap<>(); + + private final ConcurrentHashMap>> + myPinnedSession2Reference2Stack = new ConcurrentHashMap<>(); public CreationPositionTracker(Project project) { super(project); @@ -28,9 +33,8 @@ public class CreationPositionTracker extends AbstractProjectComponent { @Nullable public List getStack(@NotNull XDebugSession session, @NotNull ObjectReference ref) { - Map> ref2Stack = mySession2Reference2Stack.getOrDefault(session, null); - List stack = ref2Stack != null ? ref2Stack.getOrDefault(ref, null) : null; - return stack != null ? Collections.unmodifiableList(stack) : null; + List stack = extract(mySession2Reference2Stack, session, ref); + return stack != null ? stack : extract(myPinnedSession2Reference2Stack, session, ref); } public void addStack(@NotNull XDebugSession session, @NotNull ObjectReference ref, @@ -49,10 +53,46 @@ public class CreationPositionTracker extends AbstractProjectComponent { } public void releaseBySession(@NotNull XDebugSession session) { - if(!mySession2Reference2Stack.containsKey(session)) { - return; + if(mySession2Reference2Stack.containsKey(session)) { + mySession2Reference2Stack.put(session, new ConcurrentHashMap<>()); + } + } + + public void unpinStacks(@NotNull XDebugSession session, @NotNull ReferenceType ref) { + Map> ref2Stack = myPinnedSession2Reference2Stack.getOrDefault(session, null); + if (ref2Stack != null) { + Iterator iterator = ref2Stack.keySet().iterator(); + while (iterator.hasNext()) { + ObjectReference reference = iterator.next(); + if (ref.equals(reference.referenceType())) { + iterator.remove(); + } + } + + if (ref2Stack.isEmpty()) { + myPinnedSession2Reference2Stack.remove(session); + } + } + } + + public void pinStacks(@NotNull XDebugSession session, @NotNull ReferenceType ref) { + Map> ref2Stack = mySession2Reference2Stack.getOrDefault(session, null); + if (ref2Stack != null) { + Map> ref2StacksByReferenceType = ref2Stack.entrySet().stream() + .filter(entry -> ref.equals(entry.getKey().referenceType())) + .collect(Collectors.toMap(Map.Entry::getKey, Map.Entry::getValue)); + myPinnedSession2Reference2Stack.put(session, ref2StacksByReferenceType); + } + } + + @Nullable + private static T extract(@NotNull Map> map, + @NotNull XDebugSession session, @NotNull ObjectReference ref) { + Map ref2something = map.getOrDefault(session, null); + if(ref2something != null) { + return ref2something.getOrDefault(ref, null); } - mySession2Reference2Stack.put(session, new ConcurrentHashMap<>()); + return null; } } diff --git a/view/ClassesTable.java b/view/ClassesTable.java index b1d0239ae7f8..94bdd7ec401c 100644 --- a/view/ClassesTable.java +++ b/view/ClassesTable.java @@ -3,6 +3,7 @@ package org.jetbrains.debugger.memory.view; import com.intellij.openapi.Disposable; import com.intellij.openapi.actionSystem.DataKey; import com.intellij.openapi.actionSystem.DataProvider; +import com.intellij.openapi.util.Disposer; import com.intellij.openapi.util.TextRange; import com.intellij.psi.codeStyle.MinusculeMatcher; import com.intellij.psi.codeStyle.NameUtil; @@ -21,6 +22,7 @@ import com.sun.jdi.ReferenceType; import org.jetbrains.annotations.NonNls; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; +import org.jetbrains.debugger.memory.component.CreationPositionTracker; import org.jetbrains.debugger.memory.component.InstancesTracker; import org.jetbrains.debugger.memory.tracking.TrackerForNewInstances; import org.jetbrains.debugger.memory.tracking.TrackingType; @@ -109,7 +111,11 @@ public class ClassesTable extends JBTable implements DataProvider, Disposable { TrackerForNewInstances strategy = myParent.getStrategy(ref); if (strategy != null && strategy.isReady() && strategy.getCount() > 0) { List newInstances = strategy.getNewInstances(); - new InstancesWindow(myDebugSession, limit -> newInstances, ref.name()).show(); + CreationPositionTracker.getInstance(myDebugSession.getProject()).pinStacks(myDebugSession, ref); + InstancesWindow instancesWindow = new InstancesWindow(myDebugSession, limit -> newInstances, ref.name()); + Disposer.register(instancesWindow.getDisposable(), + () -> CreationPositionTracker.getInstance(myDebugSession.getProject()).unpinStacks(myDebugSession, ref)); + instancesWindow.show(); } } }); @@ -423,11 +429,12 @@ public class ClassesTable extends JBTable implements DataProvider, Disposable { } private class MyDiffColumnRenderer extends MyTableCellRenderer { - private final SimpleTextAttributes myClickableCellAttributes = new SimpleTextAttributes(SimpleTextAttributes.STYLE_UNDERLINE, - JBColor.BLUE); + private final SimpleTextAttributes myClickableCellAttributes = + new SimpleTextAttributes(SimpleTextAttributes.STYLE_UNDERLINE, JBColor.BLUE); @Override - protected void addText(JTable table, @NotNull Object value, boolean isSelected, boolean hasFocus, int row, int column) { + protected void addText(JTable table, @NotNull Object value, boolean isSelected, + boolean hasFocus, int row, int column) { long diff = ((DiffValue) value).diff(); setTextAlign(SwingConstants.RIGHT); @@ -439,7 +446,7 @@ public class ClassesTable extends JBTable implements DataProvider, Disposable { if (strategy != null && strategy.isReady()) { long trackedNew = strategy.getCount(); if (trackedNew == diff) { - append(text, diff == 0 ? SimpleTextAttributes.REGULAR_ATTRIBUTES : myClickableCellAttributes); + append(text, diff == 0 ? SimpleTextAttributes.REGULAR_ATTRIBUTES : myClickableCellAttributes); } else { append(text, SimpleTextAttributes.REGULAR_ATTRIBUTES); if (trackedNew != 0) { From cbc274b27f384485489e11234494509e249d10f4 Mon Sep 17 00:00:00 2001 From: Vitaliy Bibaev Date: Fri, 30 Sep 2016 20:35:01 +0300 Subject: [PATCH 110/151] Save stacks for new objects after debugger step. Associate "Jump to allocation Source" action with appropriate debug session --- ...java => JumpToAllocationSourceAction.java} | 6 +++--- component/CreationPositionTracker.java | 2 +- view/InstancesTree.java | 20 ++++++++++++++++++- view/InstancesWindow.java | 2 +- 4 files changed, 24 insertions(+), 6 deletions(-) rename action/tracking/{GetAllocationSourceAction.java => JumpToAllocationSourceAction.java} (90%) diff --git a/action/tracking/GetAllocationSourceAction.java b/action/tracking/JumpToAllocationSourceAction.java similarity index 90% rename from action/tracking/GetAllocationSourceAction.java rename to action/tracking/JumpToAllocationSourceAction.java index e396d989ae6e..dba977e27710 100644 --- a/action/tracking/GetAllocationSourceAction.java +++ b/action/tracking/JumpToAllocationSourceAction.java @@ -13,11 +13,12 @@ import org.jetbrains.annotations.Nullable; import org.jetbrains.debugger.memory.action.DebuggerTreeAction; import org.jetbrains.debugger.memory.component.CreationPositionTracker; import org.jetbrains.debugger.memory.utils.StackFrameDescriptor; +import org.jetbrains.debugger.memory.view.InstancesTree; import org.jetbrains.debugger.memory.view.StackFramePopup; import java.util.List; -public class GetAllocationSourceAction extends DebuggerTreeAction { +public class JumpToAllocationSourceAction extends DebuggerTreeAction { @Override public void update(AnActionEvent e) { e.getPresentation().setVisible(getStack(e) != null); @@ -46,8 +47,7 @@ public class GetAllocationSourceAction extends DebuggerTreeAction { return null; } - // TODO: Wrong way: session may be not current - XDebugSession session = XDebuggerManager.getInstance(project).getCurrentSession(); + XDebugSession session = e.getData(InstancesTree.DEBUG_SESSION_DATA_KEY); return session == null ? null : CreationPositionTracker.getInstance(project).getStack(session, ref); } } diff --git a/component/CreationPositionTracker.java b/component/CreationPositionTracker.java index 6bf4a4d27f56..7b2937adc1fb 100644 --- a/component/CreationPositionTracker.java +++ b/component/CreationPositionTracker.java @@ -38,7 +38,7 @@ public class CreationPositionTracker extends AbstractProjectComponent { } public void addStack(@NotNull XDebugSession session, @NotNull ObjectReference ref, - @NotNull List stack) { + @NotNull List< StackFrameDescriptor> stack) { if (!mySession2Reference2Stack.containsKey(session)) { mySession2Reference2Stack.put(session, new ConcurrentHashMap<>()); session.addSessionListener(new XDebugSessionListener() { diff --git a/view/InstancesTree.java b/view/InstancesTree.java index d2b612ef672b..4a59e3e88b4f 100644 --- a/view/InstancesTree.java +++ b/view/InstancesTree.java @@ -1,7 +1,10 @@ package org.jetbrains.debugger.memory.view; +import com.intellij.openapi.actionSystem.DataKey; +import com.intellij.openapi.actionSystem.DataProvider; import com.intellij.openapi.project.Project; import com.intellij.ui.SimpleTextAttributes; +import com.intellij.xdebugger.XDebugSession; import com.intellij.xdebugger.evaluation.XDebuggerEditorsProvider; import com.intellij.xdebugger.frame.*; import com.intellij.xdebugger.impl.actions.XDebuggerActions; @@ -9,6 +12,7 @@ import com.intellij.xdebugger.impl.frame.XValueMarkers; import com.intellij.xdebugger.impl.ui.tree.XDebuggerTree; import com.intellij.xdebugger.impl.ui.tree.XDebuggerTreeState; import com.intellij.xdebugger.impl.ui.tree.nodes.XValueNodeImpl; +import org.jetbrains.annotations.NonNls; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; @@ -16,18 +20,22 @@ import javax.swing.*; import java.util.ArrayList; import java.util.List; -class InstancesTree extends XDebuggerTree { +public class InstancesTree extends XDebuggerTree implements DataProvider { + public static final DataKey DEBUG_SESSION_DATA_KEY = DataKey.create("InstancesTree.DebugSession"); private final XValueNodeImpl myRoot; private final Runnable myOnRootExpandAction; + private final XDebugSession myDebugSession; private List myChildren; InstancesTree(@NotNull Project project, + @NotNull XDebugSession debugSession, @NotNull XDebuggerEditorsProvider editorsProvider, @Nullable XValueMarkers valueMarkers, @NotNull Runnable onRootExpand) { super(project, editorsProvider, null, XDebuggerActions.INSPECT_TREE_POPUP_GROUP, valueMarkers); myOnRootExpandAction = onRootExpand; myRoot = new XValueNodeImpl(this, null, "root", new MyRootValue()); + myDebugSession = debugSession; myRoot.children(); setRoot(myRoot, false); @@ -63,6 +71,16 @@ class InstancesTree extends XDebuggerTree { myRoot.setMessage(text, icon, textAttributes, null); } + @Nullable + @Override + public Object getData(@NonNls String dataId) { + if(DEBUG_SESSION_DATA_KEY.is(dataId)) { + return myDebugSession; + } + + return super.getData(dataId); + } + enum RebuildPolicy { RELOAD_INSTANCES, ONLY_UPDATE_LABELS } diff --git a/view/InstancesWindow.java b/view/InstancesWindow.java index 6bc8ed60e9c8..6365c49db968 100644 --- a/view/InstancesWindow.java +++ b/view/InstancesWindow.java @@ -191,7 +191,7 @@ public class InstancesWindow extends DialogWrapper { myProgress.addStopActionListener(this::cancelFilteringTask); - myInstancesTree = new InstancesTree(myProject, editorsProvider, markers, this::updateInstances); + myInstancesTree = new InstancesTree(myProject, myDebugSession, editorsProvider, markers, this::updateInstances); myFilterButton.addActionListener(e -> { String expression = myFilterConditionEditor.getExpression().getExpression(); From b2f146d9bdccf8b551a6b4550c451f0df02c0db3 Mon Sep 17 00:00:00 2001 From: "Vitaliy.Bibaev" Date: Fri, 7 Oct 2016 19:08:29 +0300 Subject: [PATCH 111/151] Open tracked instances only by right mouse click --- view/ClassesTable.java | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/view/ClassesTable.java b/view/ClassesTable.java index 94bdd7ec401c..64ef97be29a6 100644 --- a/view/ClassesTable.java +++ b/view/ClassesTable.java @@ -66,7 +66,6 @@ public class ClassesTable extends JBTable implements DataProvider, Disposable { private volatile List myElems = Collections.unmodifiableList(new ArrayList<>()); - // TODO: parent must be unknown in this context ClassesTable(@NotNull XDebugSession session, boolean onlyWithDiff, boolean onlyWithInstances, @NotNull ClassesFilteredView parent) { setModel(myModel); @@ -103,7 +102,8 @@ public class ClassesTable extends JBTable implements DataProvider, Disposable { addMouseListener(new MouseAdapter() { @Override public void mouseClicked(MouseEvent e) { - if (columnAtPoint(e.getPoint()) != DiffViewTableModel.DIFF_COLUMN_INDEX) { + if (columnAtPoint(e.getPoint()) != DiffViewTableModel.DIFF_COLUMN_INDEX || + e.getButton() != MouseEvent.BUTTON1) { return; } From 9f4524adcff51d3144a9872c459b83e93156695e Mon Sep 17 00:00:00 2001 From: "Vitaliy.Bibaev" Date: Fri, 7 Oct 2016 21:47:23 +0300 Subject: [PATCH 112/151] Fix bug when classes table was unsorted after opened --- view/ClassesTable.java | 2 ++ 1 file changed, 2 insertions(+) diff --git a/view/ClassesTable.java b/view/ClassesTable.java index 64ef97be29a6..0e097cf2078f 100644 --- a/view/ClassesTable.java +++ b/view/ClassesTable.java @@ -214,6 +214,8 @@ public class ClassesTable extends JBTable implements DataProvider, Disposable { changeSelection(ix, DiffViewTableModel.CLASSNAME_COLUMN_INDEX, false, false); } + + getRowSorter().allRowsChanged(); } void hideContent() { From dbdb249ebe23d642ce17164df5b5b943c397a3e2 Mon Sep 17 00:00:00 2001 From: "Vitaliy.Bibaev" Date: Sun, 9 Oct 2016 20:42:51 +0300 Subject: [PATCH 113/151] Disallow tracking for arrays --- .../tracking/TrackInstanceCreationAction.java | 39 ++++++++++++++++--- 1 file changed, 34 insertions(+), 5 deletions(-) diff --git a/action/tracking/TrackInstanceCreationAction.java b/action/tracking/TrackInstanceCreationAction.java index 810884643cc9..4268cd30e6cb 100644 --- a/action/tracking/TrackInstanceCreationAction.java +++ b/action/tracking/TrackInstanceCreationAction.java @@ -1,12 +1,41 @@ package org.jetbrains.debugger.memory.action.tracking; -import org.jetbrains.annotations.NotNull; +import com.intellij.openapi.actionSystem.AnActionEvent; +import com.intellij.openapi.project.Project; +import com.sun.jdi.ArrayType; +import com.sun.jdi.ReferenceType; +import org.jetbrains.debugger.memory.action.ClassesActionBase; +import org.jetbrains.debugger.memory.component.InstancesTracker; import org.jetbrains.debugger.memory.tracking.TrackingType; -public class TrackInstanceCreationAction extends InstancesTrackingActionBase { - @NotNull +public class TrackInstanceCreationAction extends ClassesActionBase { @Override - protected TrackingType getType() { - return TrackingType.CREATION; + protected boolean isEnabled(AnActionEvent e) { + Project project = e.getProject(); + if (!super.isEnabled(e) || project == null) { + return false; + } + + InstancesTracker tracker = InstancesTracker.getInstance(project); + ReferenceType selectedClass = getSelectedClass(e); + if (selectedClass != null) { + TrackingType currentType = tracker.getTrackingType(selectedClass.name()); + if (TrackingType.CREATION == currentType) { + return false; + } + } + + return selectedClass != null && !(selectedClass instanceof ArrayType); + } + + @Override + protected void perform(AnActionEvent e) { + Project project = e.getProject(); + ReferenceType selectedClass = getSelectedClass(e); + + if (selectedClass != null && project != null) { + InstancesTracker tracker = InstancesTracker.getInstance(project); + tracker.add(selectedClass, TrackingType.CREATION); + } } } From e671bfa03c87f46cbf7517d0bead37b241963608 Mon Sep 17 00:00:00 2001 From: "Vitaliy.Bibaev" Date: Sun, 9 Oct 2016 21:54:53 +0300 Subject: [PATCH 114/151] Dont track new objects before the first pause of session --- tracking/ConstructorInstancesTracker.java | 54 ++++++++++++++--------- tracking/TrackerForNewInstances.java | 3 +- view/ClassesFilteredView.java | 35 ++++++++++++--- 3 files changed, 64 insertions(+), 28 deletions(-) diff --git a/tracking/ConstructorInstancesTracker.java b/tracking/ConstructorInstancesTracker.java index c8924f518569..9dfff777e59f 100644 --- a/tracking/ConstructorInstancesTracker.java +++ b/tracking/ConstructorInstancesTracker.java @@ -28,6 +28,7 @@ import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; import org.jetbrains.debugger.memory.component.CreationPositionTracker; import org.jetbrains.debugger.memory.component.InstancesTracker; +import org.jetbrains.debugger.memory.event.InstancesTrackerListener; import org.jetbrains.debugger.memory.utils.StackFrameDescriptor; import org.jetbrains.java.debugger.breakpoints.properties.JavaLineBreakpointProperties; @@ -48,8 +49,8 @@ public class ConstructorInstancesTracker implements TrackerForNewInstances, Disp @NotNull private HashSet myTrackedObjects = new HashSet<>(); - private boolean myIsBackgroundMode; - private boolean myIsBackgroundTrackingEnabled; + private volatile boolean myIsBackgroundMode; + private volatile boolean myIsBackgroundTrackingEnabled; public ConstructorInstancesTracker(@NotNull ReferenceType ref, @NotNull XDebugSession debugSession) { @@ -57,7 +58,28 @@ public class ConstructorInstancesTracker implements TrackerForNewInstances, Disp myDebugSession = debugSession; myPositionTracker = CreationPositionTracker.getInstance(debugSession.getProject()); Project project = debugSession.getProject(); - myIsBackgroundTrackingEnabled = InstancesTracker.getInstance(myDebugSession.getProject()).isBackgroundTrackingEnabled(); + myIsBackgroundTrackingEnabled = InstancesTracker.getInstance(myDebugSession.getProject()) + .isBackgroundTrackingEnabled(); + + InstancesTracker.getInstance(myDebugSession.getProject()).addTrackerListener(new InstancesTrackerListener() { + @Override + public void backgroundTrackingValueChanged(boolean newState) { + if (myIsBackgroundTrackingEnabled != newState) { + myIsBackgroundTrackingEnabled = newState; + myDebugProcess.getManagerThread().schedule(new DebuggerCommandImpl() { + @Override + protected void action() throws Exception { + if (newState) { + myBreakpoint.enable(); + } else { + myBreakpoint.disable(); + } + } + }); + } + } + }, this); + myDebugProcess = (DebugProcessImpl) DebuggerManager.getInstance(project) .getDebugProcess(debugSession.getDebugProcess().getProcessHandler()); JavaLineBreakpointType breakPointType = new JavaLineBreakpointType(); @@ -99,6 +121,14 @@ public class ConstructorInstancesTracker implements TrackerForNewInstances, Disp return myNewObjects == null ? 0 : myNewObjects.size(); } + public void enable() { + myBreakpoint.enable(); + } + + public void disable() { + myBreakpoint.disable(); + } + @Override public boolean isReady() { return myNewObjects != null; @@ -109,23 +139,6 @@ public class ConstructorInstancesTracker implements TrackerForNewInstances, Disp myBreakpoint.dispose(); } - @Override - public void backgroundTrackingValueChanged(boolean newValue) { - if (myIsBackgroundTrackingEnabled != newValue) { - myIsBackgroundTrackingEnabled = newValue; - myDebugProcess.getManagerThread().schedule(new DebuggerCommandImpl() { - @Override - protected void action() throws Exception { - if (newValue) { - myBreakpoint.enable(); - } else { - myBreakpoint.disable(); - } - } - }); - } - } - @Override public void setBackgroundMode(boolean isBackgroundMode) { if (myIsBackgroundMode == isBackgroundMode) { @@ -243,4 +256,3 @@ public class ConstructorInstancesTracker implements TrackerForNewInstances, Disp } } } - diff --git a/tracking/TrackerForNewInstances.java b/tracking/TrackerForNewInstances.java index 75a54b0b3985..b6fc329f5d36 100644 --- a/tracking/TrackerForNewInstances.java +++ b/tracking/TrackerForNewInstances.java @@ -2,11 +2,10 @@ package org.jetbrains.debugger.memory.tracking; import com.sun.jdi.ObjectReference; import org.jetbrains.annotations.NotNull; -import org.jetbrains.debugger.memory.event.InstancesTrackerListener; import java.util.List; -public interface TrackerForNewInstances extends InstancesTrackerListener { +public interface TrackerForNewInstances { @NotNull List getNewInstances(); diff --git a/view/ClassesFilteredView.java b/view/ClassesFilteredView.java index 2be691d6b076..a1278365cda6 100644 --- a/view/ClassesFilteredView.java +++ b/view/ClassesFilteredView.java @@ -69,6 +69,16 @@ public class ClassesFilteredView extends BorderLayoutPanel implements Disposable private volatile SuspendContextImpl myLastSuspendContext; + /** + * Indicates that the debug session had been stopped at least once. + * + * State: false -> true + */ + private volatile boolean myIsTrackersActivated = false; + + /** + * Indicates that view is visible in tool window + */ private boolean myIsActive; public ClassesFilteredView(@NotNull XDebugSession debugSession) { @@ -88,7 +98,7 @@ public class ClassesFilteredView extends BorderLayoutPanel implements Disposable .schedule(new LowestPriorityCommand(getSuspendContext()) { @Override public void contextAction(@NotNull SuspendContextImpl suspendContext) throws Exception { - trackClass(ref, type); + trackClass(ref, type, myIsTrackersActivated); } }); } @@ -96,7 +106,12 @@ public class ClassesFilteredView extends BorderLayoutPanel implements Disposable @Override public void classRemoved(@NotNull String name) { - myTable.getRowSorter().allRowsChanged(); + ReferenceType ref = myTable.getClassByName(name); + if (ref != null && myConstructorTrackedClasses.containsKey(ref)) { + ConstructorInstancesTracker removed = myConstructorTrackedClasses.remove(ref); + removed.dispose(); + myTable.getRowSorter().allRowsChanged(); + } } }; @@ -116,12 +131,12 @@ public class ClassesFilteredView extends BorderLayoutPanel implements Disposable new ClassPreparedListener(className, myDebugSession) { @Override public void onClassPrepared(@NotNull ReferenceType referenceType) { - trackClass(referenceType, type); + trackClass(referenceType, type, myIsTrackersActivated); } }; } else { for (ReferenceType ref : classes) { - trackClass(ref, type); + trackClass(ref, type, myIsTrackersActivated); } } } @@ -215,6 +230,10 @@ public class ClassesFilteredView extends BorderLayoutPanel implements Disposable SwingUtilities.invokeLater(() -> myTable.getEmptyText().setText(EMPTY_TABLE_CONTENT_WHEN_SUSPENDED)); commitAllTrackers(); updateClassesAndCounts(); + if(!myIsTrackersActivated) { + myConstructorTrackedClasses.values().forEach(ConstructorInstancesTracker::enable); + myIsTrackersActivated = true; + } } }; @@ -250,7 +269,8 @@ public class ClassesFilteredView extends BorderLayoutPanel implements Disposable } private void trackClass(@NotNull ReferenceType ref, - @NotNull TrackingType type) { + @NotNull TrackingType type, + boolean isTrackerEnabled) { LOG.assertTrue(DebuggerManager.getInstance(myProject).isDebuggerManagerThread()); if (type == TrackingType.CREATION) { ConstructorInstancesTracker old = myConstructorTrackedClasses.getOrDefault(ref, null); @@ -260,6 +280,11 @@ public class ClassesFilteredView extends BorderLayoutPanel implements Disposable ConstructorInstancesTracker tracker = new ConstructorInstancesTracker(ref, myDebugSession); tracker.setBackgroundMode(!myIsActive); + if (isTrackerEnabled) { + tracker.enable(); + } else { + tracker.disable(); + } Disposer.register(ClassesFilteredView.this, tracker); myConstructorTrackedClasses.put(ref, tracker); From 714bf6ccdaf487cc514fab21bc4b22740075fe74 Mon Sep 17 00:00:00 2001 From: "Vitaliy.Bibaev" Date: Fri, 14 Oct 2016 16:28:33 +0300 Subject: [PATCH 115/151] Add counts of instances to popup dialog in classes table for "Show instances" actions --- action/ShowInstancesAction.java | 17 +++++++ .../ShowInstancesFromClassesViewAction.java | 22 ++++++++- action/ShowNewInstancesAction.java | 22 ++++++++- view/ClassesTable.java | 48 +++++++++++++++---- 4 files changed, 97 insertions(+), 12 deletions(-) create mode 100644 action/ShowInstancesAction.java diff --git a/action/ShowInstancesAction.java b/action/ShowInstancesAction.java new file mode 100644 index 000000000000..400d194a1a01 --- /dev/null +++ b/action/ShowInstancesAction.java @@ -0,0 +1,17 @@ +package org.jetbrains.debugger.memory.action; + +import com.intellij.openapi.actionSystem.AnActionEvent; + +abstract class ShowInstancesAction extends ClassesActionBase { + @Override + public void update(AnActionEvent e) { + boolean enabled = isEnabled(e); + if(enabled) { + e.getPresentation().setText(String.format("%s (%d)", getLabel(), getInstancesCount(e))); + } + } + + protected abstract String getLabel(); + + protected abstract int getInstancesCount(AnActionEvent e); +} diff --git a/action/ShowInstancesFromClassesViewAction.java b/action/ShowInstancesFromClassesViewAction.java index a03c5ec1f288..040432aeb97a 100644 --- a/action/ShowInstancesFromClassesViewAction.java +++ b/action/ShowInstancesFromClassesViewAction.java @@ -3,12 +3,14 @@ package org.jetbrains.debugger.memory.action; import com.intellij.openapi.actionSystem.AnActionEvent; import com.intellij.xdebugger.XDebugSession; import com.sun.jdi.ReferenceType; +import org.jetbrains.debugger.memory.view.ClassesTable; import org.jetbrains.debugger.memory.view.InstancesWindow; -public class ShowInstancesFromClassesViewAction extends ClassesActionBase { +public class ShowInstancesFromClassesViewAction extends ShowInstancesAction { + private static final String POPUP_ELEMENT_LABEL = "Show Instances"; @Override protected boolean isEnabled(AnActionEvent e) { - return super.isEnabled(e) && getSelectedClass(e) != null ; + return super.isEnabled(e) && getSelectedClass(e) != null; } @Override @@ -19,4 +21,20 @@ public class ShowInstancesFromClassesViewAction extends ClassesActionBase { new InstancesWindow(debugSession, selectedClass::instances, selectedClass.name()).show(); } } + + @Override + protected String getLabel() { + return POPUP_ELEMENT_LABEL; + } + + @Override + protected int getInstancesCount(AnActionEvent e) { + ClassesTable.ReferenceCountProvider countProvider = e.getData(ClassesTable.REF_COUNT_PROVIDER_KEY); + ReferenceType selectedClass = getSelectedClass(e); + if (countProvider == null || selectedClass == null) { + return -1; + } + + return countProvider.getTotalCount(selectedClass); + } } diff --git a/action/ShowNewInstancesAction.java b/action/ShowNewInstancesAction.java index 08ea5b76b6e3..cfa3922f4089 100644 --- a/action/ShowNewInstancesAction.java +++ b/action/ShowNewInstancesAction.java @@ -7,7 +7,9 @@ import org.jetbrains.debugger.memory.utils.InstancesProvider; import org.jetbrains.debugger.memory.view.ClassesTable; import org.jetbrains.debugger.memory.view.InstancesWindow; -public class ShowNewInstancesAction extends ClassesActionBase { +public class ShowNewInstancesAction extends ShowInstancesAction { + private static final String POPUP_ELEMENT_LABEL = "Show New Instances"; + @Override protected boolean isEnabled(AnActionEvent e) { XDebugSession session = getDebugSession(e); @@ -16,12 +18,28 @@ public class ShowNewInstancesAction extends ClassesActionBase { return super.isEnabled(e) && session != null && selectedClass != null && provider != null; } + @Override + protected String getLabel() { + return POPUP_ELEMENT_LABEL; + } + + @Override + protected int getInstancesCount(AnActionEvent e) { + ClassesTable.ReferenceCountProvider countProvider = e.getData(ClassesTable.REF_COUNT_PROVIDER_KEY); + ReferenceType selectedClass = getSelectedClass(e); + if (countProvider == null || selectedClass == null) { + return -1; + } + + return countProvider.getNewInstancesCount(selectedClass); + } + @Override protected void perform(AnActionEvent e) { XDebugSession session = getDebugSession(e); ReferenceType selectedClass = getSelectedClass(e); InstancesProvider provider = e.getData(ClassesTable.NEW_INSTANCES_PROVIDER_KEY); - if(selectedClass != null && provider != null && session != null) { + if (selectedClass != null && provider != null && session != null) { new InstancesWindow(session, provider, selectedClass.name()).show(); } } diff --git a/view/ClassesTable.java b/view/ClassesTable.java index 0e097cf2078f..7e9efca938cc 100644 --- a/view/ClassesTable.java +++ b/view/ClassesTable.java @@ -36,7 +36,6 @@ import javax.swing.table.TableRowSorter; import java.awt.event.MouseAdapter; import java.awt.event.MouseEvent; import java.util.*; -import java.util.List; import java.util.concurrent.ConcurrentHashMap; public class ClassesTable extends JBTable implements DataProvider, Disposable { @@ -44,6 +43,8 @@ public class ClassesTable extends JBTable implements DataProvider, Disposable { public static final DataKey DEBUG_SESSION_KEY = DataKey.create("ClassesTable.DebugSession"); public static final DataKey NEW_INSTANCES_PROVIDER_KEY = DataKey.create("ClassesTable.NewInstances"); + public static final DataKey REF_COUNT_PROVIDER_KEY = + DataKey.create("ClassesTable.ReferenceCountProvider"); private static final int CLASSES_COLUMN_PREFERRED_WIDTH = 250; private static final int COUNT_COLUMN_MIN_WIDTH = 80; @@ -57,6 +58,7 @@ public class ClassesTable extends JBTable implements DataProvider, Disposable { private final Map myCounts = new ConcurrentHashMap<>(); private final InstancesTracker myInstancesTracker; private final ClassesFilteredView myParent; + private final ReferenceCountProvider myCountProvider; private boolean myOnlyWithDiff; @@ -139,6 +141,32 @@ public class ClassesTable extends JBTable implements DataProvider, Disposable { sorter.setSortKeys(myDefaultSortingKeys); setRowSorter(sorter); setSelectionMode(ListSelectionModel.SINGLE_SELECTION); + + myCountProvider = new ReferenceCountProvider() { + @Override + public int getTotalCount(@NotNull ReferenceType ref) { + return (int) myCounts.get(ref).myCurrentCount; + } + + @Override + public int getDiffCount(@NotNull ReferenceType ref) { + return (int) myCounts.get(ref).diff(); + } + + @Override + public int getNewInstancesCount(@NotNull ReferenceType ref) { + TrackerForNewInstances strategy = myParent.getStrategy(ref); + return strategy == null || !strategy.isReady() ? -1 : strategy.getCount(); + } + }; + } + + public interface ReferenceCountProvider { + int getTotalCount(@NotNull ReferenceType ref); + + int getDiffCount(@NotNull ReferenceType ref); + + int getNewInstancesCount(@NotNull ReferenceType ref); } @Nullable @@ -226,6 +254,7 @@ public class ClassesTable extends JBTable implements DataProvider, Disposable { myModel.show(); } + @Nullable @Override public Object getData(@NonNls String dataId) { @@ -246,6 +275,10 @@ public class ClassesTable extends JBTable implements DataProvider, Disposable { } } + if (REF_COUNT_PROVIDER_KEY.is(dataId)) { + return myCountProvider; + } + return null; } @@ -437,22 +470,21 @@ public class ClassesTable extends JBTable implements DataProvider, Disposable { @Override protected void addText(JTable table, @NotNull Object value, boolean isSelected, boolean hasFocus, int row, int column) { - long diff = ((DiffValue) value).diff(); setTextAlign(SwingConstants.RIGHT); ReferenceType ref = myElems.get(convertRowIndexToModel(row)); - TrackerForNewInstances strategy = myParent.getStrategy(ref); + long diff = myCountProvider.getDiffCount(ref); String text = String.format("%s%d", diff > 0 ? "+" : "", diff); - if (strategy != null && strategy.isReady()) { - long trackedNew = strategy.getCount(); - if (trackedNew == diff) { + int newInstancesCount = myCountProvider.getNewInstancesCount(ref); + if (newInstancesCount >= 0) { + if (newInstancesCount == diff) { append(text, diff == 0 ? SimpleTextAttributes.REGULAR_ATTRIBUTES : myClickableCellAttributes); } else { append(text, SimpleTextAttributes.REGULAR_ATTRIBUTES); - if (trackedNew != 0) { - append(String.format(" (%d)", trackedNew), myClickableCellAttributes); + if (newInstancesCount != 0) { + append(String.format(" (%d)", newInstancesCount), myClickableCellAttributes); } } } else { From 182ad9c8485972b1d24ee46588e9929fc07ce27f Mon Sep 17 00:00:00 2001 From: "Vitaliy.Bibaev" Date: Fri, 14 Oct 2016 18:18:26 +0300 Subject: [PATCH 116/151] Do not update class view if there are no steps while the tool window was closed --- view/ClassesFilteredView.java | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/view/ClassesFilteredView.java b/view/ClassesFilteredView.java index a1278365cda6..7c47ddd3a721 100644 --- a/view/ClassesFilteredView.java +++ b/view/ClassesFilteredView.java @@ -230,7 +230,7 @@ public class ClassesFilteredView extends BorderLayoutPanel implements Disposable SwingUtilities.invokeLater(() -> myTable.getEmptyText().setText(EMPTY_TABLE_CONTENT_WHEN_SUSPENDED)); commitAllTrackers(); updateClassesAndCounts(); - if(!myIsTrackersActivated) { + if (!myIsTrackersActivated) { myConstructorTrackedClasses.values().forEach(ConstructorInstancesTracker::enable); myIsTrackersActivated = true; } @@ -345,8 +345,8 @@ public class ClassesFilteredView extends BorderLayoutPanel implements Disposable private void doActivate() { myDebugSession.addSessionListener(myDebugSessionListener, ClassesFilteredView.this); - if (getSuspendContext() != null) { - myConstructorTrackedClasses.values().forEach(x -> x.setBackgroundMode(false)); + myConstructorTrackedClasses.values().forEach(x -> x.setBackgroundMode(false)); + if (!myLastSuspendContext.equals(getSuspendContext())) { commitAllTrackers(); updateClassesAndCounts(); } From ca81c0270bee1fdcc5dc590dca4b00b134e0827c Mon Sep 17 00:00:00 2001 From: "Vitaliy.Bibaev" Date: Fri, 14 Oct 2016 19:07:30 +0300 Subject: [PATCH 117/151] Fix NPE when tool window has been closed before the starting of debug session --- view/ClassesFilteredView.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/view/ClassesFilteredView.java b/view/ClassesFilteredView.java index 7c47ddd3a721..69074e5d1c6f 100644 --- a/view/ClassesFilteredView.java +++ b/view/ClassesFilteredView.java @@ -346,7 +346,7 @@ public class ClassesFilteredView extends BorderLayoutPanel implements Disposable private void doActivate() { myDebugSession.addSessionListener(myDebugSessionListener, ClassesFilteredView.this); myConstructorTrackedClasses.values().forEach(x -> x.setBackgroundMode(false)); - if (!myLastSuspendContext.equals(getSuspendContext())) { + if (myLastSuspendContext == null || !myLastSuspendContext.equals(getSuspendContext())) { commitAllTrackers(); updateClassesAndCounts(); } From 492442445ac1b3fecf1b6807337d73e4bd42cd4b Mon Sep 17 00:00:00 2001 From: "Vitaliy.Bibaev" Date: Thu, 20 Oct 2016 19:45:28 +0300 Subject: [PATCH 118/151] Show stackframe in instances window (only if it exists) for selected instance --- view/InstancesTree.java | 33 ++++++- view/InstancesWindow.java | 17 +++- view/InstancesWithStackFrameView.java | 68 +++++++++++++ view/StackFrameList.java | 136 ++++++++++++++++++++++++++ view/StackFramePopup.java | 83 ++-------------- 5 files changed, 255 insertions(+), 82 deletions(-) create mode 100644 view/InstancesWithStackFrameView.java create mode 100644 view/StackFrameList.java diff --git a/view/InstancesTree.java b/view/InstancesTree.java index 4a59e3e88b4f..6352ffe02c9a 100644 --- a/view/InstancesTree.java +++ b/view/InstancesTree.java @@ -1,7 +1,9 @@ package org.jetbrains.debugger.memory.view; +import com.intellij.debugger.ui.impl.watch.NodeDescriptorProvider; +import com.intellij.debugger.ui.tree.NodeDescriptor; +import com.intellij.debugger.ui.tree.ValueDescriptor; import com.intellij.openapi.actionSystem.DataKey; -import com.intellij.openapi.actionSystem.DataProvider; import com.intellij.openapi.project.Project; import com.intellij.ui.SimpleTextAttributes; import com.intellij.xdebugger.XDebugSession; @@ -12,15 +14,18 @@ import com.intellij.xdebugger.impl.frame.XValueMarkers; import com.intellij.xdebugger.impl.ui.tree.XDebuggerTree; import com.intellij.xdebugger.impl.ui.tree.XDebuggerTreeState; import com.intellij.xdebugger.impl.ui.tree.nodes.XValueNodeImpl; +import com.sun.jdi.ObjectReference; +import com.sun.jdi.Value; import org.jetbrains.annotations.NonNls; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; import javax.swing.*; +import javax.swing.tree.TreePath; import java.util.ArrayList; import java.util.List; -public class InstancesTree extends XDebuggerTree implements DataProvider { +public class InstancesTree extends XDebuggerTree { public static final DataKey DEBUG_SESSION_DATA_KEY = DataKey.create("InstancesTree.DebugSession"); private final XValueNodeImpl myRoot; private final Runnable myOnRootExpandAction; @@ -71,10 +76,32 @@ public class InstancesTree extends XDebuggerTree implements DataProvider { myRoot.setMessage(text, icon, textAttributes, null); } + @Nullable + ObjectReference getSelectedReference() { + TreePath selectionPath = getSelectionPath(); + Object selectedItem = selectionPath != null ? selectionPath.getLastPathComponent() : null; + if (selectedItem instanceof XValueNodeImpl) { + XValueNodeImpl xValueNode = (XValueNodeImpl) selectedItem; + XValue valueContainer = xValueNode.getValueContainer(); + + if (valueContainer instanceof NodeDescriptorProvider) { + NodeDescriptor descriptor = ((NodeDescriptorProvider) valueContainer).getDescriptor(); + + if (descriptor instanceof ValueDescriptor) { + Value value = ((ValueDescriptor) descriptor).getValue(); + + if (value instanceof ObjectReference) return (ObjectReference) value; + } + } + } + + return null; + } + @Nullable @Override public Object getData(@NonNls String dataId) { - if(DEBUG_SESSION_DATA_KEY.is(dataId)) { + if (DEBUG_SESSION_DATA_KEY.is(dataId)) { return myDebugSession; } diff --git a/view/InstancesWindow.java b/view/InstancesWindow.java index 6365c49db968..7ee7d765021d 100644 --- a/view/InstancesWindow.java +++ b/view/InstancesWindow.java @@ -23,11 +23,11 @@ import com.intellij.openapi.ui.DialogWrapper; import com.intellij.openapi.util.Disposer; import com.intellij.openapi.util.Pair; import com.intellij.openapi.wm.IdeFocusManager; +import com.intellij.psi.search.GlobalSearchScope; import com.intellij.ui.JBColor; import com.intellij.ui.SimpleTextAttributes; import com.intellij.ui.components.JBLabel; import com.intellij.ui.components.JBPanel; -import com.intellij.ui.components.JBScrollPane; import com.intellij.util.ui.JBDimension; import com.intellij.util.ui.JBUI; import com.intellij.util.ui.UIUtil; @@ -57,6 +57,7 @@ import org.jetbrains.java.debugger.JavaDebuggerEditorsProvider; import javax.swing.*; import javax.swing.tree.TreeNode; import java.awt.*; +import java.util.Collections; import java.util.List; import java.util.concurrent.atomic.AtomicInteger; @@ -203,9 +204,18 @@ public class InstancesWindow extends DialogWrapper { myInstancesTree.rebuildTree(InstancesTree.RebuildPolicy.RELOAD_INSTANCES); }); - JBScrollPane treeScrollPane = new JBScrollPane(myInstancesTree); + + StackFrameList list = new StackFrameList(myProject, + Collections.emptyList(), + GlobalSearchScope.allScope(myProject)); + + list.addListSelectionListener(e -> list.navigateToSelectedValue(false)); + + InstancesWithStackFrameView instancesWithStackFrame = new InstancesWithStackFrameView(myDebugSession, + myInstancesTree, list); + add(filteringPane, BorderLayout.NORTH); - add(treeScrollPane, BorderLayout.CENTER); + add(instancesWithStackFrame.getComponent(), BorderLayout.CENTER); JComponent focusedComponent = myFilterConditionEditor.getEditorComponent(); UiNotifyConnector.doWhenFirstShown(focusedComponent, () -> @@ -268,6 +278,7 @@ public class InstancesWindow extends DialogWrapper { XExpression expression = myFilterConditionEditor.getExpression(); if (expression != null && !expression.getExpression().isEmpty()) { try { + myEvaluator.setReferenceExpression(TextWithImportsImpl.fromXExpression(expression)); myEvaluator.setReferenceExpression(TextWithImportsImpl. fromXExpression(expression)); evaluator = myEvaluator.getEvaluator(); diff --git a/view/InstancesWithStackFrameView.java b/view/InstancesWithStackFrameView.java new file mode 100644 index 000000000000..ec3609119b3b --- /dev/null +++ b/view/InstancesWithStackFrameView.java @@ -0,0 +1,68 @@ +package org.jetbrains.debugger.memory.view; + +import com.intellij.ui.JBSplitter; +import com.intellij.ui.components.JBScrollPane; +import com.intellij.xdebugger.XDebugSession; +import com.sun.jdi.ObjectReference; +import org.jetbrains.annotations.NotNull; +import org.jetbrains.debugger.memory.component.CreationPositionTracker; +import org.jetbrains.debugger.memory.utils.StackFrameDescriptor; + +import javax.swing.*; +import java.util.Collections; +import java.util.List; + +class InstancesWithStackFrameView { + private static final float DEFAULT_SPLITTER_PROPORTION = 0.7f; + private static final List EMPTY_FRAME = Collections.emptyList(); + + private float myHidedProportion; + + private final JBSplitter mySplitter = new JBSplitter(false, DEFAULT_SPLITTER_PROPORTION); + private boolean myIsHided = false; + + InstancesWithStackFrameView(@NotNull XDebugSession debugSession, @NotNull InstancesTree tree, + @NotNull StackFrameList list) { + mySplitter.setFirstComponent(new JBScrollPane(tree)); + mySplitter.setSecondComponent(new JBScrollPane(list)); + myHidedProportion = DEFAULT_SPLITTER_PROPORTION; + + hideStackFrame(); + + tree.addTreeSelectionListener(e -> { + ObjectReference ref = tree.getSelectedReference(); + if (ref != null) { + List stack = CreationPositionTracker.getInstance(debugSession.getProject()) + .getStack(debugSession, ref); + if (stack != null) { + list.setFrame(stack); + showStackFrame(); + } else { + list.setFrame(EMPTY_FRAME); + hideStackFrame(); + } + } + }); + } + + JComponent getComponent() { + return mySplitter; + } + + private void hideStackFrame() { + if (!myIsHided) { + myHidedProportion = mySplitter.getProportion(); + mySplitter.getSecondComponent().setVisible(false); + mySplitter.setProportion(1.f); + myIsHided = true; + } + } + + private void showStackFrame() { + if(myIsHided) { + mySplitter.getSecondComponent().setVisible(true); + mySplitter.setProportion(myHidedProportion); + myIsHided = false; + } + } +} diff --git a/view/StackFrameList.java b/view/StackFrameList.java new file mode 100644 index 000000000000..28f1914936ca --- /dev/null +++ b/view/StackFrameList.java @@ -0,0 +1,136 @@ +package org.jetbrains.debugger.memory.view; + +import com.intellij.debugger.engine.DebuggerUtils; +import com.intellij.execution.filters.OpenFileHyperlinkInfo; +import com.intellij.openapi.application.ApplicationManager; +import com.intellij.openapi.editor.ScrollType; +import com.intellij.openapi.fileEditor.FileEditor; +import com.intellij.openapi.fileEditor.FileEditorManager; +import com.intellij.openapi.fileEditor.OpenFileDescriptor; +import com.intellij.openapi.project.Project; +import com.intellij.openapi.vfs.VirtualFile; +import com.intellij.psi.PsiClass; +import com.intellij.psi.PsiElement; +import com.intellij.psi.search.GlobalSearchScope; +import com.intellij.psi.util.PsiUtilCore; +import com.intellij.ui.ColoredListCellRenderer; +import com.intellij.ui.SimpleTextAttributes; +import com.intellij.ui.components.JBList; +import org.apache.commons.lang.StringUtils; +import org.jetbrains.annotations.NotNull; +import org.jetbrains.debugger.memory.utils.StackFrameDescriptor; + +import javax.swing.*; +import java.util.ArrayList; +import java.util.List; + +class StackFrameList extends JBList { + private static final char ANONYMOUS_CLASS_DELIMITER = '$'; + private static final MyOpenFilesState myEditorState = new MyOpenFilesState(); + + private List myStackFrames; + private final Project myProject; + private final GlobalSearchScope myScope; + private final MyListModel myModel = new MyListModel(); + + StackFrameList(@NotNull Project project, + @NotNull List stack, + @NotNull GlobalSearchScope searchScope) { + super(); + + + myStackFrames = new ArrayList<>(stack); + myProject = project; + myScope = searchScope; + + setSelectionMode(ListSelectionModel.SINGLE_SELECTION); + + //noinspection unchecked + setModel(myModel); + + setCellRenderer(new ColoredListCellRenderer() { + @Override + protected void customizeCellRenderer(@NotNull JList list, + StackFrameDescriptor value, int index, boolean isSelected, boolean hasFocus) { + append(String.format("%s:%d, %s", value.methodName(), value.line(), value.className())); + String packageName = value.packageName(); + if (!StringUtils.isEmpty(packageName)) { + append(String.format(" (%s)", value.packageName()), SimpleTextAttributes.GRAYED_ITALIC_ATTRIBUTES); + } + } + }); + } + + void setFrame(@NotNull List stack) { + myModel.update(stack); + } + + void navigateToSelectedValue(boolean focusOnEditor) { + StackFrameDescriptor selectedValue = (StackFrameDescriptor) getSelectedValue(); + if (selectedValue != null) { + navigateToFrame(selectedValue, focusOnEditor); + } + } + + private void navigateToFrame(@NotNull StackFrameDescriptor frame, boolean focusOnEditor) { + String path = frame.path(); + int anonymousClassDelimiterIndex = path.indexOf(ANONYMOUS_CLASS_DELIMITER); + int pathLength = anonymousClassDelimiterIndex > 0 ? anonymousClassDelimiterIndex : path.length(); + path = path.substring(0, pathLength); + PsiClass psiClass = DebuggerUtils.findClass(path, myProject, myScope); + if (psiClass != null) { + ApplicationManager.getApplication().runReadAction(() -> { + PsiElement navigationElement = psiClass.getNavigationElement(); + VirtualFile file = PsiUtilCore.getVirtualFile(navigationElement); + + if (file == null) { + file = psiClass.getContainingFile().getVirtualFile(); + } + + OpenFileHyperlinkInfo info = + new OpenFileHyperlinkInfo(myProject, file, frame.line() - 1); + OpenFileDescriptor descriptor = info.getDescriptor(); + if (descriptor != null) { + FileEditorManager manager = FileEditorManager.getInstance(myProject); + if (myEditorState.myIsNeedToCloseLastOpenedFile && myEditorState.myLastOpenedFile != null && + manager.isFileOpen(myEditorState.myLastOpenedFile)) { + manager.closeFile(myEditorState.myLastOpenedFile); + } + + descriptor.setUseCurrentWindow(false); + descriptor.setScrollType(ScrollType.CENTER); + + myEditorState.myIsNeedToCloseLastOpenedFile = !manager.isFileOpen(descriptor.getFile()); + List fileEditors = manager.openEditor(descriptor, focusOnEditor); + if (!fileEditors.isEmpty()) { + myEditorState.myLastOpenedFile = descriptor.getFile(); + } + } + }); + } + } + + private static class MyOpenFilesState { + VirtualFile myLastOpenedFile; + boolean myIsNeedToCloseLastOpenedFile; + } + + private class MyListModel extends AbstractListModel { + + public void update(@NotNull List newFrame) { + fireIntervalRemoved(this, 0, getSize()); + myStackFrames = newFrame; + fireIntervalAdded(this, 0, getSize()); + } + + @Override + public int getSize() { + return myStackFrames.size(); + } + + @Override + public StackFrameDescriptor getElementAt(int index) { + return myStackFrames.get(index); + } + } +} diff --git a/view/StackFramePopup.java b/view/StackFramePopup.java index 08c68924676f..0069d0500152 100644 --- a/view/StackFramePopup.java +++ b/view/StackFramePopup.java @@ -1,31 +1,15 @@ package org.jetbrains.debugger.memory.view; -import com.intellij.debugger.engine.DebuggerUtils; -import com.intellij.execution.filters.OpenFileHyperlinkInfo; -import com.intellij.openapi.application.ApplicationManager; -import com.intellij.openapi.fileEditor.FileEditorManager; -import com.intellij.openapi.fileEditor.OpenFileDescriptor; import com.intellij.openapi.project.Project; import com.intellij.openapi.ui.popup.JBPopup; import com.intellij.openapi.ui.popup.JBPopupFactory; -import com.intellij.openapi.vfs.VirtualFile; -import com.intellij.psi.PsiClass; -import com.intellij.psi.PsiElement; import com.intellij.psi.search.GlobalSearchScope; -import com.intellij.psi.util.PsiUtilCore; -import com.intellij.ui.ColoredListCellRenderer; -import com.intellij.ui.SimpleTextAttributes; -import com.intellij.ui.components.JBList; -import org.apache.commons.lang.StringUtils; import org.jetbrains.annotations.NotNull; import org.jetbrains.debugger.memory.utils.StackFrameDescriptor; -import javax.swing.*; import java.util.List; public class StackFramePopup { - private static final char ANONYMOUS_CLASS_DELIMITER = '$'; - private final Project myProject; private final List myStackFrame; private final GlobalSearchScope myScope; @@ -39,74 +23,21 @@ public class StackFramePopup { } public void show() { - JBList list = createStackList(); + StackFrameList list = new StackFrameList(myProject, myStackFrame, myScope); + list.addListSelectionListener(e -> { + if (!e.getValueIsAdjusting()) { + list.navigateToSelectedValue(false); + } + }); JBPopup popup = JBPopupFactory.getInstance().createListPopupBuilder(list) .setTitle("Select stack frame") .setAutoSelectIfEmpty(true) .setResizable(false) - .setItemChoosenCallback(() -> navigateToSelectedFrame(list, true)) + .setItemChoosenCallback(() -> list.navigateToSelectedValue(true)) .createPopup(); list.setSelectedIndex(1); popup.showInFocusCenter(); } - - private JBList createStackList() { - JBList frameList = new JBList(new AbstractListModel() { - @Override - public int getSize() { - return myStackFrame.size(); - } - - @Override - public Object getElementAt(int index) { - return myStackFrame.get(index); - } - }); - - frameList.setCellRenderer(new ColoredListCellRenderer() { - @Override - protected void customizeCellRenderer(@NotNull JList list, - StackFrameDescriptor value, int index, boolean isSelected, boolean hasFocus) { - append(String.format("%s:%d, %s", value.methodName(), value.line(), value.className())); - String packageName = value.packageName(); - if (!StringUtils.isEmpty(packageName)) { - append(String.format(" (%s)", value.packageName()), SimpleTextAttributes.GRAYED_ITALIC_ATTRIBUTES); - } - } - }); - - frameList.addListSelectionListener(e -> navigateToSelectedFrame(frameList, false)); - - return frameList; - } - - private void navigateToSelectedFrame(@NotNull JBList frameList, boolean focusEditor) { - StackFrameDescriptor frame = (StackFrameDescriptor) frameList.getSelectedValue(); - if (frame != null) { - String path = frame.path(); - int anonymousClassDelimiterIndex = path.indexOf(ANONYMOUS_CLASS_DELIMITER); - int pathLength = anonymousClassDelimiterIndex > 0 ? anonymousClassDelimiterIndex : path.length(); - path = path.substring(0, pathLength); - PsiClass psiClass = DebuggerUtils.findClass(path, myProject, myScope); - if (psiClass != null) { - ApplicationManager.getApplication().runReadAction(() -> { - PsiElement navigationElement = psiClass.getNavigationElement(); - VirtualFile file = PsiUtilCore.getVirtualFile(navigationElement); - if (file == null) { - file = psiClass.getContainingFile().getVirtualFile(); - } - - OpenFileHyperlinkInfo info = - new OpenFileHyperlinkInfo(myProject, file, frame.line() - 1); - OpenFileDescriptor descriptor = info.getDescriptor(); - if (descriptor != null) { - descriptor.setUseCurrentWindow(true); - FileEditorManager.getInstance(myProject).openEditor(descriptor, focusEditor); - } - }); - } - } - } } From feac108339b597ec08a7ef56f196a70a31df2b54 Mon Sep 17 00:00:00 2001 From: "Vitaliy.Bibaev" Date: Thu, 20 Oct 2016 19:57:45 +0300 Subject: [PATCH 119/151] Increase the default width for instances window --- view/InstancesWindow.java | 7 +------ 1 file changed, 1 insertion(+), 6 deletions(-) diff --git a/view/InstancesWindow.java b/view/InstancesWindow.java index 7ee7d765021d..1ff270b0b1c1 100644 --- a/view/InstancesWindow.java +++ b/view/InstancesWindow.java @@ -64,7 +64,7 @@ import java.util.concurrent.atomic.AtomicInteger; import static java.lang.Math.min; public class InstancesWindow extends DialogWrapper { - private static final int DEFAULT_WINDOW_WIDTH = 700; + private static final int DEFAULT_WINDOW_WIDTH = 870; private static final int DEFAULT_WINDOW_HEIGHT = 400; private static final int FILTERING_BUTTON_ADDITIONAL_WIDTH = 30; private static final int BORDER_LAYOUT_DEFAULT_GAP = 5; @@ -108,11 +108,6 @@ public class InstancesWindow extends DialogWrapper { setTitle(String.format("Instances of %s%s", myClassName, warning)); } - @NotNull - @Override - protected String getDimensionServiceKey() { - return "#org.jetbrains.debugger.memory.view.InstancesWindow"; - } @Nullable @Override From 76f7150850e08164faf426bb8439ee9eac97fb10 Mon Sep 17 00:00:00 2001 From: "Vitaliy.Bibaev" Date: Thu, 20 Oct 2016 20:43:44 +0300 Subject: [PATCH 120/151] Do not reopen file during navigation by stack frame --- view/StackFrameList.java | 11 +++++++---- 1 file changed, 7 insertions(+), 4 deletions(-) diff --git a/view/StackFrameList.java b/view/StackFrameList.java index 28f1914936ca..91919a698ac5 100644 --- a/view/StackFrameList.java +++ b/view/StackFrameList.java @@ -92,15 +92,18 @@ class StackFrameList extends JBList { OpenFileDescriptor descriptor = info.getDescriptor(); if (descriptor != null) { FileEditorManager manager = FileEditorManager.getInstance(myProject); - if (myEditorState.myIsNeedToCloseLastOpenedFile && myEditorState.myLastOpenedFile != null && - manager.isFileOpen(myEditorState.myLastOpenedFile)) { + VirtualFile lastFile = myEditorState.myLastOpenedFile; + if (myEditorState.myIsNeedToCloseLastOpenedFile && lastFile != null && + manager.isFileOpen(lastFile) && !lastFile.equals(descriptor.getFile())) { manager.closeFile(myEditorState.myLastOpenedFile); } - descriptor.setUseCurrentWindow(false); descriptor.setScrollType(ScrollType.CENTER); - myEditorState.myIsNeedToCloseLastOpenedFile = !manager.isFileOpen(descriptor.getFile()); + if (lastFile == null || !lastFile.equals(descriptor.getFile())) { + myEditorState.myIsNeedToCloseLastOpenedFile = !manager.isFileOpen(descriptor.getFile()); + } + List fileEditors = manager.openEditor(descriptor, focusOnEditor); if (!fileEditors.isEmpty()) { myEditorState.myLastOpenedFile = descriptor.getFile(); From 8f7980b693d5adeadc5b1c4477ac10182f80497b Mon Sep 17 00:00:00 2001 From: "Vitaliy.Bibaev" Date: Thu, 20 Oct 2016 20:48:25 +0300 Subject: [PATCH 121/151] Restore persistent size of InstancesWindow --- view/InstancesWindow.java | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/view/InstancesWindow.java b/view/InstancesWindow.java index 1ff270b0b1c1..01bef3327de8 100644 --- a/view/InstancesWindow.java +++ b/view/InstancesWindow.java @@ -108,6 +108,11 @@ public class InstancesWindow extends DialogWrapper { setTitle(String.format("Instances of %s%s", myClassName, warning)); } + @NotNull + @Override + protected String getDimensionServiceKey() { + return "#org.jetbrains.debugger.memory.view.InstancesWindow"; + } @Nullable @Override From 11d36214a6d6dded22bcd46f5f95f6dc6ebbf1c6 Mon Sep 17 00:00:00 2001 From: "Vitaliy.Bibaev" Date: Fri, 21 Oct 2016 20:49:51 +0300 Subject: [PATCH 122/151] Fix class cast exception when columns of classes table were exchanged. Open only window with new instances (instead of three) on double click on diff column. Make 'count' and 'diff' columns resizeable --- view/ClassesFilteredView.java | 40 +++++++++++++++++++++++++++++--- view/ClassesTable.java | 43 ++++++----------------------------- 2 files changed, 44 insertions(+), 39 deletions(-) diff --git a/view/ClassesFilteredView.java b/view/ClassesFilteredView.java index 69074e5d1c6f..99e0e5ff1637 100644 --- a/view/ClassesFilteredView.java +++ b/view/ClassesFilteredView.java @@ -17,10 +17,12 @@ import com.intellij.util.SmartList; import com.intellij.util.ui.components.BorderLayoutPanel; import com.intellij.xdebugger.XDebugSession; import com.intellij.xdebugger.XDebugSessionListener; +import com.sun.jdi.ObjectReference; import com.sun.jdi.ReferenceType; import com.sun.jdi.VirtualMachine; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; +import org.jetbrains.debugger.memory.component.CreationPositionTracker; import org.jetbrains.debugger.memory.component.InstancesTracker; import org.jetbrains.debugger.memory.component.MemoryViewManager; import org.jetbrains.debugger.memory.component.MemoryViewManagerState; @@ -41,12 +43,14 @@ import javax.swing.event.DocumentEvent; import java.awt.*; import java.awt.event.KeyAdapter; import java.awt.event.KeyEvent; +import java.awt.event.MouseAdapter; import java.awt.event.MouseEvent; import java.util.Arrays; import java.util.List; import java.util.Map; import java.util.concurrent.ConcurrentHashMap; import java.util.concurrent.TimeUnit; +import java.util.function.Function; import java.util.stream.IntStream; public class ClassesFilteredView extends BorderLayoutPanel implements Disposable { @@ -71,7 +75,7 @@ public class ClassesFilteredView extends BorderLayoutPanel implements Disposable /** * Indicates that the debug session had been stopped at least once. - * + *

* State: false -> true */ private volatile boolean myIsTrackersActivated = false; @@ -194,14 +198,44 @@ public class ClassesFilteredView extends BorderLayoutPanel implements Disposable } }); + Function isOpenNewInstancesClick = e -> { + int column = myTable.convertColumnIndexToModel(myTable.columnAtPoint(e.getPoint())); + ReferenceType ref = myTable.getSelectedClass(); + return column == ClassesTable.DiffViewTableModel.DIFF_COLUMN_INDEX && ref != null && getStrategy(ref) != null; + }; + new DoubleClickListener() { @Override protected boolean onDoubleClick(MouseEvent event) { - handleClassSelection(myTable.getSelectedClass()); - return true; + if (!isOpenNewInstancesClick.apply(event)) { + handleClassSelection(myTable.getSelectedClass()); + return true; + } + + return false; } }.installOn(myTable); + myTable.addMouseListener(new MouseAdapter() { + @Override + public void mouseClicked(MouseEvent e) { + if (e.getClickCount() != 1 || !isOpenNewInstancesClick.apply(e) || e.getButton() != MouseEvent.BUTTON1) { + return; + } + + ReferenceType ref = myTable.getSelectedClass(); + TrackerForNewInstances strategy = ref == null ? null : getStrategy(ref); + if (strategy != null && strategy.isReady() && strategy.getCount() > 0) { + List newInstances = strategy.getNewInstances(); + CreationPositionTracker.getInstance(myDebugSession.getProject()).pinStacks(myDebugSession, ref); + InstancesWindow instancesWindow = new InstancesWindow(myDebugSession, limit -> newInstances, ref.name()); + Disposer.register(instancesWindow.getDisposable(), + () -> CreationPositionTracker.getInstance(myDebugSession.getProject()).unpinStacks(myDebugSession, ref)); + instancesWindow.show(); + } + } + }); + final MemoryViewManagerListener memoryViewManagerListener = state -> { myTable.setFilteringByDiffNonZero(state.isShowWithDiffOnly); myTable.setFilteringByInstanceExists(state.isShowWithInstancesOnly); diff --git a/view/ClassesTable.java b/view/ClassesTable.java index 7e9efca938cc..ea18ffe6f4b3 100644 --- a/view/ClassesTable.java +++ b/view/ClassesTable.java @@ -3,7 +3,6 @@ package org.jetbrains.debugger.memory.view; import com.intellij.openapi.Disposable; import com.intellij.openapi.actionSystem.DataKey; import com.intellij.openapi.actionSystem.DataProvider; -import com.intellij.openapi.util.Disposer; import com.intellij.openapi.util.TextRange; import com.intellij.psi.codeStyle.MinusculeMatcher; import com.intellij.psi.codeStyle.NameUtil; @@ -22,7 +21,6 @@ import com.sun.jdi.ReferenceType; import org.jetbrains.annotations.NonNls; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; -import org.jetbrains.debugger.memory.component.CreationPositionTracker; import org.jetbrains.debugger.memory.component.InstancesTracker; import org.jetbrains.debugger.memory.tracking.TrackerForNewInstances; import org.jetbrains.debugger.memory.tracking.TrackingType; @@ -33,8 +31,6 @@ import org.jetbrains.debugger.memory.utils.InstancesProvider; import javax.swing.*; import javax.swing.table.TableColumn; import javax.swing.table.TableRowSorter; -import java.awt.event.MouseAdapter; -import java.awt.event.MouseEvent; import java.util.*; import java.util.concurrent.ConcurrentHashMap; @@ -48,9 +44,7 @@ public class ClassesTable extends JBTable implements DataProvider, Disposable { private static final int CLASSES_COLUMN_PREFERRED_WIDTH = 250; private static final int COUNT_COLUMN_MIN_WIDTH = 80; - private static final int COUNT_COLUMN_MAX_WIDTH = 100; private static final int DIFF_COLUMN_MIN_WIDTH = 80; - private static final int DIFF_COLUMN_MAX_WIDTH = 100; private final DiffViewTableModel myModel = new DiffViewTableModel(); private final UnknownDiffValue myUnknownValue = new UnknownDiffValue(); @@ -82,17 +76,12 @@ public class ClassesTable extends JBTable implements DataProvider, Disposable { TableColumn countColumn = getColumnModel().getColumn(DiffViewTableModel.COUNT_COLUMN_INDEX); TableColumn diffColumn = getColumnModel().getColumn(DiffViewTableModel.DIFF_COLUMN_INDEX); - setAutoResizeMode(AUTO_RESIZE_ALL_COLUMNS); + setAutoResizeMode(AUTO_RESIZE_NEXT_COLUMN); classesColumn.setPreferredWidth(JBUI.scale(CLASSES_COLUMN_PREFERRED_WIDTH)); - classesColumn.setResizable(false); countColumn.setMinWidth(JBUI.scale(COUNT_COLUMN_MIN_WIDTH)); - countColumn.setMaxWidth(JBUI.scale(COUNT_COLUMN_MAX_WIDTH)); - countColumn.setResizable(false); diffColumn.setMinWidth(JBUI.scale(DIFF_COLUMN_MIN_WIDTH)); - diffColumn.setMaxWidth(JBUI.scale(DIFF_COLUMN_MAX_WIDTH)); - diffColumn.setResizable(false); setShowGrid(false); setIntercellSpacing(new JBDimension(0, 0)); @@ -101,26 +90,7 @@ public class ClassesTable extends JBTable implements DataProvider, Disposable { setDefaultRenderer(Long.class, new MyCountColumnRenderer()); setDefaultRenderer(DiffValue.class, new MyDiffColumnRenderer()); - addMouseListener(new MouseAdapter() { - @Override - public void mouseClicked(MouseEvent e) { - if (columnAtPoint(e.getPoint()) != DiffViewTableModel.DIFF_COLUMN_INDEX || - e.getButton() != MouseEvent.BUTTON1) { - return; - } - ReferenceType ref = myElems.get(convertRowIndexToModel(rowAtPoint(e.getPoint()))); - TrackerForNewInstances strategy = myParent.getStrategy(ref); - if (strategy != null && strategy.isReady() && strategy.getCount() > 0) { - List newInstances = strategy.getNewInstances(); - CreationPositionTracker.getInstance(myDebugSession.getProject()).pinStacks(myDebugSession, ref); - InstancesWindow instancesWindow = new InstancesWindow(myDebugSession, limit -> newInstances, ref.name()); - Disposer.register(instancesWindow.getDisposable(), - () -> CreationPositionTracker.getInstance(myDebugSession.getProject()).unpinStacks(myDebugSession, ref)); - instancesWindow.show(); - } - } - }); TableRowSorter sorter = new TableRowSorter<>(myModel); sorter.setRowFilter(new RowFilter() { @Override @@ -288,14 +258,14 @@ public class ClassesTable extends JBTable implements DataProvider, Disposable { @Nullable private TrackingType getTrackingType(int row) { - ReferenceType ref = (ReferenceType) getValueAt(row, DiffViewTableModel.CLASSNAME_COLUMN_INDEX); + ReferenceType ref = (ReferenceType) getValueAt(row, convertColumnIndexToView(DiffViewTableModel.CLASSNAME_COLUMN_INDEX)); return myInstancesTracker.getTrackingType(ref.name()); } - private class DiffViewTableModel extends AbstractTableModelWithColumns { - private final static int CLASSNAME_COLUMN_INDEX = 0; - private final static int COUNT_COLUMN_INDEX = 1; - private final static int DIFF_COLUMN_INDEX = 2; + class DiffViewTableModel extends AbstractTableModelWithColumns { + final static int CLASSNAME_COLUMN_INDEX = 0; + final static int COUNT_COLUMN_INDEX = 1; + final static int DIFF_COLUMN_INDEX = 2; // Workaround: save selection after content of classes table has been hided private ReferenceType mySelectedClassWhenHided = null; @@ -417,6 +387,7 @@ public class ClassesTable extends JBTable implements DataProvider, Disposable { @Override protected void customizeCellRenderer(JTable table, @Nullable Object value, boolean isSelected, boolean hasFocus, int row, int column) { + column = convertColumnIndexToModel(column); TrackingType trackingType = getTrackingType(row); if (trackingType != null && !isSelected) { JBColor color = myParent.isTrackingActive(myElems.get(convertRowIndexToModel(row))) From f763c49fa50869771fcc5f32c10f2d5a735294f2 Mon Sep 17 00:00:00 2001 From: "Vitaliy.Bibaev" Date: Fri, 21 Oct 2016 20:58:12 +0300 Subject: [PATCH 123/151] Add icons for marking of tracked classes --- view/ClassesTable.java | 11 +++++------ 1 file changed, 5 insertions(+), 6 deletions(-) diff --git a/view/ClassesTable.java b/view/ClassesTable.java index ea18ffe6f4b3..5406930fe29d 100644 --- a/view/ClassesTable.java +++ b/view/ClassesTable.java @@ -14,10 +14,10 @@ import com.intellij.ui.table.JBTable; import com.intellij.util.containers.FList; import com.intellij.util.ui.JBDimension; import com.intellij.util.ui.JBUI; -import com.intellij.util.ui.UIUtil; import com.intellij.xdebugger.XDebugSession; import com.sun.jdi.ObjectReference; import com.sun.jdi.ReferenceType; +import icons.MemoryViewIcons; import org.jetbrains.annotations.NonNls; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; @@ -389,11 +389,10 @@ public class ClassesTable extends JBTable implements DataProvider, Disposable { boolean hasFocus, int row, int column) { column = convertColumnIndexToModel(column); TrackingType trackingType = getTrackingType(row); - if (trackingType != null && !isSelected) { - JBColor color = myParent.isTrackingActive(myElems.get(convertRowIndexToModel(row))) - ? trackingType.color() - : TrackingType.inactiveColor(); - setBackground(UIUtil.toAlpha(color, 20)); + + if (trackingType != null && column == DiffViewTableModel.DIFF_COLUMN_INDEX) { + setIcon(MemoryViewIcons.CLASS_TRACKED); + setTransparentIconBackground(true); } if (value != null) { From 8b7c8cd7d07a7d5747939469a0cc6f4bdaa578bc Mon Sep 17 00:00:00 2001 From: Vitaliy Bibaev Date: Wed, 26 Oct 2016 09:39:19 +0300 Subject: [PATCH 124/151] Fix bug with using disposed project --- component/CreationPositionTracker.java | 3 ++- view/ClassesFilteredView.java | 19 +++++++------------ 2 files changed, 9 insertions(+), 13 deletions(-) diff --git a/component/CreationPositionTracker.java b/component/CreationPositionTracker.java index 7b2937adc1fb..142d0e1f164f 100644 --- a/component/CreationPositionTracker.java +++ b/component/CreationPositionTracker.java @@ -27,8 +27,9 @@ public class CreationPositionTracker extends AbstractProjectComponent { super(project); } + @Nullable public static CreationPositionTracker getInstance(@NotNull Project project) { - return project.getComponent(CreationPositionTracker.class); + return project.isDisposed() ? null : project.getComponent(CreationPositionTracker.class); } @Nullable diff --git a/view/ClassesFilteredView.java b/view/ClassesFilteredView.java index 99e0e5ff1637..92887d2c5fb2 100644 --- a/view/ClassesFilteredView.java +++ b/view/ClassesFilteredView.java @@ -57,7 +57,7 @@ public class ClassesFilteredView extends BorderLayoutPanel implements Disposable private static final Logger LOG = Logger.getInstance(ClassesFilteredView.class); private final static double DELAY_BEFORE_INSTANCES_QUERY_COEFFICIENT = 0.5; private final static int DEFAULT_BATCH_SIZE = Integer.MAX_VALUE; - private static final String EMPTY_TABLE_CONTENT_WHEN_RUNNED = "The application is running"; + private static final String EMPTY_TABLE_CONTENT_WHEN_RUNNING = "The application is running"; private static final String EMPTY_TABLE_CONTENT_WHEN_SUSPENDED = "Nothing to show"; private final Project myProject; @@ -153,7 +153,7 @@ public class ClassesFilteredView extends BorderLayoutPanel implements Disposable myTable = new ClassesTable(myDebugSession, memoryViewManagerState.isShowWithDiffOnly, memoryViewManagerState.isShowWithInstancesOnly, this); - myTable.getEmptyText().setText(EMPTY_TABLE_CONTENT_WHEN_RUNNED); + myTable.getEmptyText().setText(EMPTY_TABLE_CONTENT_WHEN_RUNNING); Disposer.register(this, myTable); myTable.addKeyListener(new KeyAdapter() { @@ -225,12 +225,12 @@ public class ClassesFilteredView extends BorderLayoutPanel implements Disposable ReferenceType ref = myTable.getSelectedClass(); TrackerForNewInstances strategy = ref == null ? null : getStrategy(ref); - if (strategy != null && strategy.isReady() && strategy.getCount() > 0) { + final CreationPositionTracker tracker = CreationPositionTracker.getInstance(myDebugSession.getProject()); + if (strategy != null && strategy.isReady() && strategy.getCount() > 0 && tracker != null) { List newInstances = strategy.getNewInstances(); - CreationPositionTracker.getInstance(myDebugSession.getProject()).pinStacks(myDebugSession, ref); + tracker.pinStacks(myDebugSession, ref); InstancesWindow instancesWindow = new InstancesWindow(myDebugSession, limit -> newInstances, ref.name()); - Disposer.register(instancesWindow.getDisposable(), - () -> CreationPositionTracker.getInstance(myDebugSession.getProject()).unpinStacks(myDebugSession, ref)); + Disposer.register(instancesWindow.getDisposable(), () -> tracker.unpinStacks(myDebugSession, ref)); instancesWindow.show(); } } @@ -248,7 +248,7 @@ public class ClassesFilteredView extends BorderLayoutPanel implements Disposable public void sessionResumed() { myConstructorTrackedClasses.values().forEach(ConstructorInstancesTracker::obsolete); SwingUtilities.invokeLater(() -> { - myTable.getEmptyText().setText(EMPTY_TABLE_CONTENT_WHEN_RUNNED); + myTable.getEmptyText().setText(EMPTY_TABLE_CONTENT_WHEN_RUNNING); myTable.hideContent(); }); mySingleAlarm.cancelAllRequests(); @@ -350,11 +350,6 @@ public class ClassesFilteredView extends BorderLayoutPanel implements Disposable return ActionManager.getInstance().createActionPopupMenu("ClassesView.PopupActionGroup", group); } - boolean isTrackingActive(@NotNull ReferenceType ref) { - TrackerForNewInstances strategy = myConstructorTrackedClasses.getOrDefault(ref, null); - return strategy != null && strategy.isReady(); - } - @Override public void dispose() { } From 7ac870c8f42133db94ecb17436a870ef7fc0be8c Mon Sep 17 00:00:00 2001 From: "Vitaliy.Bibaev" Date: Wed, 26 Oct 2016 15:28:01 +0300 Subject: [PATCH 125/151] Close the stack frames when selection was lost --- view/InstancesWithStackFrameView.java | 15 ++++++++------- 1 file changed, 8 insertions(+), 7 deletions(-) diff --git a/view/InstancesWithStackFrameView.java b/view/InstancesWithStackFrameView.java index ec3609119b3b..8c3c46fb48d8 100644 --- a/view/InstancesWithStackFrameView.java +++ b/view/InstancesWithStackFrameView.java @@ -29,19 +29,20 @@ class InstancesWithStackFrameView { hideStackFrame(); + CreationPositionTracker tracker = CreationPositionTracker.getInstance(debugSession.getProject()); tree.addTreeSelectionListener(e -> { ObjectReference ref = tree.getSelectedReference(); - if (ref != null) { - List stack = CreationPositionTracker.getInstance(debugSession.getProject()) - .getStack(debugSession, ref); + if (ref != null && tracker != null) { + List stack = tracker.getStack(debugSession, ref); if (stack != null) { list.setFrame(stack); showStackFrame(); - } else { - list.setFrame(EMPTY_FRAME); - hideStackFrame(); + return; } } + + list.setFrame(EMPTY_FRAME); + hideStackFrame(); }); } @@ -59,7 +60,7 @@ class InstancesWithStackFrameView { } private void showStackFrame() { - if(myIsHided) { + if (myIsHided) { mySplitter.getSecondComponent().setVisible(true); mySplitter.setProportion(myHidedProportion); myIsHided = false; From f72b48927f11d91c4eb95b33d295de56fe9fefdf Mon Sep 17 00:00:00 2001 From: "Vitaliy.Bibaev" Date: Wed, 26 Oct 2016 15:51:15 +0300 Subject: [PATCH 126/151] Add ability to hide stack frames --- view/InstancesWithStackFrameView.java | 1 + 1 file changed, 1 insertion(+) diff --git a/view/InstancesWithStackFrameView.java b/view/InstancesWithStackFrameView.java index 8c3c46fb48d8..f5758e70f5eb 100644 --- a/view/InstancesWithStackFrameView.java +++ b/view/InstancesWithStackFrameView.java @@ -25,6 +25,7 @@ class InstancesWithStackFrameView { @NotNull StackFrameList list) { mySplitter.setFirstComponent(new JBScrollPane(tree)); mySplitter.setSecondComponent(new JBScrollPane(list)); + mySplitter.setHonorComponentsMinimumSize(false); myHidedProportion = DEFAULT_SPLITTER_PROPORTION; hideStackFrame(); From e966aeb6f9403ce359477e3938abbcc0df774d26 Mon Sep 17 00:00:00 2001 From: "Vitaliy.Bibaev" Date: Wed, 26 Oct 2016 16:10:23 +0300 Subject: [PATCH 127/151] Move focus to the editor after a double click on an element of the stack trace --- view/InstancesWindow.java | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/view/InstancesWindow.java b/view/InstancesWindow.java index 01bef3327de8..a7ddc6385eaf 100644 --- a/view/InstancesWindow.java +++ b/view/InstancesWindow.java @@ -24,6 +24,7 @@ import com.intellij.openapi.util.Disposer; import com.intellij.openapi.util.Pair; import com.intellij.openapi.wm.IdeFocusManager; import com.intellij.psi.search.GlobalSearchScope; +import com.intellij.ui.DoubleClickListener; import com.intellij.ui.JBColor; import com.intellij.ui.SimpleTextAttributes; import com.intellij.ui.components.JBLabel; @@ -57,6 +58,7 @@ import org.jetbrains.java.debugger.JavaDebuggerEditorsProvider; import javax.swing.*; import javax.swing.tree.TreeNode; import java.awt.*; +import java.awt.event.MouseEvent; import java.util.Collections; import java.util.List; import java.util.concurrent.atomic.AtomicInteger; @@ -210,6 +212,13 @@ public class InstancesWindow extends DialogWrapper { GlobalSearchScope.allScope(myProject)); list.addListSelectionListener(e -> list.navigateToSelectedValue(false)); + new DoubleClickListener() { + @Override + protected boolean onDoubleClick(MouseEvent event) { + list.navigateToSelectedValue(true); + return true; + } + }.installOn(list); InstancesWithStackFrameView instancesWithStackFrame = new InstancesWithStackFrameView(myDebugSession, myInstancesTree, list); From 97bbd20508500033ba1f0eeab793222bd3527eb7 Mon Sep 17 00:00:00 2001 From: "Vitaliy.Bibaev" Date: Wed, 26 Oct 2016 19:44:45 +0300 Subject: [PATCH 128/151] Add ability to start tracking of new objects in instances window --- .../tracking/InstancesTrackingActionBase.java | 2 +- .../tracking/TrackInstanceCreationAction.java | 2 +- component/CreationPositionTracker.java | 3 ++ component/InstancesTracker.java | 5 +- view/ClassesFilteredView.java | 3 +- view/InstancesWindow.java | 2 +- view/InstancesWithStackFrameView.java | 51 ++++++++++++++++--- 7 files changed, 54 insertions(+), 14 deletions(-) diff --git a/action/tracking/InstancesTrackingActionBase.java b/action/tracking/InstancesTrackingActionBase.java index aacd946e7beb..64b514f01c80 100644 --- a/action/tracking/InstancesTrackingActionBase.java +++ b/action/tracking/InstancesTrackingActionBase.java @@ -21,7 +21,7 @@ abstract class InstancesTrackingActionBase extends ToggleAction { InstancesTracker tracker = InstancesTracker.getInstance(project); TrackingType currentType = tracker.getTrackingType(selectedClass.name()); if (state) { - tracker.add(selectedClass, type); + tracker.add(selectedClass.name(), type); } else if(type.equals(currentType)) { tracker.remove(selectedClass); } diff --git a/action/tracking/TrackInstanceCreationAction.java b/action/tracking/TrackInstanceCreationAction.java index 4268cd30e6cb..d1ad2c036e1a 100644 --- a/action/tracking/TrackInstanceCreationAction.java +++ b/action/tracking/TrackInstanceCreationAction.java @@ -35,7 +35,7 @@ public class TrackInstanceCreationAction extends ClassesActionBase { if (selectedClass != null && project != null) { InstancesTracker tracker = InstancesTracker.getInstance(project); - tracker.add(selectedClass, TrackingType.CREATION); + tracker.add(selectedClass.name(), TrackingType.CREATION); } } } diff --git a/component/CreationPositionTracker.java b/component/CreationPositionTracker.java index 142d0e1f164f..ee54aab35e3e 100644 --- a/component/CreationPositionTracker.java +++ b/component/CreationPositionTracker.java @@ -17,6 +17,9 @@ import java.util.concurrent.ConcurrentHashMap; import java.util.stream.Collectors; public class CreationPositionTracker extends AbstractProjectComponent { + /** + * Stores all tracked instance for each debug session. + */ private final ConcurrentHashMap>> mySession2Reference2Stack = new ConcurrentHashMap<>(); diff --git a/component/InstancesTracker.java b/component/InstancesTracker.java index 390a3a3c62ee..c9576836cbf8 100644 --- a/component/InstancesTracker.java +++ b/component/InstancesTracker.java @@ -48,13 +48,12 @@ public class InstancesTracker extends AbstractProjectComponent return new HashMap<>(myState.classes); } - public void add(@NotNull ReferenceType ref, @NotNull TrackingType type) { - String name = ref.name(); + public void add(@NotNull String name, @NotNull TrackingType type) { if (type.equals(myState.classes.getOrDefault(name, null))) { return; } - myState.classes.put(ref.name(), type); + myState.classes.put(name, type); myDispatcher.getMulticaster().classChanged(name, type); } diff --git a/view/ClassesFilteredView.java b/view/ClassesFilteredView.java index 92887d2c5fb2..b14fd73017a9 100644 --- a/view/ClassesFilteredView.java +++ b/view/ClassesFilteredView.java @@ -76,7 +76,7 @@ public class ClassesFilteredView extends BorderLayoutPanel implements Disposable /** * Indicates that the debug session had been stopped at least once. *

- * State: false -> true + * State: false to true */ private volatile boolean myIsTrackersActivated = false; @@ -106,6 +106,7 @@ public class ClassesFilteredView extends BorderLayoutPanel implements Disposable } }); } + myTable.repaint(); } @Override diff --git a/view/InstancesWindow.java b/view/InstancesWindow.java index a7ddc6385eaf..065f00847db9 100644 --- a/view/InstancesWindow.java +++ b/view/InstancesWindow.java @@ -221,7 +221,7 @@ public class InstancesWindow extends DialogWrapper { }.installOn(list); InstancesWithStackFrameView instancesWithStackFrame = new InstancesWithStackFrameView(myDebugSession, - myInstancesTree, list); + myInstancesTree, list, myClassName); add(filteringPane, BorderLayout.NORTH); add(instancesWithStackFrame.getComponent(), BorderLayout.CENTER); diff --git a/view/InstancesWithStackFrameView.java b/view/InstancesWithStackFrameView.java index f5758e70f5eb..e9af45371d75 100644 --- a/view/InstancesWithStackFrameView.java +++ b/view/InstancesWithStackFrameView.java @@ -2,18 +2,25 @@ package org.jetbrains.debugger.memory.view; import com.intellij.ui.JBSplitter; import com.intellij.ui.components.JBScrollPane; +import com.intellij.ui.components.labels.LinkLabel; import com.intellij.xdebugger.XDebugSession; import com.sun.jdi.ObjectReference; import org.jetbrains.annotations.NotNull; import org.jetbrains.debugger.memory.component.CreationPositionTracker; +import org.jetbrains.debugger.memory.component.InstancesTracker; +import org.jetbrains.debugger.memory.event.InstancesTrackerListener; +import org.jetbrains.debugger.memory.tracking.TrackingType; import org.jetbrains.debugger.memory.utils.StackFrameDescriptor; import javax.swing.*; import java.util.Collections; import java.util.List; +import java.util.Objects; class InstancesWithStackFrameView { private static final float DEFAULT_SPLITTER_PROPORTION = 0.7f; + private static final String EMPTY_TEXT_WHEN_ITEM_NOT_SELECTED = "Select instance to see stack frame"; + private static final String EMPTY_TEXT_WHEN_STACK_NOT_FOUND = "No stack frame for this instance"; private static final List EMPTY_FRAME = Collections.emptyList(); private float myHidedProportion; @@ -22,28 +29,56 @@ class InstancesWithStackFrameView { private boolean myIsHided = false; InstancesWithStackFrameView(@NotNull XDebugSession debugSession, @NotNull InstancesTree tree, - @NotNull StackFrameList list) { + @NotNull StackFrameList list, String className) { mySplitter.setFirstComponent(new JBScrollPane(tree)); - mySplitter.setSecondComponent(new JBScrollPane(list)); + + list.setEmptyText(EMPTY_TEXT_WHEN_ITEM_NOT_SELECTED); + LinkLabel linkLabel = new LinkLabel<>("Enable tracking for calls of constructors", null, + (aSource, aLinkData) -> + InstancesTracker.getInstance(debugSession.getProject()).add(className, TrackingType.CREATION)); + + linkLabel.setHorizontalAlignment(SwingConstants.CENTER); + linkLabel.setPaintUnderline(false); + + JComponent stackComponent = new JBScrollPane(list); + + CreationPositionTracker tracker = CreationPositionTracker.getInstance(debugSession.getProject()); + InstancesTracker instancesTracker = InstancesTracker.getInstance(debugSession.getProject()); + instancesTracker.addTrackerListener(new InstancesTrackerListener() { + @Override + public void classChanged(@NotNull String name, @NotNull TrackingType type) { + if (Objects.equals(className, name) && type == TrackingType.CREATION) { + mySplitter.setSecondComponent(stackComponent); + } + } + + @Override + public void classRemoved(@NotNull String name) { + if (Objects.equals(name, className)) { + mySplitter.setSecondComponent(linkLabel); + } + } + }, tree); + + mySplitter.setSecondComponent(instancesTracker.isTracked(className) ? stackComponent : linkLabel); + mySplitter.setHonorComponentsMinimumSize(false); myHidedProportion = DEFAULT_SPLITTER_PROPORTION; - hideStackFrame(); - - CreationPositionTracker tracker = CreationPositionTracker.getInstance(debugSession.getProject()); tree.addTreeSelectionListener(e -> { ObjectReference ref = tree.getSelectedReference(); if (ref != null && tracker != null) { List stack = tracker.getStack(debugSession, ref); if (stack != null) { list.setFrame(stack); - showStackFrame(); return; } + list.setEmptyText(EMPTY_TEXT_WHEN_STACK_NOT_FOUND); + } else { + list.setEmptyText(EMPTY_TEXT_WHEN_ITEM_NOT_SELECTED); } list.setFrame(EMPTY_FRAME); - hideStackFrame(); }); } @@ -51,6 +86,7 @@ class InstancesWithStackFrameView { return mySplitter; } + @SuppressWarnings("unused") private void hideStackFrame() { if (!myIsHided) { myHidedProportion = mySplitter.getProportion(); @@ -60,6 +96,7 @@ class InstancesWithStackFrameView { } } + @SuppressWarnings("unused") private void showStackFrame() { if (myIsHided) { mySplitter.getSecondComponent().setVisible(true); From 569592c3e0a7202c52a47bd08e078ca8cab63d3a Mon Sep 17 00:00:00 2001 From: "Vitaliy.Bibaev" Date: Wed, 26 Oct 2016 19:57:17 +0300 Subject: [PATCH 129/151] Remove the unused classes --- action/tracking/HashBasedTrackingAction.java | 12 ----- .../tracking/IdentityBasedTrackingAction.java | 12 ----- .../tracking/InstancesTrackingActionBase.java | 47 ------------------- .../RetainReferenceBasedTrackingAction.java | 12 ----- action/tracking/TrackingActionGroup.java | 13 ----- 5 files changed, 96 deletions(-) delete mode 100644 action/tracking/HashBasedTrackingAction.java delete mode 100644 action/tracking/IdentityBasedTrackingAction.java delete mode 100644 action/tracking/InstancesTrackingActionBase.java delete mode 100644 action/tracking/RetainReferenceBasedTrackingAction.java delete mode 100644 action/tracking/TrackingActionGroup.java diff --git a/action/tracking/HashBasedTrackingAction.java b/action/tracking/HashBasedTrackingAction.java deleted file mode 100644 index c9aa65fa102b..000000000000 --- a/action/tracking/HashBasedTrackingAction.java +++ /dev/null @@ -1,12 +0,0 @@ -package org.jetbrains.debugger.memory.action.tracking; - -import org.jetbrains.annotations.NotNull; -import org.jetbrains.debugger.memory.tracking.TrackingType; - -public class HashBasedTrackingAction extends InstancesTrackingActionBase { - @NotNull - @Override - protected TrackingType getType() { - return TrackingType.HASH; - } -} diff --git a/action/tracking/IdentityBasedTrackingAction.java b/action/tracking/IdentityBasedTrackingAction.java deleted file mode 100644 index f43f65952cf1..000000000000 --- a/action/tracking/IdentityBasedTrackingAction.java +++ /dev/null @@ -1,12 +0,0 @@ -package org.jetbrains.debugger.memory.action.tracking; - -import org.jetbrains.annotations.NotNull; -import org.jetbrains.debugger.memory.tracking.TrackingType; - -public class IdentityBasedTrackingAction extends InstancesTrackingActionBase { - @NotNull - @Override - protected TrackingType getType() { - return TrackingType.IDENTITY; - } -} diff --git a/action/tracking/InstancesTrackingActionBase.java b/action/tracking/InstancesTrackingActionBase.java deleted file mode 100644 index 64b514f01c80..000000000000 --- a/action/tracking/InstancesTrackingActionBase.java +++ /dev/null @@ -1,47 +0,0 @@ -package org.jetbrains.debugger.memory.action.tracking; - -import com.intellij.openapi.actionSystem.AnActionEvent; -import com.intellij.openapi.actionSystem.ToggleAction; -import com.intellij.openapi.project.Project; -import com.sun.jdi.ReferenceType; -import org.jetbrains.annotations.NotNull; -import org.jetbrains.annotations.Nullable; -import org.jetbrains.debugger.memory.component.InstancesTracker; -import org.jetbrains.debugger.memory.view.ClassesTable; - -import org.jetbrains.debugger.memory.tracking.TrackingType; - -abstract class InstancesTrackingActionBase extends ToggleAction { - @Override - public void setSelected(AnActionEvent e, boolean state) { - TrackingType type = getType(); - ReferenceType selectedClass = getSelectedClass(e); - Project project = e.getProject(); - if (selectedClass != null && project != null) { - InstancesTracker tracker = InstancesTracker.getInstance(project); - TrackingType currentType = tracker.getTrackingType(selectedClass.name()); - if (state) { - tracker.add(selectedClass.name(), type); - } else if(type.equals(currentType)) { - tracker.remove(selectedClass); - } - } - } - - @Override - public boolean isSelected(AnActionEvent e) { - TrackingType type = getType(); - ReferenceType selectedClass = getSelectedClass(e); - Project project = e.getProject(); - return selectedClass != null && project != null && - type.equals(InstancesTracker.getInstance(project).getTrackingType(selectedClass.name())); - } - - @NotNull - protected abstract TrackingType getType(); - - @Nullable - private ReferenceType getSelectedClass(AnActionEvent e) { - return e.getData(ClassesTable.SELECTED_CLASS_KEY); - } -} diff --git a/action/tracking/RetainReferenceBasedTrackingAction.java b/action/tracking/RetainReferenceBasedTrackingAction.java deleted file mode 100644 index bf86f08209f2..000000000000 --- a/action/tracking/RetainReferenceBasedTrackingAction.java +++ /dev/null @@ -1,12 +0,0 @@ -package org.jetbrains.debugger.memory.action.tracking; - -import org.jetbrains.annotations.NotNull; -import org.jetbrains.debugger.memory.tracking.TrackingType; - -public class RetainReferenceBasedTrackingAction extends InstancesTrackingActionBase { - @NotNull - @Override - protected TrackingType getType() { - return TrackingType.RETAIN; - } -} diff --git a/action/tracking/TrackingActionGroup.java b/action/tracking/TrackingActionGroup.java deleted file mode 100644 index fd586f3125dc..000000000000 --- a/action/tracking/TrackingActionGroup.java +++ /dev/null @@ -1,13 +0,0 @@ -package org.jetbrains.debugger.memory.action.tracking; - -import com.intellij.openapi.actionSystem.AnActionEvent; -import com.intellij.openapi.actionSystem.DefaultActionGroup; -import com.intellij.openapi.project.DumbAware; -import org.jetbrains.debugger.memory.view.ClassesTable; - -public class TrackingActionGroup extends DefaultActionGroup implements DumbAware{ - @Override - public void update(AnActionEvent e) { - e.getPresentation().setVisible(e.getData(ClassesTable.SELECTED_CLASS_KEY) != null); - } -} From 2793e72de98bd1efdec2935b50ff7906fbe810cf Mon Sep 17 00:00:00 2001 From: "Vitaliy.Bibaev" Date: Wed, 26 Oct 2016 20:13:22 +0300 Subject: [PATCH 130/151] Use one toggle action instead of the two different actions enable/disable tracking --- action/tracking/DisableTrackingAction.java | 26 ---------- .../JumpToAllocationSourceAction.java | 3 +- .../tracking/TrackInstanceCreationAction.java | 41 --------------- .../tracking/TrackInstancesToggleAction.java | 52 +++++++++++++++++++ component/InstancesTracker.java | 4 +- 5 files changed, 55 insertions(+), 71 deletions(-) delete mode 100644 action/tracking/DisableTrackingAction.java delete mode 100644 action/tracking/TrackInstanceCreationAction.java create mode 100644 action/tracking/TrackInstancesToggleAction.java diff --git a/action/tracking/DisableTrackingAction.java b/action/tracking/DisableTrackingAction.java deleted file mode 100644 index a492115e8d5b..000000000000 --- a/action/tracking/DisableTrackingAction.java +++ /dev/null @@ -1,26 +0,0 @@ -package org.jetbrains.debugger.memory.action.tracking; - -import com.intellij.openapi.actionSystem.AnActionEvent; -import com.intellij.openapi.project.Project; -import com.sun.jdi.ReferenceType; -import org.jetbrains.debugger.memory.action.ClassesActionBase; -import org.jetbrains.debugger.memory.component.InstancesTracker; - -public class DisableTrackingAction extends ClassesActionBase { - @Override - protected boolean isEnabled(AnActionEvent e) { - Project project = e.getProject(); - ReferenceType selectedClass = getSelectedClass(e); - return super.isEnabled(e) && selectedClass != null && - project != null && InstancesTracker.getInstance(project).isTracked(selectedClass.name()); - } - - @Override - protected void perform(AnActionEvent e) { - Project project = e.getProject(); - ReferenceType selectedClass = getSelectedClass(e); - if(project != null && selectedClass != null) { - InstancesTracker.getInstance(project).remove(selectedClass); - } - } -} diff --git a/action/tracking/JumpToAllocationSourceAction.java b/action/tracking/JumpToAllocationSourceAction.java index dba977e27710..218fdfa6084e 100644 --- a/action/tracking/JumpToAllocationSourceAction.java +++ b/action/tracking/JumpToAllocationSourceAction.java @@ -48,6 +48,7 @@ public class JumpToAllocationSourceAction extends DebuggerTreeAction { } XDebugSession session = e.getData(InstancesTree.DEBUG_SESSION_DATA_KEY); - return session == null ? null : CreationPositionTracker.getInstance(project).getStack(session, ref); + CreationPositionTracker tracker = CreationPositionTracker.getInstance(project); + return session == null || tracker == null ? null : tracker.getStack(session, ref); } } diff --git a/action/tracking/TrackInstanceCreationAction.java b/action/tracking/TrackInstanceCreationAction.java deleted file mode 100644 index d1ad2c036e1a..000000000000 --- a/action/tracking/TrackInstanceCreationAction.java +++ /dev/null @@ -1,41 +0,0 @@ -package org.jetbrains.debugger.memory.action.tracking; - -import com.intellij.openapi.actionSystem.AnActionEvent; -import com.intellij.openapi.project.Project; -import com.sun.jdi.ArrayType; -import com.sun.jdi.ReferenceType; -import org.jetbrains.debugger.memory.action.ClassesActionBase; -import org.jetbrains.debugger.memory.component.InstancesTracker; -import org.jetbrains.debugger.memory.tracking.TrackingType; - -public class TrackInstanceCreationAction extends ClassesActionBase { - @Override - protected boolean isEnabled(AnActionEvent e) { - Project project = e.getProject(); - if (!super.isEnabled(e) || project == null) { - return false; - } - - InstancesTracker tracker = InstancesTracker.getInstance(project); - ReferenceType selectedClass = getSelectedClass(e); - if (selectedClass != null) { - TrackingType currentType = tracker.getTrackingType(selectedClass.name()); - if (TrackingType.CREATION == currentType) { - return false; - } - } - - return selectedClass != null && !(selectedClass instanceof ArrayType); - } - - @Override - protected void perform(AnActionEvent e) { - Project project = e.getProject(); - ReferenceType selectedClass = getSelectedClass(e); - - if (selectedClass != null && project != null) { - InstancesTracker tracker = InstancesTracker.getInstance(project); - tracker.add(selectedClass.name(), TrackingType.CREATION); - } - } -} diff --git a/action/tracking/TrackInstancesToggleAction.java b/action/tracking/TrackInstancesToggleAction.java new file mode 100644 index 000000000000..58f7a8f478b2 --- /dev/null +++ b/action/tracking/TrackInstancesToggleAction.java @@ -0,0 +1,52 @@ +package org.jetbrains.debugger.memory.action.tracking; + +import com.intellij.openapi.actionSystem.AnActionEvent; +import com.intellij.openapi.actionSystem.ToggleAction; +import com.intellij.xdebugger.XDebugSession; +import com.sun.jdi.ReferenceType; +import org.jetbrains.annotations.Nullable; +import org.jetbrains.debugger.memory.component.InstancesTracker; +import org.jetbrains.debugger.memory.tracking.TrackingType; +import org.jetbrains.debugger.memory.view.ClassesTable; + +public class TrackInstancesToggleAction extends ToggleAction { + @Override + public boolean isSelected(AnActionEvent e) { + ReferenceType selectedClass = getSelectedClass(e); + XDebugSession debugSession = getDebugSession(e); + if (debugSession != null && selectedClass != null) { + InstancesTracker tracker = InstancesTracker.getInstance(debugSession.getProject()); + return tracker.isTracked(selectedClass.name()); + } + + return false; + } + + @Override + public void setSelected(AnActionEvent e, boolean state) { + ReferenceType selectedClass = getSelectedClass(e); + XDebugSession debugSession = getDebugSession(e); + if (selectedClass != null && debugSession != null) { + InstancesTracker tracker = InstancesTracker.getInstance(debugSession.getProject()); + boolean isAlreadyTracked = tracker.isTracked(selectedClass.name()); + + if (isAlreadyTracked && !state) { + tracker.remove(selectedClass.name()); + } + + if (!isAlreadyTracked && state) { + tracker.add(selectedClass.name(), TrackingType.CREATION); + } + } + } + + @Nullable + private ReferenceType getSelectedClass(AnActionEvent e) { + return e.getData(ClassesTable.SELECTED_CLASS_KEY); + } + + @Nullable + private XDebugSession getDebugSession(AnActionEvent e) { + return e.getData(ClassesTable.DEBUG_SESSION_KEY); + } +} diff --git a/component/InstancesTracker.java b/component/InstancesTracker.java index c9576836cbf8..1a47159e20ca 100644 --- a/component/InstancesTracker.java +++ b/component/InstancesTracker.java @@ -6,7 +6,6 @@ import com.intellij.openapi.project.Project; import com.intellij.util.EventDispatcher; import com.intellij.util.containers.HashMap; import com.intellij.util.xmlb.annotations.AbstractCollection; -import com.sun.jdi.ReferenceType; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; import org.jetbrains.debugger.memory.event.InstancesTrackerListener; @@ -57,8 +56,7 @@ public class InstancesTracker extends AbstractProjectComponent myDispatcher.getMulticaster().classChanged(name, type); } - public boolean remove(@NotNull ReferenceType ref) { - String name = ref.name(); + public boolean remove(@NotNull String name) { TrackingType removed = myState.classes.remove(name); if (removed != null) { myDispatcher.getMulticaster().classRemoved(name); From 136cd18763f4c893bc444ad63b6673ced45274c7 Mon Sep 17 00:00:00 2001 From: Vitaliy Bibaev Date: Thu, 27 Oct 2016 21:19:19 +0300 Subject: [PATCH 131/151] Fix minor code issues and highlight the label in stack frame --- view/ClassesFilteredView.java | 2 +- view/InstancesWithStackFrameView.java | 22 ++++++++++++++-------- view/StackFrameList.java | 3 +-- 3 files changed, 16 insertions(+), 11 deletions(-) diff --git a/view/ClassesFilteredView.java b/view/ClassesFilteredView.java index b14fd73017a9..fbc636d55946 100644 --- a/view/ClassesFilteredView.java +++ b/view/ClassesFilteredView.java @@ -114,7 +114,7 @@ public class ClassesFilteredView extends BorderLayoutPanel implements Disposable ReferenceType ref = myTable.getClassByName(name); if (ref != null && myConstructorTrackedClasses.containsKey(ref)) { ConstructorInstancesTracker removed = myConstructorTrackedClasses.remove(ref); - removed.dispose(); + Disposer.dispose(removed); myTable.getRowSorter().allRowsChanged(); } } diff --git a/view/InstancesWithStackFrameView.java b/view/InstancesWithStackFrameView.java index e9af45371d75..a595f973c423 100644 --- a/view/InstancesWithStackFrameView.java +++ b/view/InstancesWithStackFrameView.java @@ -1,10 +1,13 @@ package org.jetbrains.debugger.memory.view; +import com.intellij.openapi.actionSystem.AnAction; +import com.intellij.openapi.actionSystem.AnActionEvent; import com.intellij.ui.JBSplitter; import com.intellij.ui.components.JBScrollPane; -import com.intellij.ui.components.labels.LinkLabel; +import com.intellij.ui.components.labels.ActionLink; import com.intellij.xdebugger.XDebugSession; import com.sun.jdi.ObjectReference; +import icons.MemoryViewIcons; import org.jetbrains.annotations.NotNull; import org.jetbrains.debugger.memory.component.CreationPositionTracker; import org.jetbrains.debugger.memory.component.InstancesTracker; @@ -33,12 +36,15 @@ class InstancesWithStackFrameView { mySplitter.setFirstComponent(new JBScrollPane(tree)); list.setEmptyText(EMPTY_TEXT_WHEN_ITEM_NOT_SELECTED); - LinkLabel linkLabel = new LinkLabel<>("Enable tracking for calls of constructors", null, - (aSource, aLinkData) -> - InstancesTracker.getInstance(debugSession.getProject()).add(className, TrackingType.CREATION)); + ActionLink actionLink = new ActionLink("Enable tracking for calls of constructors", MemoryViewIcons.CLASS_TRACKED, new AnAction() { + @Override + public void actionPerformed(AnActionEvent e) { + InstancesTracker.getInstance(debugSession.getProject()).add(className, TrackingType.CREATION); + } + }); - linkLabel.setHorizontalAlignment(SwingConstants.CENTER); - linkLabel.setPaintUnderline(false); + actionLink.setHorizontalAlignment(SwingConstants.CENTER); + actionLink.setPaintUnderline(false); JComponent stackComponent = new JBScrollPane(list); @@ -55,12 +61,12 @@ class InstancesWithStackFrameView { @Override public void classRemoved(@NotNull String name) { if (Objects.equals(name, className)) { - mySplitter.setSecondComponent(linkLabel); + mySplitter.setSecondComponent(actionLink); } } }, tree); - mySplitter.setSecondComponent(instancesTracker.isTracked(className) ? stackComponent : linkLabel); + mySplitter.setSecondComponent(instancesTracker.isTracked(className) ? stackComponent : actionLink); mySplitter.setHonorComponentsMinimumSize(false); myHidedProportion = DEFAULT_SPLITTER_PROPORTION; diff --git a/view/StackFrameList.java b/view/StackFrameList.java index 91919a698ac5..02bcb0586f26 100644 --- a/view/StackFrameList.java +++ b/view/StackFrameList.java @@ -38,7 +38,6 @@ class StackFrameList extends JBList { @NotNull GlobalSearchScope searchScope) { super(); - myStackFrames = new ArrayList<>(stack); myProject = project; myScope = searchScope; @@ -120,7 +119,7 @@ class StackFrameList extends JBList { private class MyListModel extends AbstractListModel { - public void update(@NotNull List newFrame) { + void update(@NotNull List newFrame) { fireIntervalRemoved(this, 0, getSize()); myStackFrames = newFrame; fireIntervalAdded(this, 0, getSize()); From bc91eef180ff7f18f45ea9f042e116158afea758 Mon Sep 17 00:00:00 2001 From: Vitaliy Bibaev Date: Thu, 27 Oct 2016 22:23:42 +0300 Subject: [PATCH 132/151] Switch cursor to hand-cursor on a diff column if new instances can be shown --- view/ClassesFilteredView.java | 36 ++++++++++++++++++++++++++++++----- view/ClassesTable.java | 1 - 2 files changed, 31 insertions(+), 6 deletions(-) diff --git a/view/ClassesFilteredView.java b/view/ClassesFilteredView.java index fbc636d55946..531c2bc91973 100644 --- a/view/ClassesFilteredView.java +++ b/view/ClassesFilteredView.java @@ -41,10 +41,7 @@ import javax.swing.FocusManager; import javax.swing.*; import javax.swing.event.DocumentEvent; import java.awt.*; -import java.awt.event.KeyAdapter; -import java.awt.event.KeyEvent; -import java.awt.event.MouseAdapter; -import java.awt.event.MouseEvent; +import java.awt.event.*; import java.util.Arrays; import java.util.List; import java.util.Map; @@ -53,6 +50,9 @@ import java.util.concurrent.TimeUnit; import java.util.function.Function; import java.util.stream.IntStream; +import static org.jetbrains.debugger.memory.view.ClassesTable.DiffViewTableModel.CLASSNAME_COLUMN_INDEX; +import static org.jetbrains.debugger.memory.view.ClassesTable.DiffViewTableModel.DIFF_COLUMN_INDEX; + public class ClassesFilteredView extends BorderLayoutPanel implements Disposable { private static final Logger LOG = Logger.getInstance(ClassesFilteredView.class); private final static double DELAY_BEFORE_INSTANCES_QUERY_COEFFICIENT = 0.5; @@ -157,6 +157,32 @@ public class ClassesFilteredView extends BorderLayoutPanel implements Disposable myTable.getEmptyText().setText(EMPTY_TABLE_CONTENT_WHEN_RUNNING); Disposer.register(this, myTable); + myTable.addMouseMotionListener(new MouseMotionListener() { + @Override + public void mouseDragged(MouseEvent e) { + } + + @Override + public void mouseMoved(MouseEvent e) { + final int col = myTable.columnAtPoint(e.getPoint()); + final int row = myTable.rowAtPoint(e.getPoint()); + if (col == -1 || row == -1 || col != DIFF_COLUMN_INDEX) { + myTable.setCursor(Cursor.getPredefinedCursor(Cursor.DEFAULT_CURSOR)); + return; + } + + final int modelRow = myTable.convertRowIndexToModel(row); + + final ReferenceType ref = (ReferenceType) myTable.getModel().getValueAt(modelRow, CLASSNAME_COLUMN_INDEX); + final ConstructorInstancesTracker tracker = myConstructorTrackedClasses.getOrDefault(ref, null); + if (tracker != null && tracker.isReady() && tracker.getCount() > 0) { + myTable.setCursor(Cursor.getPredefinedCursor(Cursor.HAND_CURSOR)); + } else { + myTable.setCursor(Cursor.getPredefinedCursor(Cursor.DEFAULT_CURSOR)); + } + } + }); + myTable.addKeyListener(new KeyAdapter() { @Override public void keyReleased(KeyEvent e) { @@ -202,7 +228,7 @@ public class ClassesFilteredView extends BorderLayoutPanel implements Disposable Function isOpenNewInstancesClick = e -> { int column = myTable.convertColumnIndexToModel(myTable.columnAtPoint(e.getPoint())); ReferenceType ref = myTable.getSelectedClass(); - return column == ClassesTable.DiffViewTableModel.DIFF_COLUMN_INDEX && ref != null && getStrategy(ref) != null; + return column == DIFF_COLUMN_INDEX && ref != null && getStrategy(ref) != null; }; new DoubleClickListener() { diff --git a/view/ClassesTable.java b/view/ClassesTable.java index 5406930fe29d..888c1674d80f 100644 --- a/view/ClassesTable.java +++ b/view/ClassesTable.java @@ -90,7 +90,6 @@ public class ClassesTable extends JBTable implements DataProvider, Disposable { setDefaultRenderer(Long.class, new MyCountColumnRenderer()); setDefaultRenderer(DiffValue.class, new MyDiffColumnRenderer()); - TableRowSorter sorter = new TableRowSorter<>(myModel); sorter.setRowFilter(new RowFilter() { @Override From 1965ea96ed7b05a749c31c8175da29f05886cc3e Mon Sep 17 00:00:00 2001 From: "Vitaliy.Bibaev" Date: Fri, 28 Oct 2016 13:47:18 +0300 Subject: [PATCH 133/151] Disallow to track arrays --- .../tracking/TrackInstancesToggleAction.java | 12 +++++++ view/InstancesWithStackFrameView.java | 33 +++++++++++++------ 2 files changed, 35 insertions(+), 10 deletions(-) diff --git a/action/tracking/TrackInstancesToggleAction.java b/action/tracking/TrackInstancesToggleAction.java index 58f7a8f478b2..2a35327966df 100644 --- a/action/tracking/TrackInstancesToggleAction.java +++ b/action/tracking/TrackInstancesToggleAction.java @@ -3,13 +3,25 @@ package org.jetbrains.debugger.memory.action.tracking; import com.intellij.openapi.actionSystem.AnActionEvent; import com.intellij.openapi.actionSystem.ToggleAction; import com.intellij.xdebugger.XDebugSession; +import com.sun.jdi.ArrayType; import com.sun.jdi.ReferenceType; +import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; import org.jetbrains.debugger.memory.component.InstancesTracker; import org.jetbrains.debugger.memory.tracking.TrackingType; import org.jetbrains.debugger.memory.view.ClassesTable; public class TrackInstancesToggleAction extends ToggleAction { + @Override + public void update(@NotNull AnActionEvent e) { + ReferenceType selectedClass = getSelectedClass(e); + if (selectedClass instanceof ArrayType) { + e.getPresentation().setEnabled(false); + } else { + super.update(e); + } + } + @Override public boolean isSelected(AnActionEvent e) { ReferenceType selectedClass = getSelectedClass(e); diff --git a/view/InstancesWithStackFrameView.java b/view/InstancesWithStackFrameView.java index a595f973c423..9047916a2ae7 100644 --- a/view/InstancesWithStackFrameView.java +++ b/view/InstancesWithStackFrameView.java @@ -3,6 +3,7 @@ package org.jetbrains.debugger.memory.view; import com.intellij.openapi.actionSystem.AnAction; import com.intellij.openapi.actionSystem.AnActionEvent; import com.intellij.ui.JBSplitter; +import com.intellij.ui.components.JBLabel; import com.intellij.ui.components.JBScrollPane; import com.intellij.ui.components.labels.ActionLink; import com.intellij.xdebugger.XDebugSession; @@ -24,6 +25,7 @@ class InstancesWithStackFrameView { private static final float DEFAULT_SPLITTER_PROPORTION = 0.7f; private static final String EMPTY_TEXT_WHEN_ITEM_NOT_SELECTED = "Select instance to see stack frame"; private static final String EMPTY_TEXT_WHEN_STACK_NOT_FOUND = "No stack frame for this instance"; + private static final String TEXT_FOR_ARRAYS = "Arrays could not be tracked"; private static final List EMPTY_FRAME = Collections.emptyList(); private float myHidedProportion; @@ -36,15 +38,22 @@ class InstancesWithStackFrameView { mySplitter.setFirstComponent(new JBScrollPane(tree)); list.setEmptyText(EMPTY_TEXT_WHEN_ITEM_NOT_SELECTED); - ActionLink actionLink = new ActionLink("Enable tracking for calls of constructors", MemoryViewIcons.CLASS_TRACKED, new AnAction() { - @Override - public void actionPerformed(AnActionEvent e) { - InstancesTracker.getInstance(debugSession.getProject()).add(className, TrackingType.CREATION); - } - }); + JLabel stackTraceLabel; + if (isArrayType(className)) { + stackTraceLabel = new JBLabel(TEXT_FOR_ARRAYS, SwingConstants.CENTER); + } else { + ActionLink actionLink = new ActionLink("Enable tracking for calls of constructors", MemoryViewIcons.CLASS_TRACKED, new AnAction() { + @Override + public void actionPerformed(AnActionEvent e) { + InstancesTracker.getInstance(debugSession.getProject()).add(className, TrackingType.CREATION); + } + }); + + actionLink.setHorizontalAlignment(SwingConstants.CENTER); + actionLink.setPaintUnderline(false); + stackTraceLabel = actionLink; + } - actionLink.setHorizontalAlignment(SwingConstants.CENTER); - actionLink.setPaintUnderline(false); JComponent stackComponent = new JBScrollPane(list); @@ -61,12 +70,12 @@ class InstancesWithStackFrameView { @Override public void classRemoved(@NotNull String name) { if (Objects.equals(name, className)) { - mySplitter.setSecondComponent(actionLink); + mySplitter.setSecondComponent(stackTraceLabel); } } }, tree); - mySplitter.setSecondComponent(instancesTracker.isTracked(className) ? stackComponent : actionLink); + mySplitter.setSecondComponent(instancesTracker.isTracked(className) ? stackComponent : stackTraceLabel); mySplitter.setHonorComponentsMinimumSize(false); myHidedProportion = DEFAULT_SPLITTER_PROPORTION; @@ -92,6 +101,10 @@ class InstancesWithStackFrameView { return mySplitter; } + private static boolean isArrayType(@NotNull String className) { + return className.contains("[]"); + } + @SuppressWarnings("unused") private void hideStackFrame() { if (!myIsHided) { From b435bcc269e4ae43d4819294a350590facae65c5 Mon Sep 17 00:00:00 2001 From: "Vitaliy.Bibaev" Date: Fri, 28 Oct 2016 15:38:12 +0300 Subject: [PATCH 134/151] Avoid tracking before the class table not shown yet --- view/ClassesFilteredView.java | 23 ++++++++++++----------- 1 file changed, 12 insertions(+), 11 deletions(-) diff --git a/view/ClassesFilteredView.java b/view/ClassesFilteredView.java index 531c2bc91973..bb391ddccef5 100644 --- a/view/ClassesFilteredView.java +++ b/view/ClassesFilteredView.java @@ -289,23 +289,16 @@ public class ClassesFilteredView extends BorderLayoutPanel implements Disposable @Override public void sessionPaused() { SwingUtilities.invokeLater(() -> myTable.getEmptyText().setText(EMPTY_TABLE_CONTENT_WHEN_SUSPENDED)); - commitAllTrackers(); updateClassesAndCounts(); - if (!myIsTrackersActivated) { - myConstructorTrackedClasses.values().forEach(ConstructorInstancesTracker::enable); - myIsTrackersActivated = true; - } } }; mySingleAlarm = new SingleAlarmWithMutableDelay(() -> { myLastSuspendContext = getSuspendContext(); if (myLastSuspendContext != null) { - SwingUtilities.invokeLater(() -> { - myTable.setBusy(true); - myDebugProcess.getManagerThread() - .schedule(new MyUpdateClassesCommand(myLastSuspendContext)); - }); + SwingUtilities.invokeLater(() -> myTable.setBusy(true)); + myDebugProcess.getManagerThread() + .schedule(new MyUpdateClassesCommand(myLastSuspendContext)); } }, this); @@ -403,7 +396,9 @@ public class ClassesFilteredView extends BorderLayoutPanel implements Disposable myDebugSession.addSessionListener(myDebugSessionListener, ClassesFilteredView.this); myConstructorTrackedClasses.values().forEach(x -> x.setBackgroundMode(false)); if (myLastSuspendContext == null || !myLastSuspendContext.equals(getSuspendContext())) { - commitAllTrackers(); + if(myIsTrackersActivated) { + commitAllTrackers(); + } updateClassesAndCounts(); } } @@ -421,6 +416,12 @@ public class ClassesFilteredView extends BorderLayoutPanel implements Disposable @Override public void contextAction(@NotNull SuspendContextImpl suspendContext) throws Exception { + if (!myIsTrackersActivated) { + myConstructorTrackedClasses.values().forEach(ConstructorInstancesTracker::enable); + myIsTrackersActivated = true; + } else { + commitAllTrackers(); + } final List classes = myDebugProcess.getVirtualMachineProxy().allClasses(); if (classes.isEmpty()) { From 96ee7464dfd24d5edad57bb15ed1aace423924f2 Mon Sep 17 00:00:00 2001 From: "Vitaliy.Bibaev" Date: Fri, 28 Oct 2016 17:09:27 +0300 Subject: [PATCH 135/151] Do not switch focus to the editor after selecting of another stack frame --- view/StackFrameList.java | 11 +++++++---- 1 file changed, 7 insertions(+), 4 deletions(-) diff --git a/view/StackFrameList.java b/view/StackFrameList.java index 02bcb0586f26..ce9431591901 100644 --- a/view/StackFrameList.java +++ b/view/StackFrameList.java @@ -7,6 +7,7 @@ import com.intellij.openapi.editor.ScrollType; import com.intellij.openapi.fileEditor.FileEditor; import com.intellij.openapi.fileEditor.FileEditorManager; import com.intellij.openapi.fileEditor.OpenFileDescriptor; +import com.intellij.openapi.fileEditor.impl.FileEditorManagerImpl; import com.intellij.openapi.project.Project; import com.intellij.openapi.vfs.VirtualFile; import com.intellij.psi.PsiClass; @@ -90,21 +91,23 @@ class StackFrameList extends JBList { new OpenFileHyperlinkInfo(myProject, file, frame.line() - 1); OpenFileDescriptor descriptor = info.getDescriptor(); if (descriptor != null) { - FileEditorManager manager = FileEditorManager.getInstance(myProject); + FileEditorManagerImpl manager = (FileEditorManagerImpl)FileEditorManager.getInstance(myProject); VirtualFile lastFile = myEditorState.myLastOpenedFile; if (myEditorState.myIsNeedToCloseLastOpenedFile && lastFile != null && manager.isFileOpen(lastFile) && !lastFile.equals(descriptor.getFile())) { - manager.closeFile(myEditorState.myLastOpenedFile); + manager.closeFile(myEditorState.myLastOpenedFile, false, true); } descriptor.setScrollType(ScrollType.CENTER); + descriptor.setUseCurrentWindow(true); if (lastFile == null || !lastFile.equals(descriptor.getFile())) { myEditorState.myIsNeedToCloseLastOpenedFile = !manager.isFileOpen(descriptor.getFile()); } - List fileEditors = manager.openEditor(descriptor, focusOnEditor); - if (!fileEditors.isEmpty()) { + descriptor.navigateInEditor(myProject, focusOnEditor); + FileEditor[] editors = manager.getEditors(descriptor.getFile()); + if (editors.length != 0) { myEditorState.myLastOpenedFile = descriptor.getFile(); } } From 5b9d31e376be3d4c51ae2222054f575c16c2fe09 Mon Sep 17 00:00:00 2001 From: "Vitaliy.Bibaev" Date: Fri, 28 Oct 2016 18:21:48 +0300 Subject: [PATCH 136/151] Show all instances on double click on the diff column with empty tracker --- view/ClassesFilteredView.java | 131 +++++++++++++++++----------------- 1 file changed, 66 insertions(+), 65 deletions(-) diff --git a/view/ClassesFilteredView.java b/view/ClassesFilteredView.java index bb391ddccef5..e343ad974b6d 100644 --- a/view/ClassesFilteredView.java +++ b/view/ClassesFilteredView.java @@ -47,7 +47,6 @@ import java.util.List; import java.util.Map; import java.util.concurrent.ConcurrentHashMap; import java.util.concurrent.TimeUnit; -import java.util.function.Function; import java.util.stream.IntStream; import static org.jetbrains.debugger.memory.view.ClassesTable.DiffViewTableModel.CLASSNAME_COLUMN_INDEX; @@ -157,31 +156,9 @@ public class ClassesFilteredView extends BorderLayoutPanel implements Disposable myTable.getEmptyText().setText(EMPTY_TABLE_CONTENT_WHEN_RUNNING); Disposer.register(this, myTable); - myTable.addMouseMotionListener(new MouseMotionListener() { - @Override - public void mouseDragged(MouseEvent e) { - } - - @Override - public void mouseMoved(MouseEvent e) { - final int col = myTable.columnAtPoint(e.getPoint()); - final int row = myTable.rowAtPoint(e.getPoint()); - if (col == -1 || row == -1 || col != DIFF_COLUMN_INDEX) { - myTable.setCursor(Cursor.getPredefinedCursor(Cursor.DEFAULT_CURSOR)); - return; - } - - final int modelRow = myTable.convertRowIndexToModel(row); - - final ReferenceType ref = (ReferenceType) myTable.getModel().getValueAt(modelRow, CLASSNAME_COLUMN_INDEX); - final ConstructorInstancesTracker tracker = myConstructorTrackedClasses.getOrDefault(ref, null); - if (tracker != null && tracker.isReady() && tracker.getCount() > 0) { - myTable.setCursor(Cursor.getPredefinedCursor(Cursor.HAND_CURSOR)); - } else { - myTable.setCursor(Cursor.getPredefinedCursor(Cursor.DEFAULT_CURSOR)); - } - } - }); + myTable.addMouseMotionListener(new MyMouseMotionListener()); + myTable.addMouseListener(new MyOpenNewInstancesListener()); + new MyDoubleClickListener().installOn(myTable); myTable.addKeyListener(new KeyAdapter() { @Override @@ -225,44 +202,6 @@ public class ClassesFilteredView extends BorderLayoutPanel implements Disposable } }); - Function isOpenNewInstancesClick = e -> { - int column = myTable.convertColumnIndexToModel(myTable.columnAtPoint(e.getPoint())); - ReferenceType ref = myTable.getSelectedClass(); - return column == DIFF_COLUMN_INDEX && ref != null && getStrategy(ref) != null; - }; - - new DoubleClickListener() { - @Override - protected boolean onDoubleClick(MouseEvent event) { - if (!isOpenNewInstancesClick.apply(event)) { - handleClassSelection(myTable.getSelectedClass()); - return true; - } - - return false; - } - }.installOn(myTable); - - myTable.addMouseListener(new MouseAdapter() { - @Override - public void mouseClicked(MouseEvent e) { - if (e.getClickCount() != 1 || !isOpenNewInstancesClick.apply(e) || e.getButton() != MouseEvent.BUTTON1) { - return; - } - - ReferenceType ref = myTable.getSelectedClass(); - TrackerForNewInstances strategy = ref == null ? null : getStrategy(ref); - final CreationPositionTracker tracker = CreationPositionTracker.getInstance(myDebugSession.getProject()); - if (strategy != null && strategy.isReady() && strategy.getCount() > 0 && tracker != null) { - List newInstances = strategy.getNewInstances(); - tracker.pinStacks(myDebugSession, ref); - InstancesWindow instancesWindow = new InstancesWindow(myDebugSession, limit -> newInstances, ref.name()); - Disposer.register(instancesWindow.getDisposable(), () -> tracker.unpinStacks(myDebugSession, ref)); - instancesWindow.show(); - } - } - }); - final MemoryViewManagerListener memoryViewManagerListener = state -> { myTable.setFilteringByDiffNonZero(state.isShowWithDiffOnly); myTable.setFilteringByInstanceExists(state.isShowWithInstancesOnly); @@ -396,7 +335,7 @@ public class ClassesFilteredView extends BorderLayoutPanel implements Disposable myDebugSession.addSessionListener(myDebugSessionListener, ClassesFilteredView.this); myConstructorTrackedClasses.values().forEach(x -> x.setBackgroundMode(false)); if (myLastSuspendContext == null || !myLastSuspendContext.equals(getSuspendContext())) { - if(myIsTrackersActivated) { + if (myIsTrackersActivated) { commitAllTrackers(); } updateClassesAndCounts(); @@ -479,4 +418,66 @@ public class ClassesFilteredView extends BorderLayoutPanel implements Disposable return false; } } + + private class MyOpenNewInstancesListener extends MouseAdapter { + @Override + public void mouseClicked(MouseEvent e) { + if (e.getClickCount() != 1 || e.getButton() != MouseEvent.BUTTON1 || !isShowNewInstancesEvent(e)) { + return; + } + + final CreationPositionTracker tracker = CreationPositionTracker.getInstance(myDebugSession.getProject()); + ReferenceType ref = myTable.getSelectedClass(); + TrackerForNewInstances strategy = ref == null ? null : getStrategy(ref); + if (strategy != null && tracker != null) { + List newInstances = strategy.getNewInstances(); + tracker.pinStacks(myDebugSession, ref); + InstancesWindow instancesWindow = new InstancesWindow(myDebugSession, limit -> newInstances, ref.name()); + Disposer.register(instancesWindow.getDisposable(), () -> tracker.unpinStacks(myDebugSession, ref)); + instancesWindow.show(); + } + } + } + + private class MyDoubleClickListener extends DoubleClickListener { + @Override + protected boolean onDoubleClick(MouseEvent event) { + if (!isShowNewInstancesEvent(event)) { + handleClassSelection(myTable.getSelectedClass()); + return true; + } + + return false; + } + } + + private class MyMouseMotionListener implements MouseMotionListener { + @Override + public void mouseDragged(MouseEvent e) { + } + + @Override + public void mouseMoved(MouseEvent e) { + if (isShowNewInstancesEvent(e)) { + myTable.setCursor(Cursor.getPredefinedCursor(Cursor.HAND_CURSOR)); + } else { + myTable.setCursor(Cursor.getPredefinedCursor(Cursor.DEFAULT_CURSOR)); + } + } + } + + private boolean isShowNewInstancesEvent(@NotNull MouseEvent e) { + final int col = myTable.columnAtPoint(e.getPoint()); + final int row = myTable.rowAtPoint(e.getPoint()); + if (col == -1 || row == -1 || myTable.convertColumnIndexToModel(col) != DIFF_COLUMN_INDEX) { + return false; + } + + final int modelRow = myTable.convertRowIndexToModel(row); + + final ReferenceType ref = (ReferenceType) myTable.getModel().getValueAt(modelRow, CLASSNAME_COLUMN_INDEX); + final ConstructorInstancesTracker tracker = myConstructorTrackedClasses.getOrDefault(ref, null); + + return tracker != null && tracker.isReady() && tracker.getCount() > 0; + } } From e7eff50dacf6edbf92e7103d888ce85a86aa99c7 Mon Sep 17 00:00:00 2001 From: "Vitaliy.Bibaev" Date: Fri, 28 Oct 2016 18:47:27 +0300 Subject: [PATCH 137/151] Remember size of the splitter in instances view --- view/InstancesWithStackFrameView.java | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/view/InstancesWithStackFrameView.java b/view/InstancesWithStackFrameView.java index 9047916a2ae7..9ec7a35214b0 100644 --- a/view/InstancesWithStackFrameView.java +++ b/view/InstancesWithStackFrameView.java @@ -42,7 +42,7 @@ class InstancesWithStackFrameView { if (isArrayType(className)) { stackTraceLabel = new JBLabel(TEXT_FOR_ARRAYS, SwingConstants.CENTER); } else { - ActionLink actionLink = new ActionLink("Enable tracking for calls of constructors", MemoryViewIcons.CLASS_TRACKED, new AnAction() { + ActionLink actionLink = new ActionLink("Enable tracking for new instances", MemoryViewIcons.CLASS_TRACKED, new AnAction() { @Override public void actionPerformed(AnActionEvent e) { InstancesTracker.getInstance(debugSession.getProject()).add(className, TrackingType.CREATION); @@ -54,6 +54,7 @@ class InstancesWithStackFrameView { stackTraceLabel = actionLink; } + mySplitter.setSplitterProportionKey("InstancesWithStackFrameView.SplitterKey"); JComponent stackComponent = new JBScrollPane(list); @@ -86,6 +87,9 @@ class InstancesWithStackFrameView { List stack = tracker.getStack(debugSession, ref); if (stack != null) { list.setFrame(stack); + if(mySplitter.getProportion() == 1.f) { + mySplitter.setProportion(DEFAULT_SPLITTER_PROPORTION); + } return; } list.setEmptyText(EMPTY_TEXT_WHEN_STACK_NOT_FOUND); From 95810d48eb2a97834d15b00b3cc6f88c5501b2e8 Mon Sep 17 00:00:00 2001 From: "Vitaliy.Bibaev" Date: Sun, 30 Oct 2016 20:55:51 +0300 Subject: [PATCH 138/151] Fix memory leak with ClassesTable was not disposed when application closing --- toolwindow/MemoryViewToolWindowFactory.java | 17 ++++++++++------- 1 file changed, 10 insertions(+), 7 deletions(-) diff --git a/toolwindow/MemoryViewToolWindowFactory.java b/toolwindow/MemoryViewToolWindowFactory.java index 497a6926cfee..c404a39b3fed 100644 --- a/toolwindow/MemoryViewToolWindowFactory.java +++ b/toolwindow/MemoryViewToolWindowFactory.java @@ -7,6 +7,7 @@ import com.intellij.execution.ui.RunContentWithExecutorListener; import com.intellij.openapi.actionSystem.ActionGroup; import com.intellij.openapi.actionSystem.ActionManager; import com.intellij.openapi.actionSystem.DefaultActionGroup; +import com.intellij.openapi.diagnostic.Logger; import com.intellij.openapi.project.DumbAware; import com.intellij.openapi.project.Project; import com.intellij.openapi.util.Disposer; @@ -16,7 +17,6 @@ import com.intellij.openapi.wm.ex.ToolWindowEx; import com.intellij.openapi.wm.ex.ToolWindowManagerAdapter; import com.intellij.openapi.wm.impl.ToolWindowImpl; import com.intellij.ui.components.JBLabel; -import com.intellij.util.containers.HashMap; import com.intellij.util.messages.MessageBusConnection; import com.intellij.xdebugger.XDebugProcess; import com.intellij.xdebugger.XDebugSession; @@ -31,13 +31,16 @@ import org.jetbrains.debugger.memory.view.ClassesFilteredView; import javax.swing.*; import java.awt.*; import java.util.Arrays; +import java.util.Map; +import java.util.concurrent.ConcurrentHashMap; public class MemoryViewToolWindowFactory implements ToolWindowFactory, DumbAware { + private static final Logger LOG = Logger.getInstance(ClassesFilteredView.class); public final static String TOOL_WINDOW_ID = "Memory View"; private final JComponent myEmptyContent; - private final HashMap myMemoryViews = new HashMap<>(); + private final Map myMemoryViews = new ConcurrentHashMap<>(); @Nullable private ClassesFilteredView myCurrentView = null; @@ -89,6 +92,7 @@ public class MemoryViewToolWindowFactory implements ToolWindowFactory, DumbAware } private void addNewSession(@NotNull XDebugSession session) { + LOG.assertTrue(SwingUtilities.isEventDispatchThread()); ToolWindow toolWindow = getToolWindow(session.getProject()); if (!myMemoryViews.containsKey(session)) { ClassesFilteredView newView = new ClassesFilteredView(session); @@ -121,6 +125,7 @@ public class MemoryViewToolWindowFactory implements ToolWindowFactory, DumbAware } private void replaceToolWindowContent(@NotNull ToolWindow toolWindow, JComponent comp) { + LOG.assertTrue(SwingUtilities.isEventDispatchThread()); JComponent toolWindowComp = toolWindow.getComponent(); toolWindowComp.removeAll(); toolWindowComp.add(comp); @@ -174,11 +179,9 @@ public class MemoryViewToolWindowFactory implements ToolWindowFactory, DumbAware @Override public void processStopped(@NotNull XDebugProcess xDebugProcess) { - SwingUtilities.invokeLater(() -> { - XDebugSession session = xDebugProcess.getSession(); - removeSession(session); - updateView(session); - }); + XDebugSession session = xDebugProcess.getSession(); + removeSession(session); + SwingUtilities.invokeLater(() -> updateView(session)); } private void updateView(@NotNull XDebugSession debugSession) { From 70b7ea2374a91b0c3bb7599d139dbfb2f46b0149 Mon Sep 17 00:00:00 2001 From: Vitaliy Bibaev Date: Tue, 1 Nov 2016 00:23:16 +0300 Subject: [PATCH 139/151] Fix broken assertion in EAP (run the classes updater task in thread pool) --- utils/SingleAlarmWithMutableDelay.java | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/utils/SingleAlarmWithMutableDelay.java b/utils/SingleAlarmWithMutableDelay.java index 75cb937c187c..321cf46828c8 100644 --- a/utils/SingleAlarmWithMutableDelay.java +++ b/utils/SingleAlarmWithMutableDelay.java @@ -2,15 +2,13 @@ package org.jetbrains.debugger.memory.utils; import com.intellij.openapi.Disposable; import com.intellij.util.Alarm; -import com.intellij.util.config.ToggleBooleanProperty; import org.jetbrains.annotations.NotNull; -import org.jetbrains.annotations.Nullable; public class SingleAlarmWithMutableDelay extends Alarm { private final Runnable myTask; private volatile int myDelay; public SingleAlarmWithMutableDelay(@NotNull Runnable task, @NotNull Disposable parentDisposable) { - super(parentDisposable); + super(ThreadToUse.POOLED_THREAD, parentDisposable); myTask = task; } From 88e4e02f915138ee9f2bcb3321ceb815e0db30dc Mon Sep 17 00:00:00 2001 From: "Vitaliy.Bibaev" Date: Wed, 2 Nov 2016 16:51:36 +0300 Subject: [PATCH 140/151] Fix bug, when instances tracked in background were lost --- view/ClassesFilteredView.java | 3 --- 1 file changed, 3 deletions(-) diff --git a/view/ClassesFilteredView.java b/view/ClassesFilteredView.java index e343ad974b6d..cccd3f89356a 100644 --- a/view/ClassesFilteredView.java +++ b/view/ClassesFilteredView.java @@ -335,9 +335,6 @@ public class ClassesFilteredView extends BorderLayoutPanel implements Disposable myDebugSession.addSessionListener(myDebugSessionListener, ClassesFilteredView.this); myConstructorTrackedClasses.values().forEach(x -> x.setBackgroundMode(false)); if (myLastSuspendContext == null || !myLastSuspendContext.equals(getSuspendContext())) { - if (myIsTrackersActivated) { - commitAllTrackers(); - } updateClassesAndCounts(); } } From 1dc8a5894dba0bf226ee63faf6cef279fcb7936c Mon Sep 17 00:00:00 2001 From: "Vitaliy.Bibaev" Date: Wed, 2 Nov 2016 17:53:36 +0300 Subject: [PATCH 141/151] Add ability to show only tracked classes --- action/ShowInstancesAction.java | 2 +- action/ShowTrackedAction.java | 19 +++++++++++++++++++ component/MemoryViewManager.java | 11 +++++++++++ component/MemoryViewManagerState.java | 2 ++ toolwindow/MemoryViewToolWindowFactory.java | 3 +++ view/ClassesFilteredView.java | 3 ++- view/ClassesTable.java | 19 +++++++++++++++---- 7 files changed, 53 insertions(+), 6 deletions(-) create mode 100644 action/ShowTrackedAction.java diff --git a/action/ShowInstancesAction.java b/action/ShowInstancesAction.java index 400d194a1a01..865e805caaf0 100644 --- a/action/ShowInstancesAction.java +++ b/action/ShowInstancesAction.java @@ -6,7 +6,7 @@ abstract class ShowInstancesAction extends ClassesActionBase { @Override public void update(AnActionEvent e) { boolean enabled = isEnabled(e); - if(enabled) { + if (enabled) { e.getPresentation().setText(String.format("%s (%d)", getLabel(), getInstancesCount(e))); } } diff --git a/action/ShowTrackedAction.java b/action/ShowTrackedAction.java new file mode 100644 index 000000000000..9c8228530b45 --- /dev/null +++ b/action/ShowTrackedAction.java @@ -0,0 +1,19 @@ +package org.jetbrains.debugger.memory.action; + +import com.intellij.openapi.actionSystem.AnActionEvent; +import com.intellij.openapi.actionSystem.ToggleAction; +import org.jetbrains.debugger.memory.component.MemoryViewManager; + +public class ShowTrackedAction extends ToggleAction { + @Override + public boolean isSelected(AnActionEvent e) { + return MemoryViewManager.getInstance().isNeedShowTrackedOnly(); + } + + @Override + public void setSelected(AnActionEvent e, boolean state) { + if (e.getProject() != null) { + MemoryViewManager.getInstance().setShowTrackedOnly(state); + } + } +} diff --git a/component/MemoryViewManager.java b/component/MemoryViewManager.java index 1af153b035a6..58d42f890fd6 100644 --- a/component/MemoryViewManager.java +++ b/component/MemoryViewManager.java @@ -53,6 +53,13 @@ public class MemoryViewManager extends ApplicationComponent.Adapter } } + public void setShowTrackedOnly(boolean value) { + if (myState.isShowTrackedOnly != value) { + myState.isShowTrackedOnly = value; + fireStateChanged(); + } + } + public boolean isNeedShowDiffOnly() { return myState.isShowWithDiffOnly; } @@ -61,6 +68,10 @@ public class MemoryViewManager extends ApplicationComponent.Adapter return myState.isShowWithInstancesOnly; } + public boolean isNeedShowTrackedOnly() { + return myState.isShowTrackedOnly; + } + public void addMemoryViewManagerListener(MemoryViewManagerListener listener, @NotNull Disposable parentDisposable) { myDispatcher.addListener(listener, parentDisposable); } diff --git a/component/MemoryViewManagerState.java b/component/MemoryViewManagerState.java index 0694133b5842..07e6f7272c45 100644 --- a/component/MemoryViewManagerState.java +++ b/component/MemoryViewManagerState.java @@ -3,6 +3,7 @@ package org.jetbrains.debugger.memory.component; public final class MemoryViewManagerState { public boolean isShowWithInstancesOnly = true; public boolean isShowWithDiffOnly = false; + public boolean isShowTrackedOnly = false; MemoryViewManagerState() { } @@ -10,5 +11,6 @@ public final class MemoryViewManagerState { MemoryViewManagerState(MemoryViewManagerState other) { isShowWithInstancesOnly = other.isShowWithInstancesOnly; isShowWithDiffOnly = other.isShowWithDiffOnly; + isShowTrackedOnly = other.isShowTrackedOnly; } } diff --git a/toolwindow/MemoryViewToolWindowFactory.java b/toolwindow/MemoryViewToolWindowFactory.java index c404a39b3fed..3882052af371 100644 --- a/toolwindow/MemoryViewToolWindowFactory.java +++ b/toolwindow/MemoryViewToolWindowFactory.java @@ -7,6 +7,7 @@ import com.intellij.execution.ui.RunContentWithExecutorListener; import com.intellij.openapi.actionSystem.ActionGroup; import com.intellij.openapi.actionSystem.ActionManager; import com.intellij.openapi.actionSystem.DefaultActionGroup; +import com.intellij.openapi.actionSystem.Separator; import com.intellij.openapi.diagnostic.Logger; import com.intellij.openapi.project.DumbAware; import com.intellij.openapi.project.Project; @@ -79,6 +80,8 @@ public class MemoryViewToolWindowFactory implements ToolWindowFactory, DumbAware ActionGroup group = new DefaultActionGroup( ActionManager.getInstance().getAction("MemoryView.ShowOnlyWithInstances"), ActionManager.getInstance().getAction("MemoryView.ShowOnlyWithDiff"), + ActionManager.getInstance().getAction("MemoryView.ShowOnlyTracked"), + Separator.getInstance(), ActionManager.getInstance().getAction("MemoryView.EnableTrackingWithClosedWindow") ); ((ToolWindowEx) toolWindow).setAdditionalGearActions(group); diff --git a/view/ClassesFilteredView.java b/view/ClassesFilteredView.java index cccd3f89356a..a21ed8578ae6 100644 --- a/view/ClassesFilteredView.java +++ b/view/ClassesFilteredView.java @@ -152,7 +152,7 @@ public class ClassesFilteredView extends BorderLayoutPanel implements Disposable MemoryViewManagerState memoryViewManagerState = MemoryViewManager.getInstance().getState(); myTable = new ClassesTable(myDebugSession, memoryViewManagerState.isShowWithDiffOnly, - memoryViewManagerState.isShowWithInstancesOnly, this); + memoryViewManagerState.isShowWithInstancesOnly, memoryViewManagerState.isShowTrackedOnly, this); myTable.getEmptyText().setText(EMPTY_TABLE_CONTENT_WHEN_RUNNING); Disposer.register(this, myTable); @@ -205,6 +205,7 @@ public class ClassesFilteredView extends BorderLayoutPanel implements Disposable final MemoryViewManagerListener memoryViewManagerListener = state -> { myTable.setFilteringByDiffNonZero(state.isShowWithDiffOnly); myTable.setFilteringByInstanceExists(state.isShowWithInstancesOnly); + myTable.setFilteringByTrackingState(state.isShowTrackedOnly); }; MemoryViewManager.getInstance().addMemoryViewManagerListener(memoryViewManagerListener, this); diff --git a/view/ClassesTable.java b/view/ClassesTable.java index 888c1674d80f..1cfa45e7faf5 100644 --- a/view/ClassesTable.java +++ b/view/ClassesTable.java @@ -55,7 +55,7 @@ public class ClassesTable extends JBTable implements DataProvider, Disposable { private final ReferenceCountProvider myCountProvider; private boolean myOnlyWithDiff; - + private boolean myOnlyTracked; private boolean myOnlyWithInstances; private MinusculeMatcher myMatcher = NameUtil.buildMatcher("*").build(); private String myFilteringPattern = ""; @@ -63,12 +63,13 @@ public class ClassesTable extends JBTable implements DataProvider, Disposable { private volatile List myElems = Collections.unmodifiableList(new ArrayList<>()); ClassesTable(@NotNull XDebugSession session, boolean onlyWithDiff, boolean onlyWithInstances, - @NotNull ClassesFilteredView parent) { + boolean onlyTracked, @NotNull ClassesFilteredView parent) { setModel(myModel); myDebugSession = session; myOnlyWithDiff = onlyWithDiff; myOnlyWithInstances = onlyWithInstances; + myOnlyTracked = onlyTracked; myInstancesTracker = InstancesTracker.getInstance(myDebugSession.getProject()); myParent = parent; @@ -97,8 +98,11 @@ public class ClassesTable extends JBTable implements DataProvider, Disposable { int ix = entry.getIdentifier(); ReferenceType ref = myElems.get(ix); DiffValue diff = myCounts.getOrDefault(ref, myUnknownValue); - return !(myOnlyWithDiff && diff.diff() == 0 || myOnlyWithInstances && !diff.hasInstance()) - && myMatcher.matches(ref.name()); + + boolean isFilteringOptionsRefused = myOnlyWithDiff && diff.diff() == 0 + || myOnlyWithInstances && !diff.hasInstance() + || myOnlyTracked && myParent.getStrategy(ref) == null; + return !(isFilteringOptionsRefused) && myMatcher.matches(ref.name()); } }); @@ -189,6 +193,13 @@ public class ClassesTable extends JBTable implements DataProvider, Disposable { } } + void setFilteringByTrackingState(boolean value) { + if (myOnlyTracked != value) { + myOnlyTracked = value; + getRowSorter().allRowsChanged(); + } + } + void setClassesAndUpdateCounts(@NotNull List classes, @NotNull long[] counts) { assert classes.size() == counts.length; ReferenceType selectedClass = myModel.getSelectedClassBeforeHided(); From b4cc43ca0e32916102e83cd4835d79fd34d164fc Mon Sep 17 00:00:00 2001 From: "Vitaliy.Bibaev" Date: Wed, 9 Nov 2016 14:30:59 +0300 Subject: [PATCH 142/151] Add limit to waiting time for downloading of instances --- view/ClassesFilteredView.java | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/view/ClassesFilteredView.java b/view/ClassesFilteredView.java index a21ed8578ae6..4f4e68607349 100644 --- a/view/ClassesFilteredView.java +++ b/view/ClassesFilteredView.java @@ -55,6 +55,7 @@ import static org.jetbrains.debugger.memory.view.ClassesTable.DiffViewTableModel public class ClassesFilteredView extends BorderLayoutPanel implements Disposable { private static final Logger LOG = Logger.getInstance(ClassesFilteredView.class); private final static double DELAY_BEFORE_INSTANCES_QUERY_COEFFICIENT = 0.5; + private final static double MAX_DELAY_MILLIS = TimeUnit.SECONDS.toMillis(2); private final static int DEFAULT_BATCH_SIZE = Integer.MAX_VALUE; private static final String EMPTY_TABLE_CONTENT_WHEN_RUNNING = "The application is running"; private static final String EMPTY_TABLE_CONTENT_WHEN_SUSPENDED = "Nothing to show"; @@ -383,7 +384,8 @@ public class ClassesFilteredView extends BorderLayoutPanel implements Disposable chunks.add(counts); - mySingleAlarm.setDelay((int) (DELAY_BEFORE_INSTANCES_QUERY_COEFFICIENT * delay)); + int waitTime = (int) Math.min(DELAY_BEFORE_INSTANCES_QUERY_COEFFICIENT * delay, MAX_DELAY_MILLIS); + mySingleAlarm.setDelay(waitTime); LOG.info(String.format("Instances query time = %d ms. Count = %d", delay, batch.size())); } From 57309905eb930d783018252c7d7a2868ea34a933 Mon Sep 17 00:00:00 2001 From: "Vitaliy.Bibaev" Date: Wed, 9 Nov 2016 15:25:23 +0300 Subject: [PATCH 143/151] Do not open classes view when debug session is not for jvm application --- toolwindow/MemoryViewToolWindowFactory.java | 28 +++++++++++++++------ 1 file changed, 21 insertions(+), 7 deletions(-) diff --git a/toolwindow/MemoryViewToolWindowFactory.java b/toolwindow/MemoryViewToolWindowFactory.java index 3882052af371..532051d8808e 100644 --- a/toolwindow/MemoryViewToolWindowFactory.java +++ b/toolwindow/MemoryViewToolWindowFactory.java @@ -40,6 +40,7 @@ public class MemoryViewToolWindowFactory implements ToolWindowFactory, DumbAware public final static String TOOL_WINDOW_ID = "Memory View"; private final JComponent myEmptyContent; + private final JComponent myMemoryViewNotSupportedContent; private final Map myMemoryViews = new ConcurrentHashMap<>(); @@ -48,6 +49,8 @@ public class MemoryViewToolWindowFactory implements ToolWindowFactory, DumbAware { myEmptyContent = new JBLabel("Run debugging to see loaded classes", SwingConstants.CENTER); + myMemoryViewNotSupportedContent = new JBLabel("The Memory View not available for this session", + SwingConstants.CENTER); } @Override @@ -98,9 +101,13 @@ public class MemoryViewToolWindowFactory implements ToolWindowFactory, DumbAware LOG.assertTrue(SwingUtilities.isEventDispatchThread()); ToolWindow toolWindow = getToolWindow(session.getProject()); if (!myMemoryViews.containsKey(session)) { - ClassesFilteredView newView = new ClassesFilteredView(session); - newView.setActive(toolWindow != null && toolWindow.isVisible()); - myMemoryViews.put(session, newView); + try { + ClassesFilteredView newView = new ClassesFilteredView(session); + newView.setActive(toolWindow != null && toolWindow.isVisible()); + myMemoryViews.put(session, newView); + } catch (Throwable e) { + LOG.warn("Cannot create new instance of the memory view", e); + } } } @@ -112,11 +119,18 @@ public class MemoryViewToolWindowFactory implements ToolWindowFactory, DumbAware private void updateCurrentMemoryView(@NotNull Project project, @NotNull ToolWindow toolWindow) { if (!project.isDisposed()) { XDebugSession session = XDebuggerManager.getInstance(project).getCurrentSession(); - if (session != null && myMemoryViews.containsKey(session)) { - ClassesFilteredView view = myMemoryViews.get(session); - replaceToolWindowContent(toolWindow, view); + if (session != null) { + ClassesFilteredView view; + if (myMemoryViews.containsKey(session)) { + view = myMemoryViews.get(session); + replaceToolWindowContent(toolWindow, view); + } else { + view = null; + replaceToolWindowContent(toolWindow, myMemoryViewNotSupportedContent); + } + if (myCurrentView != null) { - myCurrentView.setActive(true); + myCurrentView.setActive(false); } myCurrentView = view; From 557268e1765478a52a1a19a10c3895d5c2a9072f Mon Sep 17 00:00:00 2001 From: "Vitaliy.Bibaev" Date: Wed, 9 Nov 2016 16:39:57 +0300 Subject: [PATCH 144/151] Fix NPE when classes view not exists for stepped session --- toolwindow/MemoryViewToolWindowFactory.java | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/toolwindow/MemoryViewToolWindowFactory.java b/toolwindow/MemoryViewToolWindowFactory.java index 532051d8808e..760cf3c5c835 100644 --- a/toolwindow/MemoryViewToolWindowFactory.java +++ b/toolwindow/MemoryViewToolWindowFactory.java @@ -113,7 +113,9 @@ public class MemoryViewToolWindowFactory implements ToolWindowFactory, DumbAware private void removeSession(@NotNull XDebugSession session) { ClassesFilteredView removed = myMemoryViews.remove(session); - Disposer.dispose(removed); + if (removed != null) { + Disposer.dispose(removed); + } } private void updateCurrentMemoryView(@NotNull Project project, @NotNull ToolWindow toolWindow) { From f469ae2add41185715af38785d73aa02818480a7 Mon Sep 17 00:00:00 2001 From: "Vitaliy.Bibaev" Date: Wed, 9 Nov 2016 17:55:32 +0300 Subject: [PATCH 145/151] Fix a few minor potential problem places --- toolwindow/MemoryViewToolWindowFactory.java | 2 +- tracking/ConstructorInstancesTracker.java | 9 +++++---- view/ClassesFilteredView.java | 9 ++++++++- 3 files changed, 14 insertions(+), 6 deletions(-) diff --git a/toolwindow/MemoryViewToolWindowFactory.java b/toolwindow/MemoryViewToolWindowFactory.java index 760cf3c5c835..d844cf1bf9c3 100644 --- a/toolwindow/MemoryViewToolWindowFactory.java +++ b/toolwindow/MemoryViewToolWindowFactory.java @@ -106,7 +106,7 @@ public class MemoryViewToolWindowFactory implements ToolWindowFactory, DumbAware newView.setActive(toolWindow != null && toolWindow.isVisible()); myMemoryViews.put(session, newView); } catch (Throwable e) { - LOG.warn("Cannot create new instance of the memory view", e); + LOG.warn("Cannot create new instance of the memory view. " + e.getMessage()); } } } diff --git a/tracking/ConstructorInstancesTracker.java b/tracking/ConstructorInstancesTracker.java index 9dfff777e59f..44ebf43d10b8 100644 --- a/tracking/ConstructorInstancesTracker.java +++ b/tracking/ConstructorInstancesTracker.java @@ -168,6 +168,7 @@ public class ConstructorInstancesTracker implements TrackerForNewInstances, Disp private boolean myIsEnabled = false; private final List myRequests = new ArrayList<>(); + private volatile boolean myIsDeleted = false; MyConstructorBreakpoints(Project project, XBreakpoint xBreakpoint) { super(project, xBreakpoint); @@ -197,6 +198,7 @@ public class ConstructorInstancesTracker implements TrackerForNewInstances, Disp protected void action() throws Exception { disable(); myDebugProcess.getRequestsManager().deleteRequest(MyConstructorBreakpoints.this); + myIsDeleted = true; } }); } @@ -208,7 +210,7 @@ public class ConstructorInstancesTracker implements TrackerForNewInstances, Disp SuspendContextImpl suspendContext = action.getSuspendContext(); if (suspendContext != null) { ObjectReference thisRef = getThisObject(suspendContext, event); - if (myReference.equals(thisRef.referenceType())) { + if (myReference.equals(thisRef.referenceType()) && myPositionTracker != null) { thisRef.disableCollection(); myTrackedObjects.add(thisRef); ThreadReferenceProxyImpl threadReferenceProxy = suspendContext.getThread(); @@ -227,7 +229,6 @@ public class ConstructorInstancesTracker implements TrackerForNewInstances, Disp }).filter(Objects::nonNull).collect(Collectors.toList()); myPositionTracker.addStack(myDebugSession, thisRef, stackFrameDescriptors); } - } } } catch (EvaluateException e) { @@ -242,14 +243,14 @@ public class ConstructorInstancesTracker implements TrackerForNewInstances, Disp } void enable() { - if (!myIsEnabled) { + if (!myIsEnabled && !myIsDeleted) { myRequests.forEach(EventRequest::enable); myIsEnabled = true; } } void disable() { - if (myIsEnabled) { + if (myIsEnabled && !myIsDeleted) { myRequests.forEach(EventRequest::disable); myIsEnabled = false; } diff --git a/view/ClassesFilteredView.java b/view/ClassesFilteredView.java index 4f4e68607349..92b5268239fb 100644 --- a/view/ClassesFilteredView.java +++ b/view/ClassesFilteredView.java @@ -71,6 +71,7 @@ public class ClassesFilteredView extends BorderLayoutPanel implements Disposable private final Map myConstructorTrackedClasses = new ConcurrentHashMap<>(); private final XDebugSessionListener myDebugSessionListener; + @Nullable private volatile SuspendContextImpl myLastSuspendContext; /** @@ -92,6 +93,11 @@ public class ClassesFilteredView extends BorderLayoutPanel implements Disposable myDebugSession = debugSession; myDebugProcess = (DebugProcessImpl) DebuggerManager.getInstance(myProject) .getDebugProcess(myDebugSession.getDebugProcess().getProcessHandler()); + + if (myDebugProcess == null) { + throw new NullPointerException("Failed to receive a java debug process"); + } + myInstancesTracker = InstancesTracker.getInstance(myProject); InstancesTrackerListener instancesTrackerListener = new InstancesTrackerListener() { @Override @@ -336,7 +342,8 @@ public class ClassesFilteredView extends BorderLayoutPanel implements Disposable private void doActivate() { myDebugSession.addSessionListener(myDebugSessionListener, ClassesFilteredView.this); myConstructorTrackedClasses.values().forEach(x -> x.setBackgroundMode(false)); - if (myLastSuspendContext == null || !myLastSuspendContext.equals(getSuspendContext())) { + final SuspendContextImpl lastContext = myLastSuspendContext; + if (lastContext == null || !lastContext.equals(getSuspendContext())) { updateClassesAndCounts(); } } From 35a42981d207439c992a3c5c50ae1469c32d75ba Mon Sep 17 00:00:00 2001 From: "Vitaliy.Bibaev" Date: Wed, 9 Nov 2016 20:35:08 +0300 Subject: [PATCH 146/151] Log filtering in instances window --- view/InstancesWindow.java | 15 +++++++++++---- 1 file changed, 11 insertions(+), 4 deletions(-) diff --git a/view/InstancesWindow.java b/view/InstancesWindow.java index 065f00847db9..891ebbf82da7 100644 --- a/view/InstancesWindow.java +++ b/view/InstancesWindow.java @@ -18,6 +18,7 @@ import com.intellij.debugger.ui.tree.render.CachedEvaluator; import com.intellij.openapi.Disposable; import com.intellij.openapi.actionSystem.*; import com.intellij.openapi.actionSystem.ex.AnActionListener; +import com.intellij.openapi.diagnostic.Logger; import com.intellij.openapi.project.Project; import com.intellij.openapi.ui.DialogWrapper; import com.intellij.openapi.util.Disposer; @@ -61,11 +62,14 @@ import java.awt.*; import java.awt.event.MouseEvent; import java.util.Collections; import java.util.List; +import java.util.concurrent.TimeUnit; import java.util.concurrent.atomic.AtomicInteger; import static java.lang.Math.min; public class InstancesWindow extends DialogWrapper { + private static final Logger LOG = Logger.getInstance(InstancesWindow.class); + private static final int DEFAULT_WINDOW_WIDTH = 870; private static final int DEFAULT_WINDOW_HEIGHT = 400; private static final int FILTERING_BUTTON_ADDITIONAL_WIDTH = 30; @@ -288,10 +292,9 @@ public class InstancesWindow extends DialogWrapper { if (expression != null && !expression.getExpression().isEmpty()) { try { myEvaluator.setReferenceExpression(TextWithImportsImpl.fromXExpression(expression)); - myEvaluator.setReferenceExpression(TextWithImportsImpl. - fromXExpression(expression)); evaluator = myEvaluator.getEvaluator(); } catch (EvaluateException ignored) { + LOG.info("cannot create evaluator by the expression = " + expression.getExpression()); } } @@ -392,10 +395,10 @@ public class InstancesWindow extends DialogWrapper { private class MyFilteringWorker extends SwingWorker { private final List myReferences; - private final EvaluationContextImpl myEvaluationContext; - private final ExpressionEvaluator myExpressionEvaluator; private final ErrorsValueGroup myErrorsGroup = new ErrorsValueGroup("Errors"); + private final ExpressionEvaluator myExpressionEvaluator; + private final EvaluationContextImpl myEvaluationContext; private volatile boolean myDebuggerTaskCompleted = false; @NotNull @@ -430,6 +433,7 @@ public class InstancesWindow extends DialogWrapper { AtomicInteger totalChildren = new AtomicInteger(0); AtomicInteger totalViewed = new AtomicInteger(0); + long start = System.nanoTime(); for (int i = 0, size = myReferences.size(); i < size && totalChildren.get() < MAX_TREE_NODE_COUNT; i += FILTERING_CHUNK_SIZE) { myDebuggerTaskCompleted = false; @@ -499,6 +503,9 @@ public class InstancesWindow extends DialogWrapper { } } + LOG.info(String.format("Filtering time = %d ms, count = %d, matched = %d", + TimeUnit.NANOSECONDS.toMillis(System.nanoTime() - start), totalViewed.get(), totalChildren.get())); + myCompletionReason = totalViewed.get() == myReferences.size() ? FilteringCompletionReason.ALL_CHECKED : FilteringCompletionReason.LIMIT_REACHED; From 84e44402408145f9b8c00c2dd48206265f901c28 Mon Sep 17 00:00:00 2001 From: "Vitaliy.Bibaev" Date: Fri, 11 Nov 2016 12:07:27 +0300 Subject: [PATCH 147/151] Do not draw border for focused cell in classes table. Prevented shaking of the text in focused cell --- view/ClassesTable.java | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/view/ClassesTable.java b/view/ClassesTable.java index 1cfa45e7faf5..cce435132c13 100644 --- a/view/ClassesTable.java +++ b/view/ClassesTable.java @@ -29,6 +29,7 @@ import org.jetbrains.debugger.memory.utils.AbstractTableModelWithColumns; import org.jetbrains.debugger.memory.utils.InstancesProvider; import javax.swing.*; +import javax.swing.border.Border; import javax.swing.table.TableColumn; import javax.swing.table.TableRowSorter; import java.util.*; @@ -42,6 +43,8 @@ public class ClassesTable extends JBTable implements DataProvider, Disposable { public static final DataKey REF_COUNT_PROVIDER_KEY = DataKey.create("ClassesTable.ReferenceCountProvider"); + private static final Border EMPTY_BORDER = BorderFactory.createEmptyBorder(); + private static final int CLASSES_COLUMN_PREFERRED_WIDTH = 250; private static final int COUNT_COLUMN_MIN_WIDTH = 80; private static final int DIFF_COLUMN_MIN_WIDTH = 80; @@ -400,6 +403,10 @@ public class ClassesTable extends JBTable implements DataProvider, Disposable { column = convertColumnIndexToModel(column); TrackingType trackingType = getTrackingType(row); + if (hasFocus) { + setBorder(EMPTY_BORDER); + } + if (trackingType != null && column == DiffViewTableModel.DIFF_COLUMN_INDEX) { setIcon(MemoryViewIcons.CLASS_TRACKED); setTransparentIconBackground(true); From 1dfcf10e4a145ce5e9f66c7775f80626c7945b3b Mon Sep 17 00:00:00 2001 From: "Vitaliy.Bibaev" Date: Wed, 14 Dec 2016 19:38:47 +0300 Subject: [PATCH 148/151] Collect to move to the IDEA repo --- .../com/intellij/debugger/memory/action}/ClassesActionBase.java | 0 .../com/intellij/debugger/memory/action}/DebuggerTreeAction.java | 0 .../debugger/memory/action}/EnableBackgroundTrackingAction.java | 0 .../intellij/debugger/memory/action}/JumpToTypeSourceAction.java | 0 .../debugger/memory/action}/ShowClassesWithDiffAction.java | 0 .../debugger/memory/action}/ShowClassesWithInstanceAction.java | 0 .../com/intellij/debugger/memory/action}/ShowInstancesAction.java | 0 .../debugger/memory/action}/ShowInstancesByClassAction.java | 0 .../memory/action}/ShowInstancesFromClassesViewAction.java | 0 .../intellij/debugger/memory/action}/ShowNewInstancesAction.java | 0 .../com/intellij/debugger/memory/action}/ShowTrackedAction.java | 0 .../memory/action}/tracking/JumpToAllocationSourceAction.java | 0 .../memory/action}/tracking/TrackInstancesToggleAction.java | 0 .../debugger/memory/component}/CreationPositionTracker.java | 0 .../com/intellij/debugger/memory/component}/InstancesTracker.java | 0 .../intellij/debugger/memory/component}/MemoryViewManager.java | 0 .../debugger/memory/component}/MemoryViewManagerState.java | 0 .../intellij/debugger/memory/event}/InstancesTrackerListener.java | 0 .../debugger/memory/event}/MemoryViewManagerListener.java | 0 .../com/intellij/debugger/memory/sizeof}/RetainedSizeFetcher.java | 0 .../com/intellij/debugger/memory/sizeof}/SizeOfEvalStrategy.java | 0 .../debugger/memory/toolwindow}/MemoryViewToolWindowFactory.java | 0 .../com/intellij/debugger/memory/tracking}/BackgroundTracker.java | 0 .../intellij/debugger/memory/tracking}/ClassPreparedListener.java | 0 .../debugger/memory/tracking}/ConstructorInstancesTracker.java | 0 .../com/intellij/debugger/memory/tracking}/HashBasedTracking.java | 0 .../intellij/debugger/memory/tracking}/IdentityBasedTracking.java | 0 .../debugger/memory/tracking}/InstanceTrackingStrategy.java | 0 .../debugger/memory/tracking}/RetainReferencesTracking.java | 0 .../debugger/memory/tracking}/TrackerForNewInstances.java | 0 .../src/com/intellij/debugger/memory/tracking}/TrackingType.java | 0 .../debugger/memory/utils}/AbstractTableColumnDescriptor.java | 0 .../debugger/memory/utils}/AbstractTableModelWithColumns.java | 0 .../impl/src/com/intellij/debugger/memory/utils}/AndroidUtil.java | 0 .../src/com/intellij/debugger/memory/utils}/ErrorsValueGroup.java | 0 .../com/intellij/debugger/memory/utils}/InstanceJavaValue.java | 0 .../intellij/debugger/memory/utils}/InstanceValueDescriptor.java | 0 .../com/intellij/debugger/memory/utils}/InstancesProvider.java | 0 .../src/com/intellij/debugger/memory/utils}/KeyboardUtils.java | 0 .../intellij/debugger/memory/utils}/LowestPriorityCommand.java | 0 .../impl/src/com/intellij/debugger/memory/utils}/NamesUtils.java | 0 .../debugger/memory/utils}/SingleAlarmWithMutableDelay.java | 0 .../com/intellij/debugger/memory/utils}/StackFrameDescriptor.java | 0 .../com/intellij/debugger/memory/view}/ClassesFilteredView.java | 0 .../impl/src/com/intellij/debugger/memory/view}/ClassesTable.java | 0 .../debugger/memory/view}/ExpressionEditorWithHistory.java | 0 .../com/intellij/debugger/memory/view}/FilteringProgressView.java | 0 .../src/com/intellij/debugger/memory/view}/InstancesTree.java | 0 .../src/com/intellij/debugger/memory/view}/InstancesWindow.java | 0 .../debugger/memory/view}/InstancesWithStackFrameView.java | 0 .../src/com/intellij/debugger/memory/view}/StackFrameList.java | 0 .../src/com/intellij/debugger/memory/view}/StackFramePopup.java | 0 52 files changed, 0 insertions(+), 0 deletions(-) rename {action => java/debugger/impl/src/com/intellij/debugger/memory/action}/ClassesActionBase.java (100%) rename {action => java/debugger/impl/src/com/intellij/debugger/memory/action}/DebuggerTreeAction.java (100%) rename {action => java/debugger/impl/src/com/intellij/debugger/memory/action}/EnableBackgroundTrackingAction.java (100%) rename {action => java/debugger/impl/src/com/intellij/debugger/memory/action}/JumpToTypeSourceAction.java (100%) rename {action => java/debugger/impl/src/com/intellij/debugger/memory/action}/ShowClassesWithDiffAction.java (100%) rename {action => java/debugger/impl/src/com/intellij/debugger/memory/action}/ShowClassesWithInstanceAction.java (100%) rename {action => java/debugger/impl/src/com/intellij/debugger/memory/action}/ShowInstancesAction.java (100%) rename {action => java/debugger/impl/src/com/intellij/debugger/memory/action}/ShowInstancesByClassAction.java (100%) rename {action => java/debugger/impl/src/com/intellij/debugger/memory/action}/ShowInstancesFromClassesViewAction.java (100%) rename {action => java/debugger/impl/src/com/intellij/debugger/memory/action}/ShowNewInstancesAction.java (100%) rename {action => java/debugger/impl/src/com/intellij/debugger/memory/action}/ShowTrackedAction.java (100%) rename {action => java/debugger/impl/src/com/intellij/debugger/memory/action}/tracking/JumpToAllocationSourceAction.java (100%) rename {action => java/debugger/impl/src/com/intellij/debugger/memory/action}/tracking/TrackInstancesToggleAction.java (100%) rename {component => java/debugger/impl/src/com/intellij/debugger/memory/component}/CreationPositionTracker.java (100%) rename {component => java/debugger/impl/src/com/intellij/debugger/memory/component}/InstancesTracker.java (100%) rename {component => java/debugger/impl/src/com/intellij/debugger/memory/component}/MemoryViewManager.java (100%) rename {component => java/debugger/impl/src/com/intellij/debugger/memory/component}/MemoryViewManagerState.java (100%) rename {event => java/debugger/impl/src/com/intellij/debugger/memory/event}/InstancesTrackerListener.java (100%) rename {event => java/debugger/impl/src/com/intellij/debugger/memory/event}/MemoryViewManagerListener.java (100%) rename {sizeof => java/debugger/impl/src/com/intellij/debugger/memory/sizeof}/RetainedSizeFetcher.java (100%) rename {sizeof => java/debugger/impl/src/com/intellij/debugger/memory/sizeof}/SizeOfEvalStrategy.java (100%) rename {toolwindow => java/debugger/impl/src/com/intellij/debugger/memory/toolwindow}/MemoryViewToolWindowFactory.java (100%) rename {tracking => java/debugger/impl/src/com/intellij/debugger/memory/tracking}/BackgroundTracker.java (100%) rename {tracking => java/debugger/impl/src/com/intellij/debugger/memory/tracking}/ClassPreparedListener.java (100%) rename {tracking => java/debugger/impl/src/com/intellij/debugger/memory/tracking}/ConstructorInstancesTracker.java (100%) rename {tracking => java/debugger/impl/src/com/intellij/debugger/memory/tracking}/HashBasedTracking.java (100%) rename {tracking => java/debugger/impl/src/com/intellij/debugger/memory/tracking}/IdentityBasedTracking.java (100%) rename {tracking => java/debugger/impl/src/com/intellij/debugger/memory/tracking}/InstanceTrackingStrategy.java (100%) rename {tracking => java/debugger/impl/src/com/intellij/debugger/memory/tracking}/RetainReferencesTracking.java (100%) rename {tracking => java/debugger/impl/src/com/intellij/debugger/memory/tracking}/TrackerForNewInstances.java (100%) rename {tracking => java/debugger/impl/src/com/intellij/debugger/memory/tracking}/TrackingType.java (100%) rename {utils => java/debugger/impl/src/com/intellij/debugger/memory/utils}/AbstractTableColumnDescriptor.java (100%) rename {utils => java/debugger/impl/src/com/intellij/debugger/memory/utils}/AbstractTableModelWithColumns.java (100%) rename {utils => java/debugger/impl/src/com/intellij/debugger/memory/utils}/AndroidUtil.java (100%) rename {utils => java/debugger/impl/src/com/intellij/debugger/memory/utils}/ErrorsValueGroup.java (100%) rename {utils => java/debugger/impl/src/com/intellij/debugger/memory/utils}/InstanceJavaValue.java (100%) rename {utils => java/debugger/impl/src/com/intellij/debugger/memory/utils}/InstanceValueDescriptor.java (100%) rename {utils => java/debugger/impl/src/com/intellij/debugger/memory/utils}/InstancesProvider.java (100%) rename {utils => java/debugger/impl/src/com/intellij/debugger/memory/utils}/KeyboardUtils.java (100%) rename {utils => java/debugger/impl/src/com/intellij/debugger/memory/utils}/LowestPriorityCommand.java (100%) rename {utils => java/debugger/impl/src/com/intellij/debugger/memory/utils}/NamesUtils.java (100%) rename {utils => java/debugger/impl/src/com/intellij/debugger/memory/utils}/SingleAlarmWithMutableDelay.java (100%) rename {utils => java/debugger/impl/src/com/intellij/debugger/memory/utils}/StackFrameDescriptor.java (100%) rename {view => java/debugger/impl/src/com/intellij/debugger/memory/view}/ClassesFilteredView.java (100%) rename {view => java/debugger/impl/src/com/intellij/debugger/memory/view}/ClassesTable.java (100%) rename {view => java/debugger/impl/src/com/intellij/debugger/memory/view}/ExpressionEditorWithHistory.java (100%) rename {view => java/debugger/impl/src/com/intellij/debugger/memory/view}/FilteringProgressView.java (100%) rename {view => java/debugger/impl/src/com/intellij/debugger/memory/view}/InstancesTree.java (100%) rename {view => java/debugger/impl/src/com/intellij/debugger/memory/view}/InstancesWindow.java (100%) rename {view => java/debugger/impl/src/com/intellij/debugger/memory/view}/InstancesWithStackFrameView.java (100%) rename {view => java/debugger/impl/src/com/intellij/debugger/memory/view}/StackFrameList.java (100%) rename {view => java/debugger/impl/src/com/intellij/debugger/memory/view}/StackFramePopup.java (100%) diff --git a/action/ClassesActionBase.java b/java/debugger/impl/src/com/intellij/debugger/memory/action/ClassesActionBase.java similarity index 100% rename from action/ClassesActionBase.java rename to java/debugger/impl/src/com/intellij/debugger/memory/action/ClassesActionBase.java diff --git a/action/DebuggerTreeAction.java b/java/debugger/impl/src/com/intellij/debugger/memory/action/DebuggerTreeAction.java similarity index 100% rename from action/DebuggerTreeAction.java rename to java/debugger/impl/src/com/intellij/debugger/memory/action/DebuggerTreeAction.java diff --git a/action/EnableBackgroundTrackingAction.java b/java/debugger/impl/src/com/intellij/debugger/memory/action/EnableBackgroundTrackingAction.java similarity index 100% rename from action/EnableBackgroundTrackingAction.java rename to java/debugger/impl/src/com/intellij/debugger/memory/action/EnableBackgroundTrackingAction.java diff --git a/action/JumpToTypeSourceAction.java b/java/debugger/impl/src/com/intellij/debugger/memory/action/JumpToTypeSourceAction.java similarity index 100% rename from action/JumpToTypeSourceAction.java rename to java/debugger/impl/src/com/intellij/debugger/memory/action/JumpToTypeSourceAction.java diff --git a/action/ShowClassesWithDiffAction.java b/java/debugger/impl/src/com/intellij/debugger/memory/action/ShowClassesWithDiffAction.java similarity index 100% rename from action/ShowClassesWithDiffAction.java rename to java/debugger/impl/src/com/intellij/debugger/memory/action/ShowClassesWithDiffAction.java diff --git a/action/ShowClassesWithInstanceAction.java b/java/debugger/impl/src/com/intellij/debugger/memory/action/ShowClassesWithInstanceAction.java similarity index 100% rename from action/ShowClassesWithInstanceAction.java rename to java/debugger/impl/src/com/intellij/debugger/memory/action/ShowClassesWithInstanceAction.java diff --git a/action/ShowInstancesAction.java b/java/debugger/impl/src/com/intellij/debugger/memory/action/ShowInstancesAction.java similarity index 100% rename from action/ShowInstancesAction.java rename to java/debugger/impl/src/com/intellij/debugger/memory/action/ShowInstancesAction.java diff --git a/action/ShowInstancesByClassAction.java b/java/debugger/impl/src/com/intellij/debugger/memory/action/ShowInstancesByClassAction.java similarity index 100% rename from action/ShowInstancesByClassAction.java rename to java/debugger/impl/src/com/intellij/debugger/memory/action/ShowInstancesByClassAction.java diff --git a/action/ShowInstancesFromClassesViewAction.java b/java/debugger/impl/src/com/intellij/debugger/memory/action/ShowInstancesFromClassesViewAction.java similarity index 100% rename from action/ShowInstancesFromClassesViewAction.java rename to java/debugger/impl/src/com/intellij/debugger/memory/action/ShowInstancesFromClassesViewAction.java diff --git a/action/ShowNewInstancesAction.java b/java/debugger/impl/src/com/intellij/debugger/memory/action/ShowNewInstancesAction.java similarity index 100% rename from action/ShowNewInstancesAction.java rename to java/debugger/impl/src/com/intellij/debugger/memory/action/ShowNewInstancesAction.java diff --git a/action/ShowTrackedAction.java b/java/debugger/impl/src/com/intellij/debugger/memory/action/ShowTrackedAction.java similarity index 100% rename from action/ShowTrackedAction.java rename to java/debugger/impl/src/com/intellij/debugger/memory/action/ShowTrackedAction.java diff --git a/action/tracking/JumpToAllocationSourceAction.java b/java/debugger/impl/src/com/intellij/debugger/memory/action/tracking/JumpToAllocationSourceAction.java similarity index 100% rename from action/tracking/JumpToAllocationSourceAction.java rename to java/debugger/impl/src/com/intellij/debugger/memory/action/tracking/JumpToAllocationSourceAction.java diff --git a/action/tracking/TrackInstancesToggleAction.java b/java/debugger/impl/src/com/intellij/debugger/memory/action/tracking/TrackInstancesToggleAction.java similarity index 100% rename from action/tracking/TrackInstancesToggleAction.java rename to java/debugger/impl/src/com/intellij/debugger/memory/action/tracking/TrackInstancesToggleAction.java diff --git a/component/CreationPositionTracker.java b/java/debugger/impl/src/com/intellij/debugger/memory/component/CreationPositionTracker.java similarity index 100% rename from component/CreationPositionTracker.java rename to java/debugger/impl/src/com/intellij/debugger/memory/component/CreationPositionTracker.java diff --git a/component/InstancesTracker.java b/java/debugger/impl/src/com/intellij/debugger/memory/component/InstancesTracker.java similarity index 100% rename from component/InstancesTracker.java rename to java/debugger/impl/src/com/intellij/debugger/memory/component/InstancesTracker.java diff --git a/component/MemoryViewManager.java b/java/debugger/impl/src/com/intellij/debugger/memory/component/MemoryViewManager.java similarity index 100% rename from component/MemoryViewManager.java rename to java/debugger/impl/src/com/intellij/debugger/memory/component/MemoryViewManager.java diff --git a/component/MemoryViewManagerState.java b/java/debugger/impl/src/com/intellij/debugger/memory/component/MemoryViewManagerState.java similarity index 100% rename from component/MemoryViewManagerState.java rename to java/debugger/impl/src/com/intellij/debugger/memory/component/MemoryViewManagerState.java diff --git a/event/InstancesTrackerListener.java b/java/debugger/impl/src/com/intellij/debugger/memory/event/InstancesTrackerListener.java similarity index 100% rename from event/InstancesTrackerListener.java rename to java/debugger/impl/src/com/intellij/debugger/memory/event/InstancesTrackerListener.java diff --git a/event/MemoryViewManagerListener.java b/java/debugger/impl/src/com/intellij/debugger/memory/event/MemoryViewManagerListener.java similarity index 100% rename from event/MemoryViewManagerListener.java rename to java/debugger/impl/src/com/intellij/debugger/memory/event/MemoryViewManagerListener.java diff --git a/sizeof/RetainedSizeFetcher.java b/java/debugger/impl/src/com/intellij/debugger/memory/sizeof/RetainedSizeFetcher.java similarity index 100% rename from sizeof/RetainedSizeFetcher.java rename to java/debugger/impl/src/com/intellij/debugger/memory/sizeof/RetainedSizeFetcher.java diff --git a/sizeof/SizeOfEvalStrategy.java b/java/debugger/impl/src/com/intellij/debugger/memory/sizeof/SizeOfEvalStrategy.java similarity index 100% rename from sizeof/SizeOfEvalStrategy.java rename to java/debugger/impl/src/com/intellij/debugger/memory/sizeof/SizeOfEvalStrategy.java diff --git a/toolwindow/MemoryViewToolWindowFactory.java b/java/debugger/impl/src/com/intellij/debugger/memory/toolwindow/MemoryViewToolWindowFactory.java similarity index 100% rename from toolwindow/MemoryViewToolWindowFactory.java rename to java/debugger/impl/src/com/intellij/debugger/memory/toolwindow/MemoryViewToolWindowFactory.java diff --git a/tracking/BackgroundTracker.java b/java/debugger/impl/src/com/intellij/debugger/memory/tracking/BackgroundTracker.java similarity index 100% rename from tracking/BackgroundTracker.java rename to java/debugger/impl/src/com/intellij/debugger/memory/tracking/BackgroundTracker.java diff --git a/tracking/ClassPreparedListener.java b/java/debugger/impl/src/com/intellij/debugger/memory/tracking/ClassPreparedListener.java similarity index 100% rename from tracking/ClassPreparedListener.java rename to java/debugger/impl/src/com/intellij/debugger/memory/tracking/ClassPreparedListener.java diff --git a/tracking/ConstructorInstancesTracker.java b/java/debugger/impl/src/com/intellij/debugger/memory/tracking/ConstructorInstancesTracker.java similarity index 100% rename from tracking/ConstructorInstancesTracker.java rename to java/debugger/impl/src/com/intellij/debugger/memory/tracking/ConstructorInstancesTracker.java diff --git a/tracking/HashBasedTracking.java b/java/debugger/impl/src/com/intellij/debugger/memory/tracking/HashBasedTracking.java similarity index 100% rename from tracking/HashBasedTracking.java rename to java/debugger/impl/src/com/intellij/debugger/memory/tracking/HashBasedTracking.java diff --git a/tracking/IdentityBasedTracking.java b/java/debugger/impl/src/com/intellij/debugger/memory/tracking/IdentityBasedTracking.java similarity index 100% rename from tracking/IdentityBasedTracking.java rename to java/debugger/impl/src/com/intellij/debugger/memory/tracking/IdentityBasedTracking.java diff --git a/tracking/InstanceTrackingStrategy.java b/java/debugger/impl/src/com/intellij/debugger/memory/tracking/InstanceTrackingStrategy.java similarity index 100% rename from tracking/InstanceTrackingStrategy.java rename to java/debugger/impl/src/com/intellij/debugger/memory/tracking/InstanceTrackingStrategy.java diff --git a/tracking/RetainReferencesTracking.java b/java/debugger/impl/src/com/intellij/debugger/memory/tracking/RetainReferencesTracking.java similarity index 100% rename from tracking/RetainReferencesTracking.java rename to java/debugger/impl/src/com/intellij/debugger/memory/tracking/RetainReferencesTracking.java diff --git a/tracking/TrackerForNewInstances.java b/java/debugger/impl/src/com/intellij/debugger/memory/tracking/TrackerForNewInstances.java similarity index 100% rename from tracking/TrackerForNewInstances.java rename to java/debugger/impl/src/com/intellij/debugger/memory/tracking/TrackerForNewInstances.java diff --git a/tracking/TrackingType.java b/java/debugger/impl/src/com/intellij/debugger/memory/tracking/TrackingType.java similarity index 100% rename from tracking/TrackingType.java rename to java/debugger/impl/src/com/intellij/debugger/memory/tracking/TrackingType.java diff --git a/utils/AbstractTableColumnDescriptor.java b/java/debugger/impl/src/com/intellij/debugger/memory/utils/AbstractTableColumnDescriptor.java similarity index 100% rename from utils/AbstractTableColumnDescriptor.java rename to java/debugger/impl/src/com/intellij/debugger/memory/utils/AbstractTableColumnDescriptor.java diff --git a/utils/AbstractTableModelWithColumns.java b/java/debugger/impl/src/com/intellij/debugger/memory/utils/AbstractTableModelWithColumns.java similarity index 100% rename from utils/AbstractTableModelWithColumns.java rename to java/debugger/impl/src/com/intellij/debugger/memory/utils/AbstractTableModelWithColumns.java diff --git a/utils/AndroidUtil.java b/java/debugger/impl/src/com/intellij/debugger/memory/utils/AndroidUtil.java similarity index 100% rename from utils/AndroidUtil.java rename to java/debugger/impl/src/com/intellij/debugger/memory/utils/AndroidUtil.java diff --git a/utils/ErrorsValueGroup.java b/java/debugger/impl/src/com/intellij/debugger/memory/utils/ErrorsValueGroup.java similarity index 100% rename from utils/ErrorsValueGroup.java rename to java/debugger/impl/src/com/intellij/debugger/memory/utils/ErrorsValueGroup.java diff --git a/utils/InstanceJavaValue.java b/java/debugger/impl/src/com/intellij/debugger/memory/utils/InstanceJavaValue.java similarity index 100% rename from utils/InstanceJavaValue.java rename to java/debugger/impl/src/com/intellij/debugger/memory/utils/InstanceJavaValue.java diff --git a/utils/InstanceValueDescriptor.java b/java/debugger/impl/src/com/intellij/debugger/memory/utils/InstanceValueDescriptor.java similarity index 100% rename from utils/InstanceValueDescriptor.java rename to java/debugger/impl/src/com/intellij/debugger/memory/utils/InstanceValueDescriptor.java diff --git a/utils/InstancesProvider.java b/java/debugger/impl/src/com/intellij/debugger/memory/utils/InstancesProvider.java similarity index 100% rename from utils/InstancesProvider.java rename to java/debugger/impl/src/com/intellij/debugger/memory/utils/InstancesProvider.java diff --git a/utils/KeyboardUtils.java b/java/debugger/impl/src/com/intellij/debugger/memory/utils/KeyboardUtils.java similarity index 100% rename from utils/KeyboardUtils.java rename to java/debugger/impl/src/com/intellij/debugger/memory/utils/KeyboardUtils.java diff --git a/utils/LowestPriorityCommand.java b/java/debugger/impl/src/com/intellij/debugger/memory/utils/LowestPriorityCommand.java similarity index 100% rename from utils/LowestPriorityCommand.java rename to java/debugger/impl/src/com/intellij/debugger/memory/utils/LowestPriorityCommand.java diff --git a/utils/NamesUtils.java b/java/debugger/impl/src/com/intellij/debugger/memory/utils/NamesUtils.java similarity index 100% rename from utils/NamesUtils.java rename to java/debugger/impl/src/com/intellij/debugger/memory/utils/NamesUtils.java diff --git a/utils/SingleAlarmWithMutableDelay.java b/java/debugger/impl/src/com/intellij/debugger/memory/utils/SingleAlarmWithMutableDelay.java similarity index 100% rename from utils/SingleAlarmWithMutableDelay.java rename to java/debugger/impl/src/com/intellij/debugger/memory/utils/SingleAlarmWithMutableDelay.java diff --git a/utils/StackFrameDescriptor.java b/java/debugger/impl/src/com/intellij/debugger/memory/utils/StackFrameDescriptor.java similarity index 100% rename from utils/StackFrameDescriptor.java rename to java/debugger/impl/src/com/intellij/debugger/memory/utils/StackFrameDescriptor.java diff --git a/view/ClassesFilteredView.java b/java/debugger/impl/src/com/intellij/debugger/memory/view/ClassesFilteredView.java similarity index 100% rename from view/ClassesFilteredView.java rename to java/debugger/impl/src/com/intellij/debugger/memory/view/ClassesFilteredView.java diff --git a/view/ClassesTable.java b/java/debugger/impl/src/com/intellij/debugger/memory/view/ClassesTable.java similarity index 100% rename from view/ClassesTable.java rename to java/debugger/impl/src/com/intellij/debugger/memory/view/ClassesTable.java diff --git a/view/ExpressionEditorWithHistory.java b/java/debugger/impl/src/com/intellij/debugger/memory/view/ExpressionEditorWithHistory.java similarity index 100% rename from view/ExpressionEditorWithHistory.java rename to java/debugger/impl/src/com/intellij/debugger/memory/view/ExpressionEditorWithHistory.java diff --git a/view/FilteringProgressView.java b/java/debugger/impl/src/com/intellij/debugger/memory/view/FilteringProgressView.java similarity index 100% rename from view/FilteringProgressView.java rename to java/debugger/impl/src/com/intellij/debugger/memory/view/FilteringProgressView.java diff --git a/view/InstancesTree.java b/java/debugger/impl/src/com/intellij/debugger/memory/view/InstancesTree.java similarity index 100% rename from view/InstancesTree.java rename to java/debugger/impl/src/com/intellij/debugger/memory/view/InstancesTree.java diff --git a/view/InstancesWindow.java b/java/debugger/impl/src/com/intellij/debugger/memory/view/InstancesWindow.java similarity index 100% rename from view/InstancesWindow.java rename to java/debugger/impl/src/com/intellij/debugger/memory/view/InstancesWindow.java diff --git a/view/InstancesWithStackFrameView.java b/java/debugger/impl/src/com/intellij/debugger/memory/view/InstancesWithStackFrameView.java similarity index 100% rename from view/InstancesWithStackFrameView.java rename to java/debugger/impl/src/com/intellij/debugger/memory/view/InstancesWithStackFrameView.java diff --git a/view/StackFrameList.java b/java/debugger/impl/src/com/intellij/debugger/memory/view/StackFrameList.java similarity index 100% rename from view/StackFrameList.java rename to java/debugger/impl/src/com/intellij/debugger/memory/view/StackFrameList.java diff --git a/view/StackFramePopup.java b/java/debugger/impl/src/com/intellij/debugger/memory/view/StackFramePopup.java similarity index 100% rename from view/StackFramePopup.java rename to java/debugger/impl/src/com/intellij/debugger/memory/view/StackFramePopup.java From 60f7a0cfdeae0923ad5e04921a397a6745de43d7 Mon Sep 17 00:00:00 2001 From: "Vitaliy.Bibaev" Date: Fri, 16 Dec 2016 11:34:15 +0300 Subject: [PATCH 149/151] Remove redundant files in Memory view, rename packages, add icons --- .../memory/action/ClassesActionBase.java | 19 +- .../memory/action/DebuggerTreeAction.java | 17 +- .../EnableBackgroundTrackingAction.java | 19 +- .../memory/action/JumpToTypeSourceAction.java | 19 +- .../action/ShowClassesWithDiffAction.java | 19 +- .../action/ShowClassesWithInstanceAction.java | 19 +- .../memory/action/ShowInstancesAction.java | 17 +- .../action/ShowInstancesByClassAction.java | 19 +- .../ShowInstancesFromClassesViewAction.java | 21 +- .../memory/action/ShowNewInstancesAction.java | 23 +- .../memory/action/ShowTrackedAction.java | 19 +- .../JumpToAllocationSourceAction.java | 27 +- .../tracking/TrackInstancesToggleAction.java | 27 +- .../component/CreationPositionTracker.java | 19 +- .../memory/component/InstancesTracker.java | 26 +- .../memory/component/MemoryViewManager.java | 21 +- .../component/MemoryViewManagerState.java | 17 +- .../event/InstancesTrackerListener.java | 19 +- .../event/MemoryViewManagerListener.java | 19 +- .../memory/filtering/CheckingResult.java | 33 ++ .../memory/filtering/CheckingResultImpl.java | 50 +++ .../memory/filtering/ConditionChecker.java | 33 ++ .../filtering/ConditionCheckerImpl.java | 110 ++++++ .../memory/filtering/FilteringResult.java | 27 ++ .../memory/filtering/FilteringTask.java | 88 +++++ .../filtering/FilteringTaskCallback.java | 41 ++ .../memory/sizeof/RetainedSizeFetcher.java | 109 ------ .../memory/sizeof/SizeOfEvalStrategy.java | 21 -- .../MemoryViewToolWindowFactory.java | 81 ++-- .../memory/tracking/BackgroundTracker.java | 17 +- .../tracking/ClassPreparedListener.java | 17 +- .../tracking/ConstructorInstancesTracker.java | 57 ++- .../memory/tracking/HashBasedTracking.java | 169 --------- .../tracking/IdentityBasedTracking.java | 37 -- .../tracking/InstanceTrackingStrategy.java | 52 --- .../tracking/RetainReferencesTracking.java | 30 -- .../tracking/TrackerForNewInstances.java | 17 +- .../memory/tracking/TrackingType.java | 35 +- .../{view => ui}/ClassesFilteredView.java | 76 ++-- .../memory/{view => ui}/ClassesTable.java | 145 +++---- .../ExpressionEditorWithHistory.java | 28 +- .../{view => ui}/FilteringProgressView.java | 31 +- .../memory/{view => ui}/InstancesTree.java | 23 +- .../memory/{view => ui}/InstancesWindow.java | 353 ++++++++---------- .../InstancesWithStackFrameView.java | 52 ++- .../memory/{view => ui}/StackFrameList.java | 29 +- .../memory/{view => ui}/StackFramePopup.java | 19 +- .../utils/AbstractTableColumnDescriptor.java | 17 +- .../utils/AbstractTableModelWithColumns.java | 17 +- .../debugger/memory/utils/AndroidUtil.java | 21 +- .../memory/utils/ErrorsValueGroup.java | 21 +- .../memory/utils/InstanceJavaValue.java | 27 +- .../memory/utils/InstanceValueDescriptor.java | 17 +- .../memory/utils/InstancesProvider.java | 17 +- .../debugger/memory/utils/KeyboardUtils.java | 17 +- .../memory/utils/LowestPriorityCommand.java | 17 +- .../debugger/memory/utils/NamesUtils.java | 17 +- .../utils/SingleAlarmWithMutableDelay.java | 17 +- .../memory/utils/StackFrameDescriptor.java | 17 +- .../src/debugger/memoryView/classTracked.png | Bin 0 -> 378 bytes .../debugger/memoryView/classTracked@2x.png | Bin 0 -> 1050 bytes .../memoryView/classTracked@2x_dark.png | Bin 0 -> 1047 bytes .../debugger/memoryView/classTracked_dark.png | Bin 0 -> 351 bytes .../memoryView/toolWindowDisabled.png | Bin 0 -> 241 bytes .../memoryView/toolWindowDisabled@2x.png | Bin 0 -> 472 bytes .../memoryView/toolWindowDisabled@2x_dark.png | Bin 0 -> 469 bytes .../memoryView/toolWindowDisabled_dark.png | Bin 0 -> 245 bytes .../debugger/memoryView/toolWindowEnabled.png | Bin 0 -> 291 bytes .../memoryView/toolWindowEnabled@2x.png | Bin 0 -> 604 bytes .../memoryView/toolWindowEnabled@2x_dark.png | Bin 0 -> 589 bytes .../memoryView/toolWindowEnabled_dark.png | Bin 0 -> 304 bytes .../util/src/com/intellij/icons/AllIcons.java | 7 + resources/src/componentSets/Debugger.xml | 24 +- resources/src/idea/JavaActions.xml | 55 ++- 74 files changed, 1565 insertions(+), 909 deletions(-) create mode 100644 java/debugger/impl/src/com/intellij/debugger/memory/filtering/CheckingResult.java create mode 100644 java/debugger/impl/src/com/intellij/debugger/memory/filtering/CheckingResultImpl.java create mode 100644 java/debugger/impl/src/com/intellij/debugger/memory/filtering/ConditionChecker.java create mode 100644 java/debugger/impl/src/com/intellij/debugger/memory/filtering/ConditionCheckerImpl.java create mode 100644 java/debugger/impl/src/com/intellij/debugger/memory/filtering/FilteringResult.java create mode 100644 java/debugger/impl/src/com/intellij/debugger/memory/filtering/FilteringTask.java create mode 100644 java/debugger/impl/src/com/intellij/debugger/memory/filtering/FilteringTaskCallback.java delete mode 100644 java/debugger/impl/src/com/intellij/debugger/memory/sizeof/RetainedSizeFetcher.java delete mode 100644 java/debugger/impl/src/com/intellij/debugger/memory/sizeof/SizeOfEvalStrategy.java delete mode 100644 java/debugger/impl/src/com/intellij/debugger/memory/tracking/HashBasedTracking.java delete mode 100644 java/debugger/impl/src/com/intellij/debugger/memory/tracking/IdentityBasedTracking.java delete mode 100644 java/debugger/impl/src/com/intellij/debugger/memory/tracking/InstanceTrackingStrategy.java delete mode 100644 java/debugger/impl/src/com/intellij/debugger/memory/tracking/RetainReferencesTracking.java rename java/debugger/impl/src/com/intellij/debugger/memory/{view => ui}/ClassesFilteredView.java (85%) rename java/debugger/impl/src/com/intellij/debugger/memory/{view => ui}/ClassesTable.java (75%) rename java/debugger/impl/src/com/intellij/debugger/memory/{view => ui}/ExpressionEditorWithHistory.java (76%) rename java/debugger/impl/src/com/intellij/debugger/memory/{view => ui}/FilteringProgressView.java (75%) rename java/debugger/impl/src/com/intellij/debugger/memory/{view => ui}/InstancesTree.java (82%) rename java/debugger/impl/src/com/intellij/debugger/memory/{view => ui}/InstancesWindow.java (59%) rename java/debugger/impl/src/com/intellij/debugger/memory/{view => ui}/InstancesWithStackFrameView.java (70%) rename java/debugger/impl/src/com/intellij/debugger/memory/{view => ui}/StackFrameList.java (83%) rename java/debugger/impl/src/com/intellij/debugger/memory/{view => ui}/StackFramePopup.java (64%) create mode 100644 platform/icons/src/debugger/memoryView/classTracked.png create mode 100644 platform/icons/src/debugger/memoryView/classTracked@2x.png create mode 100644 platform/icons/src/debugger/memoryView/classTracked@2x_dark.png create mode 100644 platform/icons/src/debugger/memoryView/classTracked_dark.png create mode 100644 platform/icons/src/debugger/memoryView/toolWindowDisabled.png create mode 100644 platform/icons/src/debugger/memoryView/toolWindowDisabled@2x.png create mode 100644 platform/icons/src/debugger/memoryView/toolWindowDisabled@2x_dark.png create mode 100644 platform/icons/src/debugger/memoryView/toolWindowDisabled_dark.png create mode 100644 platform/icons/src/debugger/memoryView/toolWindowEnabled.png create mode 100644 platform/icons/src/debugger/memoryView/toolWindowEnabled@2x.png create mode 100644 platform/icons/src/debugger/memoryView/toolWindowEnabled@2x_dark.png create mode 100644 platform/icons/src/debugger/memoryView/toolWindowEnabled_dark.png diff --git a/java/debugger/impl/src/com/intellij/debugger/memory/action/ClassesActionBase.java b/java/debugger/impl/src/com/intellij/debugger/memory/action/ClassesActionBase.java index e534614afd83..f9d148da405b 100644 --- a/java/debugger/impl/src/com/intellij/debugger/memory/action/ClassesActionBase.java +++ b/java/debugger/impl/src/com/intellij/debugger/memory/action/ClassesActionBase.java @@ -1,4 +1,19 @@ -package org.jetbrains.debugger.memory.action; +/* + * Copyright 2000-2016 JetBrains s.r.o. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.intellij.debugger.memory.action; import com.intellij.openapi.actionSystem.AnAction; import com.intellij.openapi.actionSystem.AnActionEvent; @@ -6,7 +21,7 @@ import com.intellij.openapi.project.Project; import com.intellij.xdebugger.XDebugSession; import com.sun.jdi.ReferenceType; import org.jetbrains.annotations.Nullable; -import org.jetbrains.debugger.memory.view.ClassesTable; +import com.intellij.debugger.memory.ui.ClassesTable; public abstract class ClassesActionBase extends AnAction { @Override diff --git a/java/debugger/impl/src/com/intellij/debugger/memory/action/DebuggerTreeAction.java b/java/debugger/impl/src/com/intellij/debugger/memory/action/DebuggerTreeAction.java index c46d72c5ff97..cca58a47732a 100644 --- a/java/debugger/impl/src/com/intellij/debugger/memory/action/DebuggerTreeAction.java +++ b/java/debugger/impl/src/com/intellij/debugger/memory/action/DebuggerTreeAction.java @@ -1,4 +1,19 @@ -package org.jetbrains.debugger.memory.action; +/* + * Copyright 2000-2016 JetBrains s.r.o. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.intellij.debugger.memory.action; import com.intellij.debugger.ui.impl.watch.NodeDescriptorProvider; import com.intellij.debugger.ui.tree.NodeDescriptor; diff --git a/java/debugger/impl/src/com/intellij/debugger/memory/action/EnableBackgroundTrackingAction.java b/java/debugger/impl/src/com/intellij/debugger/memory/action/EnableBackgroundTrackingAction.java index f475aa11e339..39c0459d3b3f 100644 --- a/java/debugger/impl/src/com/intellij/debugger/memory/action/EnableBackgroundTrackingAction.java +++ b/java/debugger/impl/src/com/intellij/debugger/memory/action/EnableBackgroundTrackingAction.java @@ -1,9 +1,24 @@ -package org.jetbrains.debugger.memory.action; +/* + * Copyright 2000-2016 JetBrains s.r.o. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.intellij.debugger.memory.action; import com.intellij.openapi.actionSystem.AnActionEvent; import com.intellij.openapi.actionSystem.ToggleAction; import com.intellij.openapi.project.Project; -import org.jetbrains.debugger.memory.component.InstancesTracker; +import com.intellij.debugger.memory.component.InstancesTracker; public class EnableBackgroundTrackingAction extends ToggleAction{ diff --git a/java/debugger/impl/src/com/intellij/debugger/memory/action/JumpToTypeSourceAction.java b/java/debugger/impl/src/com/intellij/debugger/memory/action/JumpToTypeSourceAction.java index 6e72daadb9ff..ea4c30ecdcfb 100644 --- a/java/debugger/impl/src/com/intellij/debugger/memory/action/JumpToTypeSourceAction.java +++ b/java/debugger/impl/src/com/intellij/debugger/memory/action/JumpToTypeSourceAction.java @@ -1,4 +1,19 @@ -package org.jetbrains.debugger.memory.action; +/* + * Copyright 2000-2016 JetBrains s.r.o. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.intellij.debugger.memory.action; import com.intellij.codeInsight.navigation.NavigationUtil; import com.intellij.debugger.engine.DebuggerUtils; @@ -49,7 +64,7 @@ public class JumpToTypeSourceAction extends ClassesActionBase { } @Nullable - private ReferenceType getObjectType(@NotNull ReferenceType ref) { + private static ReferenceType getObjectType(@NotNull ReferenceType ref) { if (!(ref instanceof ArrayType)) { return ref; } diff --git a/java/debugger/impl/src/com/intellij/debugger/memory/action/ShowClassesWithDiffAction.java b/java/debugger/impl/src/com/intellij/debugger/memory/action/ShowClassesWithDiffAction.java index f12c349e0593..1461398e528f 100644 --- a/java/debugger/impl/src/com/intellij/debugger/memory/action/ShowClassesWithDiffAction.java +++ b/java/debugger/impl/src/com/intellij/debugger/memory/action/ShowClassesWithDiffAction.java @@ -1,9 +1,24 @@ -package org.jetbrains.debugger.memory.action; +/* + * Copyright 2000-2016 JetBrains s.r.o. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.intellij.debugger.memory.action; import com.intellij.openapi.actionSystem.AnActionEvent; import com.intellij.openapi.actionSystem.ToggleAction; import com.intellij.openapi.project.Project; -import org.jetbrains.debugger.memory.component.MemoryViewManager; +import com.intellij.debugger.memory.component.MemoryViewManager; public class ShowClassesWithDiffAction extends ToggleAction { @Override diff --git a/java/debugger/impl/src/com/intellij/debugger/memory/action/ShowClassesWithInstanceAction.java b/java/debugger/impl/src/com/intellij/debugger/memory/action/ShowClassesWithInstanceAction.java index 17681f97dfb9..a67b28256979 100644 --- a/java/debugger/impl/src/com/intellij/debugger/memory/action/ShowClassesWithInstanceAction.java +++ b/java/debugger/impl/src/com/intellij/debugger/memory/action/ShowClassesWithInstanceAction.java @@ -1,9 +1,24 @@ -package org.jetbrains.debugger.memory.action; +/* + * Copyright 2000-2016 JetBrains s.r.o. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.intellij.debugger.memory.action; import com.intellij.openapi.actionSystem.AnActionEvent; import com.intellij.openapi.actionSystem.ToggleAction; import com.intellij.openapi.project.Project; -import org.jetbrains.debugger.memory.component.MemoryViewManager; +import com.intellij.debugger.memory.component.MemoryViewManager; public class ShowClassesWithInstanceAction extends ToggleAction { @Override diff --git a/java/debugger/impl/src/com/intellij/debugger/memory/action/ShowInstancesAction.java b/java/debugger/impl/src/com/intellij/debugger/memory/action/ShowInstancesAction.java index 865e805caaf0..2c0317d7c9de 100644 --- a/java/debugger/impl/src/com/intellij/debugger/memory/action/ShowInstancesAction.java +++ b/java/debugger/impl/src/com/intellij/debugger/memory/action/ShowInstancesAction.java @@ -1,4 +1,19 @@ -package org.jetbrains.debugger.memory.action; +/* + * Copyright 2000-2016 JetBrains s.r.o. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.intellij.debugger.memory.action; import com.intellij.openapi.actionSystem.AnActionEvent; diff --git a/java/debugger/impl/src/com/intellij/debugger/memory/action/ShowInstancesByClassAction.java b/java/debugger/impl/src/com/intellij/debugger/memory/action/ShowInstancesByClassAction.java index f4ccf9271eea..be035750b9be 100644 --- a/java/debugger/impl/src/com/intellij/debugger/memory/action/ShowInstancesByClassAction.java +++ b/java/debugger/impl/src/com/intellij/debugger/memory/action/ShowInstancesByClassAction.java @@ -1,4 +1,19 @@ -package org.jetbrains.debugger.memory.action; +/* + * Copyright 2000-2016 JetBrains s.r.o. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.intellij.debugger.memory.action; import com.intellij.openapi.actionSystem.AnActionEvent; import com.intellij.openapi.project.Project; @@ -9,7 +24,7 @@ import com.intellij.xdebugger.impl.ui.tree.nodes.XValueNodeImpl; import com.sun.jdi.ObjectReference; import com.sun.jdi.ReferenceType; import org.jetbrains.annotations.NotNull; -import org.jetbrains.debugger.memory.view.InstancesWindow; +import com.intellij.debugger.memory.ui.InstancesWindow; public class ShowInstancesByClassAction extends DebuggerTreeAction { @Override diff --git a/java/debugger/impl/src/com/intellij/debugger/memory/action/ShowInstancesFromClassesViewAction.java b/java/debugger/impl/src/com/intellij/debugger/memory/action/ShowInstancesFromClassesViewAction.java index 040432aeb97a..35ccc88f2c11 100644 --- a/java/debugger/impl/src/com/intellij/debugger/memory/action/ShowInstancesFromClassesViewAction.java +++ b/java/debugger/impl/src/com/intellij/debugger/memory/action/ShowInstancesFromClassesViewAction.java @@ -1,10 +1,25 @@ -package org.jetbrains.debugger.memory.action; +/* + * Copyright 2000-2016 JetBrains s.r.o. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.intellij.debugger.memory.action; import com.intellij.openapi.actionSystem.AnActionEvent; import com.intellij.xdebugger.XDebugSession; import com.sun.jdi.ReferenceType; -import org.jetbrains.debugger.memory.view.ClassesTable; -import org.jetbrains.debugger.memory.view.InstancesWindow; +import com.intellij.debugger.memory.ui.ClassesTable; +import com.intellij.debugger.memory.ui.InstancesWindow; public class ShowInstancesFromClassesViewAction extends ShowInstancesAction { private static final String POPUP_ELEMENT_LABEL = "Show Instances"; diff --git a/java/debugger/impl/src/com/intellij/debugger/memory/action/ShowNewInstancesAction.java b/java/debugger/impl/src/com/intellij/debugger/memory/action/ShowNewInstancesAction.java index cfa3922f4089..bc3dc86e68c3 100644 --- a/java/debugger/impl/src/com/intellij/debugger/memory/action/ShowNewInstancesAction.java +++ b/java/debugger/impl/src/com/intellij/debugger/memory/action/ShowNewInstancesAction.java @@ -1,11 +1,26 @@ -package org.jetbrains.debugger.memory.action; +/* + * Copyright 2000-2016 JetBrains s.r.o. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.intellij.debugger.memory.action; import com.intellij.openapi.actionSystem.AnActionEvent; import com.intellij.xdebugger.XDebugSession; import com.sun.jdi.ReferenceType; -import org.jetbrains.debugger.memory.utils.InstancesProvider; -import org.jetbrains.debugger.memory.view.ClassesTable; -import org.jetbrains.debugger.memory.view.InstancesWindow; +import com.intellij.debugger.memory.utils.InstancesProvider; +import com.intellij.debugger.memory.ui.ClassesTable; +import com.intellij.debugger.memory.ui.InstancesWindow; public class ShowNewInstancesAction extends ShowInstancesAction { private static final String POPUP_ELEMENT_LABEL = "Show New Instances"; diff --git a/java/debugger/impl/src/com/intellij/debugger/memory/action/ShowTrackedAction.java b/java/debugger/impl/src/com/intellij/debugger/memory/action/ShowTrackedAction.java index 9c8228530b45..f0088419173e 100644 --- a/java/debugger/impl/src/com/intellij/debugger/memory/action/ShowTrackedAction.java +++ b/java/debugger/impl/src/com/intellij/debugger/memory/action/ShowTrackedAction.java @@ -1,8 +1,23 @@ -package org.jetbrains.debugger.memory.action; +/* + * Copyright 2000-2016 JetBrains s.r.o. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.intellij.debugger.memory.action; import com.intellij.openapi.actionSystem.AnActionEvent; import com.intellij.openapi.actionSystem.ToggleAction; -import org.jetbrains.debugger.memory.component.MemoryViewManager; +import com.intellij.debugger.memory.component.MemoryViewManager; public class ShowTrackedAction extends ToggleAction { @Override diff --git a/java/debugger/impl/src/com/intellij/debugger/memory/action/tracking/JumpToAllocationSourceAction.java b/java/debugger/impl/src/com/intellij/debugger/memory/action/tracking/JumpToAllocationSourceAction.java index 218fdfa6084e..79aaa23e7d8e 100644 --- a/java/debugger/impl/src/com/intellij/debugger/memory/action/tracking/JumpToAllocationSourceAction.java +++ b/java/debugger/impl/src/com/intellij/debugger/memory/action/tracking/JumpToAllocationSourceAction.java @@ -1,6 +1,23 @@ -package org.jetbrains.debugger.memory.action.tracking; +/* + * Copyright 2000-2016 JetBrains s.r.o. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.intellij.debugger.memory.action.tracking; import com.intellij.debugger.DebuggerManager; +import com.intellij.debugger.memory.action.DebuggerTreeAction; +import com.intellij.debugger.memory.ui.StackFramePopup; import com.intellij.openapi.actionSystem.AnActionEvent; import com.intellij.openapi.project.Project; import com.intellij.psi.search.GlobalSearchScope; @@ -10,11 +27,9 @@ import com.intellij.xdebugger.impl.ui.tree.nodes.XValueNodeImpl; import com.sun.jdi.ObjectReference; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; -import org.jetbrains.debugger.memory.action.DebuggerTreeAction; -import org.jetbrains.debugger.memory.component.CreationPositionTracker; -import org.jetbrains.debugger.memory.utils.StackFrameDescriptor; -import org.jetbrains.debugger.memory.view.InstancesTree; -import org.jetbrains.debugger.memory.view.StackFramePopup; +import com.intellij.debugger.memory.component.CreationPositionTracker; +import com.intellij.debugger.memory.utils.StackFrameDescriptor; +import com.intellij.debugger.memory.ui.InstancesTree; import java.util.List; diff --git a/java/debugger/impl/src/com/intellij/debugger/memory/action/tracking/TrackInstancesToggleAction.java b/java/debugger/impl/src/com/intellij/debugger/memory/action/tracking/TrackInstancesToggleAction.java index 2a35327966df..c3131508ef49 100644 --- a/java/debugger/impl/src/com/intellij/debugger/memory/action/tracking/TrackInstancesToggleAction.java +++ b/java/debugger/impl/src/com/intellij/debugger/memory/action/tracking/TrackInstancesToggleAction.java @@ -1,5 +1,23 @@ -package org.jetbrains.debugger.memory.action.tracking; +/* + * Copyright 2000-2016 JetBrains s.r.o. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.intellij.debugger.memory.action.tracking; +import com.intellij.debugger.memory.component.InstancesTracker; +import com.intellij.debugger.memory.tracking.TrackingType; +import com.intellij.debugger.memory.ui.ClassesTable; import com.intellij.openapi.actionSystem.AnActionEvent; import com.intellij.openapi.actionSystem.ToggleAction; import com.intellij.xdebugger.XDebugSession; @@ -7,9 +25,6 @@ import com.sun.jdi.ArrayType; import com.sun.jdi.ReferenceType; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; -import org.jetbrains.debugger.memory.component.InstancesTracker; -import org.jetbrains.debugger.memory.tracking.TrackingType; -import org.jetbrains.debugger.memory.view.ClassesTable; public class TrackInstancesToggleAction extends ToggleAction { @Override @@ -53,12 +68,12 @@ public class TrackInstancesToggleAction extends ToggleAction { } @Nullable - private ReferenceType getSelectedClass(AnActionEvent e) { + private static ReferenceType getSelectedClass(AnActionEvent e) { return e.getData(ClassesTable.SELECTED_CLASS_KEY); } @Nullable - private XDebugSession getDebugSession(AnActionEvent e) { + private static XDebugSession getDebugSession(AnActionEvent e) { return e.getData(ClassesTable.DEBUG_SESSION_KEY); } } diff --git a/java/debugger/impl/src/com/intellij/debugger/memory/component/CreationPositionTracker.java b/java/debugger/impl/src/com/intellij/debugger/memory/component/CreationPositionTracker.java index ee54aab35e3e..a30a57db419a 100644 --- a/java/debugger/impl/src/com/intellij/debugger/memory/component/CreationPositionTracker.java +++ b/java/debugger/impl/src/com/intellij/debugger/memory/component/CreationPositionTracker.java @@ -1,5 +1,21 @@ -package org.jetbrains.debugger.memory.component; +/* + * Copyright 2000-2016 JetBrains s.r.o. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.intellij.debugger.memory.component; +import com.intellij.debugger.memory.utils.StackFrameDescriptor; import com.intellij.openapi.components.AbstractProjectComponent; import com.intellij.openapi.project.Project; import com.intellij.xdebugger.XDebugSession; @@ -8,7 +24,6 @@ import com.sun.jdi.ObjectReference; import com.sun.jdi.ReferenceType; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; -import org.jetbrains.debugger.memory.utils.StackFrameDescriptor; import java.util.Iterator; import java.util.List; diff --git a/java/debugger/impl/src/com/intellij/debugger/memory/component/InstancesTracker.java b/java/debugger/impl/src/com/intellij/debugger/memory/component/InstancesTracker.java index 1a47159e20ca..c158dfc376f7 100644 --- a/java/debugger/impl/src/com/intellij/debugger/memory/component/InstancesTracker.java +++ b/java/debugger/impl/src/com/intellij/debugger/memory/component/InstancesTracker.java @@ -1,4 +1,19 @@ -package org.jetbrains.debugger.memory.component; +/* + * Copyright 2000-2016 JetBrains s.r.o. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.intellij.debugger.memory.component; import com.intellij.openapi.Disposable; import com.intellij.openapi.components.*; @@ -8,8 +23,8 @@ import com.intellij.util.containers.HashMap; import com.intellij.util.xmlb.annotations.AbstractCollection; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; -import org.jetbrains.debugger.memory.event.InstancesTrackerListener; -import org.jetbrains.debugger.memory.tracking.TrackingType; +import com.intellij.debugger.memory.event.InstancesTrackerListener; +import com.intellij.debugger.memory.tracking.TrackingType; import java.util.Map; import java.util.concurrent.ConcurrentHashMap; @@ -56,14 +71,11 @@ public class InstancesTracker extends AbstractProjectComponent myDispatcher.getMulticaster().classChanged(name, type); } - public boolean remove(@NotNull String name) { + public void remove(@NotNull String name) { TrackingType removed = myState.classes.remove(name); if (removed != null) { myDispatcher.getMulticaster().classRemoved(name); - return true; } - - return false; } public void addTrackerListener(@NotNull InstancesTrackerListener listener, @NotNull Disposable parentDisposable) { diff --git a/java/debugger/impl/src/com/intellij/debugger/memory/component/MemoryViewManager.java b/java/debugger/impl/src/com/intellij/debugger/memory/component/MemoryViewManager.java index 58d42f890fd6..8d937d65297e 100644 --- a/java/debugger/impl/src/com/intellij/debugger/memory/component/MemoryViewManager.java +++ b/java/debugger/impl/src/com/intellij/debugger/memory/component/MemoryViewManager.java @@ -1,5 +1,22 @@ -package org.jetbrains.debugger.memory.component; +/* + * Copyright 2000-2016 JetBrains s.r.o. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.intellij.debugger.memory.component; +import com.intellij.debugger.memory.event.MemoryViewManagerListener; +import com.intellij.debugger.memory.toolwindow.MemoryViewToolWindowFactory; import com.intellij.openapi.Disposable; import com.intellij.openapi.application.ApplicationManager; import com.intellij.openapi.components.*; @@ -9,8 +26,6 @@ import com.intellij.openapi.wm.ToolWindowManager; import com.intellij.util.EventDispatcher; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; -import org.jetbrains.debugger.memory.event.MemoryViewManagerListener; -import org.jetbrains.debugger.memory.toolwindow.MemoryViewToolWindowFactory; @State(name = "MemoryViewSettings", storages = @Storage("memory.view.xml")) public class MemoryViewManager extends ApplicationComponent.Adapter diff --git a/java/debugger/impl/src/com/intellij/debugger/memory/component/MemoryViewManagerState.java b/java/debugger/impl/src/com/intellij/debugger/memory/component/MemoryViewManagerState.java index 07e6f7272c45..678b84e3cb9b 100644 --- a/java/debugger/impl/src/com/intellij/debugger/memory/component/MemoryViewManagerState.java +++ b/java/debugger/impl/src/com/intellij/debugger/memory/component/MemoryViewManagerState.java @@ -1,4 +1,19 @@ -package org.jetbrains.debugger.memory.component; +/* + * Copyright 2000-2016 JetBrains s.r.o. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.intellij.debugger.memory.component; public final class MemoryViewManagerState { public boolean isShowWithInstancesOnly = true; diff --git a/java/debugger/impl/src/com/intellij/debugger/memory/event/InstancesTrackerListener.java b/java/debugger/impl/src/com/intellij/debugger/memory/event/InstancesTrackerListener.java index e01372032a1c..1fa7e3f94ef8 100644 --- a/java/debugger/impl/src/com/intellij/debugger/memory/event/InstancesTrackerListener.java +++ b/java/debugger/impl/src/com/intellij/debugger/memory/event/InstancesTrackerListener.java @@ -1,7 +1,22 @@ -package org.jetbrains.debugger.memory.event; +/* + * Copyright 2000-2016 JetBrains s.r.o. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.intellij.debugger.memory.event; +import com.intellij.debugger.memory.tracking.TrackingType; import org.jetbrains.annotations.NotNull; -import org.jetbrains.debugger.memory.tracking.TrackingType; import java.util.EventListener; diff --git a/java/debugger/impl/src/com/intellij/debugger/memory/event/MemoryViewManagerListener.java b/java/debugger/impl/src/com/intellij/debugger/memory/event/MemoryViewManagerListener.java index 1c7e1cf8f031..c814114f8731 100644 --- a/java/debugger/impl/src/com/intellij/debugger/memory/event/MemoryViewManagerListener.java +++ b/java/debugger/impl/src/com/intellij/debugger/memory/event/MemoryViewManagerListener.java @@ -1,6 +1,21 @@ -package org.jetbrains.debugger.memory.event; +/* + * Copyright 2000-2016 JetBrains s.r.o. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.intellij.debugger.memory.event; -import org.jetbrains.debugger.memory.component.MemoryViewManagerState; +import com.intellij.debugger.memory.component.MemoryViewManagerState; import org.jetbrains.annotations.NotNull; import java.util.EventListener; diff --git a/java/debugger/impl/src/com/intellij/debugger/memory/filtering/CheckingResult.java b/java/debugger/impl/src/com/intellij/debugger/memory/filtering/CheckingResult.java new file mode 100644 index 000000000000..167228298b51 --- /dev/null +++ b/java/debugger/impl/src/com/intellij/debugger/memory/filtering/CheckingResult.java @@ -0,0 +1,33 @@ +/* + * Copyright 2000-2016 JetBrains s.r.o. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.intellij.debugger.memory.filtering; + +import org.jetbrains.annotations.NotNull; + +/** + * @author Vitaliy.Bibaev + */ +public interface CheckingResult { + @NotNull + Result getResult(); + + @NotNull + String getFailureDescription(); + + enum Result { + MATCH, NO_MATCH, ERROR + } +} diff --git a/java/debugger/impl/src/com/intellij/debugger/memory/filtering/CheckingResultImpl.java b/java/debugger/impl/src/com/intellij/debugger/memory/filtering/CheckingResultImpl.java new file mode 100644 index 000000000000..9b640d611985 --- /dev/null +++ b/java/debugger/impl/src/com/intellij/debugger/memory/filtering/CheckingResultImpl.java @@ -0,0 +1,50 @@ +/* + * Copyright 2000-2016 JetBrains s.r.o. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.intellij.debugger.memory.filtering; + +import org.jetbrains.annotations.NotNull; + +/** + * @author Vitaliy.Bibaev + */ +public class CheckingResultImpl implements CheckingResult { + public static final CheckingResult SUCCESS = new CheckingResultImpl(CheckingResultImpl.Result.MATCH, ""); + public static final CheckingResult FAIL = new CheckingResultImpl(CheckingResultImpl.Result.NO_MATCH, ""); + + private final Result myResult; + private final String myDescription; + + private CheckingResultImpl(@NotNull Result result, @NotNull String description) { + myResult = result; + myDescription = description; + } + + public static CheckingResult error(@NotNull String description) { + return new CheckingResultImpl(Result.ERROR, description); + } + + @Override + @NotNull + public Result getResult() { + return myResult; + } + + @Override + @NotNull + public String getFailureDescription() { + return myDescription; + } +} diff --git a/java/debugger/impl/src/com/intellij/debugger/memory/filtering/ConditionChecker.java b/java/debugger/impl/src/com/intellij/debugger/memory/filtering/ConditionChecker.java new file mode 100644 index 000000000000..9dcbc4fdba22 --- /dev/null +++ b/java/debugger/impl/src/com/intellij/debugger/memory/filtering/ConditionChecker.java @@ -0,0 +1,33 @@ +/* + * Copyright 2000-2016 JetBrains s.r.o. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.intellij.debugger.memory.filtering; + +import com.sun.jdi.ObjectReference; +import org.jetbrains.annotations.NotNull; + +/** + * @author Vitaliy.Bibaev + */ +public interface ConditionChecker { + CheckingResult check(@NotNull ObjectReference ref); + + ConditionChecker ALL_MATCHED_CHECKER = new ConditionChecker() { + @Override + public CheckingResult check(@NotNull ObjectReference ref) { + return CheckingResultImpl.SUCCESS; + } + }; +} diff --git a/java/debugger/impl/src/com/intellij/debugger/memory/filtering/ConditionCheckerImpl.java b/java/debugger/impl/src/com/intellij/debugger/memory/filtering/ConditionCheckerImpl.java new file mode 100644 index 000000000000..45752525aa29 --- /dev/null +++ b/java/debugger/impl/src/com/intellij/debugger/memory/filtering/ConditionCheckerImpl.java @@ -0,0 +1,110 @@ +/* + * Copyright 2000-2016 JetBrains s.r.o. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.intellij.debugger.memory.filtering; + +import com.intellij.debugger.engine.DebugProcessImpl; +import com.intellij.debugger.engine.SuspendContextImpl; +import com.intellij.debugger.engine.evaluation.EvaluateException; +import com.intellij.debugger.engine.evaluation.EvaluationContextImpl; +import com.intellij.debugger.engine.evaluation.TextWithImportsImpl; +import com.intellij.debugger.engine.evaluation.expression.ExpressionEvaluator; +import com.intellij.debugger.engine.events.DebuggerContextCommandImpl; +import com.intellij.debugger.ui.tree.render.CachedEvaluator; +import com.intellij.openapi.project.Project; +import com.intellij.xdebugger.XExpression; +import com.sun.jdi.BooleanValue; +import com.sun.jdi.ObjectReference; +import com.sun.jdi.Value; +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; + +import java.util.concurrent.atomic.AtomicReference; + +/** + * @author Vitaliy.Bibaev + */ +public class ConditionCheckerImpl implements ConditionChecker { + private final DebugProcessImpl myDebugProcess; + private final MyCachedEvaluator myEvaluator; + private final AtomicReference myResultReference = new AtomicReference<>(); + + public ConditionCheckerImpl(@NotNull DebugProcessImpl debugProcess, + @Nullable XExpression expression, + @NotNull String className) { + myDebugProcess = debugProcess; + myEvaluator = new MyCachedEvaluator(myDebugProcess.getProject(), className); + myEvaluator.setReferenceExpression(TextWithImportsImpl.fromXExpression(expression)); + } + + @Override + public CheckingResult check(@NotNull ObjectReference ref) { + myDebugProcess.getManagerThread().invokeAndWait(new MyCheckerCommand(ref)); + return myResultReference.get(); + } + + private class MyCheckerCommand extends DebuggerContextCommandImpl { + private final ObjectReference myReference; + + protected MyCheckerCommand(@NotNull ObjectReference ref) { + super(myDebugProcess.getDebuggerContext()); + myReference = ref; + } + + @Override + public Priority getPriority() { + return Priority.LOWEST; + } + + @Override + public void threadAction(@NotNull SuspendContextImpl suspendContext) { + ExpressionEvaluator evaluator; + try { + evaluator = myEvaluator.getEvaluator(); + Value result = + evaluator.evaluate(new EvaluationContextImpl(suspendContext, suspendContext.getFrameProxy(), myReference)); + if (result instanceof BooleanValue && ((BooleanValue)result).value()) { + myResultReference.set(CheckingResultImpl.SUCCESS); + } + else { + myResultReference.set(CheckingResultImpl.FAIL); + } + } + catch (EvaluateException e) { + myResultReference.set(CheckingResultImpl.error(e.getMessage())); + } + } + } + + private static class MyCachedEvaluator extends CachedEvaluator { + private final Project myProject; + private final String myClassName; + + public MyCachedEvaluator(@NotNull Project project, @NotNull String className) { + super(); + myProject = project; + myClassName = className; + } + + ExpressionEvaluator getEvaluator() throws EvaluateException { + return getEvaluator(myProject); + } + + @Override + protected String getClassName() { + return myClassName; + } + } +} diff --git a/java/debugger/impl/src/com/intellij/debugger/memory/filtering/FilteringResult.java b/java/debugger/impl/src/com/intellij/debugger/memory/filtering/FilteringResult.java new file mode 100644 index 000000000000..951aaeba6cd7 --- /dev/null +++ b/java/debugger/impl/src/com/intellij/debugger/memory/filtering/FilteringResult.java @@ -0,0 +1,27 @@ +/* + * Copyright 2000-2016 JetBrains s.r.o. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.intellij.debugger.memory.filtering; + +/** + * @author Vitaliy.Bibaev + */ +public enum FilteringResult { + ALL_CHECKED, + + INTERRUPTED, + + LIMIT_REACHED +} diff --git a/java/debugger/impl/src/com/intellij/debugger/memory/filtering/FilteringTask.java b/java/debugger/impl/src/com/intellij/debugger/memory/filtering/FilteringTask.java new file mode 100644 index 000000000000..a84b3360cdf5 --- /dev/null +++ b/java/debugger/impl/src/com/intellij/debugger/memory/filtering/FilteringTask.java @@ -0,0 +1,88 @@ +/* + * Copyright 2000-2016 JetBrains s.r.o. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.intellij.debugger.memory.filtering; + +import com.intellij.debugger.engine.DebugProcessImpl; +import com.intellij.openapi.util.text.StringUtil; +import com.intellij.xdebugger.XExpression; +import com.sun.jdi.ObjectReference; +import com.sun.jdi.ReferenceType; +import org.jetbrains.annotations.NotNull; + +import java.util.List; + +/** + * @author Vitaliy.Bibaev + */ +public class FilteringTask implements Runnable { + private final List myReferences; + private final ConditionChecker myChecker; + private final FilteringTaskCallback myCallback; + + private volatile boolean myIsCancelled = false; + + public FilteringTask(@NotNull ReferenceType referenceType, @NotNull DebugProcessImpl debugProcess, + @NotNull XExpression expression, @NotNull List references, + @NotNull FilteringTaskCallback callback) { + myChecker = StringUtil.isEmptyOrSpaces(expression.getExpression()) + ? ConditionChecker.ALL_MATCHED_CHECKER + : new ConditionCheckerImpl(debugProcess, expression, referenceType.name()); + myReferences = references; + myCallback = callback; + } + + public void cancel() { + myIsCancelled = true; + } + + public boolean isCancelled() { + return myIsCancelled; + } + + @Override + public void run() { + myCallback.started(myReferences.size()); + int proceedCount; + for (proceedCount = 0; proceedCount < myReferences.size(); proceedCount++) { + ObjectReference ref = myReferences.get(proceedCount); + CheckingResult result = myChecker.check(ref); + FilteringTaskCallback.Action action = FilteringTaskCallback.Action.CONTINUE; + switch (result.getResult()) { + case MATCH: + action = myCallback.matched(ref); + break; + case NO_MATCH: + action = myCallback.notMatched(ref); + break; + case ERROR: + action = myCallback.error(ref, result.getFailureDescription()); + break; + } + + if (action == FilteringTaskCallback.Action.STOP) { + break; + } + } + + FilteringResult reason = myIsCancelled + ? FilteringResult.INTERRUPTED + : proceedCount == myReferences.size() + ? FilteringResult.ALL_CHECKED + : FilteringResult.LIMIT_REACHED; + + myCallback.completed(reason); + } +} diff --git a/java/debugger/impl/src/com/intellij/debugger/memory/filtering/FilteringTaskCallback.java b/java/debugger/impl/src/com/intellij/debugger/memory/filtering/FilteringTaskCallback.java new file mode 100644 index 000000000000..7bb26999406a --- /dev/null +++ b/java/debugger/impl/src/com/intellij/debugger/memory/filtering/FilteringTaskCallback.java @@ -0,0 +1,41 @@ +/* + * Copyright 2000-2016 JetBrains s.r.o. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.intellij.debugger.memory.filtering; + +import com.sun.jdi.ObjectReference; +import org.jetbrains.annotations.NotNull; + +/** + * @author Vitaliy.Bibaev + */ +public interface FilteringTaskCallback { + void started(int totalCount); + + @NotNull + Action matched(@NotNull ObjectReference ref); + + @NotNull + Action notMatched(@NotNull ObjectReference ref); + + @NotNull + Action error(@NotNull ObjectReference ref, @NotNull String description); + + void completed(@NotNull FilteringResult reason); + + enum Action { + STOP, CONTINUE + } +} diff --git a/java/debugger/impl/src/com/intellij/debugger/memory/sizeof/RetainedSizeFetcher.java b/java/debugger/impl/src/com/intellij/debugger/memory/sizeof/RetainedSizeFetcher.java deleted file mode 100644 index 78a9f66079ba..000000000000 --- a/java/debugger/impl/src/com/intellij/debugger/memory/sizeof/RetainedSizeFetcher.java +++ /dev/null @@ -1,109 +0,0 @@ -package org.jetbrains.debugger.memory.sizeof; - -import com.sun.jdi.*; - -import java.util.Collection; -import java.util.concurrent.TimeUnit; - -public class RetainedSizeFetcher extends SizeOfEvalStrategy { - private final static long REFERENCE_SIZE = 4; - private final static long CHAR_SIZE = 2; - private final static long INT_SIZE = 4; - private final static long FLOAT_SIZE = 4; - private final static long DOUBLE_SIZE = 8; - private final static long BYTE_SIZE = 1; - private final static long BOOLEAN_SIZE = 1; // ? - private final static long LONG_SIZE = 8; - private final static long SHORT_SIZE = 2; - - private final boolean myCheckReferringObjects; - - public RetainedSizeFetcher(boolean checkReferringObjects) { - myCheckReferringObjects = checkReferringObjects; - } - - private static long getPrimitiveTypeSize(PrimitiveValue value) { - if (value instanceof IntegerValue) { - return INT_SIZE; - } - if (value instanceof LongValue) { - return LONG_SIZE; - } - if (value instanceof DoubleValue) { - return DOUBLE_SIZE; - } - if (value instanceof FloatValue) { - return FLOAT_SIZE; - } - if (value instanceof CharValue) { - return CHAR_SIZE; - } - if (value instanceof ByteValue) { - return BYTE_SIZE; - } - if (value instanceof BooleanValue) { - return BOOLEAN_SIZE; - } - - return SHORT_SIZE; - } - - private long getValueSize(Value value, Collection visited) { - if (value == null) { - return 0; - } - - if (value instanceof PrimitiveValue) { - return getPrimitiveTypeSize((PrimitiveValue) value); - } else { - ObjectReference ref = (ObjectReference) value; - if (visited.contains(ref)) { - return 0; - } - - if (myCheckReferringObjects) { - long startTime = System.nanoTime(); - int needReferringObjectCount = visited.size() + 1; - Collection referringObjects = ref.referringObjects(needReferringObjectCount); - long duration = System.nanoTime() - startTime; - System.out.println("referringObjects(" + needReferringObjectCount + ") duration = " + duration + "ns "+ - "(" + TimeUnit.NANOSECONDS.toMillis(duration) + " ms)."); - if (referringObjects.stream().anyMatch(objectReference -> !visited.contains(objectReference))) { - return 0; - } - } - - return sizeOfImpl(ref, visited, false); - } - } - - @Override - protected long sizeOfImpl(ObjectReference ref, Collection visited, boolean isRoot) { - visited.add(ref); - long result = 0; - - if (ref instanceof ArrayReference) { - ArrayReference arrRef = (ArrayReference) ref; - int length = arrRef.length(); - for (int i = 0; i < length; i++) { - result += getValueSize(arrRef.getValue(i), visited); - } - } - - for (Field field : ref.referenceType().allFields()) { - Value val = ref.getValue(field); - if (field.isStatic()) { - continue; - } - - result += REFERENCE_SIZE; - if (val == null) { - continue; - } - - result += getValueSize(val, visited); - } - - return result; - } -} diff --git a/java/debugger/impl/src/com/intellij/debugger/memory/sizeof/SizeOfEvalStrategy.java b/java/debugger/impl/src/com/intellij/debugger/memory/sizeof/SizeOfEvalStrategy.java deleted file mode 100644 index 962909f3fbaf..000000000000 --- a/java/debugger/impl/src/com/intellij/debugger/memory/sizeof/SizeOfEvalStrategy.java +++ /dev/null @@ -1,21 +0,0 @@ -package org.jetbrains.debugger.memory.sizeof; - -import com.sun.jdi.ObjectReference; - -import java.util.Collection; -import java.util.HashSet; -import java.util.concurrent.TimeUnit; - -abstract class SizeOfEvalStrategy { - public long sizeOf(ObjectReference ref) { - long startTime = System.nanoTime(); - long result = sizeOfImpl(ref, new HashSet<>(), true); - long duration = System.nanoTime() - startTime; - System.out.printf("org.jetbrains.plugin.sizeof [%d] == %d. time = %d ns (%d ms).%n", - ref.uniqueID(), result, duration, TimeUnit.NANOSECONDS.toMillis(duration)); - - return result; - } - - protected abstract long sizeOfImpl(ObjectReference ref, Collection visited, boolean isRoot); -} diff --git a/java/debugger/impl/src/com/intellij/debugger/memory/toolwindow/MemoryViewToolWindowFactory.java b/java/debugger/impl/src/com/intellij/debugger/memory/toolwindow/MemoryViewToolWindowFactory.java index d844cf1bf9c3..d760f28bbe37 100644 --- a/java/debugger/impl/src/com/intellij/debugger/memory/toolwindow/MemoryViewToolWindowFactory.java +++ b/java/debugger/impl/src/com/intellij/debugger/memory/toolwindow/MemoryViewToolWindowFactory.java @@ -1,13 +1,30 @@ -package org.jetbrains.debugger.memory.toolwindow; +/* + * Copyright 2000-2016 JetBrains s.r.o. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.intellij.debugger.memory.toolwindow; +import com.intellij.debugger.memory.component.MemoryViewManager; +import com.intellij.debugger.memory.ui.ClassesFilteredView; import com.intellij.execution.Executor; import com.intellij.execution.ui.RunContentDescriptor; import com.intellij.execution.ui.RunContentManager; import com.intellij.execution.ui.RunContentWithExecutorListener; +import com.intellij.icons.AllIcons; import com.intellij.openapi.actionSystem.ActionGroup; import com.intellij.openapi.actionSystem.ActionManager; -import com.intellij.openapi.actionSystem.DefaultActionGroup; -import com.intellij.openapi.actionSystem.Separator; +import com.intellij.openapi.application.ApplicationManager; import com.intellij.openapi.diagnostic.Logger; import com.intellij.openapi.project.DumbAware; import com.intellij.openapi.project.Project; @@ -23,11 +40,8 @@ import com.intellij.xdebugger.XDebugProcess; import com.intellij.xdebugger.XDebugSession; import com.intellij.xdebugger.XDebuggerManager; import com.intellij.xdebugger.XDebuggerManagerListener; -import icons.MemoryViewIcons; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; -import org.jetbrains.debugger.memory.component.MemoryViewManager; -import org.jetbrains.debugger.memory.view.ClassesFilteredView; import javax.swing.*; import java.awt.*; @@ -50,7 +64,7 @@ public class MemoryViewToolWindowFactory implements ToolWindowFactory, DumbAware { myEmptyContent = new JBLabel("Run debugging to see loaded classes", SwingConstants.CENTER); myMemoryViewNotSupportedContent = new JBLabel("The Memory View not available for this session", - SwingConstants.CENTER); + SwingConstants.CENTER); } @Override @@ -60,34 +74,29 @@ public class MemoryViewToolWindowFactory implements ToolWindowFactory, DumbAware connection.subscribe(RunContentManager.TOPIC, new RunContentWithExecutorListener() { @Override public void contentSelected(@Nullable RunContentDescriptor descriptor, @NotNull Executor executor) { - SwingUtilities.invokeLater(() -> updateCurrentMemoryView(project, toolWindow)); + ApplicationManager.getApplication().invokeLater(() -> updateCurrentMemoryView(project, toolWindow)); } @Override public void contentRemoved(@Nullable RunContentDescriptor descriptor, @NotNull Executor executor) { - SwingUtilities.invokeLater(() -> updateCurrentMemoryView(project, toolWindow)); + ApplicationManager.getApplication().invokeLater(() -> updateCurrentMemoryView(project, toolWindow)); } }); - ((ToolWindowImpl) toolWindow).getToolWindowManager() - .addToolWindowManagerListener(new ToolWindowManagerAdapter() { - @Override - public void stateChanged() { - if (!myMemoryViews.isEmpty() && !toolWindow.isDisposed()) { - myMemoryViews.values().forEach(classesFilteredView -> - classesFilteredView.setActive(toolWindow.isVisible())); - } + ((ToolWindowImpl)toolWindow).getToolWindowManager() + .addToolWindowManagerListener(new ToolWindowManagerAdapter() { + @Override + public void stateChanged() { + if (!myMemoryViews.isEmpty() && !toolWindow.isDisposed()) { + myMemoryViews.values().forEach(classesFilteredView -> + classesFilteredView.setActive(toolWindow.isVisible())); } - }, project); + } + }, project); - ActionGroup group = new DefaultActionGroup( - ActionManager.getInstance().getAction("MemoryView.ShowOnlyWithInstances"), - ActionManager.getInstance().getAction("MemoryView.ShowOnlyWithDiff"), - ActionManager.getInstance().getAction("MemoryView.ShowOnlyTracked"), - Separator.getInstance(), - ActionManager.getInstance().getAction("MemoryView.EnableTrackingWithClosedWindow") - ); - ((ToolWindowEx) toolWindow).setAdditionalGearActions(group); + final ActionGroup group = (ActionGroup)ActionManager.getInstance().getAction("MemoryView.SettingsPopupActionGroup"); + + ((ToolWindowEx)toolWindow).setAdditionalGearActions(group); toolWindow.getComponent().setLayout(new BorderLayout()); for (XDebugSession session : XDebuggerManager.getInstance(project).getDebugSessions()) { @@ -105,7 +114,8 @@ public class MemoryViewToolWindowFactory implements ToolWindowFactory, DumbAware ClassesFilteredView newView = new ClassesFilteredView(session); newView.setActive(toolWindow != null && toolWindow.isVisible()); myMemoryViews.put(session, newView); - } catch (Throwable e) { + } + catch (Throwable e) { LOG.warn("Cannot create new instance of the memory view. " + e.getMessage()); } } @@ -126,7 +136,8 @@ public class MemoryViewToolWindowFactory implements ToolWindowFactory, DumbAware if (myMemoryViews.containsKey(session)) { view = myMemoryViews.get(session); replaceToolWindowContent(toolWindow, view); - } else { + } + else { view = null; replaceToolWindowContent(toolWindow, myMemoryViewNotSupportedContent); } @@ -143,7 +154,7 @@ public class MemoryViewToolWindowFactory implements ToolWindowFactory, DumbAware replaceToolWindowContent(toolWindow, myEmptyContent); } - private void replaceToolWindowContent(@NotNull ToolWindow toolWindow, JComponent comp) { + private static void replaceToolWindowContent(@NotNull ToolWindow toolWindow, JComponent comp) { LOG.assertTrue(SwingUtilities.isEventDispatchThread()); JComponent toolWindowComp = toolWindow.getComponent(); toolWindowComp.removeAll(); @@ -152,7 +163,7 @@ public class MemoryViewToolWindowFactory implements ToolWindowFactory, DumbAware } @Nullable - private ToolWindow getToolWindow(@NotNull Project project) { + private static ToolWindow getToolWindow(@NotNull Project project) { return MemoryViewManager.getInstance().getToolWindow(project); } @@ -170,15 +181,15 @@ public class MemoryViewToolWindowFactory implements ToolWindowFactory, DumbAware public void processStopped(@NotNull XDebugProcess debugProcess) { Project project = debugProcess.getSession().getProject(); boolean enabled = Arrays.stream(XDebuggerManager.getInstance(project) - .getDebugSessions()).anyMatch(session -> !session.getDebugProcess().equals(debugProcess)); + .getDebugSessions()).anyMatch(session -> !session.getDebugProcess().equals(debugProcess)); updateIcon(project, enabled); } private void updateIcon(@NotNull Project project, boolean enabled) { ToolWindow toolWindow = MemoryViewManager.getInstance().getToolWindow(project); if (toolWindow != null) { - SwingUtilities.invokeLater(() -> - toolWindow.setIcon(enabled ? MemoryViewIcons.MAIN_ENABLED : MemoryViewIcons.MAIN_DISABLED)); + Icon icon = enabled ? AllIcons.Debugger.MemoryView.ToolWindowEnabled : AllIcons.Debugger.MemoryView.ToolWindowDisabled; + ApplicationManager.getApplication().invokeLater(() -> toolWindow.setIcon(icon)); } } }); @@ -189,7 +200,7 @@ public class MemoryViewToolWindowFactory implements ToolWindowFactory, DumbAware private final class MyDebuggerStatusChangedListener implements XDebuggerManagerListener { @Override public void processStarted(@NotNull XDebugProcess xDebugProcess) { - SwingUtilities.invokeLater(() -> { + ApplicationManager.getApplication().invokeLater(() -> { XDebugSession session = xDebugProcess.getSession(); addNewSession(session); updateView(session); @@ -200,7 +211,7 @@ public class MemoryViewToolWindowFactory implements ToolWindowFactory, DumbAware public void processStopped(@NotNull XDebugProcess xDebugProcess) { XDebugSession session = xDebugProcess.getSession(); removeSession(session); - SwingUtilities.invokeLater(() -> updateView(session)); + ApplicationManager.getApplication().invokeLater(() -> updateView(session)); } private void updateView(@NotNull XDebugSession debugSession) { diff --git a/java/debugger/impl/src/com/intellij/debugger/memory/tracking/BackgroundTracker.java b/java/debugger/impl/src/com/intellij/debugger/memory/tracking/BackgroundTracker.java index 51ce3955b786..18bf3869b265 100644 --- a/java/debugger/impl/src/com/intellij/debugger/memory/tracking/BackgroundTracker.java +++ b/java/debugger/impl/src/com/intellij/debugger/memory/tracking/BackgroundTracker.java @@ -1,4 +1,19 @@ -package org.jetbrains.debugger.memory.tracking; +/* + * Copyright 2000-2016 JetBrains s.r.o. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.intellij.debugger.memory.tracking; interface BackgroundTracker { void setBackgroundMode(boolean isBackgroundMode); diff --git a/java/debugger/impl/src/com/intellij/debugger/memory/tracking/ClassPreparedListener.java b/java/debugger/impl/src/com/intellij/debugger/memory/tracking/ClassPreparedListener.java index f102d4905e30..411e86e51d95 100644 --- a/java/debugger/impl/src/com/intellij/debugger/memory/tracking/ClassPreparedListener.java +++ b/java/debugger/impl/src/com/intellij/debugger/memory/tracking/ClassPreparedListener.java @@ -1,4 +1,19 @@ -package org.jetbrains.debugger.memory.tracking; +/* + * Copyright 2000-2016 JetBrains s.r.o. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.intellij.debugger.memory.tracking; import com.intellij.debugger.DebuggerManager; import com.intellij.debugger.engine.DebugProcess; diff --git a/java/debugger/impl/src/com/intellij/debugger/memory/tracking/ConstructorInstancesTracker.java b/java/debugger/impl/src/com/intellij/debugger/memory/tracking/ConstructorInstancesTracker.java index 44ebf43d10b8..6e7de547eb4d 100644 --- a/java/debugger/impl/src/com/intellij/debugger/memory/tracking/ConstructorInstancesTracker.java +++ b/java/debugger/impl/src/com/intellij/debugger/memory/tracking/ConstructorInstancesTracker.java @@ -1,4 +1,19 @@ -package org.jetbrains.debugger.memory.tracking; +/* + * Copyright 2000-2016 JetBrains s.r.o. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.intellij.debugger.memory.tracking; import com.intellij.debugger.DebuggerManager; import com.intellij.debugger.engine.DebugProcessImpl; @@ -8,11 +23,15 @@ import com.intellij.debugger.engine.events.DebuggerCommandImpl; import com.intellij.debugger.engine.events.SuspendContextCommandImpl; import com.intellij.debugger.jdi.StackFrameProxyImpl; import com.intellij.debugger.jdi.ThreadReferenceProxyImpl; +import com.intellij.debugger.memory.component.CreationPositionTracker; +import com.intellij.debugger.memory.utils.StackFrameDescriptor; import com.intellij.debugger.ui.breakpoints.JavaLineBreakpointType; import com.intellij.debugger.ui.breakpoints.LineBreakpoint; import com.intellij.openapi.Disposable; import com.intellij.openapi.project.Project; +import com.intellij.openapi.util.Disposer; import com.intellij.xdebugger.XDebugSession; +import com.intellij.xdebugger.XDebuggerManager; import com.intellij.xdebugger.breakpoints.XBreakpoint; import com.intellij.xdebugger.impl.XDebuggerManagerImpl; import com.intellij.xdebugger.impl.breakpoints.LineBreakpointState; @@ -26,10 +45,8 @@ import com.sun.jdi.request.BreakpointRequest; import com.sun.jdi.request.EventRequest; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; -import org.jetbrains.debugger.memory.component.CreationPositionTracker; -import org.jetbrains.debugger.memory.component.InstancesTracker; -import org.jetbrains.debugger.memory.event.InstancesTrackerListener; -import org.jetbrains.debugger.memory.utils.StackFrameDescriptor; +import com.intellij.debugger.memory.component.InstancesTracker; +import com.intellij.debugger.memory.event.InstancesTrackerListener; import org.jetbrains.java.debugger.breakpoints.properties.JavaLineBreakpointProperties; import java.util.*; @@ -59,7 +76,7 @@ public class ConstructorInstancesTracker implements TrackerForNewInstances, Disp myPositionTracker = CreationPositionTracker.getInstance(debugSession.getProject()); Project project = debugSession.getProject(); myIsBackgroundTrackingEnabled = InstancesTracker.getInstance(myDebugSession.getProject()) - .isBackgroundTrackingEnabled(); + .isBackgroundTrackingEnabled(); InstancesTracker.getInstance(myDebugSession.getProject()).addTrackerListener(new InstancesTrackerListener() { @Override @@ -71,7 +88,8 @@ public class ConstructorInstancesTracker implements TrackerForNewInstances, Disp protected void action() throws Exception { if (newState) { myBreakpoint.enable(); - } else { + } + else { myBreakpoint.disable(); } } @@ -80,14 +98,14 @@ public class ConstructorInstancesTracker implements TrackerForNewInstances, Disp } }, this); - myDebugProcess = (DebugProcessImpl) DebuggerManager.getInstance(project) - .getDebugProcess(debugSession.getDebugProcess().getProcessHandler()); + myDebugProcess = (DebugProcessImpl)DebuggerManager.getInstance(project) + .getDebugProcess(debugSession.getDebugProcess().getProcessHandler()); JavaLineBreakpointType breakPointType = new JavaLineBreakpointType(); XBreakpoint bpn = new XLineBreakpointImpl<>(breakPointType, - ((XDebuggerManagerImpl) XDebuggerManagerImpl.getInstance(project)).getBreakpointManager(), - new JavaLineBreakpointProperties(), - new LineBreakpointState<>()); + ((XDebuggerManagerImpl)XDebuggerManager.getInstance(project)).getBreakpointManager(), + new JavaLineBreakpointProperties(), + new LineBreakpointState<>()); myBreakpoint = new MyConstructorBreakpoints(project, bpn); myBreakpoint.createRequestForPreparedClass(myDebugProcess, myReference); @@ -136,7 +154,7 @@ public class ConstructorInstancesTracker implements TrackerForNewInstances, Disp @Override public void dispose() { - myBreakpoint.dispose(); + Disposer.dispose(myBreakpoint); } @Override @@ -148,7 +166,8 @@ public class ConstructorInstancesTracker implements TrackerForNewInstances, Disp myIsBackgroundMode = isBackgroundMode; if (isBackgroundMode) { doEnableBackgroundMode(); - } else { + } + else { doDisableBackgroundMode(); } } @@ -164,7 +183,7 @@ public class ConstructorInstancesTracker implements TrackerForNewInstances, Disp } private final class MyConstructorBreakpoints extends LineBreakpoint - implements Disposable { + implements Disposable { private boolean myIsEnabled = false; private final List myRequests = new ArrayList<>(); @@ -205,7 +224,7 @@ public class ConstructorInstancesTracker implements TrackerForNewInstances, Disp @Override public boolean processLocatableEvent(SuspendContextCommandImpl action, LocatableEvent event) - throws EventProcessingException { + throws EventProcessingException { try { SuspendContextImpl suspendContext = action.getSuspendContext(); if (suspendContext != null) { @@ -223,7 +242,8 @@ public class ConstructorInstancesTracker implements TrackerForNewInstances, Disp String typeName = loc.declaringType().name(); String methodName = loc.method().name(); return new StackFrameDescriptor(typeName, methodName, loc.lineNumber()); - } catch (EvaluateException e) { + } + catch (EvaluateException e) { return null; } }).filter(Objects::nonNull).collect(Collectors.toList()); @@ -231,7 +251,8 @@ public class ConstructorInstancesTracker implements TrackerForNewInstances, Disp } } } - } catch (EvaluateException e) { + } + catch (EvaluateException e) { return false; } diff --git a/java/debugger/impl/src/com/intellij/debugger/memory/tracking/HashBasedTracking.java b/java/debugger/impl/src/com/intellij/debugger/memory/tracking/HashBasedTracking.java deleted file mode 100644 index 110137d11191..000000000000 --- a/java/debugger/impl/src/com/intellij/debugger/memory/tracking/HashBasedTracking.java +++ /dev/null @@ -1,169 +0,0 @@ -package org.jetbrains.debugger.memory.tracking; - -import com.intellij.debugger.engine.SuspendContextImpl; -import com.intellij.debugger.engine.jdi.ThreadReferenceProxy; -import com.intellij.openapi.diagnostic.Logger; -import com.sun.jdi.*; -import org.jetbrains.annotations.NotNull; -import org.jetbrains.annotations.Nullable; - -import java.util.*; -import java.util.concurrent.TimeUnit; -import java.util.stream.Collectors; - -class HashBasedTracking extends InstanceTrackingStrategy { - private static final Logger LOG = Logger.getInstance(HashBasedTracking.class); - - private final ReferenceType myClassType; - - private MyHashCodeMethodWrapper myHashCodeWrapper; - private Set myHashesSet; - private MyState myState = MyState.WAIT_HASHES; - private int myCount = 0; - HashBasedTracking(@Nullable SuspendContextImpl suspendContext, - @NotNull ReferenceType classType, - @NotNull List initialInstances) { - myClassType = classType; - if (suspendContext != null) { - myHashesSet = toSetOfHashes(evalHashCodes(suspendContext, initialInstances)); - myState = myState.next(); - } - } - - @NotNull - @Override - protected List updateImpl(@NotNull SuspendContextImpl suspendContext, - @NotNull List references) { - Map> ref2hash = evalHashCodes(suspendContext, references); - List newInstances = new ArrayList<>(); - - for (ObjectReference ref : references) { - Optional hash = ref2hash.get(ref); - if (hash.isPresent() && !myState.equals(MyState.WAIT_HASHES) && !myHashesSet.contains(hash.get())) { - newInstances.add(ref); - } - } - - myHashesSet = toSetOfHashes(ref2hash); - myState = myState.next(); - myCount = newInstances.size(); - return newInstances; - } - - @Override - public int getCount() { - return myCount; - } - - @Override - public boolean isReady() { - return myState == MyState.READY; - } - - @NotNull - private Map> evalHashCodes(@NotNull SuspendContextImpl suspendContext, - @NotNull List references) { - MyHashCodeMethodWrapper hashCodeCaller = getHashCodeMethod(myClassType); - Map> result = new HashMap<>(); - ThreadReferenceProxy threadProxy = suspendContext.getThread(); - ThreadReference thread = threadProxy != null ? threadProxy.getThreadReference() : null; - if (thread == null || hashCodeCaller == null) { - LOG.warn(thread == null ? "Thread is null" : "hashCode not found"); - return result; - } - - long start = System.nanoTime(); - for (ObjectReference ref : references) { - Optional hash = Optional.empty(); - try { - if (!ref.isCollected()) { - ref.disableCollection(); - if (!ref.isCollected()) { - hash = hashCodeCaller.eval(ref, suspendContext, thread); - } - ref.enableCollection(); - } - } catch (InvalidTypeException | ClassNotLoadedException | - IncompatibleThreadStateException | InvocationException e) { - LOG.warn("Hash code evaluation failed", e); - } - - result.put(ref, hash); - } - - long duration = TimeUnit.NANOSECONDS.toMillis(System.nanoTime() - start); - LOG.info(String.format("Hash code evaluation: %d ms. Count = %d", duration, references.size())); - - return result; - } - - private Set toSetOfHashes(@NotNull Map> ref2hashCode) { - return ref2hashCode.values().stream() - .filter(Optional::isPresent) - .map(Optional::get) - .collect(Collectors.toCollection(HashSet::new)); - } - - @Nullable - private MyHashCodeMethodWrapper getHashCodeMethod(@NotNull ReferenceType referenceType) { - if (myHashCodeWrapper != null) { - return myHashCodeWrapper; - } - - final List referenceTypes = referenceType.virtualMachine().classesByName("java.util.Objects"); - if (!referenceTypes.isEmpty()) { - ClassType objectsClass = (ClassType) referenceTypes.get(0); - Method hashCodeFromObjects = objectsClass.methodsByName("hashCode").stream() - .filter(method -> method.argumentTypeNames().size() == 1 - && "java.lang.Object".equals(method.argumentTypeNames().get(0))) - .findFirst() - .orElse(null); - - myHashCodeWrapper = (ref, suspendContext, thread) -> { - final Value value = objectsClass.invokeMethod(thread, hashCodeFromObjects, - Collections.singletonList(ref), ClassType.INVOKE_SINGLE_THREADED); - return Optional.of(((IntegerValue) value).value()); - }; - } else { - final Method hashCode = referenceType.methodsByName("hashCode").stream() - .filter(method -> method.argumentTypeNames().isEmpty()) - .findFirst() - .orElse(null); - - myHashCodeWrapper = hashCode == null ? null : (ref, suspendContext, thread) -> { - final Value value = ref.invokeMethod(thread, hashCode, Collections.emptyList(), ClassType.INVOKE_SINGLE_THREADED); - return Optional.of(((IntegerValue) value).value()); - }; - } - - return myHashCodeWrapper; - } - - // WAIT_HASHES -> WAIT_UPDATE -> READY - enum MyState { - WAIT_HASHES { - @Override - public MyState next() { - return WAIT_UPDATE; - } - }, WAIT_UPDATE { - @Override - public MyState next() { - return READY; - } - }, READY { - @Override - public MyState next() { - return this; - } - }; - - public abstract MyState next(); - } - - private interface MyHashCodeMethodWrapper { - Optional eval(@NotNull ObjectReference ref, @NotNull SuspendContextImpl suspendContext, - @NotNull ThreadReference thread) throws InvocationException, InvalidTypeException, - ClassNotLoadedException, IncompatibleThreadStateException; - } -} diff --git a/java/debugger/impl/src/com/intellij/debugger/memory/tracking/IdentityBasedTracking.java b/java/debugger/impl/src/com/intellij/debugger/memory/tracking/IdentityBasedTracking.java deleted file mode 100644 index 32e5170103d5..000000000000 --- a/java/debugger/impl/src/com/intellij/debugger/memory/tracking/IdentityBasedTracking.java +++ /dev/null @@ -1,37 +0,0 @@ -package org.jetbrains.debugger.memory.tracking; - -import com.intellij.debugger.engine.SuspendContextImpl; -import com.sun.jdi.ObjectReference; -import org.jetbrains.annotations.NotNull; - -import java.util.List; -import java.util.stream.Collectors; - -class IdentityBasedTracking extends InstanceTrackingStrategy { - private long myLastId = -1; - private int myCount = 0; - IdentityBasedTracking(@NotNull List initialInstances) { - myLastId = getMaxId(initialInstances); - } - - @NotNull - @Override - protected List updateImpl(@NotNull SuspendContextImpl suspendContext, - @NotNull List references) { - List result = references.stream() - .filter(reference -> reference.uniqueID() > myLastId) - .collect(Collectors.toList()); - myLastId = Math.max(myLastId, getMaxId(result)); - myCount = result.size(); - return result; - } - - private long getMaxId(@NotNull List references) { - return references.stream().map(ObjectReference::uniqueID).max(Long::compare).orElse(-1L); - } - - @Override - public int getCount() { - return myCount; - } -} diff --git a/java/debugger/impl/src/com/intellij/debugger/memory/tracking/InstanceTrackingStrategy.java b/java/debugger/impl/src/com/intellij/debugger/memory/tracking/InstanceTrackingStrategy.java deleted file mode 100644 index 9385105e4380..000000000000 --- a/java/debugger/impl/src/com/intellij/debugger/memory/tracking/InstanceTrackingStrategy.java +++ /dev/null @@ -1,52 +0,0 @@ -package org.jetbrains.debugger.memory.tracking; - -import com.intellij.debugger.engine.SuspendContextImpl; -import com.sun.jdi.ObjectReference; -import com.sun.jdi.ReferenceType; -import org.jetbrains.annotations.NotNull; -import org.jetbrains.annotations.Nullable; - -import java.util.ArrayList; -import java.util.Collections; -import java.util.List; - -public abstract class InstanceTrackingStrategy implements TrackerForNewInstances { - @NotNull - private List myNewInstances = new ArrayList<>(); - private boolean myIsReady = false; - - public static InstanceTrackingStrategy create(@NotNull ReferenceType referenceType, - @Nullable SuspendContextImpl suspendContext, - @NotNull TrackingType type, - @NotNull List init) { - switch (type) { - case IDENTITY: - return new IdentityBasedTracking(init); - case HASH: - return new HashBasedTracking(suspendContext, referenceType, init); - case RETAIN: - return new RetainReferencesTracking(init); - } - - throw new UnsupportedOperationException("Such TrackingType not found"); - } - - @NotNull - public final List getNewInstances() { - return Collections.unmodifiableList(myNewInstances); - } - - public final void update(@NotNull SuspendContextImpl suspendContext, @NotNull List references) { - myNewInstances = updateImpl(suspendContext, references); - myIsReady = true; - } - - @Override - public boolean isReady() { - return myIsReady; - } - - @NotNull - protected abstract List updateImpl(@NotNull SuspendContextImpl suspendContext, - @NotNull List references); -} diff --git a/java/debugger/impl/src/com/intellij/debugger/memory/tracking/RetainReferencesTracking.java b/java/debugger/impl/src/com/intellij/debugger/memory/tracking/RetainReferencesTracking.java deleted file mode 100644 index c35070e95d7d..000000000000 --- a/java/debugger/impl/src/com/intellij/debugger/memory/tracking/RetainReferencesTracking.java +++ /dev/null @@ -1,30 +0,0 @@ -package org.jetbrains.debugger.memory.tracking; - -import com.intellij.debugger.engine.SuspendContextImpl; -import com.sun.jdi.ObjectReference; -import org.jetbrains.annotations.NotNull; - -import java.util.List; - -class RetainReferencesTracking extends InstanceTrackingStrategy { - private final IdentityBasedTracking myIdTracker; - private List myHardRefs; - RetainReferencesTracking(@NotNull List initialInstances) { - myHardRefs = initialInstances; - myIdTracker = new IdentityBasedTracking(initialInstances); - } - - @NotNull - @Override - protected List updateImpl(@NotNull SuspendContextImpl suspendContext, - @NotNull List references) { - myIdTracker.update(suspendContext, references); - myHardRefs = references; - return myIdTracker.getNewInstances(); - } - - @Override - public int getCount() { - return myHardRefs.size(); - } -} diff --git a/java/debugger/impl/src/com/intellij/debugger/memory/tracking/TrackerForNewInstances.java b/java/debugger/impl/src/com/intellij/debugger/memory/tracking/TrackerForNewInstances.java index b6fc329f5d36..9d343c63dd80 100644 --- a/java/debugger/impl/src/com/intellij/debugger/memory/tracking/TrackerForNewInstances.java +++ b/java/debugger/impl/src/com/intellij/debugger/memory/tracking/TrackerForNewInstances.java @@ -1,4 +1,19 @@ -package org.jetbrains.debugger.memory.tracking; +/* + * Copyright 2000-2016 JetBrains s.r.o. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.intellij.debugger.memory.tracking; import com.sun.jdi.ObjectReference; import org.jetbrains.annotations.NotNull; diff --git a/java/debugger/impl/src/com/intellij/debugger/memory/tracking/TrackingType.java b/java/debugger/impl/src/com/intellij/debugger/memory/tracking/TrackingType.java index a000daaade40..6c645db57043 100644 --- a/java/debugger/impl/src/com/intellij/debugger/memory/tracking/TrackingType.java +++ b/java/debugger/impl/src/com/intellij/debugger/memory/tracking/TrackingType.java @@ -1,32 +1,33 @@ -package org.jetbrains.debugger.memory.tracking; +/* + * Copyright 2000-2016 JetBrains s.r.o. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.intellij.debugger.memory.tracking; -import com.intellij.ui.JBColor; import org.jetbrains.annotations.NotNull; public enum TrackingType { - - IDENTITY("Id Based Tracking",JBColor.yellow), - HASH("Hash Based Tracking", JBColor.yellow), - RETAIN("Retaining Reference Object Tracking", JBColor.yellow), - CREATION("Track Constructors", JBColor.orange); + CREATION("Track Constructors"); private final String myDescription; - private final JBColor myHighlighting; - TrackingType(@NotNull String description, @NotNull JBColor highlighting) { + TrackingType(@SuppressWarnings("SameParameterValue") @NotNull String description) { myDescription = description; - myHighlighting = highlighting; - } - - @NotNull - public JBColor color() { - return myHighlighting; } @NotNull public String description() { return myDescription; } - - public static JBColor inactiveColor() { return JBColor.GRAY; } } diff --git a/java/debugger/impl/src/com/intellij/debugger/memory/view/ClassesFilteredView.java b/java/debugger/impl/src/com/intellij/debugger/memory/ui/ClassesFilteredView.java similarity index 85% rename from java/debugger/impl/src/com/intellij/debugger/memory/view/ClassesFilteredView.java rename to java/debugger/impl/src/com/intellij/debugger/memory/ui/ClassesFilteredView.java index 92b5268239fb..f25d6b714405 100644 --- a/java/debugger/impl/src/com/intellij/debugger/memory/view/ClassesFilteredView.java +++ b/java/debugger/impl/src/com/intellij/debugger/memory/ui/ClassesFilteredView.java @@ -1,14 +1,34 @@ -package org.jetbrains.debugger.memory.view; +/* + * Copyright 2000-2016 JetBrains s.r.o. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.intellij.debugger.memory.ui; import com.intellij.debugger.DebuggerManager; +import com.intellij.debugger.DebuggerManagerEx; import com.intellij.debugger.engine.DebugProcessImpl; import com.intellij.debugger.engine.SuspendContextImpl; import com.intellij.debugger.engine.events.DebuggerCommandImpl; -import com.intellij.debugger.impl.DebuggerManagerImpl; +import com.intellij.debugger.memory.event.MemoryViewManagerListener; +import com.intellij.debugger.memory.utils.KeyboardUtils; +import com.intellij.debugger.memory.utils.LowestPriorityCommand; +import com.intellij.debugger.memory.utils.SingleAlarmWithMutableDelay; import com.intellij.openapi.Disposable; import com.intellij.openapi.actionSystem.ActionGroup; import com.intellij.openapi.actionSystem.ActionManager; import com.intellij.openapi.actionSystem.ActionPopupMenu; +import com.intellij.openapi.application.ApplicationManager; import com.intellij.openapi.diagnostic.Logger; import com.intellij.openapi.project.Project; import com.intellij.openapi.util.Disposer; @@ -22,20 +42,16 @@ import com.sun.jdi.ReferenceType; import com.sun.jdi.VirtualMachine; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; -import org.jetbrains.debugger.memory.component.CreationPositionTracker; -import org.jetbrains.debugger.memory.component.InstancesTracker; -import org.jetbrains.debugger.memory.component.MemoryViewManager; -import org.jetbrains.debugger.memory.component.MemoryViewManagerState; -import org.jetbrains.debugger.memory.event.InstancesTrackerListener; -import org.jetbrains.debugger.memory.event.MemoryViewManagerListener; -import org.jetbrains.debugger.memory.tracking.ClassPreparedListener; -import org.jetbrains.debugger.memory.tracking.ConstructorInstancesTracker; -import org.jetbrains.debugger.memory.tracking.TrackerForNewInstances; -import org.jetbrains.debugger.memory.tracking.TrackingType; -import org.jetbrains.debugger.memory.utils.AndroidUtil; -import org.jetbrains.debugger.memory.utils.KeyboardUtils; -import org.jetbrains.debugger.memory.utils.LowestPriorityCommand; -import org.jetbrains.debugger.memory.utils.SingleAlarmWithMutableDelay; +import com.intellij.debugger.memory.component.CreationPositionTracker; +import com.intellij.debugger.memory.component.InstancesTracker; +import com.intellij.debugger.memory.component.MemoryViewManager; +import com.intellij.debugger.memory.component.MemoryViewManagerState; +import com.intellij.debugger.memory.event.InstancesTrackerListener; +import com.intellij.debugger.memory.tracking.ClassPreparedListener; +import com.intellij.debugger.memory.tracking.ConstructorInstancesTracker; +import com.intellij.debugger.memory.tracking.TrackerForNewInstances; +import com.intellij.debugger.memory.tracking.TrackingType; +import com.intellij.debugger.memory.utils.AndroidUtil; import javax.swing.FocusManager; import javax.swing.*; @@ -49,8 +65,8 @@ import java.util.concurrent.ConcurrentHashMap; import java.util.concurrent.TimeUnit; import java.util.stream.IntStream; -import static org.jetbrains.debugger.memory.view.ClassesTable.DiffViewTableModel.CLASSNAME_COLUMN_INDEX; -import static org.jetbrains.debugger.memory.view.ClassesTable.DiffViewTableModel.DIFF_COLUMN_INDEX; +import static com.intellij.debugger.memory.ui.ClassesTable.DiffViewTableModel.CLASSNAME_COLUMN_INDEX; +import static com.intellij.debugger.memory.ui.ClassesTable.DiffViewTableModel.DIFF_COLUMN_INDEX; public class ClassesFilteredView extends BorderLayoutPanel implements Disposable { private static final Logger LOG = Logger.getInstance(ClassesFilteredView.class); @@ -221,7 +237,7 @@ public class ClassesFilteredView extends BorderLayoutPanel implements Disposable @Override public void sessionResumed() { myConstructorTrackedClasses.values().forEach(ConstructorInstancesTracker::obsolete); - SwingUtilities.invokeLater(() -> { + ApplicationManager.getApplication().invokeLater(() -> { myTable.getEmptyText().setText(EMPTY_TABLE_CONTENT_WHEN_RUNNING); myTable.hideContent(); }); @@ -235,7 +251,7 @@ public class ClassesFilteredView extends BorderLayoutPanel implements Disposable @Override public void sessionPaused() { - SwingUtilities.invokeLater(() -> myTable.getEmptyText().setText(EMPTY_TABLE_CONTENT_WHEN_SUSPENDED)); + ApplicationManager.getApplication().invokeLater(() -> myTable.getEmptyText().setText(EMPTY_TABLE_CONTENT_WHEN_SUSPENDED)); updateClassesAndCounts(); } }; @@ -243,7 +259,7 @@ public class ClassesFilteredView extends BorderLayoutPanel implements Disposable mySingleAlarm = new SingleAlarmWithMutableDelay(() -> { myLastSuspendContext = getSuspendContext(); if (myLastSuspendContext != null) { - SwingUtilities.invokeLater(() -> myTable.setBusy(true)); + ApplicationManager.getApplication().invokeLater(() -> myTable.setBusy(true)); myDebugProcess.getManagerThread() .schedule(new MyUpdateClassesCommand(myLastSuspendContext)); } @@ -287,7 +303,7 @@ public class ClassesFilteredView extends BorderLayoutPanel implements Disposable tracker.disable(); } - Disposer.register(ClassesFilteredView.this, tracker); + Disposer.register(this, tracker); myConstructorTrackedClasses.put(ref, tracker); } } @@ -303,7 +319,7 @@ public class ClassesFilteredView extends BorderLayoutPanel implements Disposable } private SuspendContextImpl getSuspendContext() { - return DebuggerManagerImpl.getInstanceEx(myProject).getContext().getSuspendContext(); + return DebuggerManagerEx.getInstanceEx(myProject).getContext().getSuspendContext(); } private void updateClassesAndCounts() { @@ -312,9 +328,9 @@ public class ClassesFilteredView extends BorderLayoutPanel implements Disposable } } - private ActionPopupMenu createContextMenu() { - ActionGroup group = (ActionGroup) ActionManager.getInstance().getAction("ClassesView.PopupActionGroup"); - return ActionManager.getInstance().createActionPopupMenu("ClassesView.PopupActionGroup", group); + private static ActionPopupMenu createContextMenu() { + ActionGroup group = (ActionGroup) ActionManager.getInstance().getAction("MemoryView.ClassesPopupActionGroup"); + return ActionManager.getInstance().createActionPopupMenu("MemoryView.ClassesPopupActionGroup", group); } @Override @@ -340,7 +356,7 @@ public class ClassesFilteredView extends BorderLayoutPanel implements Disposable } private void doActivate() { - myDebugSession.addSessionListener(myDebugSessionListener, ClassesFilteredView.this); + myDebugSession.addSessionListener(myDebugSessionListener, this); myConstructorTrackedClasses.values().forEach(x -> x.setBackgroundMode(false)); final SuspendContextImpl lastContext = myLastSuspendContext; if (lastContext == null || !lastContext.equals(getSuspendContext())) { @@ -400,10 +416,10 @@ public class ClassesFilteredView extends BorderLayoutPanel implements Disposable final long[] counts = chunks.size() == 1 ? chunks.get(0) : IntStream.range(0, chunks.size()).boxed() .flatMapToLong(integer -> Arrays.stream(chunks.get(integer))) .toArray(); - SwingUtilities.invokeLater(() -> myTable.setClassesAndUpdateCounts(classes, counts)); + ApplicationManager.getApplication().invokeLater(() -> myTable.setClassesAndUpdateCounts(classes, counts)); } - SwingUtilities.invokeLater(() -> myTable.setBusy(false)); + ApplicationManager.getApplication().invokeLater(() -> myTable.setBusy(false)); } private boolean isContextValid() { @@ -411,7 +427,7 @@ public class ClassesFilteredView extends BorderLayoutPanel implements Disposable } } - private class FilterTextField extends SearchTextField { + private static class FilterTextField extends SearchTextField { FilterTextField() { super(false); } diff --git a/java/debugger/impl/src/com/intellij/debugger/memory/view/ClassesTable.java b/java/debugger/impl/src/com/intellij/debugger/memory/ui/ClassesTable.java similarity index 75% rename from java/debugger/impl/src/com/intellij/debugger/memory/view/ClassesTable.java rename to java/debugger/impl/src/com/intellij/debugger/memory/ui/ClassesTable.java index cce435132c13..6c4bcb9cbb15 100644 --- a/java/debugger/impl/src/com/intellij/debugger/memory/view/ClassesTable.java +++ b/java/debugger/impl/src/com/intellij/debugger/memory/ui/ClassesTable.java @@ -1,5 +1,24 @@ -package org.jetbrains.debugger.memory.view; +/* + * Copyright 2000-2016 JetBrains s.r.o. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.intellij.debugger.memory.ui; +import com.intellij.debugger.memory.component.InstancesTracker; +import com.intellij.debugger.memory.utils.AbstractTableColumnDescriptor; +import com.intellij.debugger.memory.utils.AbstractTableModelWithColumns; +import com.intellij.icons.AllIcons; import com.intellij.openapi.Disposable; import com.intellij.openapi.actionSystem.DataKey; import com.intellij.openapi.actionSystem.DataProvider; @@ -17,16 +36,12 @@ import com.intellij.util.ui.JBUI; import com.intellij.xdebugger.XDebugSession; import com.sun.jdi.ObjectReference; import com.sun.jdi.ReferenceType; -import icons.MemoryViewIcons; import org.jetbrains.annotations.NonNls; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; -import org.jetbrains.debugger.memory.component.InstancesTracker; -import org.jetbrains.debugger.memory.tracking.TrackerForNewInstances; -import org.jetbrains.debugger.memory.tracking.TrackingType; -import org.jetbrains.debugger.memory.utils.AbstractTableColumnDescriptor; -import org.jetbrains.debugger.memory.utils.AbstractTableModelWithColumns; -import org.jetbrains.debugger.memory.utils.InstancesProvider; +import com.intellij.debugger.memory.tracking.TrackerForNewInstances; +import com.intellij.debugger.memory.tracking.TrackingType; +import com.intellij.debugger.memory.utils.InstancesProvider; import javax.swing.*; import javax.swing.border.Border; @@ -39,9 +54,9 @@ public class ClassesTable extends JBTable implements DataProvider, Disposable { public static final DataKey SELECTED_CLASS_KEY = DataKey.create("ClassesTable.SelectedClass"); public static final DataKey DEBUG_SESSION_KEY = DataKey.create("ClassesTable.DebugSession"); public static final DataKey NEW_INSTANCES_PROVIDER_KEY = - DataKey.create("ClassesTable.NewInstances"); + DataKey.create("ClassesTable.NewInstances"); public static final DataKey REF_COUNT_PROVIDER_KEY = - DataKey.create("ClassesTable.ReferenceCountProvider"); + DataKey.create("ClassesTable.ReferenceCountProvider"); private static final Border EMPTY_BORDER = BorderFactory.createEmptyBorder(); @@ -63,7 +78,7 @@ public class ClassesTable extends JBTable implements DataProvider, Disposable { private MinusculeMatcher myMatcher = NameUtil.buildMatcher("*").build(); private String myFilteringPattern = ""; - private volatile List myElems = Collections.unmodifiableList(new ArrayList<>()); + private volatile List myItems = Collections.unmodifiableList(new ArrayList<>()); ClassesTable(@NotNull XDebugSession session, boolean onlyWithDiff, boolean onlyWithInstances, boolean onlyTracked, @NotNull ClassesFilteredView parent) { @@ -99,20 +114,20 @@ public class ClassesTable extends JBTable implements DataProvider, Disposable { @Override public boolean include(Entry entry) { int ix = entry.getIdentifier(); - ReferenceType ref = myElems.get(ix); + ReferenceType ref = myItems.get(ix); DiffValue diff = myCounts.getOrDefault(ref, myUnknownValue); boolean isFilteringOptionsRefused = myOnlyWithDiff && diff.diff() == 0 - || myOnlyWithInstances && !diff.hasInstance() - || myOnlyTracked && myParent.getStrategy(ref) == null; + || myOnlyWithInstances && !diff.hasInstance() + || myOnlyTracked && myParent.getStrategy(ref) == null; return !(isFilteringOptionsRefused) && myMatcher.matches(ref.name()); } }); List myDefaultSortingKeys = Arrays.asList( - new RowSorter.SortKey(DiffViewTableModel.DIFF_COLUMN_INDEX, SortOrder.DESCENDING), - new RowSorter.SortKey(DiffViewTableModel.COUNT_COLUMN_INDEX, SortOrder.DESCENDING), - new RowSorter.SortKey(DiffViewTableModel.CLASSNAME_COLUMN_INDEX, SortOrder.ASCENDING) + new RowSorter.SortKey(DiffViewTableModel.DIFF_COLUMN_INDEX, SortOrder.DESCENDING), + new RowSorter.SortKey(DiffViewTableModel.COUNT_COLUMN_INDEX, SortOrder.DESCENDING), + new RowSorter.SortKey(DiffViewTableModel.CLASSNAME_COLUMN_INDEX, SortOrder.ASCENDING) ); sorter.setSortKeys(myDefaultSortingKeys); setRowSorter(sorter); @@ -121,12 +136,12 @@ public class ClassesTable extends JBTable implements DataProvider, Disposable { myCountProvider = new ReferenceCountProvider() { @Override public int getTotalCount(@NotNull ReferenceType ref) { - return (int) myCounts.get(ref).myCurrentCount; + return (int)myCounts.get(ref).myCurrentCount; } @Override public int getDiffCount(@NotNull ReferenceType ref) { - return (int) myCounts.get(ref).diff(); + return (int)myCounts.get(ref).diff(); } @Override @@ -150,7 +165,7 @@ public class ClassesTable extends JBTable implements DataProvider, Disposable { int selectedRow = getSelectedRow(); if (selectedRow != -1) { int ix = convertRowIndexToModel(selectedRow); - return myElems.get(ix); + return myItems.get(ix); } return null; @@ -158,7 +173,7 @@ public class ClassesTable extends JBTable implements DataProvider, Disposable { @Nullable ReferenceType getClassByName(@NotNull String name) { - for (ReferenceType ref : myElems) { + for (ReferenceType ref : myItems) { if (name.equals(ref.name())) { return ref; } @@ -207,14 +222,14 @@ public class ClassesTable extends JBTable implements DataProvider, Disposable { assert classes.size() == counts.length; ReferenceType selectedClass = myModel.getSelectedClassBeforeHided(); int newSelectedIndex = classes.indexOf(selectedClass); - boolean isInitialized = !myElems.isEmpty(); - myElems = Collections.unmodifiableList(new ArrayList<>(classes)); + boolean isInitialized = !myItems.isEmpty(); + myItems = Collections.unmodifiableList(new ArrayList<>(classes)); for (int i = 0, size = classes.size(); i < size; i++) { ReferenceType ref = classes.get(i); DiffValue oldValue = isInitialized && !myCounts.containsKey(ref) - ? new DiffValue(0, 0) - : myCounts.getOrDefault(ref, myUnknownValue); + ? new DiffValue(0, 0) + : myCounts.getOrDefault(ref, myUnknownValue); myCounts.put(ref, oldValue.update(counts[i])); } @@ -223,7 +238,7 @@ public class ClassesTable extends JBTable implements DataProvider, Disposable { if (newSelectedIndex != -1 && !myModel.isHided()) { int ix = convertRowIndexToView(newSelectedIndex); changeSelection(ix, - DiffViewTableModel.CLASSNAME_COLUMN_INDEX, false, false); + DiffViewTableModel.CLASSNAME_COLUMN_INDEX, false, false); } getRowSorter().allRowsChanged(); @@ -253,7 +268,7 @@ public class ClassesTable extends JBTable implements DataProvider, Disposable { TrackerForNewInstances strategy = myParent.getStrategy(selectedClass); if (strategy != null && strategy.isReady()) { List newInstances = strategy.getNewInstances(); - return (InstancesProvider) limit -> newInstances; + return (InstancesProvider)limit -> newInstances; } } } @@ -271,7 +286,7 @@ public class ClassesTable extends JBTable implements DataProvider, Disposable { @Nullable private TrackingType getTrackingType(int row) { - ReferenceType ref = (ReferenceType) getValueAt(row, convertColumnIndexToView(DiffViewTableModel.CLASSNAME_COLUMN_INDEX)); + ReferenceType ref = (ReferenceType)getValueAt(row, convertColumnIndexToView(DiffViewTableModel.CLASSNAME_COLUMN_INDEX)); return myInstancesTracker.getTrackingType(ref.name()); } @@ -286,24 +301,24 @@ public class ClassesTable extends JBTable implements DataProvider, Disposable { DiffViewTableModel() { super(new AbstractTableColumnDescriptor[]{ - new AbstractTableColumnDescriptor("Class", ReferenceType.class) { - @Override - public Object getValue(int ix) { - return myElems.get(ix); - } - }, - new AbstractTableColumnDescriptor("Count", Long.class) { - @Override - public Object getValue(int ix) { - return myCounts.getOrDefault(myElems.get(ix), myUnknownValue).myCurrentCount; - } - }, - new AbstractTableColumnDescriptor("Diff", DiffValue.class) { - @Override - public Object getValue(int ix) { - return myCounts.getOrDefault(myElems.get(ix), myUnknownValue); - } + new AbstractTableColumnDescriptor("Class", ReferenceType.class) { + @Override + public Object getValue(int ix) { + return myItems.get(ix); } + }, + new AbstractTableColumnDescriptor("Count", Long.class) { + @Override + public Object getValue(int ix) { + return myCounts.getOrDefault(myItems.get(ix), myUnknownValue).myCurrentCount; + } + }, + new AbstractTableColumnDescriptor("Diff", DiffValue.class) { + @Override + public Object getValue(int ix) { + return myCounts.getOrDefault(myItems.get(ix), myUnknownValue); + } + } }); } @@ -333,7 +348,7 @@ public class ClassesTable extends JBTable implements DataProvider, Disposable { @Override public int getRowCount() { - return myIsWithContent ? myElems.size() : 0; + return myIsWithContent ? myItems.size() : 0; } } @@ -408,34 +423,34 @@ public class ClassesTable extends JBTable implements DataProvider, Disposable { } if (trackingType != null && column == DiffViewTableModel.DIFF_COLUMN_INDEX) { - setIcon(MemoryViewIcons.CLASS_TRACKED); + setIcon(AllIcons.Debugger.MemoryView.ClassTracked); setTransparentIconBackground(true); } if (value != null) { - addText(table, value, isSelected, hasFocus, row, column); + addText(value, isSelected, row); } } - protected abstract void addText(JTable table, @NotNull Object value, boolean isSelected, - boolean hasFocus, int row, int column); + protected abstract void addText(@NotNull Object value, boolean isSelected, int row); } private class MyClassColumnRenderer extends MyTableCellRenderer { @Override - protected void addText(JTable table, @NotNull Object value, boolean isSelected, - boolean hasFocus, int row, int column) { - String presentation = ((ReferenceType) value).name(); + protected void addText(@NotNull Object value, boolean isSelected, + int row) { + String presentation = ((ReferenceType)value).name(); append(" "); if (isSelected) { FList textRanges = myMatcher.matchingFragments(presentation); if (textRanges != null) { SimpleTextAttributes attributes = new SimpleTextAttributes(getBackground(), getForeground(), null, - SimpleTextAttributes.STYLE_SEARCH_MATCH); + SimpleTextAttributes.STYLE_SEARCH_MATCH); SpeedSearchUtil.appendColoredFragments(this, presentation, textRanges, - SimpleTextAttributes.REGULAR_ATTRIBUTES, attributes); + SimpleTextAttributes.REGULAR_ATTRIBUTES, attributes); } - } else { + } + else { append(String.format("%s", presentation), SimpleTextAttributes.REGULAR_ATTRIBUTES); } } @@ -443,8 +458,8 @@ public class ClassesTable extends JBTable implements DataProvider, Disposable { private class MyCountColumnRenderer extends MyTableCellRenderer { @Override - protected void addText(JTable table, @NotNull Object value, boolean isSelected, - boolean hasFocus, int row, int column) { + protected void addText(@NotNull Object value, boolean isSelected, + int row) { setTextAlign(SwingConstants.RIGHT); append(value.toString()); } @@ -452,14 +467,14 @@ public class ClassesTable extends JBTable implements DataProvider, Disposable { private class MyDiffColumnRenderer extends MyTableCellRenderer { private final SimpleTextAttributes myClickableCellAttributes = - new SimpleTextAttributes(SimpleTextAttributes.STYLE_UNDERLINE, JBColor.BLUE); + new SimpleTextAttributes(SimpleTextAttributes.STYLE_UNDERLINE, JBColor.BLUE); @Override - protected void addText(JTable table, @NotNull Object value, boolean isSelected, - boolean hasFocus, int row, int column) { + protected void addText(@NotNull Object value, boolean isSelected, + int row) { setTextAlign(SwingConstants.RIGHT); - ReferenceType ref = myElems.get(convertRowIndexToModel(row)); + ReferenceType ref = myItems.get(convertRowIndexToModel(row)); long diff = myCountProvider.getDiffCount(ref); String text = String.format("%s%d", diff > 0 ? "+" : "", diff); @@ -468,13 +483,15 @@ public class ClassesTable extends JBTable implements DataProvider, Disposable { if (newInstancesCount >= 0) { if (newInstancesCount == diff) { append(text, diff == 0 ? SimpleTextAttributes.REGULAR_ATTRIBUTES : myClickableCellAttributes); - } else { + } + else { append(text, SimpleTextAttributes.REGULAR_ATTRIBUTES); if (newInstancesCount != 0) { append(String.format(" (%d)", newInstancesCount), myClickableCellAttributes); } } - } else { + } + else { append(text, SimpleTextAttributes.REGULAR_ATTRIBUTES); } } diff --git a/java/debugger/impl/src/com/intellij/debugger/memory/view/ExpressionEditorWithHistory.java b/java/debugger/impl/src/com/intellij/debugger/memory/ui/ExpressionEditorWithHistory.java similarity index 76% rename from java/debugger/impl/src/com/intellij/debugger/memory/view/ExpressionEditorWithHistory.java rename to java/debugger/impl/src/com/intellij/debugger/memory/ui/ExpressionEditorWithHistory.java index d11e0e100873..87dbc415d098 100644 --- a/java/debugger/impl/src/com/intellij/debugger/memory/view/ExpressionEditorWithHistory.java +++ b/java/debugger/impl/src/com/intellij/debugger/memory/ui/ExpressionEditorWithHistory.java @@ -1,4 +1,19 @@ -package org.jetbrains.debugger.memory.view; +/* + * Copyright 2000-2016 JetBrains s.r.o. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.intellij.debugger.memory.ui; import com.intellij.codeInsight.lookup.LookupManager; import com.intellij.debugger.engine.DebuggerUtils; @@ -14,10 +29,10 @@ import com.intellij.openapi.ui.popup.util.BaseListPopupStep; import com.intellij.psi.PsiClass; import com.intellij.psi.search.GlobalSearchScope; import com.intellij.ui.ColoredListCellRenderer; +import com.intellij.ui.SimpleTextAttributes; import com.intellij.ui.popup.list.ListPopupImpl; import com.intellij.xdebugger.XExpression; import com.intellij.xdebugger.evaluation.XDebuggerEditorsProvider; -import com.intellij.xdebugger.impl.XSourcePositionImpl; import com.intellij.xdebugger.impl.breakpoints.XExpressionImpl; import com.intellij.xdebugger.impl.ui.XDebuggerExpressionEditor; import org.jetbrains.annotations.NotNull; @@ -34,7 +49,7 @@ class ExpressionEditorWithHistory extends XDebuggerExpressionEditor { final @NotNull XDebuggerEditorsProvider debuggerEditorsProvider, final @Nullable Disposable parentDisposable) { super(project, debuggerEditorsProvider, HISTORY_ID_PREFIX + className, null, - XExpressionImpl.EMPTY_EXPRESSION, false, true, true); + XExpressionImpl.EMPTY_EXPRESSION, false, true, true); new AnAction("InstancesWindow.ShowHistory") { @Override @@ -53,9 +68,8 @@ class ExpressionEditorWithHistory extends XDebuggerExpressionEditor { protected Void doInBackground() throws Exception { ApplicationManager.getApplication().runReadAction(() -> { final PsiClass psiClass = DebuggerUtils.findClass(className, - project, GlobalSearchScope.allScope(project)); - XSourcePositionImpl position = XSourcePositionImpl.createByElement(psiClass); - SwingUtilities.invokeLater(() -> setSourcePosition(position)); + project, GlobalSearchScope.allScope(project)); + ApplicationManager.getApplication().invokeLater(() -> setContext(psiClass)); }); return null; } @@ -79,7 +93,7 @@ class ExpressionEditorWithHistory extends XDebuggerExpressionEditor { @Override protected void customizeCellRenderer(@NotNull JList list, XExpression value, int index, boolean selected, boolean hasFocus) { - append(value.getExpression()); + append(value.getExpression(), SimpleTextAttributes.REGULAR_ATTRIBUTES); } }; } diff --git a/java/debugger/impl/src/com/intellij/debugger/memory/view/FilteringProgressView.java b/java/debugger/impl/src/com/intellij/debugger/memory/ui/FilteringProgressView.java similarity index 75% rename from java/debugger/impl/src/com/intellij/debugger/memory/view/FilteringProgressView.java rename to java/debugger/impl/src/com/intellij/debugger/memory/ui/FilteringProgressView.java index 90c7b1b6091d..2372b5ba01ff 100644 --- a/java/debugger/impl/src/com/intellij/debugger/memory/view/FilteringProgressView.java +++ b/java/debugger/impl/src/com/intellij/debugger/memory/ui/FilteringProgressView.java @@ -1,5 +1,21 @@ -package org.jetbrains.debugger.memory.view; +/* + * Copyright 2000-2016 JetBrains s.r.o. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.intellij.debugger.memory.ui; +import com.intellij.debugger.memory.filtering.FilteringResult; import com.intellij.icons.AllIcons; import com.intellij.ui.JBProgressBar; import com.intellij.ui.components.JBLabel; @@ -7,7 +23,6 @@ import com.intellij.util.ui.UIUtil; import com.intellij.util.ui.components.BorderLayoutPanel; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; -import org.jetbrains.debugger.memory.view.InstancesWindow.FilteringCompletionReason; import java.awt.event.MouseAdapter; import java.awt.event.MouseEvent; @@ -19,7 +34,7 @@ class FilteringProgressView extends BorderLayoutPanel { private final JBProgressBar myProgressBar = new JBProgressBar(); private final BorderLayoutPanel myProgressPanel = new BorderLayoutPanel(); private final JBLabel myStopButton = new JBLabel(UIUtil.isUnderDarcula() - ? AllIcons.Actions.Clean : AllIcons.Actions.CleanLight); + ? AllIcons.Actions.Clean : AllIcons.Actions.CleanLight); private final JBLabel myProgressText = new JBLabel(); private int myProceedCount = 0; @@ -29,7 +44,7 @@ class FilteringProgressView extends BorderLayoutPanel { private boolean myIsInProcess = false; @Nullable - private FilteringCompletionReason myCompletionReason = null; + private FilteringResult myCompletionReason = null; FilteringProgressView() { myStopButton.setOpaque(true); @@ -50,9 +65,9 @@ class FilteringProgressView extends BorderLayoutPanel { void updateProgress(int proceedCount, int matchedCount, int errorCount) { if (myIsInProcess) { - myProceedCount += proceedCount; - myMatchedCount += matchedCount; - myErrorCount += errorCount; + myProceedCount = proceedCount; + myMatchedCount = matchedCount; + myErrorCount = errorCount; myProgressBar.setValue(myProceedCount); myProgressText.setText(getDescription()); } @@ -68,7 +83,7 @@ class FilteringProgressView extends BorderLayoutPanel { setProgressBarVisible(true); } - void complete(@NotNull FilteringCompletionReason reason) { + void complete(@NotNull FilteringResult reason) { if (!myIsInProcess) { throw new IllegalStateException("First you need to start progress"); } diff --git a/java/debugger/impl/src/com/intellij/debugger/memory/view/InstancesTree.java b/java/debugger/impl/src/com/intellij/debugger/memory/ui/InstancesTree.java similarity index 82% rename from java/debugger/impl/src/com/intellij/debugger/memory/view/InstancesTree.java rename to java/debugger/impl/src/com/intellij/debugger/memory/ui/InstancesTree.java index 6352ffe02c9a..8bd1c851482c 100644 --- a/java/debugger/impl/src/com/intellij/debugger/memory/view/InstancesTree.java +++ b/java/debugger/impl/src/com/intellij/debugger/memory/ui/InstancesTree.java @@ -1,4 +1,19 @@ -package org.jetbrains.debugger.memory.view; +/* + * Copyright 2000-2016 JetBrains s.r.o. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.intellij.debugger.memory.ui; import com.intellij.debugger.ui.impl.watch.NodeDescriptorProvider; import com.intellij.debugger.ui.tree.NodeDescriptor; @@ -11,6 +26,7 @@ import com.intellij.xdebugger.evaluation.XDebuggerEditorsProvider; import com.intellij.xdebugger.frame.*; import com.intellij.xdebugger.impl.actions.XDebuggerActions; import com.intellij.xdebugger.impl.frame.XValueMarkers; +import com.intellij.xdebugger.impl.ui.XDebuggerUIConstants; import com.intellij.xdebugger.impl.ui.tree.XDebuggerTree; import com.intellij.xdebugger.impl.ui.tree.XDebuggerTreeState; import com.intellij.xdebugger.impl.ui.tree.nodes.XValueNodeImpl; @@ -20,7 +36,6 @@ import org.jetbrains.annotations.NonNls; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; -import javax.swing.*; import javax.swing.tree.TreePath; import java.util.ArrayList; import java.util.List; @@ -70,10 +85,10 @@ public class InstancesTree extends XDebuggerTree { rebuildTree(policy, XDebuggerTreeState.saveState(this)); } - void setMessage(@Nullable Icon icon, @NotNull String text, @NotNull SimpleTextAttributes textAttributes) { + void setInfoMessage(@SuppressWarnings("SameParameterValue") @NotNull String text) { myChildren = null; myRoot.clearChildren(); - myRoot.setMessage(text, icon, textAttributes, null); + myRoot.setMessage(text, XDebuggerUIConstants.INFORMATION_MESSAGE_ICON, SimpleTextAttributes.REGULAR_ATTRIBUTES, null); } @Nullable diff --git a/java/debugger/impl/src/com/intellij/debugger/memory/view/InstancesWindow.java b/java/debugger/impl/src/com/intellij/debugger/memory/ui/InstancesWindow.java similarity index 59% rename from java/debugger/impl/src/com/intellij/debugger/memory/view/InstancesWindow.java rename to java/debugger/impl/src/com/intellij/debugger/memory/ui/InstancesWindow.java index 891ebbf82da7..32ec574675b1 100644 --- a/java/debugger/impl/src/com/intellij/debugger/memory/view/InstancesWindow.java +++ b/java/debugger/impl/src/com/intellij/debugger/memory/ui/InstancesWindow.java @@ -1,33 +1,47 @@ -package org.jetbrains.debugger.memory.view; +/* + * Copyright 2000-2016 JetBrains s.r.o. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.intellij.debugger.memory.ui; import com.intellij.debugger.DebuggerManager; import com.intellij.debugger.engine.DebugProcessImpl; import com.intellij.debugger.engine.JavaValue; import com.intellij.debugger.engine.SuspendContextImpl; -import com.intellij.debugger.engine.evaluation.EvaluateException; import com.intellij.debugger.engine.evaluation.EvaluationContext; import com.intellij.debugger.engine.evaluation.EvaluationContextImpl; -import com.intellij.debugger.engine.evaluation.TextWithImportsImpl; -import com.intellij.debugger.engine.evaluation.expression.ExpressionEvaluator; import com.intellij.debugger.engine.events.DebuggerContextCommandImpl; +import com.intellij.debugger.memory.filtering.FilteringResult; +import com.intellij.debugger.memory.filtering.FilteringTask; +import com.intellij.debugger.memory.filtering.FilteringTaskCallback; +import com.intellij.debugger.memory.utils.*; import com.intellij.debugger.ui.impl.watch.DebuggerTreeNodeImpl; import com.intellij.debugger.ui.impl.watch.MessageDescriptor; import com.intellij.debugger.ui.impl.watch.NodeManagerImpl; import com.intellij.debugger.ui.tree.NodeDescriptor; -import com.intellij.debugger.ui.tree.render.CachedEvaluator; import com.intellij.openapi.Disposable; import com.intellij.openapi.actionSystem.*; import com.intellij.openapi.actionSystem.ex.AnActionListener; +import com.intellij.openapi.application.ApplicationManager; import com.intellij.openapi.diagnostic.Logger; import com.intellij.openapi.project.Project; import com.intellij.openapi.ui.DialogWrapper; import com.intellij.openapi.util.Disposer; -import com.intellij.openapi.util.Pair; import com.intellij.openapi.wm.IdeFocusManager; import com.intellij.psi.search.GlobalSearchScope; import com.intellij.ui.DoubleClickListener; import com.intellij.ui.JBColor; -import com.intellij.ui.SimpleTextAttributes; import com.intellij.ui.components.JBLabel; import com.intellij.ui.components.JBPanel; import com.intellij.util.ui.JBDimension; @@ -43,17 +57,13 @@ import com.intellij.xdebugger.impl.XDebugSessionImpl; import com.intellij.xdebugger.impl.actions.XDebuggerActionBase; import com.intellij.xdebugger.impl.frame.XValueMarkers; import com.intellij.xdebugger.impl.ui.XDebuggerExpressionEditor; -import com.intellij.xdebugger.impl.ui.XDebuggerUIConstants; import com.intellij.xdebugger.impl.ui.tree.ValueMarkup; import com.intellij.xdebugger.impl.ui.tree.XDebuggerTreeState; import com.intellij.xdebugger.impl.ui.tree.actions.XDebuggerTreeActionBase; import com.intellij.xdebugger.impl.ui.tree.nodes.XValueNodeImpl; -import com.sun.jdi.BooleanValue; import com.sun.jdi.ObjectReference; -import com.sun.jdi.Value; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; -import org.jetbrains.debugger.memory.utils.*; import org.jetbrains.java.debugger.JavaDebuggerEditorsProvider; import javax.swing.*; @@ -63,9 +73,6 @@ import java.awt.event.MouseEvent; import java.util.Collections; import java.util.List; import java.util.concurrent.TimeUnit; -import java.util.concurrent.atomic.AtomicInteger; - -import static java.lang.Math.min; public class InstancesWindow extends DialogWrapper { private static final Logger LOG = Logger.getInstance(InstancesWindow.class); @@ -74,7 +81,7 @@ public class InstancesWindow extends DialogWrapper { private static final int DEFAULT_WINDOW_HEIGHT = 400; private static final int FILTERING_BUTTON_ADDITIONAL_WIDTH = 30; private static final int BORDER_LAYOUT_DEFAULT_GAP = 5; - private static final int DEFAULT_INSTANCES_LIMIT = 500000; // All + private static final int DEFAULT_INSTANCES_LIMIT = 500000; private final Project myProject; private final XDebugSession myDebugSession; @@ -96,7 +103,7 @@ public class InstancesWindow extends DialogWrapper { myDebugSession.addSessionListener(new XDebugSessionListener() { @Override public void sessionStopped() { - SwingUtilities.invokeLater(() -> close(OK_EXIT_CODE)); + ApplicationManager.getApplication().invokeLater(() -> close(OK_EXIT_CODE)); } }, myDisposable); setModal(false); @@ -105,10 +112,6 @@ public class InstancesWindow extends DialogWrapper { root.setDefaultButton(myInstancesView.myFilterButton); } - enum FilteringCompletionReason { - ALL_CHECKED, INTERRUPTED, LIMIT_REACHED - } - private void addWarningMessage(@Nullable String message) { String warning = message == null ? "" : String.format(". Warning: %s", message); setTitle(String.format("Instances of %s%s", myClassName, warning)); @@ -125,7 +128,7 @@ public class InstancesWindow extends DialogWrapper { protected JComponent createCenterPanel() { myInstancesView = new MyInstancesView(); myInstancesView.setPreferredSize( - new JBDimension(DEFAULT_WINDOW_WIDTH, DEFAULT_WINDOW_HEIGHT)); + new JBDimension(DEFAULT_WINDOW_WIDTH, DEFAULT_WINDOW_HEIGHT)); return myInstancesView; } @@ -154,7 +157,6 @@ public class InstancesWindow extends DialogWrapper { private final XDebuggerExpressionEditor myFilterConditionEditor; private final XDebugSessionListener myDebugSessionListener = new MySessionListener(); - private final MyCachedEvaluator myEvaluator = new MyCachedEvaluator(); private final MyNodeManager myNodeManager = new MyNodeManager(myProject); private final JButton myFilterButton = new JButton("Filter"); @@ -170,19 +172,19 @@ public class InstancesWindow extends DialogWrapper { MyInstancesView() { super(new BorderLayout(0, JBUI.scale(BORDER_LAYOUT_DEFAULT_GAP))); - Disposer.register(InstancesWindow.this.myDisposable, MyInstancesView.this); + Disposer.register(InstancesWindow.this.myDisposable, this); XValueMarkers markers = getValueMarkers(); ActionManager.getInstance().addAnActionListener(myActionListener, InstancesWindow.this.myDisposable); myDebugSession.addSessionListener(myDebugSessionListener, InstancesWindow.this.myDisposable); JavaDebuggerEditorsProvider editorsProvider = new JavaDebuggerEditorsProvider(); myFilterConditionEditor = new ExpressionEditorWithHistory(myProject, myClassName, - editorsProvider, InstancesWindow.this.myDisposable); + editorsProvider, InstancesWindow.this.myDisposable); myFilterButton.setBorder(BorderFactory.createEmptyBorder()); Dimension filteringButtonSize = myFilterConditionEditor.getEditorComponent().getPreferredSize(); filteringButtonSize.width = JBUI.scale(FILTERING_BUTTON_ADDITIONAL_WIDTH) + - myFilterButton.getPreferredSize().width; + myFilterButton.getPreferredSize().width; myFilterButton.setPreferredSize(filteringButtonSize); JBPanel filteringPane = new JBPanel(new BorderLayout(JBUI.scale(BORDER_LAYOUT_DEFAULT_GAP), 0)); @@ -212,8 +214,8 @@ public class InstancesWindow extends DialogWrapper { StackFrameList list = new StackFrameList(myProject, - Collections.emptyList(), - GlobalSearchScope.allScope(myProject)); + Collections.emptyList(), + GlobalSearchScope.allScope(myProject)); list.addListSelectionListener(e -> list.navigateToSelectedValue(false)); new DoubleClickListener() { @@ -225,15 +227,15 @@ public class InstancesWindow extends DialogWrapper { }.installOn(list); InstancesWithStackFrameView instancesWithStackFrame = new InstancesWithStackFrameView(myDebugSession, - myInstancesTree, list, myClassName); + myInstancesTree, list, myClassName); add(filteringPane, BorderLayout.NORTH); add(instancesWithStackFrame.getComponent(), BorderLayout.CENTER); JComponent focusedComponent = myFilterConditionEditor.getEditorComponent(); UiNotifyConnector.doWhenFirstShown(focusedComponent, () -> - IdeFocusManager.findInstanceByComponent(focusedComponent) - .requestFocus(focusedComponent, true)); + IdeFocusManager.findInstanceByComponent(focusedComponent) + .requestFocus(focusedComponent, true)); } @Override @@ -243,8 +245,8 @@ public class InstancesWindow extends DialogWrapper { } private void updateInstances() { - DebugProcessImpl debugProcess = (DebugProcessImpl) DebuggerManager.getInstance(myProject) - .getDebugProcess(myDebugSession.getDebugProcess().getProcessHandler()); + DebugProcessImpl debugProcess = (DebugProcessImpl)DebuggerManager.getInstance(myProject) + .getDebugProcess(myDebugSession.getDebugProcess().getProcessHandler()); cancelFilteringTask(); @@ -258,49 +260,28 @@ public class InstancesWindow extends DialogWrapper { public void threadAction(@NotNull SuspendContextImpl suspendContext) { myIsAndroidVM = AndroidUtil.isAndroidVM(debugProcess.getVirtualMachineProxy().getVirtualMachine()); int limit = myIsAndroidVM - ? AndroidUtil.ANDROID_INSTANCES_LIMIT - : DEFAULT_INSTANCES_LIMIT; + ? AndroidUtil.ANDROID_INSTANCES_LIMIT + : DEFAULT_INSTANCES_LIMIT; List instances = myInstancesProvider.getInstances(limit + 1); EvaluationContextImpl evaluationContext = debugProcess - .getDebuggerContext().createEvaluationContext(); + .getDebuggerContext().createEvaluationContext(); if (instances.size() > limit) { addWarningMessage(String.format("Not all instances will be loaded (only %d)", limit)); instances = instances.subList(0, limit); } - final int progressSize = instances.size(); if (evaluationContext != null) { synchronized (myFilteringTaskLock) { - myFilteringTask = new MyFilteringWorker(instances, evaluationContext, createEvaluator()); - SwingUtilities.invokeLater(() -> { - myProgress.start(progressSize); - myFilteringTask.execute(); - }); + myFilteringTask = new MyFilteringWorker(instances, myFilterConditionEditor.getExpression(), evaluationContext); + myFilteringTask.execute(); } } } }); } - @Nullable - private ExpressionEvaluator createEvaluator() { - ExpressionEvaluator evaluator = null; - - XExpression expression = myFilterConditionEditor.getExpression(); - if (expression != null && !expression.getExpression().isEmpty()) { - try { - myEvaluator.setReferenceExpression(TextWithImportsImpl.fromXExpression(expression)); - evaluator = myEvaluator.getEvaluator(); - } catch (EvaluateException ignored) { - LOG.info("cannot create evaluator by the expression = " + expression.getExpression()); - } - } - - return evaluator; - } - private void cancelFilteringTask() { if (myFilteringTask != null) { synchronized (myFilteringTaskLock) { @@ -314,8 +295,8 @@ public class InstancesWindow extends DialogWrapper { private XValueMarkers getValueMarkers() { return myDebugSession instanceof XDebugSessionImpl - ? ((XDebugSessionImpl) myDebugSession).getValueMarkers() - : null; + ? ((XDebugSessionImpl)myDebugSession).getValueMarkers() + : null; } private class MySessionListener implements XDebugSessionListener { @@ -323,18 +304,18 @@ public class InstancesWindow extends DialogWrapper { @Override public void sessionResumed() { - SwingUtilities.invokeLater(() -> { + ApplicationManager.getApplication().invokeLater(() -> { myTreeState = XDebuggerTreeState.saveState(myInstancesTree); cancelFilteringTask(); - myInstancesTree.setMessage(XDebuggerUIConstants.INFORMATION_MESSAGE_ICON, - "The application is running", SimpleTextAttributes.REGULAR_ATTRIBUTES); + myInstancesTree.setInfoMessage( + "The application is running"); }); } @Override public void sessionPaused() { - SwingUtilities.invokeLater(() -> { + ApplicationManager.getApplication().invokeLater(() -> { myProgress.setVisible(true); myInstancesTree.rebuildTree(InstancesTree.RebuildPolicy.RELOAD_INSTANCES, myTreeState); }); @@ -346,8 +327,8 @@ public class InstancesWindow extends DialogWrapper { public void beforeActionPerformed(AnAction action, DataContext dataContext, AnActionEvent event) { if (dataContext.getData(PlatformDataKeys.CONTEXT_COMPONENT) == myInstancesView.myInstancesTree && (isAddToWatchesAction(action) || isEvaluateExpressionAction(action))) { - XValueNodeImpl selectedNode = XDebuggerTreeActionBase.getSelectedNode(dataContext); - XValueMarkers markers = getValueMarkers(); + final XValueNodeImpl selectedNode = XDebuggerTreeActionBase.getSelectedNode(dataContext); + final XValueMarkers markers = getValueMarkers(); if (markers != null && selectedNode != null) { @@ -356,175 +337,143 @@ public class InstancesWindow extends DialogWrapper { currentNode = currentNode.getParent(); } - XValue valueContainer = ((XValueNodeImpl) currentNode).getValueContainer(); + final XValue valueContainer = ((XValueNodeImpl)currentNode).getValueContainer(); - String expression = valueContainer.getEvaluationExpression(); + final String expression = valueContainer.getEvaluationExpression(); if (expression != null) { markers.markValue(valueContainer, - new ValueMarkup(expression.replace("@", ""), new JBColor(0, 0), null)); + new ValueMarkup(expression.replace("@", ""), new JBColor(0, 0), null)); } - SwingUtilities.invokeLater(() -> myInstancesTree - .rebuildTree(InstancesTree.RebuildPolicy.ONLY_UPDATE_LABELS)); + ApplicationManager.getApplication().invokeLater(() -> myInstancesTree + .rebuildTree(InstancesTree.RebuildPolicy.ONLY_UPDATE_LABELS)); } } } private boolean isAddToWatchesAction(AnAction action) { - String className = action.getClass().getSimpleName(); + final String className = action.getClass().getSimpleName(); return action instanceof XDebuggerTreeActionBase && className.equals("XAddToWatchesAction"); } private boolean isEvaluateExpressionAction(AnAction action) { - String className = action.getClass().getSimpleName(); + final String className = action.getClass().getSimpleName(); return action instanceof XDebuggerActionBase && className.equals("EvaluateAction"); } } - private final class MyCachedEvaluator extends CachedEvaluator { - @Override - @NotNull - protected String getClassName() { - return myClassName; + private class MyFilteringCallback implements FilteringTaskCallback { + private final ErrorsValueGroup myErrorsGroup = new ErrorsValueGroup(); + private final EvaluationContextImpl myEvaluationContext; + + private long myFilteringStartedTime; + + private int myProceedCount = 0; + private int myMatchedCount = 0; + private int myErrorsCount = 0; + + public MyFilteringCallback(@NotNull EvaluationContextImpl evaluationContext) { + myEvaluationContext = evaluationContext; } - ExpressionEvaluator getEvaluator() throws EvaluateException { - return getEvaluator(myProject); + private XValueChildrenList myChildren = new XValueChildrenList(); + + @Override + public void started(int total) { + myFilteringStartedTime = System.nanoTime(); + ApplicationManager.getApplication().invokeLater(() -> myProgress.start(total)); + } + + @NotNull + @Override + public Action matched(@NotNull ObjectReference ref) { + final JavaValue val = new InstanceJavaValue(new InstanceValueDescriptor(myProject, ref), + myEvaluationContext, myNodeManager); + myMatchedCount++; + myProceedCount++; + myChildren.add(val); + if (myChildren.size() >= FILTERING_CHUNK_SIZE) { + final int proceed = myProceedCount; + final int matched = myMatchedCount; + final int errors = myErrorsCount; + XValueChildrenList children = myChildren; + ApplicationManager.getApplication().invokeLater(() -> { + myProgress.updateProgress(proceed, matched, errors); + myInstancesTree.addChildren(children, false); + }); + myChildren = new XValueChildrenList(); + } + + return myMatchedCount < MAX_TREE_NODE_COUNT ? Action.CONTINUE : Action.STOP; + } + + @NotNull + @Override + public Action notMatched(@NotNull ObjectReference ref) { + myProceedCount++; + return Action.CONTINUE; + } + + @NotNull + @Override + public Action error(@NotNull ObjectReference ref, @NotNull String description) { + final JavaValue val = new InstanceJavaValue(new InstanceValueDescriptor(myProject, ref), + myEvaluationContext, myNodeManager); + myErrorsGroup.addErrorValue(description, val); + myProceedCount++; + myErrorsCount++; + return Action.CONTINUE; + } + + @Override + public void completed(@NotNull FilteringResult reason) { + if (!myErrorsGroup.isEmpty()) { + myChildren.addBottomGroup(myErrorsGroup); + } + + final long duration = System.nanoTime() - myFilteringStartedTime; + LOG.info(String.format("Filtering completed in %d ms for %d instances", + TimeUnit.NANOSECONDS.toMillis(duration), + myProceedCount)); + + final int proceed = myProceedCount; + final int matched = myMatchedCount; + final int errors = myErrorsCount; + final XValueChildrenList childrenList = myChildren; + ApplicationManager.getApplication().invokeLater(() -> { + myProgress.updateProgress(proceed, matched, errors); + myInstancesTree.addChildren(childrenList, true); + myFilterButton.setEnabled(true); + myProgress.complete(reason); + }); } } private class MyFilteringWorker extends SwingWorker { - private final List myReferences; - private final ErrorsValueGroup myErrorsGroup = new ErrorsValueGroup("Errors"); - private final ExpressionEvaluator myExpressionEvaluator; - - private final EvaluationContextImpl myEvaluationContext; - private volatile boolean myDebuggerTaskCompleted = false; - - @NotNull - private volatile FilteringCompletionReason myCompletionReason = FilteringCompletionReason.INTERRUPTED; + private final FilteringTask myTask; MyFilteringWorker(@NotNull List refs, - @NotNull EvaluationContextImpl evaluationContext, - @Nullable ExpressionEvaluator evaluator) { - myReferences = refs; - myEvaluationContext = evaluationContext; - myExpressionEvaluator = evaluator; - } - - @Override - protected void done() { - if (!myErrorsGroup.isEmpty()) { - XValueChildrenList lst = new XValueChildrenList(); - lst.addBottomGroup(myErrorsGroup); - myInstancesTree.addChildren(lst, true); - } else { - myInstancesTree.addChildren(XValueChildrenList.EMPTY, true); + @NotNull XExpression expression, + @NotNull EvaluationContextImpl evaluationContext) { + if (refs.size() != 0) { + DebugProcessImpl debugProcess = + (DebugProcessImpl)DebuggerManager.getInstance(myProject).getDebugProcess(myDebugSession.getDebugProcess().getProcessHandler()); + myTask = new FilteringTask(refs.get(0).referenceType(), debugProcess, expression, refs, + new MyFilteringCallback(evaluationContext)); + } + else { + myTask = null; } - - myFilterButton.setEnabled(true); - SwingUtilities.invokeLater(() -> myProgress.complete(myCompletionReason)); } @Override protected Void doInBackground() throws Exception { - DebugProcessImpl debugProcess = (DebugProcessImpl) DebuggerManager.getInstance(myProject) - .getDebugProcess(myDebugSession.getDebugProcess().getProcessHandler()); - - AtomicInteger totalChildren = new AtomicInteger(0); - AtomicInteger totalViewed = new AtomicInteger(0); - long start = System.nanoTime(); - for (int i = 0, size = myReferences.size(); i < size && totalChildren.get() < MAX_TREE_NODE_COUNT; - i += FILTERING_CHUNK_SIZE) { - myDebuggerTaskCompleted = false; - final int chunkBegin = i; - debugProcess.getManagerThread().schedule(new DebuggerContextCommandImpl(debugProcess.getDebuggerContext()) { - @Override - public Priority getPriority() { - return Priority.LOWEST; - } - - @Override - public void threadAction(@NotNull SuspendContextImpl suspendContext) { - // TODO: need to rewrite this - XValueChildrenList children = new XValueChildrenList(); - int endOfChunk = min(chunkBegin + FILTERING_CHUNK_SIZE, size); - int errorCount = 0; - for (int j = chunkBegin; j < endOfChunk && totalChildren.get() < MAX_TREE_NODE_COUNT; j++) { - ObjectReference ref = myReferences.get(j); - totalViewed.incrementAndGet(); - Pair comparison = null; - if (myExpressionEvaluator != null) { - comparison = isSatisfy(myExpressionEvaluator, ref); - if (comparison.first == MyFilteringResult.EVAL_ERROR) { - errorCount++; - } - - if (comparison.first == MyFilteringResult.NO_MATCH) { - continue; - } - } - - JavaValue val = new InstanceJavaValue(null, new InstanceValueDescriptor(myProject, ref), - myEvaluationContext, myNodeManager, true); - if (comparison == null || comparison.first == MyFilteringResult.MATCH) { - children.add(val); - } else { - myErrorsGroup.addErrorValue(comparison.second, val); - } - - totalChildren.incrementAndGet(); - } - - if (children.size() > 0) { - SwingUtilities.invokeLater(() -> { - if (MyFilteringWorker.this == myFilteringTask) { - myInstancesTree.addChildren(children, false); - } - }); - } - - final int childrenSize = children.size(); - final int finalErrorsCount = errorCount; - SwingUtilities.invokeLater(() -> - myProgress.updateProgress(endOfChunk - chunkBegin, childrenSize, finalErrorsCount)); - - synchronized (MyFilteringWorker.this) { - myDebuggerTaskCompleted = true; - MyFilteringWorker.this.notify(); - } - } - }); - - synchronized (MyFilteringWorker.this) { - while (!myDebuggerTaskCompleted) { - MyFilteringWorker.this.wait(); - } - } + if (myTask != null) { + myTask.run(); } - LOG.info(String.format("Filtering time = %d ms, count = %d, matched = %d", - TimeUnit.NANOSECONDS.toMillis(System.nanoTime() - start), totalViewed.get(), totalChildren.get())); - - myCompletionReason = totalViewed.get() == myReferences.size() - ? FilteringCompletionReason.ALL_CHECKED - : FilteringCompletionReason.LIMIT_REACHED; - return null; } - - private Pair isSatisfy(@NotNull ExpressionEvaluator evaluator, @NotNull Value value) { - try { - Value result = evaluator.evaluate(myEvaluationContext.createEvaluationContext(value)); - if (result instanceof BooleanValue && ((BooleanValue) result).value()) { - return Pair.create(MyFilteringResult.MATCH, ""); - } - } catch (EvaluateException e) { - return Pair.create(MyFilteringResult.EVAL_ERROR, e.getMessage()); - } - - return Pair.create(MyFilteringResult.NO_MATCH, ""); - } } } @@ -548,8 +497,4 @@ public class InstancesWindow extends DialogWrapper { return new DebuggerTreeNodeImpl(null, new MessageDescriptor(message)); } } - - private enum MyFilteringResult { - MATCH, NO_MATCH, EVAL_ERROR - } } diff --git a/java/debugger/impl/src/com/intellij/debugger/memory/view/InstancesWithStackFrameView.java b/java/debugger/impl/src/com/intellij/debugger/memory/ui/InstancesWithStackFrameView.java similarity index 70% rename from java/debugger/impl/src/com/intellij/debugger/memory/view/InstancesWithStackFrameView.java rename to java/debugger/impl/src/com/intellij/debugger/memory/ui/InstancesWithStackFrameView.java index 9ec7a35214b0..5016a14ee7bc 100644 --- a/java/debugger/impl/src/com/intellij/debugger/memory/view/InstancesWithStackFrameView.java +++ b/java/debugger/impl/src/com/intellij/debugger/memory/ui/InstancesWithStackFrameView.java @@ -1,5 +1,21 @@ -package org.jetbrains.debugger.memory.view; +/* + * Copyright 2000-2016 JetBrains s.r.o. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.intellij.debugger.memory.ui; +import com.intellij.icons.AllIcons; import com.intellij.openapi.actionSystem.AnAction; import com.intellij.openapi.actionSystem.AnActionEvent; import com.intellij.ui.JBSplitter; @@ -8,13 +24,12 @@ import com.intellij.ui.components.JBScrollPane; import com.intellij.ui.components.labels.ActionLink; import com.intellij.xdebugger.XDebugSession; import com.sun.jdi.ObjectReference; -import icons.MemoryViewIcons; import org.jetbrains.annotations.NotNull; -import org.jetbrains.debugger.memory.component.CreationPositionTracker; -import org.jetbrains.debugger.memory.component.InstancesTracker; -import org.jetbrains.debugger.memory.event.InstancesTrackerListener; -import org.jetbrains.debugger.memory.tracking.TrackingType; -import org.jetbrains.debugger.memory.utils.StackFrameDescriptor; +import com.intellij.debugger.memory.component.CreationPositionTracker; +import com.intellij.debugger.memory.component.InstancesTracker; +import com.intellij.debugger.memory.event.InstancesTrackerListener; +import com.intellij.debugger.memory.tracking.TrackingType; +import com.intellij.debugger.memory.utils.StackFrameDescriptor; import javax.swing.*; import java.util.Collections; @@ -41,13 +56,17 @@ class InstancesWithStackFrameView { JLabel stackTraceLabel; if (isArrayType(className)) { stackTraceLabel = new JBLabel(TEXT_FOR_ARRAYS, SwingConstants.CENTER); - } else { - ActionLink actionLink = new ActionLink("Enable tracking for new instances", MemoryViewIcons.CLASS_TRACKED, new AnAction() { - @Override - public void actionPerformed(AnActionEvent e) { - InstancesTracker.getInstance(debugSession.getProject()).add(className, TrackingType.CREATION); - } - }); + } + else { + ActionLink actionLink = new ActionLink("Enable tracking for new instances", + AllIcons.Debugger.MemoryView.ClassTracked, + new AnAction() { + @Override + public void actionPerformed(AnActionEvent e) { + InstancesTracker.getInstance(debugSession.getProject()) + .add(className, TrackingType.CREATION); + } + }); actionLink.setHorizontalAlignment(SwingConstants.CENTER); actionLink.setPaintUnderline(false); @@ -87,13 +106,14 @@ class InstancesWithStackFrameView { List stack = tracker.getStack(debugSession, ref); if (stack != null) { list.setFrame(stack); - if(mySplitter.getProportion() == 1.f) { + if (mySplitter.getProportion() == 1.f) { mySplitter.setProportion(DEFAULT_SPLITTER_PROPORTION); } return; } list.setEmptyText(EMPTY_TEXT_WHEN_STACK_NOT_FOUND); - } else { + } + else { list.setEmptyText(EMPTY_TEXT_WHEN_ITEM_NOT_SELECTED); } diff --git a/java/debugger/impl/src/com/intellij/debugger/memory/view/StackFrameList.java b/java/debugger/impl/src/com/intellij/debugger/memory/ui/StackFrameList.java similarity index 83% rename from java/debugger/impl/src/com/intellij/debugger/memory/view/StackFrameList.java rename to java/debugger/impl/src/com/intellij/debugger/memory/ui/StackFrameList.java index ce9431591901..60898d823133 100644 --- a/java/debugger/impl/src/com/intellij/debugger/memory/view/StackFrameList.java +++ b/java/debugger/impl/src/com/intellij/debugger/memory/ui/StackFrameList.java @@ -1,4 +1,19 @@ -package org.jetbrains.debugger.memory.view; +/* + * Copyright 2000-2016 JetBrains s.r.o. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.intellij.debugger.memory.ui; import com.intellij.debugger.engine.DebuggerUtils; import com.intellij.execution.filters.OpenFileHyperlinkInfo; @@ -17,15 +32,14 @@ import com.intellij.psi.util.PsiUtilCore; import com.intellij.ui.ColoredListCellRenderer; import com.intellij.ui.SimpleTextAttributes; import com.intellij.ui.components.JBList; -import org.apache.commons.lang.StringUtils; import org.jetbrains.annotations.NotNull; -import org.jetbrains.debugger.memory.utils.StackFrameDescriptor; +import com.intellij.debugger.memory.utils.StackFrameDescriptor; import javax.swing.*; import java.util.ArrayList; import java.util.List; -class StackFrameList extends JBList { +class StackFrameList extends JBList { private static final char ANONYMOUS_CLASS_DELIMITER = '$'; private static final MyOpenFilesState myEditorState = new MyOpenFilesState(); @@ -45,7 +59,6 @@ class StackFrameList extends JBList { setSelectionMode(ListSelectionModel.SINGLE_SELECTION); - //noinspection unchecked setModel(myModel); setCellRenderer(new ColoredListCellRenderer() { @@ -54,7 +67,7 @@ class StackFrameList extends JBList { StackFrameDescriptor value, int index, boolean isSelected, boolean hasFocus) { append(String.format("%s:%d, %s", value.methodName(), value.line(), value.className())); String packageName = value.packageName(); - if (!StringUtils.isEmpty(packageName)) { + if (packageName.trim().isEmpty() /*!StringUtils.isEmpty(packageName)*/) { append(String.format(" (%s)", value.packageName()), SimpleTextAttributes.GRAYED_ITALIC_ATTRIBUTES); } } @@ -66,7 +79,7 @@ class StackFrameList extends JBList { } void navigateToSelectedValue(boolean focusOnEditor) { - StackFrameDescriptor selectedValue = (StackFrameDescriptor) getSelectedValue(); + StackFrameDescriptor selectedValue = getSelectedValue(); if (selectedValue != null) { navigateToFrame(selectedValue, focusOnEditor); } @@ -88,7 +101,7 @@ class StackFrameList extends JBList { } OpenFileHyperlinkInfo info = - new OpenFileHyperlinkInfo(myProject, file, frame.line() - 1); + new OpenFileHyperlinkInfo(myProject, file, frame.line() - 1); OpenFileDescriptor descriptor = info.getDescriptor(); if (descriptor != null) { FileEditorManagerImpl manager = (FileEditorManagerImpl)FileEditorManager.getInstance(myProject); diff --git a/java/debugger/impl/src/com/intellij/debugger/memory/view/StackFramePopup.java b/java/debugger/impl/src/com/intellij/debugger/memory/ui/StackFramePopup.java similarity index 64% rename from java/debugger/impl/src/com/intellij/debugger/memory/view/StackFramePopup.java rename to java/debugger/impl/src/com/intellij/debugger/memory/ui/StackFramePopup.java index 0069d0500152..fccbc280f83f 100644 --- a/java/debugger/impl/src/com/intellij/debugger/memory/view/StackFramePopup.java +++ b/java/debugger/impl/src/com/intellij/debugger/memory/ui/StackFramePopup.java @@ -1,11 +1,26 @@ -package org.jetbrains.debugger.memory.view; +/* + * Copyright 2000-2016 JetBrains s.r.o. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.intellij.debugger.memory.ui; import com.intellij.openapi.project.Project; import com.intellij.openapi.ui.popup.JBPopup; import com.intellij.openapi.ui.popup.JBPopupFactory; import com.intellij.psi.search.GlobalSearchScope; import org.jetbrains.annotations.NotNull; -import org.jetbrains.debugger.memory.utils.StackFrameDescriptor; +import com.intellij.debugger.memory.utils.StackFrameDescriptor; import java.util.List; diff --git a/java/debugger/impl/src/com/intellij/debugger/memory/utils/AbstractTableColumnDescriptor.java b/java/debugger/impl/src/com/intellij/debugger/memory/utils/AbstractTableColumnDescriptor.java index 3abb30a98e89..c651a142e716 100644 --- a/java/debugger/impl/src/com/intellij/debugger/memory/utils/AbstractTableColumnDescriptor.java +++ b/java/debugger/impl/src/com/intellij/debugger/memory/utils/AbstractTableColumnDescriptor.java @@ -1,4 +1,19 @@ -package org.jetbrains.debugger.memory.utils; +/* + * Copyright 2000-2016 JetBrains s.r.o. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.intellij.debugger.memory.utils; public abstract class AbstractTableColumnDescriptor implements AbstractTableModelWithColumns.TableColumnDescriptor { private final String myName; diff --git a/java/debugger/impl/src/com/intellij/debugger/memory/utils/AbstractTableModelWithColumns.java b/java/debugger/impl/src/com/intellij/debugger/memory/utils/AbstractTableModelWithColumns.java index d0a67129078c..d14633c11473 100644 --- a/java/debugger/impl/src/com/intellij/debugger/memory/utils/AbstractTableModelWithColumns.java +++ b/java/debugger/impl/src/com/intellij/debugger/memory/utils/AbstractTableModelWithColumns.java @@ -1,4 +1,19 @@ -package org.jetbrains.debugger.memory.utils; +/* + * Copyright 2000-2016 JetBrains s.r.o. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.intellij.debugger.memory.utils; import javax.swing.table.AbstractTableModel; diff --git a/java/debugger/impl/src/com/intellij/debugger/memory/utils/AndroidUtil.java b/java/debugger/impl/src/com/intellij/debugger/memory/utils/AndroidUtil.java index 112297fd4a96..4b22c6f9df6c 100644 --- a/java/debugger/impl/src/com/intellij/debugger/memory/utils/AndroidUtil.java +++ b/java/debugger/impl/src/com/intellij/debugger/memory/utils/AndroidUtil.java @@ -1,13 +1,30 @@ -package org.jetbrains.debugger.memory.utils; +/* + * Copyright 2000-2016 JetBrains s.r.o. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.intellij.debugger.memory.utils; import com.sun.jdi.VirtualMachine; import org.jetbrains.annotations.NotNull; +import java.util.Locale; + public class AndroidUtil { public static final int ANDROID_COUNT_BY_CLASSES_BATCH_SIZE = 500; public static final int ANDROID_INSTANCES_LIMIT = 30000; public static boolean isAndroidVM(@NotNull VirtualMachine vm) { - return vm.name().toLowerCase().contains("dalvik"); + return vm.name().toLowerCase(Locale.ENGLISH).contains("dalvik"); } } diff --git a/java/debugger/impl/src/com/intellij/debugger/memory/utils/ErrorsValueGroup.java b/java/debugger/impl/src/com/intellij/debugger/memory/utils/ErrorsValueGroup.java index 922a70cf9af3..50ba2cf355ef 100644 --- a/java/debugger/impl/src/com/intellij/debugger/memory/utils/ErrorsValueGroup.java +++ b/java/debugger/impl/src/com/intellij/debugger/memory/utils/ErrorsValueGroup.java @@ -1,4 +1,19 @@ -package org.jetbrains.debugger.memory.utils; +/* + * Copyright 2000-2016 JetBrains s.r.o. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.intellij.debugger.memory.utils; import com.intellij.icons.AllIcons; import com.intellij.util.containers.HashMap; @@ -17,8 +32,8 @@ import java.util.Map; public class ErrorsValueGroup extends XValueGroup { private final Map> myErrorMessage2ValueMap = new HashMap<>(); - public ErrorsValueGroup(@NotNull String name) { - super(name); + public ErrorsValueGroup() { + super("Errors"); } public void addErrorValue(@NotNull String message, @NotNull XNamedValue value) { diff --git a/java/debugger/impl/src/com/intellij/debugger/memory/utils/InstanceJavaValue.java b/java/debugger/impl/src/com/intellij/debugger/memory/utils/InstanceJavaValue.java index 0c8046e6c989..a7d2042bce74 100644 --- a/java/debugger/impl/src/com/intellij/debugger/memory/utils/InstanceJavaValue.java +++ b/java/debugger/impl/src/com/intellij/debugger/memory/utils/InstanceJavaValue.java @@ -1,4 +1,19 @@ -package org.jetbrains.debugger.memory.utils; +/* + * Copyright 2000-2016 JetBrains s.r.o. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.intellij.debugger.memory.utils; import com.intellij.debugger.engine.JavaValue; import com.intellij.debugger.engine.evaluation.EvaluationContextImpl; @@ -9,12 +24,10 @@ import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; public class InstanceJavaValue extends JavaValue { - public InstanceJavaValue(JavaValue javaValue, - @NotNull ValueDescriptorImpl valueDescriptor, - @NotNull EvaluationContextImpl evaluationContext, - NodeManagerImpl nodeManager, - boolean contextSet) { - super(javaValue, valueDescriptor, evaluationContext, nodeManager, contextSet); + public InstanceJavaValue(@NotNull ValueDescriptorImpl valueDescriptor, + @NotNull EvaluationContextImpl evaluationContext, + NodeManagerImpl nodeManager) { + super(null, valueDescriptor, evaluationContext, nodeManager, false); } @Nullable diff --git a/java/debugger/impl/src/com/intellij/debugger/memory/utils/InstanceValueDescriptor.java b/java/debugger/impl/src/com/intellij/debugger/memory/utils/InstanceValueDescriptor.java index 101d30714643..76202a68253e 100644 --- a/java/debugger/impl/src/com/intellij/debugger/memory/utils/InstanceValueDescriptor.java +++ b/java/debugger/impl/src/com/intellij/debugger/memory/utils/InstanceValueDescriptor.java @@ -1,4 +1,19 @@ -package org.jetbrains.debugger.memory.utils; +/* + * Copyright 2000-2016 JetBrains s.r.o. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.intellij.debugger.memory.utils; import com.intellij.debugger.DebuggerContext; import com.intellij.debugger.engine.ContextUtil; diff --git a/java/debugger/impl/src/com/intellij/debugger/memory/utils/InstancesProvider.java b/java/debugger/impl/src/com/intellij/debugger/memory/utils/InstancesProvider.java index 1a717474b1b8..159c5884e5ff 100644 --- a/java/debugger/impl/src/com/intellij/debugger/memory/utils/InstancesProvider.java +++ b/java/debugger/impl/src/com/intellij/debugger/memory/utils/InstancesProvider.java @@ -1,4 +1,19 @@ -package org.jetbrains.debugger.memory.utils; +/* + * Copyright 2000-2016 JetBrains s.r.o. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.intellij.debugger.memory.utils; import com.sun.jdi.ObjectReference; import org.jetbrains.annotations.NotNull; diff --git a/java/debugger/impl/src/com/intellij/debugger/memory/utils/KeyboardUtils.java b/java/debugger/impl/src/com/intellij/debugger/memory/utils/KeyboardUtils.java index 57899d96122e..0ae37a822941 100644 --- a/java/debugger/impl/src/com/intellij/debugger/memory/utils/KeyboardUtils.java +++ b/java/debugger/impl/src/com/intellij/debugger/memory/utils/KeyboardUtils.java @@ -1,4 +1,19 @@ -package org.jetbrains.debugger.memory.utils; +/* + * Copyright 2000-2016 JetBrains s.r.o. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.intellij.debugger.memory.utils; import java.awt.event.KeyEvent; diff --git a/java/debugger/impl/src/com/intellij/debugger/memory/utils/LowestPriorityCommand.java b/java/debugger/impl/src/com/intellij/debugger/memory/utils/LowestPriorityCommand.java index 214cc5681f66..757b990a826d 100644 --- a/java/debugger/impl/src/com/intellij/debugger/memory/utils/LowestPriorityCommand.java +++ b/java/debugger/impl/src/com/intellij/debugger/memory/utils/LowestPriorityCommand.java @@ -1,4 +1,19 @@ -package org.jetbrains.debugger.memory.utils; +/* + * Copyright 2000-2016 JetBrains s.r.o. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.intellij.debugger.memory.utils; import com.intellij.debugger.engine.SuspendContextImpl; import com.intellij.debugger.engine.events.SuspendContextCommandImpl; diff --git a/java/debugger/impl/src/com/intellij/debugger/memory/utils/NamesUtils.java b/java/debugger/impl/src/com/intellij/debugger/memory/utils/NamesUtils.java index d7fdd334bf7d..eec5e949ba72 100644 --- a/java/debugger/impl/src/com/intellij/debugger/memory/utils/NamesUtils.java +++ b/java/debugger/impl/src/com/intellij/debugger/memory/utils/NamesUtils.java @@ -1,4 +1,19 @@ -package org.jetbrains.debugger.memory.utils; +/* + * Copyright 2000-2016 JetBrains s.r.o. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.intellij.debugger.memory.utils; import com.intellij.openapi.util.text.StringUtil; import com.sun.jdi.ArrayReference; diff --git a/java/debugger/impl/src/com/intellij/debugger/memory/utils/SingleAlarmWithMutableDelay.java b/java/debugger/impl/src/com/intellij/debugger/memory/utils/SingleAlarmWithMutableDelay.java index 321cf46828c8..7537abbb6d38 100644 --- a/java/debugger/impl/src/com/intellij/debugger/memory/utils/SingleAlarmWithMutableDelay.java +++ b/java/debugger/impl/src/com/intellij/debugger/memory/utils/SingleAlarmWithMutableDelay.java @@ -1,4 +1,19 @@ -package org.jetbrains.debugger.memory.utils; +/* + * Copyright 2000-2016 JetBrains s.r.o. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.intellij.debugger.memory.utils; import com.intellij.openapi.Disposable; import com.intellij.util.Alarm; diff --git a/java/debugger/impl/src/com/intellij/debugger/memory/utils/StackFrameDescriptor.java b/java/debugger/impl/src/com/intellij/debugger/memory/utils/StackFrameDescriptor.java index 7d1ca9d6ebef..e37f6ec763c3 100644 --- a/java/debugger/impl/src/com/intellij/debugger/memory/utils/StackFrameDescriptor.java +++ b/java/debugger/impl/src/com/intellij/debugger/memory/utils/StackFrameDescriptor.java @@ -1,4 +1,19 @@ -package org.jetbrains.debugger.memory.utils; +/* + * Copyright 2000-2016 JetBrains s.r.o. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.intellij.debugger.memory.utils; import com.intellij.openapi.util.text.StringUtil; import org.jetbrains.annotations.NotNull; diff --git a/platform/icons/src/debugger/memoryView/classTracked.png b/platform/icons/src/debugger/memoryView/classTracked.png new file mode 100644 index 0000000000000000000000000000000000000000..27cdbcb6c2d0463533c25301f862d200a30ee45a GIT binary patch literal 378 zcmV-=0fqjFP)OfMT{_To$K`rsChLI zuK>|N{%^;b?pNb?rF8)L;UF65vTHyMH$h&+Fu-k|&u5^*J3xFBh;IRH{0_uFf$T*< zJP$+z`M-f0da%ZV^K8$PiF-2s0~P)U(LnwQmpNV_11+%$1-mMp&T&6emJw`JLD^@X8#yxzN**e7(l6_Yw#a@n6Zfmo(mux$**C2{1D?6WXi8kX#(s$I>?pI zA%I#%ktwR^z%v1arhEyL4VDEeZn%!s>~VF~(&mU*i4c8MP`~p90xTz0Gz4!?RYem4 z@{d|KVxp(^_KMi{v(;{Jf(Z1cf1xQy*@KSQzH8@CzN{{(yV*A?|`MY*?2O zpmwJ7Vj=(^HvMR9vfQsay*&mJxWNe`(Ap5+fmJv{Ca=b%{}gs??yNlT+zYh@ZN+uy z{|li+GPznsDRMo108EfBik9B4{LT@F7)am-Cy05?1s`FB?wX?3cQMg$$?38y<=u`G z5P~mYmw2-DGmLq}hK$gShJ&=Gn#6SXfK~vG^ z8temYBmV@;Jy3eK{8zXN=iwxTq23g;wlF?nki5Mu>!Vm3;Qux?ZWi{~xaEkRCWQ*ULh9vYr z0^$&3{QOO=pYtjKEUiUj);3!j9>Q;M9WKHtbF}ab0w{-$c2++|I!Rbb`Sc?~gGn}i zV@^X7dLY4j@~&unY>M&nZ_z=0+R$Q_%~4S?1pROqZop-sIsYeY(!PwNc;*1g2eb{G zk@B)5Y`q|Jb;dL#bzcehHD2~H?cwP0F(LjE6B1;Utm;y{n#t-D;2{A4Kf$XQw_ra+WOEpq786sUpPQED6jrw zRK%E8;$-F^KMvPV(Kd>{-Rih`3!u<=be3F6_h+tGH#!d2Xfsb9v-qFUVt{P?4}u>> UDE4W#(EtDd07*qoM6N<$f+}M6=l}o! literal 0 HcmV?d00001 diff --git a/platform/icons/src/debugger/memoryView/classTracked@2x_dark.png b/platform/icons/src/debugger/memoryView/classTracked@2x_dark.png new file mode 100644 index 0000000000000000000000000000000000000000..3775b78fcc386bf128d8925bf1de2411b0a67e1c GIT binary patch literal 1047 zcmV+y1nB#TP)sWdUw6v_3r4_#-)VJ5kjJ-N`hJ; zF3>U8-U13Zo0b zWdv}#=+Hb}wD-0ED4t+6abkCA&xN;6#$Vl@?{l8TWt?S;-k=z+Ik zr%(#NiQ+f>nrJ=lz4qPjXlf(E#p}Z1_EBk|qYN@6AptRn^4U-qEY=hQ!?nY0#?h2PbbG z59Zo`fOAlUBhU-`*n)Hr0VeFb=bPS&sY38KI>k>hy<8PWpbRo3AweJE`za;(9YUDw zG06yIq$*s3^Ps>m^tICiyXKKov%y3^F8r$GMACp8L=W zK&SaSJ;WPu9e#vwL4z!t6_4R%wKkoXmh@vp4EP`R6}V}4kI!s-k1(JLBT)A2r_T6_ ze2vVo4^buFAk(Z#e8E^IVGJ(A1^5bU&Qc0rY1oRJ_zLg=&j8&w`+e)ZX-4yA z{1v$79cBvd6t%&b_oV5kdR+2-|{(8!Q8Yb%RGL^0h-}X)31}Kxca6( zkGb)K&HuphEsV{$hv{9;x)lpxX&odlkCi+%;?e8Lb6q#@~4G*mbrL R9xea?002ovPDHLkV1gC)^ZNh* literal 0 HcmV?d00001 diff --git a/platform/icons/src/debugger/memoryView/classTracked_dark.png b/platform/icons/src/debugger/memoryView/classTracked_dark.png new file mode 100644 index 0000000000000000000000000000000000000000..955ba09d608e4599a762e0727038940ed8173a49 GIT binary patch literal 351 zcmV-l0igbgP)#$Q1qP{T)^oehIbj_eLDCX5V_5cw)G#W?yA5KRahVH0DZ zLwQcq8ljgU(Zu1&+xOmk-sGHnt2vIN{RZt%0<7-cDn*#Wxnge#Rlot%VAl)TDqsih zVc+wG9|auUrd?Qw!>qj=n&2ZNR_&jSIdCf%9R_NEiSB002ovPDHLkV1kbno8JHc literal 0 HcmV?d00001 diff --git a/platform/icons/src/debugger/memoryView/toolWindowDisabled.png b/platform/icons/src/debugger/memoryView/toolWindowDisabled.png new file mode 100644 index 0000000000000000000000000000000000000000..9c6218fa9d163ba867fe082a762286f82ef4ca73 GIT binary patch literal 241 zcmeAS@N?(olHy`uVBq!ia0vp@Ak4u6ByT*@`3|IZc)B=-RNP8_k?V0$Q+@IM$_jA3QCU^3G{BB_t&d;9~^=;zw-mt$b pyWB}7F{r~JhIb;17E?D9!_~P554YRs2?71U;OXk;vd$@?2>_hYVoU%4 literal 0 HcmV?d00001 diff --git a/platform/icons/src/debugger/memoryView/toolWindowDisabled@2x.png b/platform/icons/src/debugger/memoryView/toolWindowDisabled@2x.png new file mode 100644 index 0000000000000000000000000000000000000000..68c7411bfa2d1ff95cd0e68d55a7b7d86e83b800 GIT binary patch literal 472 zcmV;}0Vn>6P)NklJbj?OfSP<{2O7o@rZ6>zh37_vrm0OwKN&G1R$a?>VF8TO;^^i6fLbd|NRQZc z&3MIQAJ8<_TC^55RlS8z%zU#oj3$NB7=^(glO!P;_LNV;0N8OClkCZih`|gduq3wrohTuGU#j@ zbC~`Sszrh6V>#s59hcSQrZ@1QbtA5%Mzqj$-w5ELauwz<$t1p~myf%aS3S!&X>P)4 zZl;gTeGDF>n_S9@Sdm;xzjemQ(aDpCt*=H1nwX?8u-U$dfpj%uaA~~5OdlJA;||u6 zF1C3LBzHuQ*kq4eLL2`sU}S%@br-NrAs0J!v0__gh42?Ir(oW8wzC^ z2pC~)ty5gV!;qc=}Co@ueF7- zMxDh#@J0-gj*gfm)alk=D%_7heH~sGw`=cyW{tfZp4Y#9JKe8e z@lM44dHGYrfK5C%4`_$SG_ZX-^U(5jdh)co9D56<1VLbO1G7 z2jZWF1^KsoYO^|^nipZU38_yc|0Io zQIhKkvJq+C!h<^cXxC_z%WdCok&fW>rh#t9%fVd2ZEs^4> p7^DGNBf2JRX>T>MMyNq>wE#<#a1#SuN6-KO002ovPDHLkV1hphcDn!o literal 0 HcmV?d00001 diff --git a/platform/icons/src/debugger/memoryView/toolWindowEnabled@2x.png b/platform/icons/src/debugger/memoryView/toolWindowEnabled@2x.png new file mode 100644 index 0000000000000000000000000000000000000000..0c617eeb83176833def52827eeeb624389d33080 GIT binary patch literal 604 zcmV-i0;BzjP)HD^46c6XX@YA?>$OQP#!+hU+&Jgi^KQc_G`7*$tk%+t|iFlrOhee3w zH3?sQ1wxTZ(S#Kk+$tV*}(N)!TfUk zCb5f!lAr~yrVYatsMcyV!*$&=NUnje;d(c3e*Ju~W@5prN~UccURsnmj`K|{_6yXD zuC>yrtUo5a&ZO<8_{`sQt?#GxHACE4Xi-w!2_?O3HoJ`Xq9}^VZ0D?Hw6e$VRtn~s zXk#~Q7@mRw_z9j3g(0S5Cuc2*@BVJT{yVc)*f6{kce5|0E~W!*{Ky&vq$AQeS(M3S zE?9&at>BHAAUaOOnX!h+6wwdWx;~4_Mo55>D8$9o|gmU}4JusJ7n zE;ej?o+IdBYr}fc+*^M{cWLcQUM3iToXruN}&1X~R<7CGD>)@o6#QZaOK7wq6r}(QBg$8!l)Rc#+YI$w9YgkzJ%^z z1wH{mQR_KnxH!&WxfmB8X-a24=A3_~cx>DD^kSt}+So(l;pap=u~_UI;}G$4BKTno z*ceuXV?}fRyC^(@C@d6FN`=6n5XUBP3Y3THkw7v7>{7E(qm(oqOua_6EVVAe5tt|l zC_sJCFw}u)JRT3Js#*fcCtyqXr1|SdOIcfo38NyJv~=*HphVa8CpOs|AtXtQ>Jq(4 zOgrE;F;n=R^Gq@KY@Muz*vedsf@~!yscM=QfO}pT$ bmh|Wg;=pAPy}iS900000NkvXXu0mjff07T9 literal 0 HcmV?d00001 diff --git a/platform/icons/src/debugger/memoryView/toolWindowEnabled_dark.png b/platform/icons/src/debugger/memoryView/toolWindowEnabled_dark.png new file mode 100644 index 0000000000000000000000000000000000000000..10ba7a9b194da2e90b93763cc4ede23893d61800 GIT binary patch literal 304 zcmV-00nh%4P)Upjlt@9LFHkD(gNa5?V^ z5W7H~#{709LQmVX*#;PLkJ{}tO>=Y%a<E9-u=Z{;dB10000 + + + com.intellij.debugger.memory.component.MemoryViewManager + + + com.intellij.debugger.ui.DebuggerPanelsManager @@ -10,7 +16,7 @@ com.intellij.debugger.ui.HotSwapUI - com.intellij.debugger.ui.HotSwapUIImpl + com.intellij.debugger.ui.HotSwapUIImpl @@ -19,8 +25,24 @@ + + + com.intellij.debugger.memory.component.InstancesTracker + + + + com.intellij.debugger.memory.component.CreationPositionTracker + + + + +