Merge remote-tracking branch 'origin/master'

This commit is contained in:
Mikhail Golubev
2015-09-15 16:15:55 +03:00
15 changed files with 702 additions and 927 deletions
@@ -20,6 +20,7 @@ import com.intellij.debugger.SourcePosition;
import com.intellij.debugger.engine.evaluation.EvaluateException;
import com.intellij.debugger.engine.evaluation.EvaluationContextImpl;
import com.intellij.debugger.engine.evaluation.TextWithImports;
import com.intellij.debugger.engine.evaluation.TextWithImportsImpl;
import com.intellij.debugger.engine.events.DebuggerContextCommandImpl;
import com.intellij.debugger.impl.DebuggerContextImpl;
import com.intellij.debugger.impl.DebuggerSession;
@@ -31,16 +32,21 @@ import com.intellij.debugger.jdi.StackFrameProxyImpl;
import com.intellij.debugger.settings.DebuggerSettings;
import com.intellij.debugger.settings.NodeRendererSettings;
import com.intellij.debugger.ui.breakpoints.Breakpoint;
import com.intellij.debugger.ui.impl.FrameVariablesTree;
import com.intellij.debugger.ui.impl.watch.*;
import com.intellij.debugger.ui.tree.render.DescriptorLabelListener;
import com.intellij.lang.java.JavaLanguage;
import com.intellij.openapi.application.ApplicationManager;
import com.intellij.openapi.diagnostic.Logger;
import com.intellij.openapi.util.Computable;
import com.intellij.openapi.util.Pair;
import com.intellij.openapi.editor.Document;
import com.intellij.openapi.fileEditor.FileDocumentManager;
import com.intellij.openapi.util.*;
import com.intellij.openapi.util.text.StringUtil;
import com.intellij.openapi.vfs.VirtualFile;
import com.intellij.psi.*;
import com.intellij.psi.util.PsiTreeUtil;
import com.intellij.ui.ColoredTextContainer;
import com.intellij.util.containers.ContainerUtil;
import com.intellij.util.text.CharArrayUtil;
import com.intellij.xdebugger.XDebugSession;
import com.intellij.xdebugger.XSourcePosition;
import com.intellij.xdebugger.evaluation.XDebuggerEvaluator;
@@ -55,9 +61,7 @@ import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import javax.swing.*;
import java.util.HashSet;
import java.util.Map;
import java.util.Set;
import java.util.*;
/**
* @author egor
@@ -246,7 +250,7 @@ public class JavaStackFrame extends XStackFrame {
// copied from FrameVariablesTree
private void buildVariables(DebuggerContextImpl debuggerContext,
EvaluationContextImpl evaluationContext,
final EvaluationContextImpl evaluationContext,
@NotNull DebugProcessImpl debugProcess,
XValueChildrenList children,
ObjectReference thisObjectReference,
@@ -283,13 +287,12 @@ public class JavaStackFrame extends XStackFrame {
superBuildVariables(evaluationContext, children);
}
else {
final Map<String, LocalVariableProxyImpl> visibleVariables = FrameVariablesTree.getVisibleVariables(getStackFrameProxy());
final EvaluationContextImpl evalContext = evaluationContext;
final Map<String, LocalVariableProxyImpl> visibleVariables = getVisibleVariables(getStackFrameProxy());
final Pair<Set<String>, Set<TextWithImports>> usedVars =
ApplicationManager.getApplication().runReadAction(new Computable<Pair<Set<String>, Set<TextWithImports>>>() {
@Override
public Pair<Set<String>, Set<TextWithImports>> compute() {
return FrameVariablesTree.findReferencedVars(ContainerUtil.union(visibleVariables.keySet(), visibleLocals), sourcePosition, evalContext);
return findReferencedVars(ContainerUtil.union(visibleVariables.keySet(), visibleLocals), sourcePosition, evaluationContext);
}
});
// add locals
@@ -307,7 +310,7 @@ public class JavaStackFrame extends XStackFrame {
final EvaluationContextImpl evalContextCopy = evaluationContext.createEvaluationContext(evaluationContext.getThisObject());
evalContextCopy.setAutoLoadClasses(false);
final Set<TextWithImports> extraVars = computeExtraVars(usedVars, sourcePosition, evalContext);
final Set<TextWithImports> extraVars = computeExtraVars(usedVars, sourcePosition, evaluationContext);
// add extra vars
addToChildrenFrom(extraVars, children, evaluationContext);
@@ -420,4 +423,267 @@ public class JavaStackFrame extends XStackFrame {
return "JavaFrame position unknown";
}
}
private static class VariablesCollector extends JavaRecursiveElementVisitor {
private final Set<String> myVisibleLocals;
private final TextRange myLineRange;
private final Set<TextWithImports> myExpressions;
private final Set<String> myVars;
private final SourcePosition myPosition;
private final EvaluationContextImpl myEvalContext;
private final boolean myCollectExpressions;
public VariablesCollector(final Set<String> visibleLocals,
final TextRange lineRange,
final Set<TextWithImports> expressions,
final Set<String> vars,
SourcePosition position, EvaluationContextImpl evalContext) {
myVisibleLocals = visibleLocals;
myLineRange = lineRange;
myExpressions = expressions;
myVars = vars;
myPosition = position;
myEvalContext = evalContext;
myCollectExpressions = XDebuggerSettingsManager.getInstance().getDataViewSettings().isAutoExpressions();
}
@Override
public void visitElement(final PsiElement element) {
if (myLineRange.intersects(element.getTextRange())) {
super.visitElement(element);
}
}
@Override
public void visitMethodCallExpression(final PsiMethodCallExpression expression) {
if (myCollectExpressions) {
final PsiMethod psiMethod = expression.resolveMethod();
if (psiMethod != null && !DebuggerUtils.hasSideEffectsOrReferencesMissingVars(expression, myVisibleLocals)) {
myExpressions.add(new TextWithImportsImpl(expression));
}
}
super.visitMethodCallExpression(expression);
}
@Override
public void visitReferenceExpression(final PsiReferenceExpression reference) {
if (myLineRange.intersects(reference.getTextRange())) {
final PsiElement psiElement = reference.resolve();
if (psiElement instanceof PsiVariable) {
final PsiVariable var = (PsiVariable)psiElement;
if (var instanceof PsiField) {
if (myCollectExpressions && !DebuggerUtils.hasSideEffectsOrReferencesMissingVars(reference, myVisibleLocals)) {
/*
if (var instanceof PsiEnumConstant && reference.getQualifier() == null) {
final PsiClass enumClass = ((PsiEnumConstant)var).getContainingClass();
if (enumClass != null) {
final PsiExpression expression = JavaPsiFacade.getInstance(var.getProject()).getParserFacade().createExpressionFromText(enumClass.getName() + "." + var.getName(), var);
final PsiReference ref = expression.getReference();
if (ref != null) {
ref.bindToElement(var);
myExpressions.add(new TextWithImportsImpl(expression));
}
}
}
else {
myExpressions.add(new TextWithImportsImpl(reference));
}
*/
final PsiModifierList modifierList = var.getModifierList();
boolean isConstant = (var instanceof PsiEnumConstant) ||
(modifierList != null && modifierList.hasModifierProperty(PsiModifier.STATIC) && modifierList.hasModifierProperty(PsiModifier.FINAL));
if (!isConstant) {
myExpressions.add(new TextWithImportsImpl(reference));
}
}
}
else {
if (myVisibleLocals.contains(var.getName())) {
myVars.add(var.getName());
}
else {
// fix for variables used in inner classes
if (!Comparing.equal(PsiTreeUtil.getParentOfType(reference, PsiClass.class),
PsiTreeUtil.getParentOfType(var, PsiClass.class))) {
myExpressions.add(new TextWithImportsImpl(reference));
}
}
}
}
}
super.visitReferenceExpression(reference);
}
@Override
public void visitArrayAccessExpression(final PsiArrayAccessExpression expression) {
if (myCollectExpressions && !DebuggerUtils.hasSideEffectsOrReferencesMissingVars(expression, myVisibleLocals)) {
myExpressions.add(new TextWithImportsImpl(expression));
}
super.visitArrayAccessExpression(expression);
}
@Override
public void visitParameter(final PsiParameter parameter) {
processVariable(parameter);
super.visitParameter(parameter);
}
@Override
public void visitLocalVariable(final PsiLocalVariable variable) {
processVariable(variable);
super.visitLocalVariable(variable);
}
private void processVariable(final PsiVariable variable) {
if (myLineRange.intersects(variable.getTextRange()) && myVisibleLocals.contains(variable.getName())) {
myVars.add(variable.getName());
}
}
@Override
public void visitClass(final PsiClass aClass) {
// Do not step in to local and anonymous classes...
}
}
public static Map<String, LocalVariableProxyImpl> getVisibleVariables(final StackFrameProxyImpl frame) throws EvaluateException {
final Map<String, LocalVariableProxyImpl> vars = new HashMap<String, LocalVariableProxyImpl>();
for (LocalVariableProxyImpl localVariableProxy : frame.visibleVariables()) {
vars.put(localVariableProxy.name(), localVariableProxy);
}
return vars;
}
private static boolean shouldSkipLine(final PsiFile file, Document doc, int line) {
final int start = doc.getLineStartOffset(line);
final int end = doc.getLineEndOffset(line);
final int _start = CharArrayUtil.shiftForward(doc.getCharsSequence(), start, " \n\t");
if (_start >= end) {
return true;
}
TextRange alreadyChecked = null;
for (PsiElement elem = file.findElementAt(_start); elem != null && elem.getTextOffset() <= end && (alreadyChecked == null || !alreadyChecked .contains(elem.getTextRange())); elem = elem.getNextSibling()) {
for (PsiElement _elem = elem; _elem.getTextOffset() >= _start; _elem = _elem.getParent()) {
alreadyChecked = _elem.getTextRange();
if (_elem instanceof PsiDeclarationStatement) {
final PsiElement[] declared = ((PsiDeclarationStatement)_elem).getDeclaredElements();
for (PsiElement declaredElement : declared) {
if (declaredElement instanceof PsiVariable) {
return false;
}
}
}
if (_elem instanceof PsiJavaCodeReferenceElement) {
final PsiElement resolved = ((PsiJavaCodeReferenceElement)_elem).resolve();
if (resolved instanceof PsiVariable) {
return false;
}
}
}
}
return true;
}
public static Pair<Set<String>, Set<TextWithImports>> findReferencedVars(final Set<String> visibleVars,
final SourcePosition position,
EvaluationContextImpl evalContext) {
final int line = position.getLine();
if (line < 0) {
return Pair.create(Collections.<String>emptySet(), Collections.<TextWithImports>emptySet());
}
final PsiFile positionFile = position.getFile();
if (!positionFile.getLanguage().isKindOf(JavaLanguage.INSTANCE)) {
return Pair.create(visibleVars, Collections.<TextWithImports>emptySet());
}
final VirtualFile vFile = positionFile.getVirtualFile();
final Document doc = vFile != null ? FileDocumentManager.getInstance().getDocument(vFile) : null;
if (doc == null || doc.getLineCount() == 0 || line > (doc.getLineCount() - 1)) {
return Pair.create(Collections.<String>emptySet(), Collections.<TextWithImports>emptySet());
}
final TextRange limit = calculateLimitRange(positionFile, doc, line);
int startLine = Math.max(limit.getStartOffset(), line - 1);
startLine = Math.min(startLine, limit.getEndOffset());
while (startLine > limit.getStartOffset() && shouldSkipLine(positionFile, doc, startLine)) {
startLine--;
}
final int startOffset = doc.getLineStartOffset(startLine);
int endLine = Math.min(line + 2, limit.getEndOffset());
while (endLine < limit.getEndOffset() && shouldSkipLine(positionFile, doc, endLine)) {
endLine++;
}
final int endOffset = doc.getLineEndOffset(endLine);
final TextRange lineRange = new TextRange(startOffset, endOffset);
if (!lineRange.isEmpty()) {
final int offset = CharArrayUtil.shiftForward(doc.getCharsSequence(), doc.getLineStartOffset(line), " \t");
PsiElement element = positionFile.findElementAt(offset);
if (element != null) {
PsiMethod method = PsiTreeUtil.getNonStrictParentOfType(element, PsiMethod.class);
if (method != null) {
element = method;
}
else {
PsiField field = PsiTreeUtil.getNonStrictParentOfType(element, PsiField.class);
if (field != null) {
element = field;
}
else {
final PsiClassInitializer initializer = PsiTreeUtil.getNonStrictParentOfType(element, PsiClassInitializer.class);
if (initializer != null) {
element = initializer;
}
}
}
//noinspection unchecked
if (element instanceof PsiCompiledElement) {
return Pair.create(visibleVars, Collections.<TextWithImports>emptySet());
}
else {
final Set<String> vars = new HashSet<String>();
final Set<TextWithImports> expressions = new HashSet<TextWithImports>();
final PsiElementVisitor variablesCollector = new VariablesCollector(visibleVars, adjustRange(element, lineRange), expressions, vars, position, evalContext);
element.accept(variablesCollector);
return Pair.create(vars, expressions);
}
}
}
return Pair.create(Collections.<String>emptySet(), Collections.<TextWithImports>emptySet());
}
private static TextRange calculateLimitRange(final PsiFile file, final Document doc, final int line) {
final int offset = doc.getLineStartOffset(line);
if (offset > 0) {
PsiMethod method = PsiTreeUtil.getParentOfType(file.findElementAt(offset), PsiMethod.class, false);
if (method != null) {
final TextRange elemRange = method.getTextRange();
return new TextRange(doc.getLineNumber(elemRange.getStartOffset()), doc.getLineNumber(elemRange.getEndOffset()));
}
}
return new TextRange(0, doc.getLineCount() - 1);
}
private static TextRange adjustRange(final PsiElement element, final TextRange originalRange) {
final Ref<TextRange> rangeRef = new Ref<TextRange>(originalRange);
element.accept(new JavaRecursiveElementVisitor() {
@Override public void visitExpressionStatement(final PsiExpressionStatement statement) {
final TextRange stRange = statement.getTextRange();
if (originalRange.intersects(stRange)) {
final TextRange currentRange = rangeRef.get();
final int start = Math.min(currentRange.getStartOffset(), stRange.getStartOffset());
final int end = Math.max(currentRange.getEndOffset(), stRange.getEndOffset());
rangeRef.set(new TextRange(start, end));
}
}
});
return rangeRef.get();
}
}
@@ -1,614 +0,0 @@
/*
* Copyright 2000-2015 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.
*/
/*
* Class FrameDebuggerTree
* @author Jeka
*/
package com.intellij.debugger.ui.impl;
import com.intellij.debugger.DebuggerBundle;
import com.intellij.debugger.DebuggerInvocationUtil;
import com.intellij.debugger.SourcePosition;
import com.intellij.debugger.engine.DebuggerUtils;
import com.intellij.debugger.engine.SuspendManager;
import com.intellij.debugger.engine.evaluation.EvaluateException;
import com.intellij.debugger.engine.evaluation.EvaluationContextImpl;
import com.intellij.debugger.engine.evaluation.TextWithImports;
import com.intellij.debugger.engine.evaluation.TextWithImportsImpl;
import com.intellij.debugger.engine.events.DebuggerCommandImpl;
import com.intellij.debugger.impl.DebuggerContextImpl;
import com.intellij.debugger.impl.DebuggerSession;
import com.intellij.debugger.jdi.*;
import com.intellij.debugger.settings.ViewsGeneralSettings;
import com.intellij.debugger.ui.impl.watch.*;
import com.intellij.lang.java.JavaLanguage;
import com.intellij.openapi.application.ApplicationManager;
import com.intellij.openapi.diagnostic.Logger;
import com.intellij.openapi.editor.Document;
import com.intellij.openapi.fileEditor.FileDocumentManager;
import com.intellij.openapi.project.Project;
import com.intellij.openapi.util.*;
import com.intellij.openapi.vfs.VirtualFile;
import com.intellij.psi.*;
import com.intellij.psi.util.PsiTreeUtil;
import com.intellij.util.text.CharArrayUtil;
import com.intellij.util.ui.tree.TreeModelAdapter;
import com.intellij.xdebugger.XDebuggerBundle;
import com.intellij.xdebugger.frame.XStackFrame;
import com.intellij.xdebugger.settings.XDebuggerSettingsManager;
import com.sun.jdi.AbsentInformationException;
import com.sun.jdi.ObjectCollectedException;
import com.sun.jdi.Value;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import javax.swing.event.TreeModelEvent;
import javax.swing.tree.TreeModel;
import javax.swing.tree.TreePath;
import java.util.*;
public class FrameVariablesTree extends DebuggerTree {
private static final Logger LOG = Logger.getInstance(FrameVariablesTree.class);
private boolean myAnyNewLocals;
private boolean myAutoWatchMode = false;
private final VariablesPanel myVariablesPanel;
public FrameVariablesTree(@NotNull Project project) {
this(project, null);
}
public FrameVariablesTree(@NotNull Project project, @Nullable VariablesPanel variablesPanel) {
super(project);
getEmptyText().setText(XDebuggerBundle.message("debugger.variables.not.available"));
myVariablesPanel = variablesPanel;
}
public boolean isAutoWatchMode() {
return myAutoWatchMode;
}
public void setAutoVariablesMode(final boolean autoWatchMode) {
final boolean valueChanged = myAutoWatchMode != autoWatchMode;
myAutoWatchMode = autoWatchMode;
if (valueChanged) {
rebuild(getDebuggerContext());
}
}
@Override
protected void build(DebuggerContextImpl context) {
myAnyNewLocals = false;
buildWhenPaused(context, new RefreshFrameTreeCommand(context));
}
@Override
public void restoreNodeState(DebuggerTreeNodeImpl node) {
if (myAnyNewLocals) {
final NodeDescriptorImpl descriptor = node.getDescriptor();
final boolean isLocalVar = descriptor instanceof LocalVariableDescriptorImpl;
descriptor.myIsSelected &= isLocalVar;
// override this setting so that tree will scroll to new locals
descriptor.myIsVisible = isLocalVar && descriptor.myIsSelected;
if (!descriptor.myIsVisible) {
descriptor.putUserData(VISIBLE_RECT, null);
}
}
super.restoreNodeState(node);
if (myAnyNewLocals && node.getDescriptor().myIsExpanded) {
DebuggerTreeNodeImpl root = (DebuggerTreeNodeImpl)getMutableModel().getRoot();
scrollToVisible(root);
}
}
@Override
protected DebuggerCommandImpl getBuildNodeCommand(@NotNull DebuggerTreeNodeImpl node) {
if (node.getDescriptor() instanceof StackFrameDescriptorImpl) {
return new BuildFrameTreeVariablesCommand(node);
}
return super.getBuildNodeCommand(node);
}
private class BuildFrameTreeVariablesCommand extends BuildStackFrameCommand {
public BuildFrameTreeVariablesCommand(DebuggerTreeNodeImpl stackNode) {
super(stackNode);
}
@Override
public void threadAction() {
if (myVariablesPanel != null) {
StackFrameDescriptorImpl stackDescriptor = (StackFrameDescriptorImpl)getNode().getDescriptor();
XStackFrame xStackFrame = stackDescriptor.getXStackFrame();
myVariablesPanel.stackChanged(xStackFrame);
if (xStackFrame != null) {
return;
}
}
super.threadAction();
}
@Override
protected void buildVariables(final StackFrameDescriptorImpl stackDescriptor, final EvaluationContextImpl evaluationContext) throws EvaluateException {
final DebuggerContextImpl debuggerContext = getDebuggerContext();
final SourcePosition sourcePosition = debuggerContext.getSourcePosition();
if (sourcePosition == null) {
return;
}
try {
if (!XDebuggerSettingsManager.getInstance().getDataViewSettings().isAutoExpressions() && !myAutoWatchMode) {
// optimization
super.buildVariables(stackDescriptor, evaluationContext);
}
else {
final Map<String, LocalVariableProxyImpl> visibleVariables = getVisibleVariables(stackDescriptor.getFrameProxy());
final EvaluationContextImpl evalContext = debuggerContext.createEvaluationContext();
final Pair<Set<String>, Set<TextWithImports>> usedVars =
ApplicationManager.getApplication().runReadAction(new Computable<Pair<Set<String>, Set<TextWithImports>>>() {
@Override
public Pair<Set<String>, Set<TextWithImports>> compute() {
return findReferencedVars(visibleVariables.keySet(), sourcePosition, evalContext);
}
});
// add locals
if (myAutoWatchMode) {
for (String var : usedVars.first) {
final LocalVariableDescriptorImpl descriptor = myNodeManager.getLocalVariableDescriptor(stackDescriptor, visibleVariables.get(var));
myChildren.add(myNodeManager.createNode(descriptor, evaluationContext));
}
}
else {
super.buildVariables(stackDescriptor, evaluationContext);
}
// add expressions
final EvaluationContextImpl evalContextCopy = evaluationContext.createEvaluationContext(evaluationContext.getThisObject());
evalContextCopy.setAutoLoadClasses(false);
for (TextWithImports text : usedVars.second) {
myChildren.add(myNodeManager.createNode(myNodeManager.getWatchItemDescriptor(stackDescriptor, text, null), evalContextCopy));
}
}
}
catch (EvaluateException e) {
if (e.getCause() instanceof AbsentInformationException) {
final StackFrameProxyImpl frame = stackDescriptor.getFrameProxy();
final Collection<Value> argValues = frame.getArgumentValues();
int index = 0;
for (Value argValue : argValues) {
final ArgumentValueDescriptorImpl descriptor =
myNodeManager.getArgumentValueDescriptor(stackDescriptor, index++, argValue, false);
final DebuggerTreeNodeImpl variableNode = myNodeManager.createNode(descriptor, evaluationContext);
myChildren.add(variableNode);
}
myChildren.add(myNodeManager.createMessageNode(MessageDescriptor.LOCAL_VARIABLES_INFO_UNAVAILABLE));
// trying to collect values from variable slots
try {
Map<DecompiledLocalVariable, Value> values = LocalVariablesUtil.fetchValues(frame);
for (Map.Entry<DecompiledLocalVariable, Value> entry : values.entrySet()) {
DecompiledLocalVariable var = entry.getKey();
final ArgumentValueDescriptorImpl descriptor =
myNodeManager.getArgumentValueDescriptor(stackDescriptor, var.getSlot(), entry.getValue(), var.isParam());
final DebuggerTreeNodeImpl variableNode = myNodeManager.createNode(descriptor, evaluationContext);
myChildren.add(variableNode);
}
}
catch (Exception ex) {
LOG.info(ex);
}
}
else {
throw e;
}
}
}
}
public static Map<String, LocalVariableProxyImpl> getVisibleVariables(final StackFrameProxyImpl frame) throws EvaluateException {
final Map<String, LocalVariableProxyImpl> vars = new HashMap<String, LocalVariableProxyImpl>();
for (LocalVariableProxyImpl localVariableProxy : frame.visibleVariables()) {
vars.put(localVariableProxy.name(), localVariableProxy);
}
return vars;
}
private static boolean shouldSkipLine(final PsiFile file, Document doc, int line) {
final int start = doc.getLineStartOffset(line);
final int end = doc.getLineEndOffset(line);
final int _start = CharArrayUtil.shiftForward(doc.getCharsSequence(), start, " \n\t");
if (_start >= end) {
return true;
}
TextRange alreadyChecked = null;
for (PsiElement elem = file.findElementAt(_start); elem != null && elem.getTextOffset() <= end && (alreadyChecked == null || !alreadyChecked .contains(elem.getTextRange())); elem = elem.getNextSibling()) {
for (PsiElement _elem = elem; _elem.getTextOffset() >= _start; _elem = _elem.getParent()) {
alreadyChecked = _elem.getTextRange();
if (_elem instanceof PsiDeclarationStatement) {
final PsiElement[] declared = ((PsiDeclarationStatement)_elem).getDeclaredElements();
for (PsiElement declaredElement : declared) {
if (declaredElement instanceof PsiVariable) {
return false;
}
}
}
if (_elem instanceof PsiJavaCodeReferenceElement) {
final PsiElement resolved = ((PsiJavaCodeReferenceElement)_elem).resolve();
if (resolved instanceof PsiVariable) {
return false;
}
}
}
}
return true;
}
public static Pair<Set<String>, Set<TextWithImports>> findReferencedVars(final Set<String> visibleVars,
final SourcePosition position,
EvaluationContextImpl evalContext) {
final int line = position.getLine();
if (line < 0) {
return Pair.create(Collections.<String>emptySet(), Collections.<TextWithImports>emptySet());
}
final PsiFile positionFile = position.getFile();
if (!positionFile.getLanguage().isKindOf(JavaLanguage.INSTANCE)) {
return Pair.create(visibleVars, Collections.<TextWithImports>emptySet());
}
final VirtualFile vFile = positionFile.getVirtualFile();
final Document doc = vFile != null? FileDocumentManager.getInstance().getDocument(vFile) : null;
if (doc == null || doc.getLineCount() == 0 || line > (doc.getLineCount() - 1)) {
return Pair.create(Collections.<String>emptySet(), Collections.<TextWithImports>emptySet());
}
final TextRange limit = calculateLimitRange(positionFile, doc, line);
int startLine = Math.max(limit.getStartOffset(), line - 1);
startLine = Math.min(startLine, limit.getEndOffset());
while (startLine > limit.getStartOffset() && shouldSkipLine(positionFile, doc, startLine)) {
startLine--;
}
final int startOffset = doc.getLineStartOffset(startLine);
int endLine = Math.min(line + 2, limit.getEndOffset());
while (endLine < limit.getEndOffset() && shouldSkipLine(positionFile, doc, endLine)) {
endLine++;
}
final int endOffset = doc.getLineEndOffset(endLine);
final TextRange lineRange = new TextRange(startOffset, endOffset);
if (!lineRange.isEmpty()) {
final int offset = CharArrayUtil.shiftForward(doc.getCharsSequence(), doc.getLineStartOffset(line), " \t");
PsiElement element = positionFile.findElementAt(offset);
if (element != null) {
PsiMethod method = PsiTreeUtil.getNonStrictParentOfType(element, PsiMethod.class);
if (method != null) {
element = method;
}
else {
PsiField field = PsiTreeUtil.getNonStrictParentOfType(element, PsiField.class);
if (field != null) {
element = field;
}
else {
final PsiClassInitializer initializer = PsiTreeUtil.getNonStrictParentOfType(element, PsiClassInitializer.class);
if (initializer != null) {
element = initializer;
}
}
}
//noinspection unchecked
if (element instanceof PsiCompiledElement) {
return Pair.create(visibleVars, Collections.<TextWithImports>emptySet());
}
else {
final Set<String> vars = new HashSet<String>();
final Set<TextWithImports> expressions = new HashSet<TextWithImports>();
final PsiElementVisitor variablesCollector = new VariablesCollector(visibleVars, adjustRange(element, lineRange), expressions, vars, position, evalContext);
element.accept(variablesCollector);
return Pair.create(vars, expressions);
}
}
}
return Pair.create(Collections.<String>emptySet(), Collections.<TextWithImports>emptySet());
}
private static TextRange calculateLimitRange(final PsiFile file, final Document doc, final int line) {
final int offset = doc.getLineStartOffset(line);
if (offset > 0) {
PsiMethod method = PsiTreeUtil.getParentOfType(file.findElementAt(offset), PsiMethod.class, false);
if (method != null) {
final TextRange elemRange = method.getTextRange();
return new TextRange(doc.getLineNumber(elemRange.getStartOffset()), doc.getLineNumber(elemRange.getEndOffset()));
}
}
return new TextRange(0, doc.getLineCount() - 1);
}
private static TextRange adjustRange(final PsiElement element, final TextRange originalRange) {
final Ref<TextRange> rangeRef = new Ref<TextRange>(originalRange);
element.accept(new JavaRecursiveElementVisitor() {
@Override public void visitExpressionStatement(final PsiExpressionStatement statement) {
final TextRange stRange = statement.getTextRange();
if (originalRange.intersects(stRange)) {
final TextRange currentRange = rangeRef.get();
final int start = Math.min(currentRange.getStartOffset(), stRange.getStartOffset());
final int end = Math.max(currentRange.getEndOffset(), stRange.getEndOffset());
rangeRef.set(new TextRange(start, end));
}
}
});
return rangeRef.get();
}
private class RefreshFrameTreeCommand extends RefreshDebuggerTreeCommand {
public RefreshFrameTreeCommand(DebuggerContextImpl context) {
super(context);
}
@Override
public void contextAction() throws Exception {
DebuggerTreeNodeImpl rootNode;
final DebuggerContextImpl debuggerContext = getDebuggerContext();
final ThreadReferenceProxyImpl currentThread = debuggerContext.getThreadProxy();
if (currentThread == null) {
return;
}
try {
StackFrameProxyImpl frame = debuggerContext.getFrameProxy();
if (frame != null) {
NodeManagerImpl nodeManager = getNodeFactory();
rootNode = nodeManager.createNode(nodeManager.getStackFrameDescriptor(null, frame), debuggerContext.createEvaluationContext());
}
else {
rootNode = getNodeFactory().getDefaultNode();
SuspendManager suspendManager = getSuspendContext().getDebugProcess().getSuspendManager();
try {
if (suspendManager.isSuspended(currentThread)) {
try {
if (currentThread.frameCount() == 0) {
rootNode.add(MessageDescriptor.THREAD_IS_EMPTY);
}
else {
rootNode.add(MessageDescriptor.DEBUG_INFO_UNAVAILABLE);
}
}
catch (EvaluateException e) {
rootNode.add(new MessageDescriptor(e.getMessage()));
}
}
else {
rootNode.add(MessageDescriptor.THREAD_IS_RUNNING);
}
}
catch (ObjectCollectedException ignored) {
rootNode.add(new MessageDescriptor(DebuggerBundle.message("label.thread.node.thread.collected", currentThread.name())));
}
}
}
catch (Exception ex) {
if (LOG.isDebugEnabled()) {
LOG.debug(ex);
}
rootNode = getNodeFactory().getDefaultNode();
rootNode.add(MessageDescriptor.DEBUG_INFO_UNAVAILABLE);
}
final DebuggerTreeNodeImpl rootNode1 = rootNode;
DebuggerInvocationUtil.swingInvokeLater(getProject(), new Runnable() {
@Override
public void run() {
getMutableModel().setRoot(rootNode1);
treeChanged();
final TreeModel model = getModel();
model.addTreeModelListener(new TreeModelAdapter() {
@Override
public void treeStructureChanged(TreeModelEvent e) {
final Object[] path = e.getPath();
if (path.length > 0 && path[path.length - 1] == rootNode1) {
// wait until rootNode1 (the root just set) becomes the root
model.removeTreeModelListener(this);
if (ViewsGeneralSettings.getInstance().AUTOSCROLL_TO_NEW_LOCALS) {
autoscrollToNewLocals(rootNode1);
}
else {
// should clear this flag, otherwise, if AUTOSCROLL_TO_NEW_LOCALS option turned
// to true during the debug process, all these variables will be considered 'new'
for (Enumeration children = rootNode1.rawChildren(); children.hasMoreElements();) {
final DebuggerTreeNodeImpl child = (DebuggerTreeNodeImpl)children.nextElement();
final NodeDescriptorImpl descriptor = child.getDescriptor();
if (descriptor instanceof LocalVariableDescriptorImpl) {
((LocalVariableDescriptorImpl)descriptor).setNewLocal(false);
}
}
}
}
}
});
}
private void autoscrollToNewLocals(DebuggerTreeNodeImpl frameNode) {
final DebuggerSession debuggerSession = debuggerContext.getDebuggerSession();
final boolean isSteppingThrough = debuggerSession != null && debuggerSession.isSteppingThrough(debuggerContext.getThreadProxy());
final List<DebuggerTreeNodeImpl> toClear = new ArrayList<DebuggerTreeNodeImpl>();
final List<DebuggerTreeNodeImpl> newLocalsToSelect = new ArrayList<DebuggerTreeNodeImpl>();
for (Enumeration e = frameNode.rawChildren(); e.hasMoreElements();) {
final DebuggerTreeNodeImpl child = (DebuggerTreeNodeImpl)e.nextElement();
final NodeDescriptorImpl descriptor = child.getDescriptor();
if (!(descriptor instanceof LocalVariableDescriptorImpl)) {
continue;
}
final LocalVariableDescriptorImpl localVariableDescriptor = (LocalVariableDescriptorImpl)descriptor;
if (isSteppingThrough && localVariableDescriptor.isNewLocal()) {
myAnyNewLocals = true;
newLocalsToSelect.add(child);
}
else {
toClear.add(child);
}
localVariableDescriptor.setNewLocal(false);
}
if (!newLocalsToSelect.isEmpty()) {
for (DebuggerTreeNodeImpl child : toClear) {
removeSelectionPath(new TreePath(child.getPath()));
child.getDescriptor().myIsSelected = false;
}
for (DebuggerTreeNodeImpl child : newLocalsToSelect) {
addSelectionPath(new TreePath(child.getPath()));
child.getDescriptor().myIsSelected = true;
}
}
}
});
}
}
private static class VariablesCollector extends JavaRecursiveElementVisitor {
private final Set<String> myVisibleLocals;
private final TextRange myLineRange;
private final Set<TextWithImports> myExpressions;
private final Set<String> myVars;
private final SourcePosition myPosition;
private final EvaluationContextImpl myEvalContext;
private final boolean myCollectExpressions;
public VariablesCollector(final Set<String> visibleLocals,
final TextRange lineRange,
final Set<TextWithImports> expressions,
final Set<String> vars,
SourcePosition position, EvaluationContextImpl evalContext) {
myVisibleLocals = visibleLocals;
myLineRange = lineRange;
myExpressions = expressions;
myVars = vars;
myPosition = position;
myEvalContext = evalContext;
myCollectExpressions = XDebuggerSettingsManager.getInstance().getDataViewSettings().isAutoExpressions();
}
@Override
public void visitElement(final PsiElement element) {
if (myLineRange.intersects(element.getTextRange())) {
super.visitElement(element);
}
}
@Override
public void visitMethodCallExpression(final PsiMethodCallExpression expression) {
if (myCollectExpressions) {
final PsiMethod psiMethod = expression.resolveMethod();
if (psiMethod != null && !DebuggerUtils.hasSideEffectsOrReferencesMissingVars(expression, myVisibleLocals)) {
myExpressions.add(new TextWithImportsImpl(expression));
}
}
super.visitMethodCallExpression(expression);
}
@Override
public void visitReferenceExpression(final PsiReferenceExpression reference) {
if (myLineRange.intersects(reference.getTextRange())) {
final PsiElement psiElement = reference.resolve();
if (psiElement instanceof PsiVariable) {
final PsiVariable var = (PsiVariable)psiElement;
if (var instanceof PsiField) {
if (myCollectExpressions && !DebuggerUtils.hasSideEffectsOrReferencesMissingVars(reference, myVisibleLocals)) {
/*
if (var instanceof PsiEnumConstant && reference.getQualifier() == null) {
final PsiClass enumClass = ((PsiEnumConstant)var).getContainingClass();
if (enumClass != null) {
final PsiExpression expression = JavaPsiFacade.getInstance(var.getProject()).getParserFacade().createExpressionFromText(enumClass.getName() + "." + var.getName(), var);
final PsiReference ref = expression.getReference();
if (ref != null) {
ref.bindToElement(var);
myExpressions.add(new TextWithImportsImpl(expression));
}
}
}
else {
myExpressions.add(new TextWithImportsImpl(reference));
}
*/
final PsiModifierList modifierList = var.getModifierList();
boolean isConstant = (var instanceof PsiEnumConstant) ||
(modifierList != null && modifierList.hasModifierProperty(PsiModifier.STATIC) && modifierList.hasModifierProperty(PsiModifier.FINAL));
if (!isConstant) {
myExpressions.add(new TextWithImportsImpl(reference));
}
}
}
else {
if (myVisibleLocals.contains(var.getName())) {
myVars.add(var.getName());
}
else {
// fix for variables used in inner classes
if (!Comparing.equal(PsiTreeUtil.getParentOfType(reference, PsiClass.class),
PsiTreeUtil.getParentOfType(var, PsiClass.class))) {
myExpressions.add(new TextWithImportsImpl(reference));
}
}
}
}
}
super.visitReferenceExpression(reference);
}
@Override
public void visitArrayAccessExpression(final PsiArrayAccessExpression expression) {
if (myCollectExpressions && !DebuggerUtils.hasSideEffectsOrReferencesMissingVars(expression, myVisibleLocals)) {
myExpressions.add(new TextWithImportsImpl(expression));
}
super.visitArrayAccessExpression(expression);
}
@Override
public void visitParameter(final PsiParameter parameter) {
processVariable(parameter);
super.visitParameter(parameter);
}
@Override
public void visitLocalVariable(final PsiLocalVariable variable) {
processVariable(variable);
super.visitLocalVariable(variable);
}
private void processVariable(final PsiVariable variable) {
if (myLineRange.intersects(variable.getTextRange()) && myVisibleLocals.contains(variable.getName())) {
myVars.add(variable.getName());
}
}
@Override
public void visitClass(final PsiClass aClass) {
// Do not step in to local and anonymous classes...
}
}
}
@@ -1,155 +0,0 @@
/*
* Copyright 2000-2015 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.ui.impl;
import com.intellij.debugger.actions.DebuggerAction;
import com.intellij.debugger.actions.DebuggerActions;
import com.intellij.debugger.impl.DebuggerContextImpl;
import com.intellij.debugger.impl.DebuggerSession;
import com.intellij.debugger.impl.DebuggerStateManager;
import com.intellij.debugger.ui.impl.watch.DebuggerTree;
import com.intellij.openapi.Disposable;
import com.intellij.openapi.actionSystem.*;
import com.intellij.openapi.editor.Document;
import com.intellij.openapi.fileTypes.FileType;
import com.intellij.openapi.project.Project;
import com.intellij.ui.AppUIUtil;
import com.intellij.ui.ScrollPaneFactory;
import com.intellij.util.SystemProperties;
import com.intellij.xdebugger.XSourcePosition;
import com.intellij.xdebugger.evaluation.EvaluationMode;
import com.intellij.xdebugger.evaluation.XDebuggerEditorsProvider;
import com.intellij.xdebugger.frame.XStackFrame;
import com.intellij.xdebugger.impl.frame.XVariablesViewBase;
import org.jetbrains.annotations.NonNls;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import javax.swing.*;
import java.awt.*;
import java.awt.event.KeyEvent;
public class VariablesPanel extends DebuggerTreePanel implements DataProvider {
@NonNls private static final String HELP_ID = "debugging.debugFrame";
private static final String TREE = "tree";
private static final String X_TREE = "xTree";
private final JPanel myCards;
private final MyXVariablesView myXTree;
public VariablesPanel(Project project, DebuggerStateManager stateManager, Disposable parent) {
super(project, stateManager);
setBorder(null);
final FrameVariablesTree frameTree = getFrameTree();
myCards = new JPanel(new CardLayout());
myCards.add(frameTree, TREE);
myXTree = new MyXVariablesView(project);
registerDisposable(myXTree);
myCards.add(myXTree.getTree(), X_TREE);
JScrollPane pane = ScrollPaneFactory.createScrollPane(myCards);
pane.getVerticalScrollBar().setUnitIncrement(10);
add(pane, BorderLayout.CENTER);
registerDisposable(DebuggerAction.installEditAction(frameTree, DebuggerActions.EDIT_NODE_SOURCE));
overrideShortcut(frameTree, DebuggerActions.COPY_VALUE, CommonShortcuts.getCopy());
overrideShortcut(frameTree, DebuggerActions.SET_VALUE, new CustomShortcutSet(KeyStroke.getKeyStroke(KeyEvent.VK_F2, 0)));
new ValueNodeDnD(myTree, parent);
}
@Override
protected DebuggerTree createTreeView() {
return new FrameVariablesTree(getProject(), SystemProperties.getBooleanProperty("java.debugger.xTree", true) ? this : null);
}
@Override
protected void changeEvent(DebuggerContextImpl newContext, DebuggerSession.Event event) {
if (event != DebuggerSession.Event.THREADS_REFRESH) {
super.changeEvent(newContext, event);
}
}
@Override
protected ActionPopupMenu createPopupMenu() {
ActionGroup group = (ActionGroup)ActionManager.getInstance().getAction(DebuggerActions.FRAME_PANEL_POPUP);
return ActionManager.getInstance().createActionPopupMenu(DebuggerActions.FRAME_PANEL_POPUP, group);
}
@Override
public Object getData(String dataId) {
if (PlatformDataKeys.HELP_ID.is(dataId)) {
return HELP_ID;
}
return super.getData(dataId);
}
public FrameVariablesTree getFrameTree() {
return (FrameVariablesTree)getTree();
}
public void stackChanged(@Nullable final XStackFrame xStackFrame) {
AppUIUtil.invokeOnEdt(new Runnable() {
@Override
public void run() {
myXTree.stackChanged(xStackFrame);
((CardLayout)(myCards.getLayout())).show(myCards, xStackFrame == null ? TREE : X_TREE);
}
});
}
private static final class MyXVariablesView extends XVariablesViewBase {
private XStackFrame myCurrentXStackFrame;
public MyXVariablesView(@NotNull Project project) {
super(project, new XDebuggerEditorsProvider() {
@NotNull
@Override
public FileType getFileType() {
throw new UnsupportedOperationException();
}
@NotNull
@Override
public Document createDocument(@NotNull Project project, @NotNull String text, @Nullable XSourcePosition sourcePosition, @NotNull EvaluationMode mode) {
throw new UnsupportedOperationException();
}
}, null);
}
public void stackChanged(@Nullable XStackFrame stackFrame) {
if (myCurrentXStackFrame != null) {
saveCurrentTreeState(stackFrame);
}
myCurrentXStackFrame = stackFrame;
if (stackFrame == null) {
getTree().setSourcePosition(null);
}
else {
buildTreeAndRestoreState(stackFrame);
}
}
@Override
public void processSessionEvent(@NotNull SessionEvent event) {
}
}
}
@@ -1,119 +0,0 @@
/*
* Copyright 2000-2009 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.ui.impl;
import com.intellij.debugger.engine.evaluation.TextWithImports;
import com.intellij.debugger.impl.DebuggerContextImpl;
import com.intellij.debugger.ui.impl.watch.DebuggerTree;
import com.intellij.debugger.ui.impl.watch.DebuggerTreeNodeImpl;
import com.intellij.debugger.ui.impl.watch.WatchItemDescriptor;
import com.intellij.openapi.application.ApplicationManager;
import com.intellij.openapi.diagnostic.Logger;
import com.intellij.openapi.project.Project;
import com.intellij.xdebugger.XDebuggerBundle;
import org.jetbrains.annotations.Nullable;
import javax.swing.tree.TreePath;
import java.util.Enumeration;
public class WatchDebuggerTree extends DebuggerTree {
private static final Logger LOG = Logger.getInstance("#com.intellij.debugger.ui.impl.WatchDebuggerTree");
public WatchDebuggerTree(Project project) {
super(project);
getEmptyText().setText(XDebuggerBundle.message("debugger.no.watches"));
}
public DebuggerTreeNodeImpl[] getWatches() {
DebuggerTreeNodeImpl root = (DebuggerTreeNodeImpl)getModel().getRoot();
DebuggerTreeNodeImpl[] watches = new DebuggerTreeNodeImpl[root.getChildCount()];
final Enumeration e = root.children();
int i = 0;
while(e.hasMoreElements()) {
watches[i++] = (DebuggerTreeNodeImpl)e.nextElement();
}
return watches;
}
public int getWatchCount() {
DebuggerTreeNodeImpl root = (DebuggerTreeNodeImpl) getModel().getRoot();
return root != null ? root.getChildCount() : 0;
}
public DebuggerTreeNodeImpl addWatch(WatchItemDescriptor descriptor) {
ApplicationManager.getApplication().assertIsDispatchThread();
final DebuggerTreeNodeImpl root = (DebuggerTreeNodeImpl) getModel().getRoot();
WatchItemDescriptor watchDescriptor = new WatchItemDescriptor(getProject(), descriptor.getEvaluationText());
watchDescriptor.displayAs(descriptor);
final DebuggerTreeNodeImpl node = DebuggerTreeNodeImpl.createNodeNoUpdate(this, watchDescriptor);
root.add(node);
treeChanged();
getSelectionModel().setSelectionPath(new TreePath(node.getPath()));
//node.calcValue();
return node;
}
public DebuggerTreeNodeImpl addWatch(TextWithImports text, @Nullable String customName) {
ApplicationManager.getApplication().assertIsDispatchThread();
final DebuggerTreeNodeImpl root = (DebuggerTreeNodeImpl) getModel().getRoot();
final WatchItemDescriptor descriptor = new WatchItemDescriptor(getProject(), text, customName);
DebuggerTreeNodeImpl node = DebuggerTreeNodeImpl.createNodeNoUpdate(this, descriptor);
root.add(node);
treeChanged();
final TreePath path = new TreePath(node.getPath());
getSelectionModel().setSelectionPath(path);
scrollPathToVisible(path);
return node;
}
public void removeWatch(DebuggerTreeNodeImpl node) {
ApplicationManager.getApplication().assertIsDispatchThread();
LOG.assertTrue(node.getDescriptor() instanceof WatchItemDescriptor);
DebuggerTreeNodeImpl root = (DebuggerTreeNodeImpl) getModel().getRoot();
DebuggerTreeNodeImpl nodeToSelect = (DebuggerTreeNodeImpl) node.getNextSibling();
getMutableModel().removeNodeFromParent(node);
treeChanged();
if(nodeToSelect == null && root.getChildCount() > 0) {
nodeToSelect = (DebuggerTreeNodeImpl) root.getChildAt(root.getChildCount() - 1);
}
if(nodeToSelect != null) {
setSelectionPath(new TreePath(nodeToSelect.getPath()));
}
}
protected void build(DebuggerContextImpl context) {
for (DebuggerTreeNodeImpl node : getWatches()) {
node.calcValue();
}
}
public static void setWatchNodeText(final DebuggerTreeNodeImpl node, TextWithImports text) {
((WatchItemDescriptor)node.getDescriptor()).setEvaluationText(text);
node.calcValue();
}
}
@@ -140,6 +140,7 @@ public class JavaScratchCompilationSupport implements ProjectComponent, CompileT
}
final List<String> options = new ArrayList<String>();
options.add("-g"); // always compile with debug info
final JavaSdkVersion sdkVersion = JavaSdk.getInstance().getVersion(targetSdk);
if (sdkVersion != null) {
final String langLevel = "1." + Integer.valueOf(3 + sdkVersion.getMaxLanguageLevel().ordinal());
@@ -61,6 +61,7 @@ import com.intellij.xdebugger.XDebugProcess;
import com.intellij.xdebugger.XDebugProcessStarter;
import com.intellij.xdebugger.XDebugSession;
import com.intellij.xdebugger.XDebuggerManager;
import com.sun.jdi.Location;
import org.jdom.Element;
import org.jetbrains.annotations.NotNull;
@@ -421,6 +422,17 @@ public abstract class DebuggerTestCase extends ExecutionWithDebuggerToolsTestCas
return createDebuggerContext(suspendContext, suspendContext.getFrameProxy());
}
protected void printLocation(SuspendContextImpl suspendContext) {
try {
Location location = suspendContext.getFrameProxy().location();
String message = "paused at " + location.sourcePath() + ":" + location.lineNumber();
println(message, ProcessOutputTypes.SYSTEM);
}
catch (Throwable e) {
addException(e);
}
}
protected void createBreakpointInHelloWorld() {
DebuggerInvocationUtil.invokeAndWait(myProject, new Runnable() {
@Override
@@ -127,6 +127,20 @@ public abstract class ExecutionWithDebuggerToolsTestCase extends ExecutionTestCa
myScriptRunnables.add(runnable);
}
protected void doWhenPausedThenResume(final SuspendContextRunnable runnable) {
onBreakpoint(new SuspendContextRunnable() {
@Override
public void run(SuspendContextImpl suspendContext) throws Exception {
try {
runnable.run(suspendContext);
}
finally {
resume(suspendContext);
}
}
});
}
protected void addDefaultBreakpointListener() {
if (myPauseScriptListener == null) {
final DebugProcessImpl debugProcess = getDebugProcess();
@@ -35,7 +35,6 @@ import com.intellij.openapi.project.Project;
import com.intellij.openapi.ui.Messages;
import com.intellij.openapi.ui.popup.PopupChooserBuilder;
import com.intellij.openapi.util.Disposer;
import com.intellij.openapi.util.Pair;
import com.intellij.openapi.util.text.StringUtil;
import com.intellij.openapi.vfs.VirtualFile;
import com.intellij.openapi.wm.ToolWindow;
@@ -494,7 +493,7 @@ public class ExecutionHelper {
try {
final boolean finished = processHandler.waitFor(1000 * mode.getTimeout());
if (!finished) {
mode.getTimeoutCallback().fun(Pair.create(mode, presentableCmdline));
mode.getTimeoutCallback().consume(mode, presentableCmdline);
processHandler.destroyProcess();
}
}
@@ -16,9 +16,8 @@
package com.intellij.execution;
import com.intellij.execution.process.ProcessListener;
import com.intellij.openapi.util.Pair;
import com.intellij.util.Function;
import com.intellij.util.Functions;
import com.intellij.util.PairConsumer;
import com.intellij.util.containers.ContainerUtil;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
@@ -30,6 +29,11 @@ import java.util.List;
* @author Roman.Chernyatchik
*/
public class ExecutionMode {
private static final PairConsumer<ExecutionMode, String> NOOP_CONSUMER = new PairConsumer<ExecutionMode, String>() {
@Override
public void consume(ExecutionMode mode, String s) {
}
};
private final boolean myCancelable;
private final String myTitle;
private final String myTitle2;
@@ -60,8 +64,8 @@ public class ExecutionMode {
}
@NotNull
public Function<Pair<ExecutionMode, String>, Void> getTimeoutCallback() {
return Functions.identity();
public PairConsumer<ExecutionMode, String> getTimeoutCallback() {
return NOOP_CONSUMER;
}
@Nullable
@@ -16,8 +16,7 @@
package com.intellij.execution;
import com.intellij.openapi.diagnostic.Logger;
import com.intellij.openapi.util.Pair;
import com.intellij.util.Function;
import com.intellij.util.PairConsumer;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
@@ -28,12 +27,11 @@ import javax.swing.*;
*/
public class ExecutionModes {
private static final Logger LOG = Logger.getInstance(ExecutionMode.class);
private static final Function<Pair<ExecutionMode, String>, Void> DEFAULT_TIMEOUT_CALLBACK = new Function<Pair<ExecutionMode, String>, Void>() {
private static final PairConsumer<ExecutionMode, String> DEFAULT_TIMEOUT_CALLBACK = new PairConsumer<ExecutionMode, String>() {
@Override
public Void fun(Pair<ExecutionMode, String> pair) {
final String msg = "Timeout (" + pair.getFirst().getTimeout() + " sec) on executing: " + pair.getSecond();
public void consume(ExecutionMode mode, String presentableCmdLine) {
final String msg = "Timeout (" + mode.getTimeout() + " sec) on executing: " + presentableCmdLine;
LOG.error(msg);
return null;
}
};
@@ -75,7 +73,7 @@ public class ExecutionModes {
public static class SameThreadMode extends ExecutionMode {
private final int myTimeout;
@NotNull
private final Function<Pair<ExecutionMode, String>, Void> myTimeoutCallback;
private final PairConsumer<ExecutionMode, String> myTimeoutCallback;
public SameThreadMode(final boolean cancelable,
@Nullable final String title2,
@@ -86,7 +84,7 @@ public class ExecutionModes {
public SameThreadMode(final boolean cancelable,
@Nullable final String title2,
final int timeout,
@NotNull final Function<Pair<ExecutionMode, String>, Void> timeoutCallback
@NotNull final PairConsumer<ExecutionMode, String> timeoutCallback
) {
super(cancelable, null, title2, false, false, null);
myTimeout = timeout;
@@ -122,7 +120,7 @@ public class ExecutionModes {
@NotNull
@Override
public Function<Pair<ExecutionMode, String>, Void> getTimeoutCallback() {
public PairConsumer<ExecutionMode, String> getTimeoutCallback() {
return myTimeoutCallback;
}
}
@@ -60,6 +60,58 @@ public class FileNameCacheMicroBenchmark {
System.exit(0);
}
private static final TestIteration LONG_RANDOM_ACCESS = new TestIteration() {
@Override
public void doTest(int threadNumber, int[] ids, Random threadRandom, int queryCount) {
final int blackHole = threadRandom.nextInt();
for (int j = 0; j < queryCount; j++) {
final CharSequence name = FileNameCache.getVFileName(ids[threadRandom.nextInt(ids.length)]);
if (blackHole == System.identityHashCode(name.hashCode())) {
failure();
}
}
}
};
private static final TestIteration LONG_RANDOM_ACCESS_WITH_GET_PATH = new TestIteration() {
@Override
public void doTest(int threadNumber, int[] ids, Random threadRandom, int queryCount) {
final int blackHole = threadRandom.nextInt();
for (int j = 0; j < queryCount; j++) {
final int hash = getPath(threadRandom.nextInt(ids.length), ids);
if (blackHole == hash) {
failure();
}
}
}
};
private static final TestIteration LINEAR_SCAN_AND_RANDOM_ACCESS_WITH_GET_PATH = new TestIteration() {
@Override
public void doTest(int threadNumber, int[] ids, Random threadRandom, int queryCount) {
if (threadNumber % 2 == 1) { // linear scan for every second_case
final int blackHole = threadRandom.nextInt();
int currentId = 0;
for(int j = 0; j < queryCount; ++j) {
final int hash = getPath(currentId++, ids);
if (currentId == ids.length) currentId = 0;
if (blackHole == hash) {
failure();
}
}
return;
}
LONG_RANDOM_ACCESS_WITH_GET_PATH.doTest(threadNumber, ids, threadRandom, queryCount);
}
};
private static void runTest(int nameCount, String name) throws InterruptedException, ExecutionException {
System.out.println("----- " + name + " ------ name count: "+nameCount);
@@ -68,15 +120,23 @@ public class FileNameCacheMicroBenchmark {
checkNames(map, ids);
warmUp(ids);
measureAverageTime(ids, 1);
measureAverageTime(ids, 4);
measureAverageTime(ids, 1, LONG_RANDOM_ACCESS);
measureAverageTime(ids, 4, LONG_RANDOM_ACCESS);
//measureAverageTime(ids, 1, LONG_RANDOM_ACCESS_WITH_GET_PATH);
//measureAverageTime(ids, 4, LONG_RANDOM_ACCESS_WITH_GET_PATH);
//
//measureAverageTime(ids, 1, LINEAR_SCAN_AND_RANDOM_ACCESS_WITH_GET_PATH);
//measureAverageTime(ids, 4, LINEAR_SCAN_AND_RANDOM_ACCESS_WITH_GET_PATH);
}
private static boolean warmedUp;
private static void warmUp(int[] ids) throws InterruptedException, ExecutionException {
if (warmedUp) return;
for (int i = 0; i < 200000; i++) {
runThreads(ids, 2, 1000);
runThreads(ids, 2, 1000, LONG_RANDOM_ACCESS);
//runThreads(ids, 2, 1000, LONG_RANDOM_ACCESS_WITH_GET_PATH);
//runThreads(ids, 2, 1000, LINEAR_SCAN_AND_RANDOM_ACCESS_WITH_GET_PATH);
}
Thread.sleep(10000);
@@ -84,11 +144,22 @@ public class FileNameCacheMicroBenchmark {
warmedUp = true;
}
private static void measureAverageTime(int[] ids, int threadCount) throws InterruptedException, ExecutionException {
private static int getPath(int id, int[] ids) {
int result = 0;
result += System.identityHashCode(FileNameCache.getVFileName(ids[id]));
while (id > 10) {
result += System.identityHashCode(FileNameCache.getVFileName(ids[id / 10]));
id /= 10;
}
return result;
}
private static void measureAverageTime(int[] ids, int threadCount, TestIteration iteration) throws InterruptedException, ExecutionException {
System.out.println("Running "+threadCount+" threads");
TLongArrayList times = new TLongArrayList();
for (int i = 0; i < 11; i++) {
long time = runThreads(ids, threadCount, 20000000);
for (int i = 0; i < 10; i++) {
long time = runThreads(ids, threadCount, 20000000, iteration);
System.out.println(time);
times.add(time);
}
@@ -98,18 +169,25 @@ public class FileNameCacheMicroBenchmark {
System.out.println();
}
private static long runThreads(final int[] ids, int threadCount, final int queryCount) throws InterruptedException, ExecutionException {
abstract static class TestIteration {
abstract void doTest(int threadNumber, int[] ids, Random threadRandom, int queryCount);
protected final void failure() {
System.out.println("Failure");
assert false;
}
}
private static long runThreads(final int[] ids, int threadCount, final int queryCount, final TestIteration testIteration) throws InterruptedException, ExecutionException {
long start = System.currentTimeMillis();
List<Future<?>> futures = ContainerUtil.newArrayList();
Random seedRandom = new Random();
for (int i = 0; i < threadCount; i++) {
final Random threadRandom = new Random(seedRandom.nextInt());
final int finalI = i;
futures.add(ApplicationManager.getApplication().executeOnPooledThread(new Runnable() {
@Override
public void run() {
for (int j = 0; j < queryCount; j++) {
FileNameCache.getVFileName(ids[threadRandom.nextInt(ids.length)]);
}
testIteration.doTest(finalI, ids, threadRandom, queryCount);
}
}));
}
@@ -50,9 +50,10 @@ public class ShelvedBinaryFile implements JDOMExternalizable {
public ShelvedBinaryFile(final String beforePath, final String afterPath, @Nullable final String shelvedPath) {
assert beforePath != null || afterPath != null;
BEFORE_PATH = convertToSystemIndependent(beforePath);
AFTER_PATH = convertToSystemIndependent(afterPath);
SHELVED_PATH = convertToSystemIndependent(shelvedPath);
BEFORE_PATH = beforePath;
AFTER_PATH = afterPath;
SHELVED_PATH = shelvedPath;
convertPathsToSystemIndependent();
}
@Nullable
@@ -60,8 +61,15 @@ public class ShelvedBinaryFile implements JDOMExternalizable {
return beforePath != null ? FileUtil.toSystemIndependentName(beforePath) : null;
}
private void convertPathsToSystemIndependent() {
BEFORE_PATH = convertToSystemIndependent(BEFORE_PATH);
AFTER_PATH = convertToSystemIndependent(AFTER_PATH);
SHELVED_PATH = convertToSystemIndependent(SHELVED_PATH);
}
public void readExternal(Element element) throws InvalidDataException {
DefaultJDOMExternalizer.readExternal(this, element);
convertPathsToSystemIndependent();
}
public void writeExternal(Element element) throws WriteExternalException {
@@ -84,6 +84,7 @@ public class ShelvedChangeList implements JDOMExternalizable, ExternalizableSche
@Override
public void readExternal(Element element) throws InvalidDataException {
DefaultJDOMExternalizer.readExternal(this, element);
PATH = FileUtil.toSystemIndependentName(PATH);
mySchemeName = element.getAttributeValue(NAME_ATTRIBUTE);
DATE = new Date(Long.parseLong(element.getAttributeValue(ATTRIBUTE_DATE)));
myRecycled = Boolean.parseBoolean(element.getAttributeValue(ATTRIBUTE_RECYCLED_CHANGELIST));
@@ -0,0 +1,6 @@
from alembic.operations import Operations
from alembic.environment import EnvironmentContext
op = Operations()
context = EnvironmentContext()
@@ -0,0 +1,276 @@
"""Skeleton for 'pytest'.
Project: pytest 2.6.4 <http://pytest.org/latest/>
Skeleton by: Bruno Oliveira <nicoddemus@gmail.com>
Exposing everything that can be extracted from `pytest_namespace` hook
in standard pytest modules, using original docstrings.
"""
# _pytest.genscript
def freeze_includes():
"""
Returns a list of module names used by py.test that should be
included by cx_freeze.
"""
# _pytest.main
class collect:
class Item:
""" a basic test invocation item. Note that for a single function
there might be multiple test invocation items.
"""
class Collector:
""" Collector instances create children through collect()
and thus iteratively build a tree.
"""
class File:
""" base class for collecting tests from a file. """
class Session:
"""
"""
# _pytest.python
class Module:
""" Collector for test classes and functions. """
class Class:
""" Collector for test methods. """
class Instance:
"""
"""
class Function:
""" a Function Item is responsible for setting up and executing a
Python test function.
"""
class Generator:
"""
"""
@staticmethod
def _fillfuncargs(function):
""" fill missing funcargs for a test function. """
# _pytest.mark
class mark:
@staticmethod
def skipif(condition, reason=None):
"""skip the given test function if eval(condition) results in a True
value.
Optionally specify a reason for better reporting.
Evaluation happens within the module global context.
Example: ``skipif('sys.platform == "win32"')`` skips the test if
we are on the win32 platform.
see http://pytest.org/latest/skipping.html
"""
@staticmethod
def xfail(condition=None, reason=None, run=True):
"""mark the the test function as an expected failure if eval(condition)
has a True value.
Optionally specify a reason for better reporting and run=False if
you don't even want to execute the test function.
See http://pytest.org/latest/skipping.html
"""
@staticmethod
def parametrize(argnames, argvalues):
"""call a test function multiple times passing in different arguments
in turn.
:type argnames: str | list[str]
:param argvalues: generally needs to be a list of values if argnames
specifies only one name or a list of tuples of values if
argnames specifies multiple names.
Example: @parametrize('arg1', [1,2]) would lead to two calls of the
decorated test function, one with arg1=1 and another with arg1=2.
see http://pytest.org/latest/parametrize.html for more info
and examples.
"""
@staticmethod
def usefixtures(*fixturenames):
"""mark tests as needing all of the specified fixtures.
see http://pytest.org/latest/fixture.html#usefixtures
"""
@staticmethod
def tryfirst(f):
"""mark a hook implementation function such that the plugin machinery
will try to call it first/as early as possible.
"""
@staticmethod
def trylast(f):
"""mark a hook implementation function such that the plugin machinery
will try to call it last/as late as possible.
"""
@staticmethod
def hookwrapper(f):
"""A hook wrapper is a generator function which yields exactly once.
When pytest invokes hooks it first executes hook wrappers and passes
the same arguments as to the regular hooks.
"""
# _pytest.pdb
def set_trace():
""" invoke PDB set_trace debugging, dropping any IO capturing. """
# _pytest.python
def raises(ExpectedException, *args, **kwargs):
""" assert that a code block/function call raises @ExpectedException and
raise a failure exception otherwise.
:type ExpectedException: T
This helper produces a ``py.code.ExceptionInfo()`` object.
If using Python 2.5 or above, you may use this function as a
context manager::
>>> with raises(ZeroDivisionError):
... 1/0
Or you can specify a callable by passing a to-be-called lambda::
>>> raises(ZeroDivisionError, lambda: 1/0)
<ExceptionInfo ...>
or you can specify an arbitrary callable with arguments::
>>> def f(x): return 1/x
...
>>> raises(ZeroDivisionError, f, 0)
<ExceptionInfo ...>
>>> raises(ZeroDivisionError, f, x=0)
<ExceptionInfo ...>
A third possibility is to use a string to be executed::
>>> raises(ZeroDivisionError, "f(0)")
<ExceptionInfo ...>
Performance note:
-----------------
Similar to caught exception objects in Python, explicitly clearing
local references to returned ``py.code.ExceptionInfo`` objects can
help the Python interpreter speed up its garbage collection.
Clearing those references breaks a reference cycle
(``ExceptionInfo`` --> caught exception --> frame stack raising
the exception --> current frame stack --> local variables -->
``ExceptionInfo``) which makes Python keep all objects referenced
from that cycle (including all local variables in the current
frame) alive until the next cyclic garbage collection run. See the
official Python ``try`` statement documentation for more detailed
information.
"""
def fixture(scope="function", params=None, autouse=False, ids=None):
""" (return a) decorator to mark a fixture factory function.
This decorator can be used (with or or without parameters) to define
a fixture function. The name of the fixture function can later be
referenced to cause its invocation ahead of running tests: test
modules or classes can use the pytest.mark.usefixtures(fixturename)
marker. Test functions can directly use fixture names as input
arguments in which case the fixture instance returned from the fixture
function will be injected.
:arg scope: the scope for which this fixture is shared, one of
"function" (default), "class", "module", "session".
:arg params: an optional list of parameters which will cause multiple
invocations of the fixture function and all of the tests
using it.
:arg autouse: if True, the fixture func is activated for all tests that
can see it. If False (the default) then an explicit
reference is needed to activate the fixture.
:arg ids: list of string ids each corresponding to the params
so that they are part of the test id. If no ids are provided
they will be generated automatically from the params.
"""
def yield_fixture(scope="function", params=None, autouse=False, ids=None):
""" (return a) decorator to mark a yield-fixture factory function
(EXPERIMENTAL).
This takes the same arguments as :py:func:`pytest.fixture` but
expects a fixture function to use a ``yield`` instead of a ``return``
statement to provide a fixture. See
http://pytest.org/en/latest/yieldfixture.html for more info.
"""
# _pytest.recwarn
def deprecated_call(func, *args, **kwargs):
""" assert that calling ``func(*args, **kwargs)``
triggers a DeprecationWarning.
"""
# _pytest.runner
def exit(msg):
""" exit testing process as if KeyboardInterrupt was triggered. """
exit.Exception = Exception
def skip(msg=""):
""" skip an executing test with the given message. Note: it's usually
better to use the pytest.mark.skipif marker to declare a test to be
skipped under certain conditions like mismatching platforms or
dependencies. See the pytest_skipping plugin for details.
"""
skip.Exception = Exception
def fail(msg="", pytrace=True):
""" explicitely fail an currently-executing test with the given Message.
:arg pytrace: if false the msg represents the full failure information
and no python traceback will be reported.
"""
fail.Exception = Exception
def importorskip(modname, minversion=None):
""" return imported module if it has at least "minversion" as its
__version__ attribute. If no minversion is specified the a skip
is only triggered if the module can not be imported.
Note that version comparison only works with simple version strings
like "1.2.3" but not "1.2.3.dev1" or others.
"""
# _pytest.skipping
def xfail(reason=""):
""" xfail an executing test or setup functions with the given reason.
"""