IDEA-211761 Smart step into fail if source code does not match the bytecode

GitOrigin-RevId: d3d5d2b8ba4ce26f2e9288ebe3148ab30247eccd
This commit is contained in:
Egor Ushakov
2019-05-03 01:42:01 +03:00
committed by intellij-monorepo-bot
parent 61a3e18b78
commit 2e00fd16c3
27 changed files with 105 additions and 155 deletions
@@ -2,6 +2,7 @@
package com.intellij.debugger.actions;
import com.intellij.debugger.SourcePosition;
import com.intellij.debugger.engine.DebugProcessImpl;
import com.intellij.debugger.engine.DebuggerUtils;
import com.intellij.debugger.engine.SuspendContextImpl;
import com.intellij.debugger.engine.evaluation.EvaluateException;
@@ -26,7 +27,10 @@ import com.intellij.psi.*;
import com.intellij.util.DocumentUtil;
import com.intellij.util.Range;
import com.intellij.util.ThreeState;
import com.intellij.util.containers.ContainerUtil;
import com.sun.jdi.Location;
import com.sun.jdi.Method;
import com.sun.jdi.ReferenceType;
import one.util.streamex.StreamEx;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
@@ -338,42 +342,67 @@ public class JavaSmartStepIntoHandler extends JvmSmartStepIntoHandler {
if (!targets.isEmpty()) {
StackFrameProxyImpl frameProxy = suspendContext != null ? suspendContext.getFrameProxy() : null;
if (frameProxy != null) {
// sanity check
try {
Location location = frameProxy.location();
MethodBytecodeUtil.visit(location.method(), location.codeIndex(), new MethodVisitor(Opcodes.API_VERSION) {
boolean myLineMatch = false;
@Override
public void visitLineNumber(int line, Label start) {
myLineMatch = lines.isWithin(line - 1);
}
@Override
public void visitMethodInsn(int opcode, String owner, String name, String desc, boolean itf) {
if (myLineMatch) {
Iterator<SmartStepTarget> iterator = targets.iterator();
while (iterator.hasNext()) {
SmartStepTarget e = iterator.next();
if (e instanceof MethodSmartStepTarget &&
DebuggerUtilsEx.methodMatches(((MethodSmartStepTarget)e).getMethod(),
owner.replace("/", "."), name, desc,
suspendContext.getDebugProcess())) {
iterator.remove();
MethodSmartStepTarget target = (MethodSmartStepTarget)e;
// fix ordinals
existingMethodCalls(targets, target.getMethod())
.forEach(t -> {
int ordinal = t.getOrdinal();
if (ordinal > target.getOrdinal()) {
t.setOrdinal(ordinal - 1);
}
});
break;
}
List<MethodSmartStepTarget> methodTargets =
StreamEx.of(targets)
.select(MethodSmartStepTarget.class)
.filter(target -> !target.needsBreakpointRequest())
.toList();
visitLinesMethods(frameProxy.location(), true, lines, (opcode, owner, name, desc, itf) -> {
if (name.startsWith("access$")) { // bridge method
ReferenceType cls = ContainerUtil.getFirstItem(frameProxy.getVirtualMachine().classesByName(owner));
if (cls != null) {
Method method = DebuggerUtils.findMethod(cls, name, desc);
if (method != null) {
MethodBytecodeUtil.visit(method, new MethodVisitor(Opcodes.API_VERSION) {
@Override
public void visitMethodInsn(int opcode, String owner, String name, String desc, boolean itf) {
if ("java/lang/AbstractMethodError".equals(owner)) {
return;
}
removeMatchingMethod(methodTargets, owner, name, desc, suspendContext.getDebugProcess());
}
}, false);
}
}
}
}, true);
else {
removeMatchingMethod(methodTargets, owner, name, desc, suspendContext.getDebugProcess());
}
});
if (!methodTargets.isEmpty()) {
return Collections.emptyList();
}
}
catch (Exception e) {
LOG.error(e);
}
// remove already executed
try {
visitLinesMethods(frameProxy.location(), false, lines, (opcode, owner, name, desc, itf) -> {
Iterator<SmartStepTarget> iterator = targets.iterator();
while (iterator.hasNext()) {
SmartStepTarget e = iterator.next();
if (e instanceof MethodSmartStepTarget &&
DebuggerUtilsEx.methodMatches(((MethodSmartStepTarget)e).getMethod(),
owner.replace("/", "."), name, desc,
suspendContext.getDebugProcess())) {
iterator.remove();
MethodSmartStepTarget target = (MethodSmartStepTarget)e;
// fix ordinals
existingMethodCalls(targets, target.getMethod())
.forEach(t -> {
int ordinal = t.getOrdinal();
if (ordinal > target.getOrdinal()) {
t.setOrdinal(ordinal - 1);
}
});
break;
}
}
});
}
catch (Exception e) {
LOG.info(e);
@@ -386,6 +415,42 @@ public class JavaSmartStepIntoHandler extends JvmSmartStepIntoHandler {
return Collections.emptyList();
}
private static void removeMatchingMethod(List<MethodSmartStepTarget> targets,
String owner,
String name,
String desc,
DebugProcessImpl process) {
Iterator<MethodSmartStepTarget> iterator = targets.iterator();
while (iterator.hasNext()) {
if (DebuggerUtilsEx.methodMatches(iterator.next().getMethod(), owner.replace("/", "."), name, desc, process)) {
iterator.remove();
break;
}
}
}
private interface MethodInsnVisitor {
void visitMethodInsn(int opcode, String owner, String name, String desc, boolean itf);
}
private static void visitLinesMethods(Location location, boolean full, Range<Integer> lines, MethodInsnVisitor visitor) {
MethodBytecodeUtil.visit(location.method(), full ? Long.MAX_VALUE : location.codeIndex(), new MethodVisitor(Opcodes.API_VERSION) {
boolean myLineMatch = false;
@Override
public void visitLineNumber(int line, Label start) {
myLineMatch = lines.isWithin(line - 1);
}
@Override
public void visitMethodInsn(int opcode, String owner, String name, String desc, boolean itf) {
if (myLineMatch) {
visitor.visitMethodInsn(opcode, owner, name, desc, itf);
}
}
}, true);
}
private static StreamEx<MethodSmartStepTarget> existingMethodCalls(List<SmartStepTarget> targets, PsiMethod psiMethod) {
return StreamEx.of(targets)
.select(MethodSmartStepTarget.class)
@@ -476,11 +476,6 @@ public class CreateLocalVarFromInstanceofAction extends BaseIntentionAction {
public Result calculateQuickResult(ExpressionContext context) {
return result;
}
@Override
public boolean requiresCommittedPSI() {
return false;
}
};
template.addVariable("", expr, expr, true);
@@ -93,11 +93,6 @@ public class JavaWithCastSurrounder extends JavaExpressionSurrounder {
public Result calculateQuickResult(ExpressionContext context) {
return null;
}
@Override
public boolean requiresCommittedPSI() {
return false;
}
};
template.addTextSegment("((");
template.addVariable(TYPE_TEMPLATE_VARIABLE, expr, expr, true);
@@ -278,11 +278,6 @@ public class SurroundAutoCloseableAction extends PsiElementBaseIntentionAction {
return new TextResult(myNames[0]);
}
@Override
public boolean requiresCommittedPSI() {
return false;
}
@Override
public LookupElement[] calculateLookupItems(ExpressionContext context) {
return Stream.of(myNames).map(LookupElementBuilder::create).toArray(LookupElement[]::new);
@@ -21,7 +21,6 @@ import com.intellij.codeInsight.template.PsiElementResult;
import com.intellij.codeInsight.template.PsiTypeResult;
import com.intellij.codeInsight.template.Result;
import com.intellij.openapi.diagnostic.Logger;
import com.intellij.openapi.editor.Editor;
import com.intellij.openapi.project.Project;
import com.intellij.psi.*;
import com.intellij.psi.scope.util.PsiScopesUtil;
@@ -74,11 +73,7 @@ public class MacroUtil {
String text = result.toString();
if (text == null) return null;
PsiManager manager = PsiManager.getInstance(project);
PsiDocumentManager psiDocumentManager = PsiDocumentManager.getInstance(project);
Editor editor = context.getEditor();
if(editor == null) return null;
psiDocumentManager.commitDocument(editor.getDocument());
PsiFile file = psiDocumentManager.getPsiFile(editor.getDocument());
PsiFile file = PsiDocumentManager.getInstance(project).getPsiFile(context.getEditor().getDocument());
//-1: Hack to deal with resolve algorithm
PsiElement place = file != null ? file.findElementAt(context.getStartOffset()) : null;
if (place != null) {
@@ -19,7 +19,6 @@ import com.intellij.codeInsight.CodeInsightBundle;
import com.intellij.codeInsight.lookup.LookupElement;
import com.intellij.codeInsight.lookup.LookupElementBuilder;
import com.intellij.codeInsight.template.*;
import com.intellij.openapi.editor.Editor;
import com.intellij.psi.PsiDocumentManager;
import com.intellij.psi.PsiElement;
import com.intellij.psi.PsiFile;
@@ -76,16 +75,11 @@ public class SuggestVariableNameMacro extends Macro {
private static String[] getNames (final ExpressionContext context) {
String[] names = ExpressionUtil.getNames(context);
if (names == null || names.length == 0) return names;
PsiDocumentManager documentManager = PsiDocumentManager.getInstance(context.getProject());
Editor editor = context.getEditor();
if(editor == null) return null;
documentManager.commitDocument(editor.getDocument());
PsiFile file = documentManager.getPsiFile(editor.getDocument());
PsiElement e = file != null ? file.findElementAt(context.getStartOffset()) : null;
PsiFile file = PsiDocumentManager.getInstance(context.getProject()).getPsiFile(context.getEditor().getDocument());
PsiElement e = file.findElementAt(context.getStartOffset());
PsiVariable[] vars = MacroUtil.getVariablesVisibleAt(e, "");
LinkedList<String> namesList = new LinkedList<>(Arrays.asList(names));
for (PsiVariable var : vars) {
//noinspection ConstantConditions
if (e.equals(var.getNameIdentifier())) continue;
namesList.remove(var.getName());
}
@@ -96,7 +90,6 @@ public class SuggestVariableNameMacro extends Macro {
for (int j = 1; ; j++) {
String name1 = name + j;
for (PsiVariable var : vars) {
//noinspection ConstantConditions
if (name1.equals(var.getName()) && !var.getNameIdentifier().equals(e)) continue index;
}
return new String[]{name1};
@@ -76,11 +76,6 @@ public class CastVarPostfixTemplate extends StringBasedPostfixTemplate {
public Result calculateQuickResult(ExpressionContext context) {
return null;
}
@Override
public boolean requiresCommittedPSI() {
return false;
}
};
template.addVariable(TYPE_VAR, expr, expr, true);
@@ -110,11 +110,6 @@ public class InstanceofExpressionPostfixTemplate extends PostfixTemplate {
public Result calculateQuickResult(ExpressionContext context) {
return null;
}
@Override
public boolean requiresCommittedPSI() {
return false;
}
};
template.addTextSegment(exprText);
@@ -34,8 +34,6 @@ internal class FieldExpression(
override fun calculateQuickResult(context: ExpressionContext): Result? = calculateResult(context)
override fun requiresCommittedPSI(): Boolean = false
override fun calculateLookupItems(context: ExpressionContext): Array<LookupElement> {
val psiClass = myClassPointer.element ?: return LookupElement.EMPTY_ARRAY
@@ -29,7 +29,5 @@ internal class RangeExpression(
override fun calculateQuickResult(context: ExpressionContext): Result? = calculateResult(context)
override fun requiresCommittedPSI(): Boolean = false
override fun calculateLookupItems(context: ExpressionContext): Array<out LookupElement> = LookupElement.EMPTY_ARRAY
}
@@ -194,11 +194,6 @@ public abstract class AbstractJavaInplaceIntroducer extends AbstractInplaceIntro
return new TextResult(defaultType);
}
@Override
public boolean requiresCommittedPSI() {
return false;
}
@Override
public LookupElement[] calculateLookupItems(ExpressionContext context) {
final LookupElement[] elements = expression.calculateLookupItems(context);
@@ -124,11 +124,6 @@ public class IntroduceEmptyVariableHandler {
return calculateResult(context);
}
@Override
public boolean requiresCommittedPSI() {
return false;
}
@Nullable
@Override
public LookupElement[] calculateLookupItems(ExpressionContext context) {
@@ -32,10 +32,4 @@ public abstract class Expression {
public String getAdvertisingText() {
return null;
}
// true if calculateResult or calculateQuickResult require committed PSI for their calculation
// false otherwise
public boolean requiresCommittedPSI() {
return true;
}
}
@@ -37,11 +37,6 @@ public class TextExpression extends Expression {
return calculateResult(expressionContext);
}
@Override
public boolean requiresCommittedPSI() {
return false;
}
@Override
public LookupElement[] calculateLookupItems(ExpressionContext expressionContext) {
return LookupElement.EMPTY_ARRAY;
@@ -38,11 +38,6 @@ public class EmptyExpression extends Expression {
return null;
}
@Override
public boolean requiresCommittedPSI() {
return false;
}
@Override
public LookupElement[] calculateLookupItems(ExpressionContext context) {
return null;
@@ -39,11 +39,6 @@ public class ConstantNode extends Expression {
return myValue;
}
@Override
public boolean requiresCommittedPSI() {
return false;
}
@Override
public LookupElement[] calculateLookupItems(ExpressionContext context) {
return LookupElement.EMPTY_ARRAY;
@@ -35,11 +35,6 @@ public class EmptyNode extends Expression {
return null;
}
@Override
public boolean requiresCommittedPSI() {
return false;
}
@Override
public LookupElement[] calculateLookupItems(ExpressionContext context) {
return null;
@@ -45,8 +45,4 @@ public class SelectionNode extends Expression {
return calculateQuickResult(context);
}
@Override
public boolean requiresCommittedPSI() {
return false;
}
}
@@ -832,13 +832,11 @@ public class TemplateState implements Disposable {
int start = mySegments.getSegmentStart(segmentNumber);
int end = mySegments.getSegmentEnd(segmentNumber);
if(expressionNode.requiresCommittedPSI()) {
PsiDocumentManager.getInstance(myProject).commitDocument(myDocument);
}
PsiDocumentManager.getInstance(myProject).commitDocument(myDocument);
PsiFile psiFile = getPsiFile();
PsiElement element = psiFile != null ? psiFile.findElementAt(start) : null;
if (element != null && !element.isValid()) {
element = null;
if (element != null) {
PsiUtilCore.ensureValid(element);
}
ExpressionContext context = createExpressionContext(start);
@@ -860,8 +858,8 @@ public class TemplateState implements Disposable {
if (defaultValue != null && resultIsNullOrEmpty) {
result = defaultValue.calculateResult(context);
}
if (element != null && !element.isValid()) {
element = null;
if (element != null) {
PsiUtilCore.ensureValid(element);
}
if (result == null || result.equalsToText(oldValue, element)) return;
@@ -47,11 +47,6 @@ public class VariableNode extends Expression {
return TemplateManagerImpl.getTemplateState(context.getEditor()).getVariableValue(getName());
}
@Override
public boolean requiresCommittedPSI() {
return myInitialValue != null && myInitialValue.requiresCommittedPSI();
}
@Override
public LookupElement[] calculateLookupItems(ExpressionContext context) {
if (myInitialValue == null){
@@ -97,11 +97,6 @@ public class MyLookupExpression extends Expression {
return new TextResult(myName);
}
@Override
public boolean requiresCommittedPSI() {
return false;
}
@Override
public String getAdvertisingText() {
return myAdvertisementText;
@@ -102,11 +102,6 @@ public class StructuralSearchTemplateBuilder {
return calculateResult(context);
}
@Override
public boolean requiresCommittedPSI() {
return false;
}
@Nullable
@Override
public LookupElement[] calculateLookupItems(ExpressionContext context) {
@@ -10,6 +10,5 @@ class EditorConfigTemplateConstantExpression(source: Iterable<String>) : Express
private val elements = source.map(LookupElementBuilder::create).toTypedArray()
override fun calculateResult(context: ExpressionContext): Result? = null
override fun calculateQuickResult(context: ExpressionContext): Result? = null
override fun requiresCommittedPSI(): Boolean = false
override fun calculateLookupItems(context: ExpressionContext) = elements
}
@@ -9,6 +9,5 @@ import com.intellij.codeInsight.template.TextResult
class EditorConfigTemplateSingletonExpression(private val source: String) : Expression() {
override fun calculateResult(context: ExpressionContext) = TextResult(source)
override fun calculateQuickResult(context: ExpressionContext) = TextResult(source)
override fun requiresCommittedPSI(): Boolean = false
override fun calculateLookupItems(context: ExpressionContext) = arrayOf(LookupElementBuilder.create(source))
}
@@ -135,11 +135,6 @@ public class PyConvertLambdaToFunctionIntention extends PyBaseIntentionAction {
return null;
}
@Override
public boolean requiresCommittedPSI() {
return false;
}
@Override
public LookupElement[] calculateLookupItems(ExpressionContext context) {
return null;
@@ -127,11 +127,6 @@ public class InsertRequiredAttributeFix extends LocalQuickFixAndIntentionActionO
return null;
}
@Override
public boolean requiresCommittedPSI() {
return false;
}
@Override
public LookupElement[] calculateLookupItems(ExpressionContext context) {
final LookupElement[] items = new LookupElement[myValues.length];
@@ -281,11 +281,6 @@ public class RncNameImpl extends RncElementImpl implements RncName, PsiReference
return calculateResult(context);
}
@Override
public boolean requiresCommittedPSI() {
return false;
}
@Override
public LookupItem[] calculateLookupItems(ExpressionContext context) {
return LookupItem.EMPTY_ARRAY;