mirror of
https://gitflic.ru/project/openide/openide.git
synced 2026-09-27 10:03:11 +07:00
Merge remote-tracking branch 'origin/master'
This commit is contained in:
@@ -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;
|
||||
}
|
||||
}
|
||||
|
||||
+88
-10
@@ -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);
|
||||
}
|
||||
}));
|
||||
}
|
||||
|
||||
+11
-3
@@ -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.
|
||||
"""
|
||||
Reference in New Issue
Block a user