From de1778309a5a8f91f23ddf2f75dfa066b86b0129 Mon Sep 17 00:00:00 2001 From: Oleg Shpynov Date: Mon, 25 Jan 2010 19:40:33 +0300 Subject: [PATCH] Scope implementation for Python --- .../controlflow/PyControlFlowBuilder.java | 3 +- ...{ControlFlowOwner.java => ScopeOwner.java} | 4 +- .../dataflow/PyReachingDefsDfaInstance.java | 3 + .../dataflow/PyReachingDefsSemilattice.java | 2 + .../codeInsight/dataflow/scope/Scope.java | 24 +++++++ .../dataflow/{ => scope}/ScopeUtil.java | 6 +- .../dataflow/{ => scope}/ScopeVariable.java | 2 +- .../dataflow/scope/impl/ScopeImpl.java | 64 +++++++++++++++++++ .../{ => scope/impl}/ScopeVariableImpl.java | 4 +- .../src/com/jetbrains/python/psi/PyClass.java | 5 +- .../src/com/jetbrains/python/psi/PyFile.java | 4 +- .../com/jetbrains/python/psi/PyFunction.java | 4 +- .../python/psi/impl/PyClassImpl.java | 16 +++++ .../jetbrains/python/psi/impl/PyFileImpl.java | 14 ++++ .../python/psi/impl/PyFunctionImpl.java | 14 ++++ 15 files changed, 153 insertions(+), 16 deletions(-) rename python/src/com/jetbrains/python/codeInsight/controlflow/{ControlFlowOwner.java => ScopeOwner.java} (62%) create mode 100644 python/src/com/jetbrains/python/codeInsight/dataflow/scope/Scope.java rename python/src/com/jetbrains/python/codeInsight/dataflow/{ => scope}/ScopeUtil.java (84%) rename python/src/com/jetbrains/python/codeInsight/dataflow/{ => scope}/ScopeVariable.java (94%) create mode 100644 python/src/com/jetbrains/python/codeInsight/dataflow/scope/impl/ScopeImpl.java rename python/src/com/jetbrains/python/codeInsight/dataflow/{ => scope/impl}/ScopeVariableImpl.java (89%) diff --git a/python/src/com/jetbrains/python/codeInsight/controlflow/PyControlFlowBuilder.java b/python/src/com/jetbrains/python/codeInsight/controlflow/PyControlFlowBuilder.java index 490fdd7408a1..87c58cdf4e19 100644 --- a/python/src/com/jetbrains/python/codeInsight/controlflow/PyControlFlowBuilder.java +++ b/python/src/com/jetbrains/python/codeInsight/controlflow/PyControlFlowBuilder.java @@ -3,7 +3,6 @@ package com.jetbrains.python.codeInsight.controlflow; import com.intellij.codeInsight.controlflow.ControlFlowBuilder; import com.intellij.codeInsight.controlflow.ControlFlow; import com.intellij.codeInsight.controlflow.Instruction; -import com.intellij.codeInsight.controlflow.impl.ControlFlowImpl; import com.intellij.codeInsight.controlflow.impl.InstructionImpl; import com.intellij.openapi.util.Pair; import com.intellij.openapi.util.Ref; @@ -26,7 +25,7 @@ public class PyControlFlowBuilder extends PyRecursiveElementVisitor { //// Control flow builder staff //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - public ControlFlow buildControlFlow(@NotNull final ControlFlowOwner owner) { + public ControlFlow buildControlFlow(@NotNull final ScopeOwner owner) { return myBuilder.build(this, owner); } diff --git a/python/src/com/jetbrains/python/codeInsight/controlflow/ControlFlowOwner.java b/python/src/com/jetbrains/python/codeInsight/controlflow/ScopeOwner.java similarity index 62% rename from python/src/com/jetbrains/python/codeInsight/controlflow/ControlFlowOwner.java rename to python/src/com/jetbrains/python/codeInsight/controlflow/ScopeOwner.java index e31ce70747d2..be46f9be84d3 100644 --- a/python/src/com/jetbrains/python/codeInsight/controlflow/ControlFlowOwner.java +++ b/python/src/com/jetbrains/python/codeInsight/controlflow/ScopeOwner.java @@ -1,11 +1,13 @@ package com.jetbrains.python.codeInsight.controlflow; import com.intellij.codeInsight.controlflow.ControlFlow; +import com.jetbrains.python.codeInsight.dataflow.scope.Scope; import com.jetbrains.python.psi.PyElement; /** * @author oleg */ -public interface ControlFlowOwner extends PyElement { +public interface ScopeOwner extends PyElement { ControlFlow getControlFlow(); + Scope getScope(); } diff --git a/python/src/com/jetbrains/python/codeInsight/dataflow/PyReachingDefsDfaInstance.java b/python/src/com/jetbrains/python/codeInsight/dataflow/PyReachingDefsDfaInstance.java index 25932623f160..53690ffbe68e 100644 --- a/python/src/com/jetbrains/python/codeInsight/dataflow/PyReachingDefsDfaInstance.java +++ b/python/src/com/jetbrains/python/codeInsight/dataflow/PyReachingDefsDfaInstance.java @@ -6,6 +6,9 @@ import com.intellij.codeInsight.dataflow.DfaInstance; import com.intellij.psi.PsiElement; import com.intellij.psi.util.PsiTreeUtil; import com.jetbrains.python.codeInsight.controlflow.WriteInstruction; +import com.jetbrains.python.codeInsight.dataflow.scope.ScopeUtil; +import com.jetbrains.python.codeInsight.dataflow.scope.ScopeVariable; +import com.jetbrains.python.codeInsight.dataflow.scope.impl.ScopeVariableImpl; import org.jetbrains.annotations.NotNull; import java.util.Map; diff --git a/python/src/com/jetbrains/python/codeInsight/dataflow/PyReachingDefsSemilattice.java b/python/src/com/jetbrains/python/codeInsight/dataflow/PyReachingDefsSemilattice.java index b398f92e5167..f9c5543e17b7 100644 --- a/python/src/com/jetbrains/python/codeInsight/dataflow/PyReachingDefsSemilattice.java +++ b/python/src/com/jetbrains/python/codeInsight/dataflow/PyReachingDefsSemilattice.java @@ -5,6 +5,8 @@ import com.intellij.codeInsight.dataflow.Semilattice; import com.intellij.psi.PsiElement; import com.intellij.psi.util.PsiTreeUtil; import com.intellij.util.containers.HashSet; +import com.jetbrains.python.codeInsight.dataflow.scope.ScopeVariable; +import com.jetbrains.python.codeInsight.dataflow.scope.impl.ScopeVariableImpl; import org.jetbrains.annotations.Nullable; import java.util.ArrayList; diff --git a/python/src/com/jetbrains/python/codeInsight/dataflow/scope/Scope.java b/python/src/com/jetbrains/python/codeInsight/dataflow/scope/Scope.java new file mode 100644 index 000000000000..4e8003f30fae --- /dev/null +++ b/python/src/com/jetbrains/python/codeInsight/dataflow/scope/Scope.java @@ -0,0 +1,24 @@ +package com.jetbrains.python.codeInsight.dataflow.scope; + +import com.intellij.psi.PsiElement; +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; + +import java.util.Collection; + +/** + * @author oleg + */ +public interface Scope { + /* + * @return defined scope local/instance/class variables and parameters, using reaching defs + */ + Collection getDeclaredVariables(@NotNull PsiElement anchorElement); + + + /* + * @return defined scope local/instance/class variables and parameters, using reaching defs + */ + @Nullable + ScopeVariable getDeclaredVariable(@NotNull PsiElement anchorElement, @NotNull String name); +} diff --git a/python/src/com/jetbrains/python/codeInsight/dataflow/ScopeUtil.java b/python/src/com/jetbrains/python/codeInsight/dataflow/scope/ScopeUtil.java similarity index 84% rename from python/src/com/jetbrains/python/codeInsight/dataflow/ScopeUtil.java rename to python/src/com/jetbrains/python/codeInsight/dataflow/scope/ScopeUtil.java index 3d5e5bb4cc04..fe437749ddc6 100644 --- a/python/src/com/jetbrains/python/codeInsight/dataflow/ScopeUtil.java +++ b/python/src/com/jetbrains/python/codeInsight/dataflow/scope/ScopeUtil.java @@ -1,8 +1,8 @@ -package com.jetbrains.python.codeInsight.dataflow; +package com.jetbrains.python.codeInsight.dataflow.scope; import com.intellij.psi.PsiElement; import com.intellij.psi.util.PsiTreeUtil; -import com.jetbrains.python.codeInsight.controlflow.ControlFlowOwner; +import com.jetbrains.python.codeInsight.controlflow.ScopeOwner; import com.jetbrains.python.psi.*; import com.jetbrains.python.psi.impl.PyExceptPartNavigator; import com.jetbrains.python.psi.impl.PyForStatementNavigator; @@ -35,7 +35,7 @@ public class ScopeUtil { return listCompExpression; } - final ControlFlowOwner owner = PsiTreeUtil.getParentOfType(element, ControlFlowOwner.class, false); + final ScopeOwner owner = PsiTreeUtil.getParentOfType(element, ScopeOwner.class, false); assert owner != null : "element should have not null controlflow owner"; return owner; } diff --git a/python/src/com/jetbrains/python/codeInsight/dataflow/ScopeVariable.java b/python/src/com/jetbrains/python/codeInsight/dataflow/scope/ScopeVariable.java similarity index 94% rename from python/src/com/jetbrains/python/codeInsight/dataflow/ScopeVariable.java rename to python/src/com/jetbrains/python/codeInsight/dataflow/scope/ScopeVariable.java index 06124d38b449..d5c0fbed2057 100644 --- a/python/src/com/jetbrains/python/codeInsight/dataflow/ScopeVariable.java +++ b/python/src/com/jetbrains/python/codeInsight/dataflow/scope/ScopeVariable.java @@ -14,7 +14,7 @@ * limitations under the License. */ -package com.jetbrains.python.codeInsight.dataflow; +package com.jetbrains.python.codeInsight.dataflow.scope; import com.intellij.psi.PsiElement; import org.jetbrains.annotations.NotNull; diff --git a/python/src/com/jetbrains/python/codeInsight/dataflow/scope/impl/ScopeImpl.java b/python/src/com/jetbrains/python/codeInsight/dataflow/scope/impl/ScopeImpl.java new file mode 100644 index 000000000000..df69a89665e3 --- /dev/null +++ b/python/src/com/jetbrains/python/codeInsight/dataflow/scope/impl/ScopeImpl.java @@ -0,0 +1,64 @@ +package com.jetbrains.python.codeInsight.dataflow.scope.impl; + +import com.intellij.codeInsight.controlflow.Instruction; +import com.intellij.codeInsight.dataflow.DFAEngine; +import com.intellij.codeInsight.dataflow.DFAMap; +import com.intellij.psi.PsiElement; +import com.jetbrains.python.codeInsight.controlflow.ScopeOwner; +import com.jetbrains.python.codeInsight.dataflow.PyReachingDefsDfaInstance; +import com.jetbrains.python.codeInsight.dataflow.PyReachingDefsSemilattice; +import com.jetbrains.python.codeInsight.dataflow.scope.Scope; +import com.jetbrains.python.codeInsight.dataflow.scope.ScopeVariable; +import org.jetbrains.annotations.NotNull; + +import java.util.Collection; +import java.util.Collections; +import java.util.List; + +/** + * @author oleg + */ +public class ScopeImpl implements Scope { + private final Instruction[] myFlow; + private List> myCachedScopeVariables; + + public ScopeImpl(final ScopeOwner flowOwner) { + myFlow = flowOwner.getControlFlow().getInstructions(); + } + + @NotNull + public Collection getDeclaredVariables(@NotNull final PsiElement anchorElement) { + computeScopeVariables(); + for (int i = 0; i < myFlow.length; i++) { + Instruction instruction = myFlow[i]; + final PsiElement element = instruction.getElement(); + if (element == anchorElement) { + return myCachedScopeVariables.get(i).values(); + } + } + return Collections.emptyList(); + } + + public ScopeVariable getDeclaredVariable(@NotNull final PsiElement anchorElement, + @NotNull final String name) { + computeScopeVariables(); + for (int i = 0; i < myFlow.length; i++) { + Instruction instruction = myFlow[i]; + final PsiElement element = instruction.getElement(); + if (element == anchorElement) { + return myCachedScopeVariables.get(i).get(name); + } + } + return null; + } + + public List> computeScopeVariables() { + if (myCachedScopeVariables == null) { + final PyReachingDefsDfaInstance dfaInstance = new PyReachingDefsDfaInstance(); + final PyReachingDefsSemilattice semilattice = new PyReachingDefsSemilattice(); + final DFAEngine engine = new DFAEngine(myFlow, dfaInstance, semilattice); + myCachedScopeVariables = engine.performDFA(); + } + return myCachedScopeVariables; + } +} diff --git a/python/src/com/jetbrains/python/codeInsight/dataflow/ScopeVariableImpl.java b/python/src/com/jetbrains/python/codeInsight/dataflow/scope/impl/ScopeVariableImpl.java similarity index 89% rename from python/src/com/jetbrains/python/codeInsight/dataflow/ScopeVariableImpl.java rename to python/src/com/jetbrains/python/codeInsight/dataflow/scope/impl/ScopeVariableImpl.java index 738bb4ebac22..5ab0387c2d8c 100644 --- a/python/src/com/jetbrains/python/codeInsight/dataflow/ScopeVariableImpl.java +++ b/python/src/com/jetbrains/python/codeInsight/dataflow/scope/impl/ScopeVariableImpl.java @@ -1,7 +1,7 @@ -package com.jetbrains.python.codeInsight.dataflow; +package com.jetbrains.python.codeInsight.dataflow.scope.impl; import com.intellij.psi.PsiElement; -import com.jetbrains.python.psi.PyElement; +import com.jetbrains.python.codeInsight.dataflow.scope.ScopeVariable; import org.jetbrains.annotations.NotNull; import java.util.Collection; diff --git a/python/src/com/jetbrains/python/psi/PyClass.java b/python/src/com/jetbrains/python/psi/PyClass.java index 4045da959cb1..86d7c50ea2d9 100644 --- a/python/src/com/jetbrains/python/psi/PyClass.java +++ b/python/src/com/jetbrains/python/psi/PyClass.java @@ -3,7 +3,7 @@ package com.jetbrains.python.psi; import com.intellij.psi.PsiElement; import com.intellij.psi.PsiNamedElement; import com.intellij.psi.StubBasedPsiElement; -import com.jetbrains.python.codeInsight.controlflow.ControlFlowOwner; +import com.jetbrains.python.codeInsight.controlflow.ScopeOwner; import com.jetbrains.python.psi.stubs.PyClassStub; import org.jetbrains.annotations.NonNls; import org.jetbrains.annotations.NotNull; @@ -12,8 +12,7 @@ import org.jetbrains.annotations.Nullable; /** * Represents a class declaration in source. */ -public interface PyClass extends PsiNamedElement, PyStatement, NameDefiner, PyDocStringOwner, StubBasedPsiElement, - ControlFlowOwner { +public interface PyClass extends PsiNamedElement, PyStatement, NameDefiner, PyDocStringOwner, StubBasedPsiElement, ScopeOwner { @NotNull PyStatementList getStatementList(); diff --git a/python/src/com/jetbrains/python/psi/PyFile.java b/python/src/com/jetbrains/python/psi/PyFile.java index 057d3e743024..40835f052d00 100644 --- a/python/src/com/jetbrains/python/psi/PyFile.java +++ b/python/src/com/jetbrains/python/psi/PyFile.java @@ -6,13 +6,13 @@ package com.jetbrains.python.psi; import com.intellij.openapi.util.Key; import com.intellij.psi.PsiElement; import com.intellij.psi.PsiFile; -import com.jetbrains.python.codeInsight.controlflow.ControlFlowOwner; +import com.jetbrains.python.codeInsight.controlflow.ScopeOwner; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; import java.util.List; -public interface PyFile extends PyElement, PsiFile, PyDocStringOwner, ControlFlowOwner { +public interface PyFile extends PyElement, PsiFile, PyDocStringOwner, ScopeOwner { Key KEY_IS_DIRECTORY = Key.create("Dir impersonated by __init__.py"); Key KEY_EXCLUDE_BUILTINS = Key.create("Don't include builtins to processDeclaration results"); diff --git a/python/src/com/jetbrains/python/psi/PyFunction.java b/python/src/com/jetbrains/python/psi/PyFunction.java index a12da2900f56..dc57fae3b93d 100644 --- a/python/src/com/jetbrains/python/psi/PyFunction.java +++ b/python/src/com/jetbrains/python/psi/PyFunction.java @@ -3,7 +3,7 @@ package com.jetbrains.python.psi; import com.intellij.lang.ASTNode; import com.intellij.psi.PsiNamedElement; import com.intellij.psi.StubBasedPsiElement; -import com.jetbrains.python.codeInsight.controlflow.ControlFlowOwner; +import com.jetbrains.python.codeInsight.controlflow.ScopeOwner; import com.jetbrains.python.psi.stubs.PyFunctionStub; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; @@ -14,7 +14,7 @@ import org.jetbrains.annotations.Nullable; * @author yole */ public interface PyFunction extends PsiNamedElement, PyStatement, NameDefiner, PyDocStringOwner, StubBasedPsiElement, - ControlFlowOwner { + ScopeOwner { PyFunction[] EMPTY_ARRAY = new PyFunction[0]; /** diff --git a/python/src/com/jetbrains/python/psi/impl/PyClassImpl.java b/python/src/com/jetbrains/python/psi/impl/PyClassImpl.java index 280207bcfc3b..e9a7c01c801c 100644 --- a/python/src/com/jetbrains/python/psi/impl/PyClassImpl.java +++ b/python/src/com/jetbrains/python/psi/impl/PyClassImpl.java @@ -13,6 +13,8 @@ import com.jetbrains.python.PyElementTypes; import com.jetbrains.python.PyNames; import com.jetbrains.python.PyTokenTypes; import com.jetbrains.python.PythonDosStringFinder; +import com.jetbrains.python.codeInsight.dataflow.scope.Scope; +import com.jetbrains.python.codeInsight.dataflow.scope.impl.ScopeImpl; import com.jetbrains.python.psi.*; import com.jetbrains.python.codeInsight.controlflow.PyControlFlowBuilder; import com.jetbrains.python.psi.resolve.PyResolveUtil; @@ -355,6 +357,7 @@ public class PyClassImpl extends PyPresentableElementImpl implement private SoftReference myControlFlowRef; + @NotNull public ControlFlow getControlFlow() { ControlFlow flow = getRefValue(myControlFlowRef); @@ -365,6 +368,19 @@ public class PyClassImpl extends PyPresentableElementImpl implement return flow; } + private SoftReference myScopeRef; + + @NotNull + public Scope getScope() { + Scope scope = getRefValue(myScopeRef); + if (scope == null) { + scope = new ScopeImpl(this); + myScopeRef = new SoftReference(scope); + } + return scope; + } + + @Nullable private static T getRefValue(final SoftReference reference){ return reference != null ? reference.get() : null; diff --git a/python/src/com/jetbrains/python/psi/impl/PyFileImpl.java b/python/src/com/jetbrains/python/psi/impl/PyFileImpl.java index ede5aef773e6..bb02e373c51f 100644 --- a/python/src/com/jetbrains/python/psi/impl/PyFileImpl.java +++ b/python/src/com/jetbrains/python/psi/impl/PyFileImpl.java @@ -17,6 +17,8 @@ import com.jetbrains.python.PyElementTypes; import com.jetbrains.python.PythonDosStringFinder; import com.jetbrains.python.PythonFileType; import com.jetbrains.python.PythonLanguage; +import com.jetbrains.python.codeInsight.dataflow.scope.Scope; +import com.jetbrains.python.codeInsight.dataflow.scope.impl.ScopeImpl; import com.jetbrains.python.psi.*; import com.jetbrains.python.codeInsight.controlflow.PyControlFlowBuilder; import com.jetbrains.python.psi.resolve.PyResolveUtil; @@ -270,6 +272,18 @@ public class PyFileImpl extends PsiFileBase implements PyFile, PyExpression { return flow; } + private SoftReference myScopeRef; + + @NotNull + public Scope getScope() { + Scope scope = getRefValue(myScopeRef); + if (scope == null) { + scope = new ScopeImpl(this); + myScopeRef = new SoftReference(scope); + } + return scope; + } + @Nullable private static T getRefValue(final SoftReference reference){ return reference != null ? reference.get() : null; diff --git a/python/src/com/jetbrains/python/psi/impl/PyFunctionImpl.java b/python/src/com/jetbrains/python/psi/impl/PyFunctionImpl.java index 34d7a5157e9d..e449da049135 100644 --- a/python/src/com/jetbrains/python/psi/impl/PyFunctionImpl.java +++ b/python/src/com/jetbrains/python/psi/impl/PyFunctionImpl.java @@ -28,6 +28,8 @@ import com.intellij.util.IncorrectOperationException; import com.jetbrains.python.PyElementTypes; import com.jetbrains.python.PyTokenTypes; import com.jetbrains.python.PythonDosStringFinder; +import com.jetbrains.python.codeInsight.dataflow.scope.Scope; +import com.jetbrains.python.codeInsight.dataflow.scope.impl.ScopeImpl; import com.jetbrains.python.psi.*; import com.jetbrains.python.codeInsight.controlflow.PyControlFlowBuilder; import com.jetbrains.python.psi.stubs.PyClassStub; @@ -195,6 +197,18 @@ public class PyFunctionImpl extends PyPresentableElementImpl imp return flow; } + private SoftReference myScopeRef; + + @NotNull + public Scope getScope() { + Scope scope = getRefValue(myScopeRef); + if (scope == null) { + scope = new ScopeImpl(this); + myScopeRef = new SoftReference(scope); + } + return scope; + } + @Nullable private static T getRefValue(final SoftReference reference){ return reference != null ? reference.get() : null;