mirror of
https://gitflic.ru/project/openide/openide.git
synced 2026-09-27 10:03:11 +07:00
[debugger] Convert JavaDfaAssistProvider to Kotlin
GitOrigin-RevId: 1ad4e08de387adee04d5dd5792dd604c90747fd0
This commit is contained in:
committed by
intellij-monorepo-bot
parent
65e13c2135
commit
ad35b8a70b
@@ -56,7 +56,7 @@ interface DfaAssistProvider {
|
||||
|
||||
/**
|
||||
* @param proxy proxy to create JDI values
|
||||
* @param var DfaVariableValue to find value for
|
||||
* @param dfaVar DfaVariableValue to find value for
|
||||
* @param anchor anchor previously returned by [.getAnchor] call, where analysis takes place
|
||||
* @return JDI value for a variable; null if value is not known; NullConst if value is known to be null
|
||||
* (use [.wrap] utility method for this purpose).
|
||||
@@ -65,7 +65,7 @@ interface DfaAssistProvider {
|
||||
@Throws(EvaluateException::class)
|
||||
fun getJdiValueForDfaVariable(
|
||||
proxy: StackFrameProxyEx,
|
||||
`var`: DfaVariableValue,
|
||||
dfaVar: DfaVariableValue,
|
||||
anchor: PsiElement,
|
||||
): Value?
|
||||
|
||||
|
||||
+128
-133
@@ -1,196 +1,191 @@
|
||||
// Copyright 2000-2024 JetBrains s.r.o. and contributors. Use of this source code is governed by the Apache 2.0 license.
|
||||
package com.intellij.debugger.engine.dfaassist.java;
|
||||
package com.intellij.debugger.engine.dfaassist.java
|
||||
|
||||
import com.intellij.codeInspection.dataFlow.TypeConstraint;
|
||||
import com.intellij.codeInspection.dataFlow.TypeConstraints;
|
||||
import com.intellij.codeInspection.dataFlow.jvm.SpecialField;
|
||||
import com.intellij.codeInspection.dataFlow.jvm.descriptors.ArrayElementDescriptor;
|
||||
import com.intellij.codeInspection.dataFlow.value.DfaVariableValue;
|
||||
import com.intellij.codeInspection.dataFlow.value.VariableDescriptor;
|
||||
import com.intellij.debugger.engine.DebuggerUtils;
|
||||
import com.intellij.debugger.engine.JVMNameUtil;
|
||||
import com.intellij.debugger.engine.dfaassist.DebuggerDfaListener;
|
||||
import com.intellij.debugger.engine.dfaassist.DfaAssistProvider;
|
||||
import com.intellij.debugger.engine.evaluation.EvaluateException;
|
||||
import com.intellij.debugger.engine.evaluation.expression.CaptureTraverser;
|
||||
import com.intellij.debugger.engine.jdi.LocalVariableProxy;
|
||||
import com.intellij.debugger.impl.DebuggerUtilsEx;
|
||||
import com.intellij.debugger.jdi.StackFrameProxyEx;
|
||||
import com.intellij.psi.*;
|
||||
import com.intellij.psi.tree.IElementType;
|
||||
import com.intellij.psi.util.PsiTreeUtil;
|
||||
import com.intellij.util.containers.ContainerUtil;
|
||||
import com.sun.jdi.*;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
import com.intellij.codeInspection.dataFlow.TypeConstraint
|
||||
import com.intellij.codeInspection.dataFlow.TypeConstraints
|
||||
import com.intellij.codeInspection.dataFlow.jvm.SpecialField
|
||||
import com.intellij.codeInspection.dataFlow.jvm.descriptors.ArrayElementDescriptor
|
||||
import com.intellij.codeInspection.dataFlow.value.DfaVariableValue
|
||||
import com.intellij.debugger.engine.DebuggerUtils
|
||||
import com.intellij.debugger.engine.JVMNameUtil
|
||||
import com.intellij.debugger.engine.dfaassist.DebuggerDfaListener
|
||||
import com.intellij.debugger.engine.dfaassist.DfaAssistProvider
|
||||
import com.intellij.debugger.engine.dfaassist.DfaAssistProvider.Companion.wrap
|
||||
import com.intellij.debugger.engine.evaluation.EvaluateException
|
||||
import com.intellij.debugger.engine.evaluation.expression.CaptureTraverser
|
||||
import com.intellij.debugger.impl.DebuggerUtilsEx
|
||||
import com.intellij.debugger.jdi.StackFrameProxyEx
|
||||
import com.intellij.psi.*
|
||||
import com.intellij.psi.util.PsiTreeUtil
|
||||
import com.intellij.util.containers.ContainerUtil
|
||||
import com.sun.jdi.*
|
||||
|
||||
public final class JavaDfaAssistProvider implements DfaAssistProvider {
|
||||
@Override
|
||||
public boolean locationMatches(@NotNull PsiElement element, @NotNull Location location) {
|
||||
Method method = location.method();
|
||||
PsiElement context = DebuggerUtilsEx.getContainingMethod(element);
|
||||
if (context instanceof PsiMethod psiMethod) {
|
||||
String name = psiMethod.isConstructor() ? "<init>" : psiMethod.getName();
|
||||
return name.equals(method.name()) && psiMethod.getParameterList().getParametersCount() == method.argumentTypeNames().size();
|
||||
class JavaDfaAssistProvider : DfaAssistProvider {
|
||||
override fun locationMatches(element: PsiElement, location: Location): Boolean {
|
||||
val method = location.method()
|
||||
val context = DebuggerUtilsEx.getContainingMethod(element)
|
||||
if (context is PsiMethod) {
|
||||
val name = if (context.isConstructor()) "<init>" else context.getName()
|
||||
return name == method.name() && context.getParameterList().getParametersCount() == method.argumentTypeNames().size
|
||||
}
|
||||
if (context instanceof PsiLambdaExpression lambda) {
|
||||
if (context is PsiLambdaExpression) {
|
||||
return DebuggerUtilsEx.isLambda(method) &&
|
||||
method.argumentTypeNames().size() >= lambda.getParameterList().getParametersCount();
|
||||
method.argumentTypeNames().size >= context.getParameterList().getParametersCount()
|
||||
}
|
||||
if (context instanceof PsiClassInitializer initializer) {
|
||||
String expectedMethod = initializer.hasModifierProperty(PsiModifier.STATIC) ? "<clinit>" : "<init>";
|
||||
return method.name().equals(expectedMethod);
|
||||
if (context is PsiClassInitializer) {
|
||||
val expectedMethod = if (context.hasModifierProperty(PsiModifier.STATIC)) "<clinit>" else "<init>"
|
||||
return method.name() == expectedMethod
|
||||
}
|
||||
return false;
|
||||
return false
|
||||
}
|
||||
|
||||
@Override
|
||||
public @Nullable PsiElement getAnchor(@NotNull PsiElement element) {
|
||||
while (element instanceof PsiWhiteSpace || element instanceof PsiComment) {
|
||||
element = element.getNextSibling();
|
||||
override fun getAnchor(element: PsiElement): PsiElement? {
|
||||
var element = element
|
||||
while (element is PsiWhiteSpace || element is PsiComment) {
|
||||
element = element.getNextSibling()
|
||||
}
|
||||
while (!(element instanceof PsiStatement)) {
|
||||
PsiElement parent = element.getParent();
|
||||
if (!(parent instanceof PsiStatement) && (parent == null || element.getTextRangeInParent().getStartOffset() > 0)) {
|
||||
if (parent instanceof PsiCodeBlock && ((PsiCodeBlock)parent).getRBrace() == element) {
|
||||
PsiElement grandParent = parent.getParent();
|
||||
if (grandParent instanceof PsiBlockStatement) {
|
||||
return PsiTreeUtil.getNextSiblingOfType(grandParent, PsiStatement.class);
|
||||
while (element !is PsiStatement) {
|
||||
val parent = element.getParent()
|
||||
if (parent !is PsiStatement && (parent == null || element.textRangeInParent.startOffset > 0)) {
|
||||
if (parent is PsiCodeBlock && parent.getRBrace() === element) {
|
||||
val grandParent = parent.getParent()
|
||||
if (grandParent is PsiBlockStatement) {
|
||||
return PsiTreeUtil.getNextSiblingOfType(grandParent, PsiStatement::class.java)
|
||||
}
|
||||
}
|
||||
if (parent instanceof PsiPolyadicExpression) {
|
||||
if (parent is PsiPolyadicExpression) {
|
||||
// If we are inside the expression we can position only at locations where the stack is empty
|
||||
// currently only && and || chains inside if/return/yield are allowed
|
||||
IElementType tokenType = ((PsiPolyadicExpression)parent).getOperationTokenType();
|
||||
if (tokenType.equals(JavaTokenType.ANDAND) || tokenType.equals(JavaTokenType.OROR)) {
|
||||
PsiElement grandParent = parent.getParent();
|
||||
if (grandParent instanceof PsiIfStatement || grandParent instanceof PsiYieldStatement ||
|
||||
grandParent instanceof PsiReturnStatement) {
|
||||
if (element instanceof PsiExpression) {
|
||||
return element;
|
||||
val tokenType = parent.getOperationTokenType()
|
||||
if (tokenType == JavaTokenType.ANDAND || tokenType == JavaTokenType.OROR) {
|
||||
val grandParent = parent.getParent()
|
||||
if (grandParent is PsiIfStatement || grandParent is PsiYieldStatement ||
|
||||
grandParent is PsiReturnStatement
|
||||
) {
|
||||
if (element is PsiExpression) {
|
||||
return element
|
||||
}
|
||||
return PsiTreeUtil.getNextSiblingOfType(element, PsiExpression.class);
|
||||
return PsiTreeUtil.getNextSiblingOfType(element, PsiExpression::class.java)
|
||||
}
|
||||
}
|
||||
}
|
||||
return null;
|
||||
return null
|
||||
}
|
||||
element = parent;
|
||||
element = parent
|
||||
}
|
||||
return element;
|
||||
return element
|
||||
}
|
||||
|
||||
@Override
|
||||
public @Nullable PsiElement getCodeBlock(@NotNull PsiElement anchor) {
|
||||
if (anchor instanceof PsiSwitchLabelStatementBase) {
|
||||
return null; // unsupported yet
|
||||
override fun getCodeBlock(anchor: PsiElement): PsiElement? {
|
||||
if (anchor is PsiSwitchLabelStatementBase) {
|
||||
return null // unsupported yet
|
||||
}
|
||||
PsiElement e = anchor;
|
||||
while (e != null && !(e instanceof PsiClass) && !(e instanceof PsiFileSystemItem)) {
|
||||
e = e.getParent();
|
||||
if (e instanceof PsiCodeBlock) {
|
||||
PsiElement parent = e.getParent();
|
||||
if (parent instanceof PsiMethod || parent instanceof PsiLambdaExpression || parent instanceof PsiClassInitializer ||
|
||||
var e: PsiElement? = anchor
|
||||
while (e != null && (e !is PsiClass) && (e !is PsiFileSystemItem)) {
|
||||
e = e.getParent()
|
||||
if (e is PsiCodeBlock) {
|
||||
val parent = e.getParent()
|
||||
if (parent is PsiMethod || parent is PsiLambdaExpression || parent is PsiClassInitializer ||
|
||||
// We cannot properly restore context if we started from finally, so let's analyze just finally block
|
||||
parent instanceof PsiTryStatement && ((PsiTryStatement)parent).getFinallyBlock() == e ||
|
||||
parent instanceof PsiBlockStatement &&
|
||||
(parent.getParent() instanceof PsiSwitchLabeledRuleStatement &&
|
||||
((PsiSwitchLabeledRuleStatement)parent.getParent()).getEnclosingSwitchBlock() instanceof PsiSwitchExpression)) {
|
||||
return e;
|
||||
parent is PsiTryStatement && parent.getFinallyBlock() === e || parent is PsiBlockStatement &&
|
||||
(parent.getParent() is PsiSwitchLabeledRuleStatement &&
|
||||
(parent.getParent() as PsiSwitchLabeledRuleStatement).getEnclosingSwitchBlock() is PsiSwitchExpression)) {
|
||||
return e
|
||||
}
|
||||
}
|
||||
}
|
||||
return null;
|
||||
return null
|
||||
}
|
||||
|
||||
@Override
|
||||
public @Nullable Value getJdiValueForDfaVariable(@NotNull StackFrameProxyEx proxy,
|
||||
@NotNull DfaVariableValue var,
|
||||
@NotNull PsiElement anchor) throws EvaluateException {
|
||||
if (var.getQualifier() != null) {
|
||||
VariableDescriptor descriptor = var.getDescriptor();
|
||||
if (descriptor instanceof SpecialField) {
|
||||
@Throws(EvaluateException::class)
|
||||
override fun getJdiValueForDfaVariable(
|
||||
proxy: StackFrameProxyEx,
|
||||
dfaVar: DfaVariableValue,
|
||||
anchor: PsiElement,
|
||||
): Value? {
|
||||
val qualifier = dfaVar.qualifier
|
||||
if (qualifier != null) {
|
||||
val descriptor = dfaVar.descriptor
|
||||
if (descriptor is SpecialField) {
|
||||
// Special fields facts are applied from qualifiers
|
||||
return null;
|
||||
return null
|
||||
}
|
||||
Value qualifierValue = getJdiValueForDfaVariable(proxy, var.getQualifier(), anchor);
|
||||
if (qualifierValue == null) return null;
|
||||
PsiElement element = descriptor.getPsiElement();
|
||||
if (element instanceof PsiField psiField && qualifierValue instanceof ObjectReference objectRef) {
|
||||
ReferenceType type = objectRef.referenceType();
|
||||
PsiClass psiClass = psiField.getContainingClass();
|
||||
if (psiClass != null && type.name().equals(JVMNameUtil.getClassVMName(psiClass))) {
|
||||
Field field = DebuggerUtils.findField(type, psiField.getName());
|
||||
val qualifierValue = getJdiValueForDfaVariable(proxy, qualifier, anchor)
|
||||
if (qualifierValue == null) return null
|
||||
val element = descriptor.psiElement
|
||||
if (element is PsiField && qualifierValue is ObjectReference) {
|
||||
val type = qualifierValue.referenceType()
|
||||
val psiClass = element.getContainingClass()
|
||||
if (psiClass != null && type.name() == JVMNameUtil.getClassVMName(psiClass)) {
|
||||
val field = DebuggerUtils.findField(type, element.getName())
|
||||
if (field != null) {
|
||||
return DfaAssistProvider.wrap(objectRef.getValue(field));
|
||||
return wrap(qualifierValue.getValue(field))
|
||||
}
|
||||
}
|
||||
}
|
||||
if (descriptor instanceof ArrayElementDescriptor arrayDesc && qualifierValue instanceof ArrayReference arrayRef) {
|
||||
int index = arrayDesc.getIndex();
|
||||
int length = arrayRef.length();
|
||||
if (index >= 0 && index < length) {
|
||||
return DfaAssistProvider.wrap(arrayRef.getValue(index));
|
||||
if (descriptor is ArrayElementDescriptor && qualifierValue is ArrayReference) {
|
||||
val index = descriptor.index
|
||||
val length = qualifierValue.length()
|
||||
if (index in 0..<length) {
|
||||
return wrap(qualifierValue.getValue(index))
|
||||
}
|
||||
}
|
||||
return null;
|
||||
return null
|
||||
}
|
||||
PsiElement psi = var.getPsiVariable();
|
||||
if (psi instanceof PsiClass) {
|
||||
val psi = dfaVar.psiVariable
|
||||
if (psi is PsiClass) {
|
||||
// this; probably qualified
|
||||
PsiClass currentClass = PsiTreeUtil.getParentOfType(anchor, PsiClass.class);
|
||||
return CaptureTraverser.create((PsiClass)psi, currentClass, true).traverse(proxy.thisObject());
|
||||
val currentClass = PsiTreeUtil.getParentOfType(anchor, PsiClass::class.java)
|
||||
return CaptureTraverser.create(psi, currentClass, true).traverse(proxy.thisObject())
|
||||
}
|
||||
if (psi instanceof PsiLocalVariable || psi instanceof PsiParameter) {
|
||||
String varName = ((PsiVariable)psi).getName();
|
||||
if (PsiResolveHelper.getInstance(psi.getProject()).resolveReferencedVariable(varName, anchor) != psi) {
|
||||
if (psi is PsiLocalVariable || psi is PsiParameter) {
|
||||
val varName: String = psi.getName()!!
|
||||
if (PsiResolveHelper.getInstance(psi.getProject()).resolveReferencedVariable(varName, anchor) !== psi) {
|
||||
// Another variable with the same name could be tracked by DFA in different code branch but not visible at current code location
|
||||
return null;
|
||||
return null
|
||||
}
|
||||
LocalVariableProxy variable = proxy.visibleVariableByName(varName);
|
||||
val variable = proxy.visibleVariableByName(varName)
|
||||
if (variable != null) {
|
||||
return DfaAssistProvider.wrap(proxy.getVariableValue(variable));
|
||||
return wrap(proxy.getVariableValue(variable))
|
||||
}
|
||||
PsiClass currentClass = PsiTreeUtil.getParentOfType(anchor, PsiClass.class);
|
||||
PsiClass varClass = PsiTreeUtil.getParentOfType(psi, PsiClass.class);
|
||||
ObjectReference thisRef = CaptureTraverser.create(varClass, currentClass, false)
|
||||
.oneLevelLess().traverse(proxy.thisObject());
|
||||
val currentClass = PsiTreeUtil.getParentOfType(anchor, PsiClass::class.java)
|
||||
val varClass = PsiTreeUtil.getParentOfType(psi, PsiClass::class.java)
|
||||
val thisRef = CaptureTraverser.create(varClass, currentClass, false)
|
||||
.oneLevelLess().traverse(proxy.thisObject())
|
||||
if (thisRef != null) {
|
||||
ReferenceType type = thisRef.referenceType();
|
||||
if (type instanceof ClassType && type.isPrepared()) {
|
||||
Field field = DebuggerUtils.findField(type, "val$" + varName);
|
||||
val type = thisRef.referenceType()
|
||||
if (type is ClassType && type.isPrepared) {
|
||||
val field = DebuggerUtils.findField(type, "val$$varName")
|
||||
if (field != null) {
|
||||
return DfaAssistProvider.wrap(thisRef.getValue(field));
|
||||
return wrap(thisRef.getValue(field))
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
if (psi instanceof PsiField psiField && psiField.hasModifierProperty(PsiModifier.STATIC)) {
|
||||
PsiClass psiClass = psiField.getContainingClass();
|
||||
if (psi is PsiField && psi.hasModifierProperty(PsiModifier.STATIC)) {
|
||||
val psiClass = psi.getContainingClass()
|
||||
if (psiClass != null) {
|
||||
String name = psiClass.getQualifiedName();
|
||||
val name = psiClass.getQualifiedName()
|
||||
if (name != null) {
|
||||
ReferenceType type = ContainerUtil.getOnlyItem(proxy.getVirtualMachine().classesByName(name));
|
||||
if (type != null && type.isPrepared()) {
|
||||
Field field = DebuggerUtils.findField(type, psiField.getName());
|
||||
if (field != null && field.isStatic()) {
|
||||
return DfaAssistProvider.wrap(type.getValue(field));
|
||||
val type = ContainerUtil.getOnlyItem(proxy.getVirtualMachine().classesByName(name))
|
||||
if (type != null && type.isPrepared) {
|
||||
val field = DebuggerUtils.findField(type, psi.getName())
|
||||
if (field != null && field.isStatic) {
|
||||
return wrap(type.getValue(field))
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
return null;
|
||||
return null
|
||||
}
|
||||
|
||||
@Override
|
||||
public @NotNull DebuggerDfaListener createListener() {
|
||||
return new JavaDebuggerDfaListener();
|
||||
override fun createListener(): DebuggerDfaListener {
|
||||
return JavaDebuggerDfaListener()
|
||||
}
|
||||
|
||||
@Override
|
||||
public @NotNull TypeConstraint constraintFromJvmClassName(@NotNull PsiElement anchor, @NotNull String jvmClassName) {
|
||||
PsiClass aClass = DebuggerUtils.findClass(jvmClassName.replace('/', '.'), anchor.getProject(), anchor.getResolveScope());
|
||||
return aClass != null ? TypeConstraints.exactClass(aClass) : TypeConstraints.TOP;
|
||||
override fun constraintFromJvmClassName(anchor: PsiElement, jvmClassName: String): TypeConstraint {
|
||||
val aClass = DebuggerUtils.findClass(jvmClassName.replace('/', '.'), anchor.getProject(), anchor.getResolveScope())
|
||||
return if (aClass != null) TypeConstraints.exactClass(aClass) else TypeConstraints.TOP
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user