mirror of
https://gitflic.ru/project/openide/openide.git
synced 2026-09-27 10:03:11 +07:00
IDEA-211761 Smart step into fail if source code does not match the bytecode
GitOrigin-RevId: d3d5d2b8ba4ce26f2e9288ebe3148ab30247eccd
This commit is contained in:
committed by
intellij-monorepo-bot
parent
61a3e18b78
commit
2e00fd16c3
+97
-32
@@ -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)
|
||||
|
||||
-5
@@ -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);
|
||||
|
||||
|
||||
-5
@@ -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);
|
||||
|
||||
-5
@@ -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) {
|
||||
|
||||
+2
-9
@@ -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};
|
||||
|
||||
-5
@@ -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);
|
||||
|
||||
-5
@@ -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
|
||||
}
|
||||
|
||||
-5
@@ -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);
|
||||
|
||||
-5
@@ -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;
|
||||
|
||||
-5
@@ -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;
|
||||
|
||||
-5
@@ -102,11 +102,6 @@ public class StructuralSearchTemplateBuilder {
|
||||
return calculateResult(context);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean requiresCommittedPSI() {
|
||||
return false;
|
||||
}
|
||||
|
||||
@Nullable
|
||||
@Override
|
||||
public LookupElement[] calculateLookupItems(ExpressionContext context) {
|
||||
|
||||
-1
@@ -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
|
||||
}
|
||||
|
||||
-1
@@ -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))
|
||||
}
|
||||
|
||||
-5
@@ -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;
|
||||
|
||||
-5
@@ -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;
|
||||
|
||||
Reference in New Issue
Block a user