mirror of
https://gitflic.ru/project/openide/openide.git
synced 2026-09-18 09:34:34 +07:00
1535 lines
52 KiB
Java
1535 lines
52 KiB
Java
/*
|
|
* 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.codeInspection.dataFlow;
|
|
|
|
import com.intellij.codeInsight.ExceptionUtil;
|
|
import com.intellij.codeInsight.NullableNotNullManager;
|
|
import com.intellij.codeInspection.dataFlow.instructions.*;
|
|
import com.intellij.codeInspection.dataFlow.value.DfaUnknownValue;
|
|
import com.intellij.codeInspection.dataFlow.value.DfaValue;
|
|
import com.intellij.codeInspection.dataFlow.value.DfaValueFactory;
|
|
import com.intellij.codeInspection.dataFlow.value.DfaVariableValue;
|
|
import com.intellij.openapi.diagnostic.Logger;
|
|
import com.intellij.openapi.progress.ProgressManager;
|
|
import com.intellij.psi.*;
|
|
import com.intellij.psi.tree.IElementType;
|
|
import com.intellij.psi.util.PsiTreeUtil;
|
|
import com.intellij.psi.util.PsiUtil;
|
|
import com.intellij.psi.util.RedundantCastUtil;
|
|
import com.intellij.psi.util.TypeConversionUtil;
|
|
import com.intellij.util.IncorrectOperationException;
|
|
import com.intellij.util.containers.Stack;
|
|
import org.jetbrains.annotations.NonNls;
|
|
import org.jetbrains.annotations.NotNull;
|
|
|
|
import java.util.ArrayList;
|
|
import java.util.HashSet;
|
|
import java.util.List;
|
|
import java.util.Set;
|
|
|
|
class ControlFlowAnalyzer extends JavaElementVisitor {
|
|
private static final Logger LOG = Logger.getInstance("#com.intellij.codeInspection.dataFlow.ControlFlowAnalyzer");
|
|
private static final int NOT_FOUND = -10;
|
|
private ControlFlow myPass1Flow;
|
|
private ControlFlow myCurrentFlow;
|
|
private int myPassNumber;
|
|
private HashSet<DfaVariableValue> myFields;
|
|
private Stack<CatchDescriptor> myCatchStack;
|
|
private PsiType myRuntimeException;
|
|
private final DfaValueFactory myFactory;
|
|
private boolean myHonorRuntimeExceptions = true;
|
|
|
|
ControlFlowAnalyzer(final DfaValueFactory valueFactory) {
|
|
myFactory = valueFactory;
|
|
}
|
|
|
|
private static class CantAnalyzeException extends RuntimeException {
|
|
}
|
|
|
|
public void setHonorRuntimeExceptions(final boolean honorRuntimeExceptions) {
|
|
myHonorRuntimeExceptions = honorRuntimeExceptions;
|
|
}
|
|
|
|
public ControlFlow buildControlFlow(PsiElement codeFragment) {
|
|
if (codeFragment == null) return null;
|
|
|
|
myRuntimeException = PsiType.getJavaLangRuntimeException(codeFragment.getManager(), codeFragment.getResolveScope());
|
|
myFields = new HashSet<DfaVariableValue>();
|
|
myCatchStack = new Stack<CatchDescriptor>();
|
|
myPassNumber = 1;
|
|
myPass1Flow = new ControlFlow(myFactory);
|
|
myCurrentFlow = myPass1Flow;
|
|
|
|
try {
|
|
codeFragment.accept(this);
|
|
}
|
|
catch (CantAnalyzeException e) {
|
|
return null;
|
|
}
|
|
|
|
myPassNumber = 2;
|
|
final ControlFlow pass2Flow = new ControlFlow(myFactory);
|
|
myCurrentFlow = pass2Flow;
|
|
|
|
codeFragment.accept(this);
|
|
|
|
pass2Flow.setFields(myFields.toArray(new DfaVariableValue[myFields.size()]));
|
|
|
|
LOG.assertTrue(myPass1Flow.getInstructionCount() == pass2Flow.getInstructionCount());
|
|
|
|
addInstruction(new ReturnInstruction());
|
|
|
|
return pass2Flow;
|
|
}
|
|
|
|
private boolean myRecursionStopper = false;
|
|
|
|
private void addInstruction(Instruction instruction) {
|
|
ProgressManager.checkCanceled();
|
|
|
|
if (!myRecursionStopper) {
|
|
myRecursionStopper = true;
|
|
try {
|
|
//Add extra conditional goto in order to handle possible runtime exceptions that could be caught by finally block.
|
|
if (instruction instanceof BranchingInstruction || instruction instanceof AssignInstruction ||
|
|
instruction instanceof MethodCallInstruction) {
|
|
addConditionalRuntimeThrow();
|
|
}
|
|
}
|
|
finally {
|
|
myRecursionStopper = false;
|
|
}
|
|
}
|
|
|
|
myCurrentFlow.addInstruction(instruction);
|
|
}
|
|
|
|
private int getEndOffset(PsiElement element) {
|
|
return myPassNumber == 2 ? myPass1Flow.getEndOffset(element) : 0;
|
|
}
|
|
|
|
private int getStartOffset(PsiElement element) {
|
|
return myPassNumber == 2 ? myPass1Flow.getStartOffset(element) : 0;
|
|
}
|
|
|
|
private void startElement(PsiElement element) {
|
|
myCurrentFlow.startElement(element);
|
|
}
|
|
|
|
private void finishElement(PsiElement element) {
|
|
myCurrentFlow.finishElement(element);
|
|
}
|
|
|
|
@Override public void visitAssignmentExpression(PsiAssignmentExpression expression) {
|
|
startElement(expression);
|
|
|
|
try {
|
|
PsiExpression lExpr = expression.getLExpression();
|
|
PsiExpression rExpr = expression.getRExpression();
|
|
|
|
if (rExpr == null) {
|
|
pushUnknown();
|
|
return;
|
|
}
|
|
|
|
lExpr.accept(this);
|
|
|
|
IElementType op = expression.getOperationTokenType();
|
|
PsiType type = expression.getType();
|
|
boolean isBoolean = PsiType.BOOLEAN.equals(type);
|
|
if (op == JavaTokenType.EQ) {
|
|
rExpr.accept(this);
|
|
generateBoxingUnboxingInstructionFor(rExpr, type);
|
|
}
|
|
else if (op == JavaTokenType.ANDEQ) {
|
|
if (isBoolean) {
|
|
generateNonMaccartyExpression(true, lExpr, rExpr, type);
|
|
}
|
|
else {
|
|
generateDefaultBinop(lExpr, rExpr, type);
|
|
}
|
|
}
|
|
else if (op == JavaTokenType.OREQ) {
|
|
if (isBoolean) {
|
|
generateNonMaccartyExpression(false, lExpr, rExpr, type);
|
|
}
|
|
else {
|
|
generateDefaultBinop(lExpr, rExpr, type);
|
|
}
|
|
}
|
|
else if (op == JavaTokenType.XOREQ) {
|
|
if (isBoolean) {
|
|
generateXorExpression(expression, new PsiExpression[]{lExpr, rExpr}, type);
|
|
}
|
|
else {
|
|
generateDefaultBinop(lExpr, rExpr, type);
|
|
}
|
|
}
|
|
else {
|
|
generateDefaultBinop(lExpr, rExpr, type);
|
|
}
|
|
|
|
addInstruction(new AssignInstruction(rExpr));
|
|
}
|
|
finally {
|
|
finishElement(expression);
|
|
}
|
|
}
|
|
|
|
private void generateDefaultBinop(PsiExpression lExpr, PsiExpression rExpr, final PsiType exprType) {
|
|
lExpr.accept(this);
|
|
generateBoxingUnboxingInstructionFor(lExpr,exprType);
|
|
rExpr.accept(this);
|
|
generateBoxingUnboxingInstructionFor(rExpr,exprType);
|
|
addInstruction(new BinopInstruction(null, null, lExpr.getProject()));
|
|
}
|
|
|
|
@Override public void visitAssertStatement(PsiAssertStatement statement) {
|
|
startElement(statement);
|
|
final PsiExpression condition = statement.getAssertCondition();
|
|
final PsiExpression description = statement.getAssertDescription();
|
|
if (condition != null) {
|
|
condition.accept(this);
|
|
generateBoxingUnboxingInstructionFor(condition, PsiType.BOOLEAN);
|
|
addInstruction(new ConditionalGotoInstruction(getEndOffset(statement), false, condition));
|
|
if (description != null) {
|
|
description.accept(this);
|
|
}
|
|
addInstruction(new ReturnInstruction());
|
|
}
|
|
finishElement(statement);
|
|
}
|
|
|
|
@Override public void visitDeclarationStatement(PsiDeclarationStatement statement) {
|
|
startElement(statement);
|
|
|
|
PsiElement[] elements = statement.getDeclaredElements();
|
|
for (PsiElement element : elements) {
|
|
if (element instanceof PsiClass) {
|
|
element.accept(this);
|
|
}
|
|
else if (element instanceof PsiVariable) {
|
|
PsiVariable variable = (PsiVariable)element;
|
|
PsiExpression initializer = variable.getInitializer();
|
|
if (initializer != null) {
|
|
initializeVariable(variable, initializer);
|
|
}
|
|
}
|
|
}
|
|
|
|
finishElement(statement);
|
|
}
|
|
|
|
private void initializeVariable(PsiVariable variable, PsiExpression initializer) {
|
|
DfaVariableValue dfaVariable = myFactory.getVarFactory().create(variable, false);
|
|
addInstruction(new PushInstruction(dfaVariable, initializer));
|
|
initializer.accept(this);
|
|
generateBoxingUnboxingInstructionFor(initializer, variable.getType());
|
|
addInstruction(new AssignInstruction(initializer));
|
|
addInstruction(new PopInstruction());
|
|
}
|
|
|
|
@Override
|
|
public void visitCodeFragment(JavaCodeFragment codeFragment) {
|
|
startElement(codeFragment);
|
|
if (codeFragment instanceof PsiExpressionCodeFragment) {
|
|
PsiExpression expression = ((PsiExpressionCodeFragment)codeFragment).getExpression();
|
|
if (expression != null) {
|
|
expression.accept(this);
|
|
}
|
|
}
|
|
finishElement(codeFragment);
|
|
}
|
|
|
|
@Override public void visitCodeBlock(PsiCodeBlock block) {
|
|
startElement(block);
|
|
|
|
PsiStatement[] statements = block.getStatements();
|
|
for (PsiStatement statement : statements) {
|
|
statement.accept(this);
|
|
}
|
|
|
|
for (PsiStatement statement : statements) {
|
|
if (statement instanceof PsiDeclarationStatement) {
|
|
PsiDeclarationStatement declarationStatement = (PsiDeclarationStatement)statement;
|
|
PsiElement[] declarations = declarationStatement.getDeclaredElements();
|
|
for (PsiElement declaration : declarations) {
|
|
if (declaration instanceof PsiVariable) {
|
|
myCurrentFlow.removeVariable((PsiVariable)declaration);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
finishElement(block);
|
|
}
|
|
|
|
@Override public void visitBlockStatement(PsiBlockStatement statement) {
|
|
startElement(statement);
|
|
statement.getCodeBlock().accept(this);
|
|
finishElement(statement);
|
|
}
|
|
|
|
@Override public void visitBreakStatement(PsiBreakStatement statement) {
|
|
startElement(statement);
|
|
|
|
PsiStatement exitedStatement = statement.findExitedStatement();
|
|
|
|
if (exitedStatement != null) {
|
|
int offset = myPass1Flow.getEndOffset(exitedStatement);
|
|
if (offset == -1) offset = myPass1Flow.getInstructionCount();
|
|
addInstruction(new GotoInstruction(offset));
|
|
}
|
|
|
|
finishElement(statement);
|
|
}
|
|
|
|
@Override public void visitContinueStatement(PsiContinueStatement statement) {
|
|
startElement(statement);
|
|
PsiStatement continuedStatement = statement.findContinuedStatement();
|
|
if (continuedStatement != null) {
|
|
int offset = -1;
|
|
if (continuedStatement instanceof PsiForStatement) {
|
|
PsiStatement body = ((PsiForStatement)continuedStatement).getBody();
|
|
offset = myPass1Flow.getEndOffset(body);
|
|
}
|
|
else if (continuedStatement instanceof PsiWhileStatement) {
|
|
PsiStatement body = ((PsiWhileStatement)continuedStatement).getBody();
|
|
offset = myPass1Flow.getEndOffset(body);
|
|
}
|
|
else if (continuedStatement instanceof PsiDoWhileStatement) {
|
|
PsiStatement body = ((PsiDoWhileStatement)continuedStatement).getBody();
|
|
offset = myPass1Flow.getEndOffset(body);
|
|
}
|
|
else if (continuedStatement instanceof PsiForeachStatement) {
|
|
PsiStatement body = ((PsiForeachStatement)continuedStatement).getBody();
|
|
offset = myPass1Flow.getEndOffset(body);
|
|
}
|
|
Instruction instruction = offset == -1 ? new EmptyInstruction() : new GotoInstruction(offset);
|
|
addInstruction(instruction);
|
|
}
|
|
finishElement(statement);
|
|
}
|
|
|
|
@Override public void visitDoWhileStatement(PsiDoWhileStatement statement) {
|
|
startElement(statement);
|
|
|
|
PsiStatement body = statement.getBody();
|
|
if (body != null) {
|
|
body.accept(this);
|
|
PsiExpression condition = statement.getCondition();
|
|
if (condition != null) {
|
|
condition.accept(this);
|
|
generateBoxingUnboxingInstructionFor(condition, PsiType.BOOLEAN);
|
|
addInstruction(new ConditionalGotoInstruction(getStartOffset(statement), false, condition));
|
|
}
|
|
}
|
|
|
|
finishElement(statement);
|
|
}
|
|
|
|
@Override public void visitEmptyStatement(PsiEmptyStatement statement) {
|
|
startElement(statement);
|
|
finishElement(statement);
|
|
}
|
|
|
|
@Override public void visitExpressionStatement(PsiExpressionStatement statement) {
|
|
startElement(statement);
|
|
final PsiExpression expr = statement.getExpression();
|
|
expr.accept(this);
|
|
addInstruction(new PopInstruction());
|
|
finishElement(statement);
|
|
}
|
|
|
|
@Override public void visitExpressionListStatement(PsiExpressionListStatement statement) {
|
|
startElement(statement);
|
|
PsiExpression[] expressions = statement.getExpressionList().getExpressions();
|
|
for (PsiExpression expr : expressions) {
|
|
expr.accept(this);
|
|
addInstruction(new PopInstruction());
|
|
}
|
|
finishElement(statement);
|
|
}
|
|
|
|
@Override public void visitForeachStatement(PsiForeachStatement statement) {
|
|
startElement(statement);
|
|
final PsiParameter parameter = statement.getIterationParameter();
|
|
final PsiExpression iteratedValue = statement.getIteratedValue();
|
|
|
|
if (iteratedValue != null) {
|
|
iteratedValue.accept(this);
|
|
addInstruction(new FieldReferenceInstruction(iteratedValue, "Collection iterator or array.length"));
|
|
}
|
|
|
|
int offset = myCurrentFlow.getInstructionCount();
|
|
DfaVariableValue dfaVariable = myFactory.getVarFactory().create(parameter, false);
|
|
addInstruction(new PushInstruction(dfaVariable, null));
|
|
pushUnknown();
|
|
addInstruction(new AssignInstruction(null));
|
|
|
|
addInstruction(new PopInstruction());
|
|
|
|
pushUnknown();
|
|
addInstruction(new ConditionalGotoInstruction(getEndOffset(statement), true, null));
|
|
|
|
final PsiStatement body = statement.getBody();
|
|
if (body != null) {
|
|
body.accept(this);
|
|
}
|
|
|
|
addInstruction(new GotoInstruction(offset));
|
|
|
|
finishElement(statement);
|
|
myCurrentFlow.removeVariable(parameter);
|
|
}
|
|
|
|
@Override public void visitForStatement(PsiForStatement statement) {
|
|
startElement(statement);
|
|
final ArrayList<PsiElement> declaredVariables = new ArrayList<PsiElement>();
|
|
|
|
PsiStatement initialization = statement.getInitialization();
|
|
if (initialization != null) {
|
|
initialization.accept(this);
|
|
initialization.accept(new JavaRecursiveElementWalkingVisitor() {
|
|
@Override public void visitReferenceExpression(PsiReferenceExpression expression) {
|
|
visitElement(expression);
|
|
}
|
|
|
|
@Override public void visitDeclarationStatement(PsiDeclarationStatement statement) {
|
|
PsiElement[] declaredElements = statement.getDeclaredElements();
|
|
for (PsiElement element : declaredElements) {
|
|
if (element instanceof PsiVariable) {
|
|
declaredVariables.add(element);
|
|
}
|
|
}
|
|
}
|
|
});
|
|
}
|
|
|
|
PsiExpression condition = statement.getCondition();
|
|
if (condition != null) {
|
|
condition.accept(this);
|
|
generateBoxingUnboxingInstructionFor(condition, PsiType.BOOLEAN);
|
|
}
|
|
else {
|
|
addInstruction(new PushInstruction(myFactory.getConstFactory().getTrue(), null));
|
|
}
|
|
addInstruction(new ConditionalGotoInstruction(getEndOffset(statement), true, condition));
|
|
|
|
PsiStatement body = statement.getBody();
|
|
if (body != null) {
|
|
body.accept(this);
|
|
}
|
|
|
|
PsiStatement update = statement.getUpdate();
|
|
if (update != null) {
|
|
update.accept(this);
|
|
}
|
|
|
|
int offset = initialization != null
|
|
? getEndOffset(initialization)
|
|
: getStartOffset(statement);
|
|
|
|
addInstruction(new GotoInstruction(offset));
|
|
finishElement(statement);
|
|
|
|
for (PsiElement declaredVariable : declaredVariables) {
|
|
PsiVariable psiVariable = (PsiVariable)declaredVariable;
|
|
myCurrentFlow.removeVariable(psiVariable);
|
|
}
|
|
}
|
|
|
|
@Override public void visitIfStatement(PsiIfStatement statement) {
|
|
startElement(statement);
|
|
|
|
PsiExpression condition = statement.getCondition();
|
|
|
|
PsiStatement thenStatement = statement.getThenBranch();
|
|
PsiStatement elseStatement = statement.getElseBranch();
|
|
|
|
int offset = elseStatement != null
|
|
? getStartOffset(elseStatement)
|
|
: getEndOffset(statement);
|
|
|
|
if (condition != null) {
|
|
condition.accept(this);
|
|
generateBoxingUnboxingInstructionFor(condition, PsiType.BOOLEAN);
|
|
addInstruction(new ConditionalGotoInstruction(offset, true, condition));
|
|
}
|
|
|
|
if (thenStatement != null) {
|
|
thenStatement.accept(this);
|
|
}
|
|
|
|
if (elseStatement != null) {
|
|
offset = getEndOffset(statement);
|
|
Instruction instruction = new GotoInstruction(offset);
|
|
addInstruction(instruction);
|
|
elseStatement.accept(this);
|
|
}
|
|
|
|
finishElement(statement);
|
|
}
|
|
|
|
// in case of JspTemplateStatement
|
|
@Override public void visitStatement(PsiStatement statement) {
|
|
startElement(statement);
|
|
finishElement(statement);
|
|
}
|
|
|
|
@Override public void visitLabeledStatement(PsiLabeledStatement statement) {
|
|
startElement(statement);
|
|
PsiStatement childStatement = statement.getStatement();
|
|
if (childStatement != null) {
|
|
childStatement.accept(this);
|
|
}
|
|
finishElement(statement);
|
|
}
|
|
|
|
@Override public void visitReturnStatement(PsiReturnStatement statement) {
|
|
startElement(statement);
|
|
|
|
PsiExpression returnValue = statement.getReturnValue();
|
|
if (returnValue != null) {
|
|
returnValue.accept(this);
|
|
PsiMethod method = PsiTreeUtil.getParentOfType(statement, PsiMethod.class, true, PsiMember.class);
|
|
if (method != null) {
|
|
generateBoxingUnboxingInstructionFor(returnValue, method.getReturnType());
|
|
}
|
|
addInstruction(new CheckReturnValueInstruction(statement));
|
|
}
|
|
|
|
int finallyOffset = getFinallyOffset();
|
|
if (finallyOffset != NOT_FOUND) {
|
|
addInstruction(new GosubInstruction(finallyOffset));
|
|
}
|
|
addInstruction(new ReturnInstruction());
|
|
finishElement(statement);
|
|
}
|
|
|
|
@Override public void visitSwitchLabelStatement(PsiSwitchLabelStatement statement) {
|
|
startElement(statement);
|
|
finishElement(statement);
|
|
}
|
|
|
|
@Override public void visitSwitchStatement(PsiSwitchStatement switchStmt) {
|
|
startElement(switchStmt);
|
|
PsiElementFactory psiFactory = JavaPsiFacade.getInstance(switchStmt.getProject()).getElementFactory();
|
|
PsiExpression caseExpression = switchStmt.getExpression();
|
|
Set<PsiEnumConstant> enumVals = null;
|
|
if (caseExpression != null /*&& !(caseExpression instanceof PsiReferenceExpression)*/) {
|
|
caseExpression.accept(this);
|
|
|
|
generateBoxingUnboxingInstructionFor(caseExpression, PsiType.INT);
|
|
final PsiClass psiClass = PsiUtil.resolveClassInType(caseExpression.getType());
|
|
if (psiClass != null && psiClass.isEnum()) {
|
|
addInstruction(new FieldReferenceInstruction(caseExpression, "switch statement expression"));
|
|
enumVals = new HashSet<PsiEnumConstant>();
|
|
for (PsiField f : psiClass.getFields()) {
|
|
if (f instanceof PsiEnumConstant) {
|
|
enumVals.add((PsiEnumConstant)f);
|
|
}
|
|
}
|
|
} else {
|
|
addInstruction(new PopInstruction());
|
|
}
|
|
|
|
}
|
|
|
|
PsiCodeBlock body = switchStmt.getBody();
|
|
|
|
if (body != null) {
|
|
PsiStatement[] statements = body.getStatements();
|
|
PsiSwitchLabelStatement defaultLabel = null;
|
|
for (PsiStatement statement : statements) {
|
|
if (statement instanceof PsiSwitchLabelStatement) {
|
|
PsiSwitchLabelStatement psiLabelStatement = (PsiSwitchLabelStatement)statement;
|
|
if (psiLabelStatement.isDefaultCase()) {
|
|
defaultLabel = psiLabelStatement;
|
|
}
|
|
else {
|
|
try {
|
|
int offset = getStartOffset(statement);
|
|
PsiExpression caseValue = psiLabelStatement.getCaseValue();
|
|
|
|
if (caseValue != null &&
|
|
caseExpression instanceof PsiReferenceExpression &&
|
|
((PsiReferenceExpression)caseExpression).getQualifierExpression() == null &&
|
|
JavaPsiFacade.getInstance(body.getProject()).getConstantEvaluationHelper().computeConstantExpression(caseValue) != null) {
|
|
PsiExpression psiComparison = psiFactory.createExpressionFromText(
|
|
caseExpression.getText() + "==" + caseValue.getText(), switchStmt);
|
|
psiComparison.accept(this);
|
|
}
|
|
else {
|
|
pushUnknown();
|
|
}
|
|
|
|
addInstruction(new ConditionalGotoInstruction(offset, false, statement));
|
|
|
|
if (enumVals != null) {
|
|
if (caseValue instanceof PsiReferenceExpression) {
|
|
enumVals.remove(((PsiReferenceExpression)caseValue).resolve());
|
|
}
|
|
}
|
|
}
|
|
catch (IncorrectOperationException e) {
|
|
LOG.error(e);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
if (enumVals == null || !enumVals.isEmpty()) {
|
|
int offset = defaultLabel != null ? getStartOffset(defaultLabel) : getEndOffset(body);
|
|
addInstruction(new GotoInstruction(offset));
|
|
}
|
|
|
|
body.accept(this);
|
|
}
|
|
|
|
finishElement(switchStmt);
|
|
}
|
|
|
|
@Override public void visitSynchronizedStatement(PsiSynchronizedStatement statement) {
|
|
startElement(statement);
|
|
|
|
PsiExpression lock = statement.getLockExpression();
|
|
if (lock != null) {
|
|
lock.accept(this);
|
|
addInstruction(new PopInstruction());
|
|
}
|
|
|
|
addInstruction(new FlushVariableInstruction(null));
|
|
|
|
PsiCodeBlock body = statement.getBody();
|
|
if (body != null) {
|
|
body.accept(this);
|
|
}
|
|
|
|
finishElement(statement);
|
|
}
|
|
|
|
@Override public void visitThrowStatement(PsiThrowStatement statement) {
|
|
startElement(statement);
|
|
|
|
PsiExpression exception = statement.getException();
|
|
|
|
if (exception != null) {
|
|
exception.accept(this);
|
|
addThrowCode(exception.getType());
|
|
}
|
|
|
|
finishElement(statement);
|
|
}
|
|
|
|
private void addConditionalRuntimeThrow() {
|
|
for (int i = myCatchStack.size() - 1; i >= 0; i--) {
|
|
CatchDescriptor cd = myCatchStack.get(i);
|
|
if (cd.isFinally()) {
|
|
pushUnknown();
|
|
final ConditionalGotoInstruction branch = new ConditionalGotoInstruction(-1, false, null);
|
|
addInstruction(branch);
|
|
addInstruction(new EmptyStackInstruction());
|
|
addInstruction(new GosubInstruction(cd.getJumpOffset()));
|
|
addInstruction(new ReturnInstruction());
|
|
branch.setOffset(myCurrentFlow.getInstructionCount());
|
|
}
|
|
else if (cd.getType() instanceof PsiClassType &&
|
|
ExceptionUtil.isUncheckedExceptionOrSuperclass((PsiClassType)cd.getType())) {
|
|
pushUnknown();
|
|
final ConditionalGotoInstruction branch = new ConditionalGotoInstruction(-1, false, null);
|
|
addInstruction(branch);
|
|
addInstruction(new EmptyStackInstruction());
|
|
addInstruction(new PushInstruction(myFactory.getNotNullFactory().create(myRuntimeException), null));
|
|
addGotoCatch(cd);
|
|
branch.setOffset(myCurrentFlow.getInstructionCount());
|
|
return;
|
|
}
|
|
}
|
|
}
|
|
|
|
private void addThrowCode(PsiType exceptionClass) {
|
|
if (exceptionClass == null) return;
|
|
for (int i = myCatchStack.size() - 1; i >= 0; i--) {
|
|
CatchDescriptor cd = myCatchStack.get(i);
|
|
if (cd.isFinally()) {
|
|
addInstruction(new GosubInstruction(cd.getJumpOffset()));
|
|
}
|
|
else if (cd.getType().isAssignableFrom(exceptionClass)) { // Definite catch.
|
|
addGotoCatch(cd);
|
|
return;
|
|
}
|
|
else if (cd.getType().isConvertibleFrom(exceptionClass)) { // Probable catch
|
|
addInstruction(new DupInstruction());
|
|
pushUnknown();
|
|
final ConditionalGotoInstruction branch = new ConditionalGotoInstruction(-1, false, null);
|
|
addInstruction(branch);
|
|
addGotoCatch(cd);
|
|
branch.setOffset(myCurrentFlow.getInstructionCount());
|
|
}
|
|
}
|
|
|
|
addInstruction(new ReturnInstruction());
|
|
}
|
|
|
|
/**
|
|
* Exception is expected on the stack.
|
|
*
|
|
* @param cd
|
|
*/
|
|
private void addGotoCatch(CatchDescriptor cd) {
|
|
addInstruction(new PushInstruction(myFactory.getVarFactory().create(cd.getParameter(), false), null));
|
|
addInstruction(new SwapInstruction());
|
|
addInstruction(new AssignInstruction(null));
|
|
addInstruction(new PopInstruction());
|
|
addInstruction(new GotoInstruction(cd.getJumpOffset()));
|
|
}
|
|
|
|
private int getFinallyOffset() {
|
|
for (int i = myCatchStack.size() - 1; i >= 0; i--) {
|
|
CatchDescriptor cd = myCatchStack.get(i);
|
|
if (cd.isFinally()) return cd.getJumpOffset();
|
|
}
|
|
|
|
return NOT_FOUND;
|
|
}
|
|
|
|
class CatchDescriptor {
|
|
private final PsiType myType;
|
|
private PsiParameter myParameter;
|
|
private final PsiCodeBlock myBlock;
|
|
private final boolean myIsFinally;
|
|
|
|
CatchDescriptor(PsiCodeBlock finallyBlock) {
|
|
myType = null;
|
|
myBlock = finallyBlock;
|
|
myIsFinally = true;
|
|
}
|
|
|
|
CatchDescriptor(PsiParameter parameter, PsiCodeBlock catchBlock) {
|
|
myType = parameter.getType();
|
|
myParameter = parameter;
|
|
myBlock = catchBlock;
|
|
myIsFinally = false;
|
|
}
|
|
|
|
public PsiType getType() {
|
|
return myType;
|
|
}
|
|
|
|
public boolean isFinally() {
|
|
return myIsFinally;
|
|
}
|
|
|
|
public int getJumpOffset() {
|
|
return getStartOffset(myBlock);
|
|
}
|
|
|
|
public PsiParameter getParameter() { return myParameter; }
|
|
}
|
|
|
|
@Override public void visitErrorElement(PsiErrorElement element) {
|
|
throw new CantAnalyzeException();
|
|
}
|
|
|
|
@Override public void visitTryStatement(PsiTryStatement statement) {
|
|
startElement(statement);
|
|
PsiCodeBlock finallyBlock = statement.getFinallyBlock();
|
|
|
|
if (finallyBlock != null) {
|
|
myCatchStack.push(new CatchDescriptor(finallyBlock));
|
|
}
|
|
|
|
int catchesPushCount = 0;
|
|
PsiCatchSection[] sections = statement.getCatchSections();
|
|
for (int i = sections.length - 1; i >= 0; i--) {
|
|
PsiCatchSection section = sections[i];
|
|
PsiCodeBlock catchBlock = section.getCatchBlock();
|
|
PsiParameter parameter = section.getParameter();
|
|
if (parameter != null && catchBlock != null && parameter.getType() instanceof PsiClassType &&
|
|
(!myHonorRuntimeExceptions || !ExceptionUtil.isUncheckedException((PsiClassType)parameter.getType()))) {
|
|
myCatchStack.push(new CatchDescriptor(parameter, catchBlock));
|
|
catchesPushCount++;
|
|
}
|
|
else {
|
|
throw new CantAnalyzeException();
|
|
}
|
|
}
|
|
|
|
int endOffset = finallyBlock == null ? getEndOffset(statement) : getStartOffset(finallyBlock) - 2;
|
|
|
|
PsiCodeBlock tryBlock = statement.getTryBlock();
|
|
|
|
if (tryBlock != null) {
|
|
tryBlock.accept(this);
|
|
}
|
|
|
|
for (int i = 0; i < catchesPushCount; i++) myCatchStack.pop();
|
|
|
|
addInstruction(new GotoInstruction(endOffset));
|
|
|
|
for (PsiCatchSection section : sections) {
|
|
section.accept(this);
|
|
addInstruction(new GotoInstruction(endOffset));
|
|
}
|
|
|
|
if (finallyBlock != null) {
|
|
myCatchStack.pop();
|
|
addInstruction(new GosubInstruction(getStartOffset(finallyBlock)));
|
|
addInstruction(new GotoInstruction(getEndOffset(statement)));
|
|
finallyBlock.accept(this);
|
|
addInstruction(new ReturnFromSubInstruction());
|
|
}
|
|
|
|
finishElement(statement);
|
|
}
|
|
|
|
@Override public void visitCatchSection(PsiCatchSection section) {
|
|
PsiCodeBlock catchBlock = section.getCatchBlock();
|
|
if (catchBlock != null) catchBlock.accept(this);
|
|
}
|
|
|
|
@Override public void visitWhileStatement(PsiWhileStatement statement) {
|
|
startElement(statement);
|
|
|
|
PsiExpression condition = statement.getCondition();
|
|
|
|
if (condition != null) {
|
|
condition.accept(this);
|
|
generateBoxingUnboxingInstructionFor(condition, PsiType.BOOLEAN);
|
|
addInstruction(new ConditionalGotoInstruction(getEndOffset(statement), true, condition));
|
|
}
|
|
|
|
PsiStatement body = statement.getBody();
|
|
if (body != null) {
|
|
body.accept(this);
|
|
}
|
|
|
|
if (condition != null) {
|
|
addInstruction(new GotoInstruction(getStartOffset(statement)));
|
|
}
|
|
|
|
finishElement(statement);
|
|
}
|
|
|
|
@Override public void visitExpressionList(PsiExpressionList list) {
|
|
startElement(list);
|
|
|
|
PsiExpression[] expressions = list.getExpressions();
|
|
for (PsiExpression expression : expressions) {
|
|
expression.accept(this);
|
|
}
|
|
|
|
finishElement(list);
|
|
}
|
|
|
|
@Override public void visitExpression(PsiExpression expression) {
|
|
startElement(expression);
|
|
DfaValue dfaValue = myFactory.create(expression);
|
|
addInstruction(new PushInstruction(dfaValue, expression));
|
|
finishElement(expression);
|
|
}
|
|
|
|
@Override public void visitArrayAccessExpression(PsiArrayAccessExpression expression) {
|
|
//TODO:::
|
|
startElement(expression);
|
|
PsiExpression arrayExpression = expression.getArrayExpression();
|
|
arrayExpression.accept(this);
|
|
addInstruction(new FieldReferenceInstruction(expression, null));
|
|
|
|
PsiExpression indexExpression = expression.getIndexExpression();
|
|
if (indexExpression != null) {
|
|
indexExpression.accept(this);
|
|
generateBoxingUnboxingInstructionFor(indexExpression, PsiType.INT);
|
|
addInstruction(new PopInstruction());
|
|
}
|
|
|
|
pushTypeOrUnknown(arrayExpression);
|
|
finishElement(expression);
|
|
}
|
|
|
|
@Override public void visitArrayInitializerExpression(PsiArrayInitializerExpression expression) {
|
|
//TODO:::
|
|
startElement(expression);
|
|
PsiType type = expression.getType();
|
|
PsiExpression[] initializers = expression.getInitializers();
|
|
for (PsiExpression initializer : initializers) {
|
|
initializer.accept(this);
|
|
if (type instanceof PsiArrayType) {
|
|
generateBoxingUnboxingInstructionFor(initializer, ((PsiArrayType)type).getComponentType());
|
|
}
|
|
addInstruction(new PopInstruction());
|
|
}
|
|
pushUnknown();
|
|
finishElement(expression);
|
|
}
|
|
|
|
@Override
|
|
public void visitPolyadicExpression(PsiPolyadicExpression expression) {
|
|
startElement(expression);
|
|
|
|
try {
|
|
DfaValue dfaValue = myFactory.create(expression);
|
|
if (dfaValue != null) {
|
|
addInstruction(new PushInstruction(dfaValue, expression));
|
|
return;
|
|
}
|
|
IElementType op = expression.getOperationTokenType();
|
|
|
|
PsiExpression[] operands = expression.getOperands();
|
|
if (operands.length <= 1) {
|
|
pushUnknown();
|
|
return;
|
|
}
|
|
PsiType type = expression.getType();
|
|
if (op == JavaTokenType.ANDAND) {
|
|
generateAndExpression(operands, type);
|
|
}
|
|
else if (op == JavaTokenType.OROR) {
|
|
generateOrExpression(operands, type);
|
|
}
|
|
else if (op == JavaTokenType.XOR && PsiType.BOOLEAN.equals(type)) {
|
|
generateXorExpression(expression, operands, type);
|
|
}
|
|
else {
|
|
generateOther(expression, op, operands, type);
|
|
}
|
|
}
|
|
finally {
|
|
finishElement(expression);
|
|
}
|
|
}
|
|
|
|
private void generateOther(PsiPolyadicExpression expression,
|
|
IElementType op,
|
|
PsiExpression[] operands,
|
|
PsiType type) {
|
|
boolean comparing = op == JavaTokenType.EQEQ || op == JavaTokenType.NE;
|
|
PsiExpression lExpr = operands[0];
|
|
lExpr.accept(this);
|
|
PsiType lType = lExpr.getType();
|
|
PsiExpression rExpr = operands[1];
|
|
|
|
for (int i = 1; i < operands.length; i++) {
|
|
rExpr = operands[i];
|
|
PsiType rType = rExpr.getType();
|
|
|
|
boolean comparingRef = comparing
|
|
&& !TypeConversionUtil.isPrimitiveAndNotNull(lType)
|
|
&& !TypeConversionUtil.isPrimitiveAndNotNull(rType);
|
|
|
|
boolean comparingPrimitiveNumerics = comparing &&
|
|
TypeConversionUtil.isPrimitiveAndNotNull(lType) &&
|
|
TypeConversionUtil.isPrimitiveAndNotNull(rType) &&
|
|
TypeConversionUtil.isNumericType(lType) &&
|
|
TypeConversionUtil.isNumericType(rType);
|
|
|
|
PsiType castType = comparingPrimitiveNumerics ? PsiType.LONG : type;
|
|
|
|
if (!comparingRef) {
|
|
generateBoxingUnboxingInstructionFor(lExpr,castType);
|
|
}
|
|
|
|
lExpr = rExpr;
|
|
lType = rType;
|
|
|
|
rExpr.accept(this);
|
|
if (!comparingRef) {
|
|
generateBoxingUnboxingInstructionFor(rExpr,castType);
|
|
}
|
|
|
|
if (JavaTokenType.PLUS == op) {
|
|
if (type == null || !type.equalsToText("java.lang.String")) {
|
|
op = null;
|
|
}
|
|
}
|
|
|
|
PsiElement psiAnchor = expression.isPhysical() ? expression : null;
|
|
addInstruction(new BinopInstruction(op, psiAnchor, expression.getProject()));
|
|
}
|
|
}
|
|
|
|
private void generateBoxingUnboxingInstructionFor(PsiExpression expression, PsiType expectedType) {
|
|
PsiType exprType = expression.getType();
|
|
|
|
if (TypeConversionUtil.isPrimitiveAndNotNull(expectedType) && TypeConversionUtil.isPrimitiveWrapper(exprType)) {
|
|
addInstruction(new MethodCallInstruction(expression, MethodCallInstruction.MethodType.UNBOXING));
|
|
}
|
|
else if (TypeConversionUtil.isPrimitiveWrapper(expectedType) && TypeConversionUtil.isPrimitiveAndNotNull(exprType)) {
|
|
addInstruction(new MethodCallInstruction(expression, MethodCallInstruction.MethodType.BOXING));
|
|
}
|
|
else if (exprType != expectedType &&
|
|
TypeConversionUtil.isPrimitiveAndNotNull(exprType) &&
|
|
TypeConversionUtil.isPrimitiveAndNotNull(expectedType) &&
|
|
TypeConversionUtil.isNumericType(exprType) &&
|
|
TypeConversionUtil.isNumericType(expectedType)) {
|
|
addInstruction(new MethodCallInstruction(expression, MethodCallInstruction.MethodType.CAST, expectedType) {
|
|
@Override
|
|
public DfaInstructionState[] accept(DataFlowRunner runner, DfaMemoryState stateBefore, InstructionVisitor visitor) {
|
|
return visitor.visitCast(this, runner, stateBefore);
|
|
}
|
|
});
|
|
}
|
|
}
|
|
|
|
private void generateXorExpression(PsiExpression expression, PsiExpression[] operands, final PsiType exprType) {
|
|
PsiExpression operand = operands[0];
|
|
operand.accept(this);
|
|
generateBoxingUnboxingInstructionFor(operand, exprType);
|
|
for (int i = 1; i < operands.length; i++) {
|
|
operand = operands[i];
|
|
operand.accept(this);
|
|
generateBoxingUnboxingInstructionFor(operand, exprType);
|
|
PsiElement psiAnchor = expression.isPhysical() ? expression : null;
|
|
addInstruction(new BinopInstruction(JavaTokenType.NE, psiAnchor, expression.getProject()));
|
|
}
|
|
}
|
|
|
|
private void generateOrExpression(PsiExpression[] operands, final PsiType exprType) {
|
|
for (int i = 0; i < operands.length; i++) {
|
|
PsiExpression operand = operands[i];
|
|
operand.accept(this);
|
|
generateBoxingUnboxingInstructionFor(operand, exprType);
|
|
PsiExpression nextOperand = i == operands.length - 1 ? null : operands[i + 1];
|
|
if (nextOperand != null) {
|
|
addInstruction(new ConditionalGotoInstruction(getStartOffset(nextOperand), true, operand));
|
|
addInstruction(new PushInstruction(myFactory.getConstFactory().getTrue(), null));
|
|
addInstruction(new GotoInstruction(getEndOffset(operands[operands.length - 1])));
|
|
}
|
|
}
|
|
}
|
|
|
|
private void generateNonMaccartyExpression(boolean and, PsiExpression lExpression, PsiExpression rExpression, final PsiType exprType) {
|
|
rExpression.accept(this);
|
|
generateBoxingUnboxingInstructionFor(rExpression, exprType);
|
|
|
|
lExpression.accept(this);
|
|
generateBoxingUnboxingInstructionFor(lExpression, exprType);
|
|
|
|
ConditionalGotoInstruction toPopAndPushSuccess = new ConditionalGotoInstruction(-1, and, lExpression);
|
|
addInstruction(toPopAndPushSuccess);
|
|
final GotoInstruction overPushSuccess = new GotoInstruction(-1);
|
|
addInstruction(overPushSuccess);
|
|
|
|
final PopInstruction pop = new PopInstruction();
|
|
addInstruction(pop);
|
|
final PushInstruction pushSuccess = new PushInstruction(and
|
|
? myFactory.getConstFactory().getFalse()
|
|
: myFactory.getConstFactory().getTrue(), null);
|
|
addInstruction(pushSuccess);
|
|
|
|
toPopAndPushSuccess.setOffset(pop.getIndex());
|
|
overPushSuccess.setOffset(pushSuccess.getIndex() + 1);
|
|
}
|
|
|
|
private void generateAndExpression(PsiExpression[] operands, final PsiType exprType) {
|
|
List<ConditionalGotoInstruction> branchToFail = new ArrayList<ConditionalGotoInstruction>();
|
|
for (int i = 0; i < operands.length; i++) {
|
|
PsiExpression operand = operands[i];
|
|
operand.accept(this);
|
|
generateBoxingUnboxingInstructionFor(operand, exprType);
|
|
PsiExpression nextOperand = i == operands.length - 1 ? null : operands[i + 1];
|
|
|
|
if (nextOperand != null) {
|
|
ConditionalGotoInstruction onFail = new ConditionalGotoInstruction(-1, true, operand);
|
|
branchToFail.add(onFail);
|
|
addInstruction(onFail);
|
|
}
|
|
}
|
|
|
|
GotoInstruction toSuccess = new GotoInstruction(-1);
|
|
addInstruction(toSuccess);
|
|
PushInstruction pushFalse = new PushInstruction(myFactory.getConstFactory().getFalse(), null);
|
|
addInstruction(pushFalse);
|
|
for (ConditionalGotoInstruction toFail : branchToFail) {
|
|
toFail.setOffset(pushFalse.getIndex());
|
|
}
|
|
toSuccess.setOffset(pushFalse.getIndex()+1);
|
|
|
|
}
|
|
|
|
@Override public void visitClassObjectAccessExpression(PsiClassObjectAccessExpression expression) {
|
|
startElement(expression);
|
|
PsiElement[] children = expression.getChildren();
|
|
for (PsiElement child : children) {
|
|
child.accept(this);
|
|
}
|
|
pushUnknown();
|
|
finishElement(expression);
|
|
}
|
|
|
|
@Override public void visitConditionalExpression(PsiConditionalExpression expression) {
|
|
startElement(expression);
|
|
|
|
DfaValue dfaValue = myFactory.create(expression);
|
|
if (dfaValue != null) {
|
|
addInstruction(new PushInstruction(dfaValue, expression));
|
|
}
|
|
else {
|
|
PsiExpression condition = expression.getCondition();
|
|
|
|
PsiExpression thenExpression = expression.getThenExpression();
|
|
PsiExpression elseExpression = expression.getElseExpression();
|
|
|
|
final int elseOffset = elseExpression == null ? getEndOffset(expression) - 1 : getStartOffset(elseExpression);
|
|
if (thenExpression != null) {
|
|
condition.accept(this);
|
|
generateBoxingUnboxingInstructionFor(condition, PsiType.BOOLEAN);
|
|
PsiType type = expression.getType();
|
|
addInstruction(new ConditionalGotoInstruction(elseOffset, true, condition));
|
|
thenExpression.accept(this);
|
|
generateBoxingUnboxingInstructionFor(thenExpression,type);
|
|
|
|
addInstruction(new GotoInstruction(getEndOffset(expression)));
|
|
|
|
if (elseExpression != null) {
|
|
elseExpression.accept(this);
|
|
generateBoxingUnboxingInstructionFor(elseExpression,type);
|
|
}
|
|
else {
|
|
pushUnknown();
|
|
}
|
|
}
|
|
else {
|
|
pushUnknown();
|
|
}
|
|
}
|
|
|
|
finishElement(expression);
|
|
}
|
|
|
|
private void pushUnknown() {
|
|
addInstruction(new PushInstruction(DfaUnknownValue.getInstance(), null));
|
|
}
|
|
|
|
@Override public void visitInstanceOfExpression(PsiInstanceOfExpression expression) {
|
|
startElement(expression);
|
|
PsiExpression operand = expression.getOperand();
|
|
PsiTypeElement checkType = expression.getCheckType();
|
|
if (checkType != null) {
|
|
operand.accept(this);
|
|
PsiType type = checkType.getType();
|
|
if (type instanceof PsiClassType) {
|
|
type = ((PsiClassType)type).rawType();
|
|
}
|
|
addInstruction(new PushInstruction(myFactory.getTypeFactory().create(type), null));
|
|
addInstruction(new InstanceofInstruction(expression, expression.getProject(), operand, type));
|
|
}
|
|
else {
|
|
pushUnknown();
|
|
}
|
|
|
|
finishElement(expression);
|
|
}
|
|
|
|
private void addMethodThrows(PsiMethod method) {
|
|
if (method != null) {
|
|
PsiClassType[] refs = method.getThrowsList().getReferencedTypes();
|
|
for (PsiClassType ref : refs) {
|
|
pushUnknown();
|
|
ConditionalGotoInstruction cond = new ConditionalGotoInstruction(NOT_FOUND, false, null);
|
|
addInstruction(cond);
|
|
addInstruction(new EmptyStackInstruction());
|
|
addInstruction(new PushInstruction(myFactory.getTypeFactory().create(ref), null));
|
|
addThrowCode(ref);
|
|
cond.setOffset(myCurrentFlow.getInstructionCount());
|
|
}
|
|
}
|
|
}
|
|
|
|
@Override public void visitMethodCallExpression(PsiMethodCallExpression expression) {
|
|
try {
|
|
startElement(expression);
|
|
|
|
if (processSpecialMethods(expression)) {
|
|
return;
|
|
}
|
|
|
|
PsiReferenceExpression methodExpression = expression.getMethodExpression();
|
|
PsiExpression qualifierExpression = methodExpression.getQualifierExpression();
|
|
|
|
if (qualifierExpression != null) {
|
|
qualifierExpression.accept(this);
|
|
}
|
|
else {
|
|
pushUnknown();
|
|
}
|
|
|
|
PsiExpression[] paramExprs = expression.getArgumentList().getExpressions();
|
|
PsiElement method = methodExpression.resolve();
|
|
PsiParameter[] parameters = method instanceof PsiMethod ? ((PsiMethod)method).getParameterList().getParameters() : null;
|
|
for (int i = 0; i < paramExprs.length; i++) {
|
|
PsiExpression paramExpr = paramExprs[i];
|
|
paramExpr.accept(this);
|
|
if (parameters != null && i < parameters.length) {
|
|
generateBoxingUnboxingInstructionFor(paramExpr, parameters[i].getType());
|
|
}
|
|
}
|
|
|
|
addInstruction(new MethodCallInstruction(expression));
|
|
|
|
if (!myCatchStack.isEmpty()) {
|
|
addMethodThrows(expression.resolveMethod());
|
|
}
|
|
}
|
|
finally {
|
|
finishElement(expression);
|
|
}
|
|
}
|
|
|
|
private boolean processSpecialMethods(PsiMethodCallExpression expression) {
|
|
PsiReferenceExpression methodExpression = expression.getMethodExpression();
|
|
PsiExpression qualifierExpression = methodExpression.getQualifierExpression();
|
|
|
|
PsiMethod resolved = expression.resolveMethod();
|
|
if (resolved != null) {
|
|
final PsiExpressionList argList = expression.getArgumentList();
|
|
@NonNls String methodName = resolved.getName();
|
|
|
|
PsiExpression[] params = argList.getExpressions();
|
|
PsiClass owner = resolved.getContainingClass();
|
|
final int exitPoint = getEndOffset(expression) - 1;
|
|
if (owner != null) {
|
|
final String className = owner.getQualifiedName();
|
|
if ("java.lang.System".equals(className)) {
|
|
if ("exit".equals(methodName)) {
|
|
pushParameters(params, false, false);
|
|
addInstruction(new ReturnInstruction());
|
|
return true;
|
|
}
|
|
}
|
|
else if ("junit.framework.Assert".equals(className) || "org.junit.Assert".equals(className) || "org.testng.Assert".equals(className)) {
|
|
boolean testng = "org.testng.Assert".equals(className);
|
|
if ("fail".equals(methodName)) {
|
|
pushParameters(params, false, !testng);
|
|
addInstruction(new ReturnInstruction());
|
|
return true;
|
|
}
|
|
else if ("assertTrue".equals(methodName)) {
|
|
pushParameters(params, true, !testng);
|
|
conditionalExit(exitPoint, false);
|
|
return true;
|
|
}
|
|
else if ("assertFalse".equals(methodName)) {
|
|
pushParameters(params, true, !testng);
|
|
conditionalExit(exitPoint, true);
|
|
return true;
|
|
}
|
|
else if ("assertNull".equals(methodName)) {
|
|
pushParameters(params, true, !testng);
|
|
|
|
addInstruction(new PushInstruction(myFactory.getConstFactory().getNull(), null));
|
|
addInstruction(new BinopInstruction(JavaTokenType.EQEQ, null, expression.getProject()));
|
|
conditionalExit(exitPoint, false);
|
|
return true;
|
|
}
|
|
else if ("assertNotNull".equals(methodName)) {
|
|
pushParameters(params, true, !testng);
|
|
|
|
addInstruction(new PushInstruction(myFactory.getConstFactory().getNull(), null));
|
|
addInstruction(new BinopInstruction(JavaTokenType.EQEQ, null, expression.getProject()));
|
|
conditionalExit(exitPoint, true);
|
|
return true;
|
|
}
|
|
return false;
|
|
}
|
|
}
|
|
|
|
// Idea project only.
|
|
if (qualifierExpression != null) {
|
|
if (qualifierExpression.textMatches("LOG")) {
|
|
final PsiType qualifierType = qualifierExpression.getType();
|
|
if (qualifierType != null && qualifierType.equalsToText("com.intellij.openapi.diagnostic.Logger")) {
|
|
if ("error".equals(methodName)) {
|
|
for (PsiExpression param : params) {
|
|
param.accept(this);
|
|
addInstruction(new PopInstruction());
|
|
}
|
|
addInstruction(new ReturnInstruction());
|
|
return true;
|
|
}
|
|
else if ("assertTrue".equals(methodName)) {
|
|
params[0].accept(this);
|
|
for (int i = 1; i < params.length; i++) {
|
|
params[i].accept(this);
|
|
addInstruction(new PopInstruction());
|
|
}
|
|
conditionalExit(exitPoint, false);
|
|
return true;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
return false;
|
|
}
|
|
|
|
private void conditionalExit(final int continuePoint, final boolean exitIfTrue) {
|
|
addInstruction(new ConditionalGotoInstruction(continuePoint, exitIfTrue, null));
|
|
addInstruction(new ReturnInstruction());
|
|
pushUnknown();
|
|
}
|
|
|
|
private void pushParameters(final PsiExpression[] params, final boolean leaveOnStack, boolean lastParameterIsSignificant) {
|
|
for (int i = 0; i < params.length; i++) {
|
|
PsiExpression param = params[i];
|
|
param.accept(this);
|
|
if (leaveOnStack) {
|
|
if (lastParameterIsSignificant && i == params.length - 1 || !lastParameterIsSignificant && i == 0) continue;
|
|
}
|
|
|
|
addInstruction(new PopInstruction());
|
|
}
|
|
}
|
|
|
|
private void pushTypeOrUnknown(PsiExpression expr) {
|
|
PsiType type = expr.getType();
|
|
|
|
final DfaValue dfaValue;
|
|
if (type instanceof PsiClassType) {
|
|
dfaValue = myFactory.getTypeFactory().create(type);
|
|
}
|
|
else {
|
|
dfaValue = null;
|
|
}
|
|
|
|
addInstruction(new PushInstruction(dfaValue, null));
|
|
}
|
|
|
|
@Override public void visitNewExpression(PsiNewExpression expression) {
|
|
startElement(expression);
|
|
|
|
pushUnknown();
|
|
|
|
if (expression.getType() instanceof PsiArrayType) {
|
|
final PsiExpression[] dimensions = expression.getArrayDimensions();
|
|
for (final PsiExpression dimension : dimensions) {
|
|
dimension.accept(this);
|
|
}
|
|
for (PsiExpression dimension : dimensions) {
|
|
addInstruction(new PopInstruction());
|
|
}
|
|
final PsiArrayInitializerExpression arrayInitializer = expression.getArrayInitializer();
|
|
if (arrayInitializer != null) {
|
|
for (final PsiExpression initializer : arrayInitializer.getInitializers()) {
|
|
initializer.accept(this);
|
|
addInstruction(new PopInstruction());
|
|
}
|
|
}
|
|
addInstruction(new MethodCallInstruction(expression));
|
|
}
|
|
else {
|
|
final PsiExpressionList args = expression.getArgumentList();
|
|
PsiMethod ctr = expression.resolveConstructor();
|
|
if (args != null) {
|
|
PsiExpression[] params = args.getExpressions();
|
|
PsiParameter[] parameters = ctr == null ? null : ctr.getParameterList().getParameters();
|
|
for (int i = 0; i < params.length; i++) {
|
|
PsiExpression param = params[i];
|
|
param.accept(this);
|
|
if (parameters != null && i < parameters.length) {
|
|
generateBoxingUnboxingInstructionFor(param, parameters[i].getType());
|
|
}
|
|
}
|
|
}
|
|
|
|
addInstruction(new MethodCallInstruction(expression));
|
|
|
|
if (!myCatchStack.isEmpty()) {
|
|
addMethodThrows(ctr);
|
|
}
|
|
|
|
}
|
|
|
|
finishElement(expression);
|
|
}
|
|
|
|
@Override public void visitParenthesizedExpression(PsiParenthesizedExpression expression) {
|
|
startElement(expression);
|
|
PsiExpression inner = expression.getExpression();
|
|
if (inner != null) {
|
|
inner.accept(this);
|
|
}
|
|
else {
|
|
pushUnknown();
|
|
}
|
|
finishElement(expression);
|
|
}
|
|
|
|
@Override public void visitPostfixExpression(PsiPostfixExpression expression) {
|
|
startElement(expression);
|
|
|
|
PsiExpression operand = expression.getOperand();
|
|
operand.accept(this);
|
|
generateBoxingUnboxingInstructionFor(operand, PsiType.INT);
|
|
|
|
addInstruction(new PopInstruction());
|
|
pushUnknown();
|
|
|
|
if (operand instanceof PsiReferenceExpression) {
|
|
PsiVariable psiVariable = DfaValueFactory.resolveVariable((PsiReferenceExpression)expression.getOperand());
|
|
if (psiVariable != null) {
|
|
DfaVariableValue dfaVariable = myFactory.getVarFactory().create(psiVariable, false);
|
|
addInstruction(new FlushVariableInstruction(dfaVariable));
|
|
}
|
|
}
|
|
|
|
finishElement(expression);
|
|
}
|
|
|
|
@Override public void visitPrefixExpression(PsiPrefixExpression expression) {
|
|
startElement(expression);
|
|
|
|
DfaValue dfaValue = myFactory.create(expression);
|
|
if (dfaValue == null) {
|
|
PsiExpression operand = expression.getOperand();
|
|
|
|
if (operand == null) {
|
|
pushUnknown();
|
|
}
|
|
else {
|
|
operand.accept(this);
|
|
PsiType type = expression.getType();
|
|
PsiPrimitiveType unboxed = PsiPrimitiveType.getUnboxedType(type);
|
|
generateBoxingUnboxingInstructionFor(operand, unboxed == null ? type : unboxed);
|
|
if (expression.getOperationTokenType() == JavaTokenType.EXCL) {
|
|
addInstruction(new NotInstruction());
|
|
}
|
|
else {
|
|
addInstruction(new PopInstruction());
|
|
pushUnknown();
|
|
|
|
if (operand instanceof PsiReferenceExpression) {
|
|
PsiVariable psiVariable = DfaValueFactory.resolveVariable((PsiReferenceExpression)operand);
|
|
if (psiVariable != null) {
|
|
DfaVariableValue dfaVariable = myFactory.getVarFactory().create(psiVariable, false);
|
|
addInstruction(new FlushVariableInstruction(dfaVariable));
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
else {
|
|
addInstruction(new PushInstruction(dfaValue, expression));
|
|
}
|
|
|
|
finishElement(expression);
|
|
}
|
|
|
|
@Override public void visitReferenceExpression(PsiReferenceExpression expression) {
|
|
startElement(expression);
|
|
|
|
DfaValue dfaValue = myFactory.create(expression);
|
|
PsiElement resolved = expression.resolve();
|
|
if (dfaValue instanceof DfaVariableValue) {
|
|
DfaVariableValue dfaVariable = (DfaVariableValue)dfaValue;
|
|
PsiVariable psiVariable = dfaVariable.getPsiVariable();
|
|
if (psiVariable instanceof PsiField) {
|
|
addField(dfaVariable);
|
|
}
|
|
}
|
|
|
|
final PsiExpression qualifierExpression = expression.getQualifierExpression();
|
|
if (qualifierExpression != null) {
|
|
qualifierExpression.accept(this);
|
|
if (resolved instanceof PsiField) {
|
|
addInstruction(new FieldReferenceInstruction(expression, null));
|
|
}
|
|
else {
|
|
addInstruction(new PopInstruction());
|
|
}
|
|
}
|
|
|
|
if (dfaValue == null && resolved instanceof PsiField) {
|
|
// Accessing a field from another instance
|
|
dfaValue = myFactory.getTypeFactory().create(((PsiField)resolved).getType(),
|
|
NullableNotNullManager.isNullable((PsiModifierListOwner)resolved));
|
|
}
|
|
|
|
addInstruction(new PushInstruction(dfaValue, expression));
|
|
|
|
finishElement(expression);
|
|
}
|
|
|
|
private void addField(DfaVariableValue field) {
|
|
myFields.add(field);
|
|
}
|
|
|
|
@Override public void visitSuperExpression(PsiSuperExpression expression) {
|
|
startElement(expression);
|
|
addInstruction(new PushInstruction(myFactory.getNotNullFactory().create(expression.getType()), null));
|
|
finishElement(expression);
|
|
}
|
|
|
|
@Override public void visitThisExpression(PsiThisExpression expression) {
|
|
startElement(expression);
|
|
addInstruction(new PushInstruction(myFactory.getNotNullFactory().create(expression.getType()), null));
|
|
finishElement(expression);
|
|
}
|
|
|
|
@Override public void visitLiteralExpression(PsiLiteralExpression expression) {
|
|
startElement(expression);
|
|
|
|
DfaValue dfaValue = myFactory.create(expression);
|
|
addInstruction(new PushInstruction(dfaValue, expression));
|
|
|
|
finishElement(expression);
|
|
}
|
|
|
|
@Override public void visitTypeCastExpression(PsiTypeCastExpression castExpression) {
|
|
startElement(castExpression);
|
|
PsiExpression operand = castExpression.getOperand();
|
|
|
|
if (operand != null) {
|
|
operand.accept(this);
|
|
generateBoxingUnboxingInstructionFor(operand, castExpression.getType());
|
|
}
|
|
else {
|
|
pushTypeOrUnknown(castExpression);
|
|
}
|
|
|
|
addInstruction(createCastInstruction(castExpression));
|
|
finishElement(castExpression);
|
|
}
|
|
|
|
@Override public void visitClass(PsiClass aClass) {
|
|
}
|
|
|
|
@NotNull
|
|
private static Instruction createCastInstruction(PsiTypeCastExpression castExpression) {
|
|
PsiExpression expr = castExpression.getOperand();
|
|
final PsiTypeElement typeElement = castExpression.getCastType();
|
|
if (typeElement != null && !RedundantCastUtil.isTypeCastSemantical(castExpression)) {
|
|
PsiType castType = typeElement.getType();
|
|
if (expr != null) {
|
|
return new TypeCastInstruction(castExpression, expr, castType);
|
|
}
|
|
}
|
|
return new Instruction() {
|
|
|
|
@Override
|
|
public DfaInstructionState[] accept(DataFlowRunner runner, DfaMemoryState stateBefore, InstructionVisitor visitor) {
|
|
stateBefore.pop();
|
|
stateBefore.push(DfaUnknownValue.getInstance());
|
|
return nextInstruction(runner, stateBefore);
|
|
}
|
|
};
|
|
}
|
|
|
|
|
|
}
|