/plugins/groovy/src/org/jetbrains/plugins/groovy/lang/psi/controlFlow/impl/ControlFlowBuilder.java

https://bitbucket.org/nbargnesi/idea · Java · 1192 lines · 951 code · 188 blank · 53 comment · 294 complexity · 1a340bad5508ed1884b38fe143e9fdac MD5 · raw file

  1. /*
  2. * Copyright 2000-2009 JetBrains s.r.o.
  3. *
  4. * Licensed under the Apache License, Version 2.0 (the "License");
  5. * you may not use this file except in compliance with the License.
  6. * You may obtain a copy of the License at
  7. *
  8. * http://www.apache.org/licenses/LICENSE-2.0
  9. *
  10. * Unless required by applicable law or agreed to in writing, software
  11. * distributed under the License is distributed on an "AS IS" BASIS,
  12. * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  13. * See the License for the specific language governing permissions and
  14. * limitations under the License.
  15. */
  16. package org.jetbrains.plugins.groovy.lang.psi.controlFlow.impl;
  17. import com.intellij.openapi.project.Project;
  18. import com.intellij.openapi.util.NullableComputable;
  19. import com.intellij.openapi.util.Pair;
  20. import com.intellij.openapi.util.RecursionManager;
  21. import com.intellij.psi.*;
  22. import com.intellij.psi.tree.IElementType;
  23. import com.intellij.psi.util.PsiTreeUtil;
  24. import com.intellij.util.ArrayUtil;
  25. import com.intellij.util.containers.hash.HashSet;
  26. import org.jetbrains.annotations.NotNull;
  27. import org.jetbrains.annotations.Nullable;
  28. import org.jetbrains.plugins.groovy.codeInspection.utils.ControlFlowUtils;
  29. import org.jetbrains.plugins.groovy.lang.psi.GroovyFileBase;
  30. import org.jetbrains.plugins.groovy.lang.psi.GroovyPsiElement;
  31. import org.jetbrains.plugins.groovy.lang.psi.GroovyRecursiveElementVisitor;
  32. import org.jetbrains.plugins.groovy.lang.psi.api.auxiliary.GrCondition;
  33. import org.jetbrains.plugins.groovy.lang.psi.api.formatter.GrControlStatement;
  34. import org.jetbrains.plugins.groovy.lang.psi.api.statements.*;
  35. import org.jetbrains.plugins.groovy.lang.psi.api.statements.arguments.GrArgumentList;
  36. import org.jetbrains.plugins.groovy.lang.psi.api.statements.blocks.GrClosableBlock;
  37. import org.jetbrains.plugins.groovy.lang.psi.api.statements.blocks.GrOpenBlock;
  38. import org.jetbrains.plugins.groovy.lang.psi.api.statements.branch.*;
  39. import org.jetbrains.plugins.groovy.lang.psi.api.statements.clauses.GrCaseSection;
  40. import org.jetbrains.plugins.groovy.lang.psi.api.statements.clauses.GrForClause;
  41. import org.jetbrains.plugins.groovy.lang.psi.api.statements.clauses.GrForInClause;
  42. import org.jetbrains.plugins.groovy.lang.psi.api.statements.clauses.GrTraditionalForClause;
  43. import org.jetbrains.plugins.groovy.lang.psi.api.statements.expressions.*;
  44. import org.jetbrains.plugins.groovy.lang.psi.api.statements.expressions.literals.GrStringInjection;
  45. import org.jetbrains.plugins.groovy.lang.psi.api.statements.expressions.path.GrMethodCallExpression;
  46. import org.jetbrains.plugins.groovy.lang.psi.api.statements.params.GrParameter;
  47. import org.jetbrains.plugins.groovy.lang.psi.api.statements.typedef.GrAnonymousClassDefinition;
  48. import org.jetbrains.plugins.groovy.lang.psi.api.statements.typedef.GrTypeDefinition;
  49. import org.jetbrains.plugins.groovy.lang.psi.api.statements.typedef.members.GrMethod;
  50. import org.jetbrains.plugins.groovy.lang.psi.controlFlow.*;
  51. import org.jetbrains.plugins.groovy.lang.psi.impl.statements.expressions.TypesUtil;
  52. import org.jetbrains.plugins.groovy.lang.psi.util.PsiUtil;
  53. import java.util.*;
  54. import static org.jetbrains.plugins.groovy.lang.lexer.GroovyTokenTypes.*;
  55. import static org.jetbrains.plugins.groovy.lang.psi.controlFlow.ReadWriteVariableInstruction.READ;
  56. import static org.jetbrains.plugins.groovy.lang.psi.controlFlow.ReadWriteVariableInstruction.WRITE;
  57. /**
  58. * @author ven
  59. */
  60. public class ControlFlowBuilder extends GroovyRecursiveElementVisitor {
  61. private List<InstructionImpl> myInstructions;
  62. private Deque<InstructionImpl> myProcessingStack;
  63. private final PsiConstantEvaluationHelper myConstantEvaluator;
  64. private static class ExceptionInfo {
  65. final GrCatchClause myClause;
  66. /**
  67. * list of nodes containing throw statement with corresponding exception
  68. */
  69. final List<InstructionImpl> myThrowers = new ArrayList<InstructionImpl>();
  70. private ExceptionInfo(GrCatchClause clause) {
  71. myClause = clause;
  72. }
  73. }
  74. /**
  75. * stack of current catch blocks
  76. */
  77. private Deque<ExceptionInfo> myCaughtExceptionInfos;
  78. /**
  79. * stack of current conditions
  80. */
  81. private Deque<ConditionInstruction> myConditions;
  82. /**
  83. * count of finally blocks surrounding current statement
  84. */
  85. private int myFinallyCount;
  86. /**
  87. * last visited node
  88. */
  89. private InstructionImpl myHead;
  90. /**
  91. * list of pending nodes and corresponding scopes sorted by scopes from the biggest to smallest.
  92. */
  93. private List<Pair<InstructionImpl, GroovyPsiElement>> myPending;
  94. private int myInstructionNumber;
  95. public ControlFlowBuilder(Project project) {
  96. myConstantEvaluator = JavaPsiFacade.getInstance(project).getConstantEvaluationHelper();
  97. }
  98. public void visitOpenBlock(GrOpenBlock block) {
  99. final PsiElement parent = block.getParent();
  100. final PsiElement lbrace = block.getLBrace();
  101. if (lbrace != null && parent instanceof GrMethod) {
  102. for (GrParameter parameter : ((GrMethod)parent).getParameters()) {
  103. addNode(new ReadWriteVariableInstruction(parameter.getName(), parameter, WRITE));
  104. }
  105. }
  106. super.visitOpenBlock(block);
  107. if (!(block.getParent() instanceof GrBlockStatement && block.getParent().getParent() instanceof GrLoopStatement)) {
  108. final GrStatement[] statements = block.getStatements();
  109. if (statements.length > 0) {
  110. handlePossibleReturn(statements[statements.length - 1]);
  111. }
  112. }
  113. }
  114. @Override
  115. public void visitFile(GroovyFileBase file) {
  116. super.visitFile(file);
  117. final GrStatement[] statements = file.getStatements();
  118. if (statements.length > 0) {
  119. handlePossibleReturn(statements[statements.length - 1]);
  120. }
  121. }
  122. private static boolean isCertainlyReturnStatement(GrStatement st) {
  123. final PsiElement parent = st.getParent();
  124. if (parent instanceof GrOpenBlock) {
  125. if (st != ArrayUtil.getLastElement(((GrOpenBlock)parent).getStatements())) return false;
  126. PsiElement pparent = parent.getParent();
  127. if (pparent instanceof GrMethod) {
  128. return true;
  129. }
  130. if (pparent instanceof GrBlockStatement || pparent instanceof GrCatchClause || pparent instanceof GrLabeledStatement) pparent = pparent.getParent();
  131. if (pparent instanceof GrIfStatement || pparent instanceof GrControlStatement || pparent instanceof GrTryCatchStatement) {
  132. return isCertainlyReturnStatement((GrStatement)pparent);
  133. }
  134. }
  135. else if (parent instanceof GrClosableBlock) {
  136. return st == ArrayUtil.getLastElement(((GrClosableBlock)parent).getStatements());
  137. }
  138. else if (parent instanceof GroovyFileBase) {
  139. return st == ArrayUtil.getLastElement(((GroovyFileBase)parent).getStatements());
  140. }
  141. else if (parent instanceof GrControlStatement ||
  142. parent instanceof GrConditionalExpression && st != ((GrConditionalExpression)parent).getCondition() ||
  143. parent instanceof GrElvisExpression) {
  144. return isCertainlyReturnStatement((GrStatement)parent);
  145. }
  146. else if (parent instanceof GrCaseSection) {
  147. final GrStatement[] statements = ((GrCaseSection)parent).getStatements();
  148. final GrStatement last = ArrayUtil.getLastElement(statements);
  149. final GrSwitchStatement switchStatement = (GrSwitchStatement)parent.getParent();
  150. if (last instanceof GrBreakStatement && statements.length > 1 && statements[statements.length - 2] == st) {
  151. return isCertainlyReturnStatement(switchStatement);
  152. }
  153. else if (st == last) {
  154. if (st instanceof GrBreakStatement || isLastStatementInCaseSection((GrCaseSection)parent, switchStatement)) {
  155. return isCertainlyReturnStatement(switchStatement);
  156. }
  157. }
  158. }
  159. return false;
  160. }
  161. private static boolean isLastStatementInCaseSection(GrCaseSection caseSection, GrSwitchStatement switchStatement) {
  162. final GrCaseSection[] sections = switchStatement.getCaseSections();
  163. final int i = ArrayUtil.find(sections, caseSection);
  164. if (i == sections.length - 1) {
  165. return true;
  166. }
  167. for (int j = i + 1; j < sections.length; j++) {
  168. GrCaseSection section = sections[j];
  169. for (GrStatement statement : section.getStatements()) {
  170. if (!(statement instanceof GrBreakStatement)) {
  171. return false;
  172. }
  173. }
  174. }
  175. return true;
  176. }
  177. private void handlePossibleReturn(@NotNull GrStatement possibleReturn) {
  178. if (possibleReturn instanceof GrExpression && isCertainlyReturnStatement(possibleReturn)) {
  179. addNodeAndCheckPending(new MaybeReturnInstruction((GrExpression)possibleReturn));
  180. }
  181. }
  182. public Instruction[] buildControlFlow(GroovyPsiElement scope) {
  183. myInstructions = new ArrayList<InstructionImpl>();
  184. myProcessingStack = new ArrayDeque<InstructionImpl>();
  185. myCaughtExceptionInfos = new ArrayDeque<ExceptionInfo>();
  186. myConditions = new ArrayDeque<ConditionInstruction>();
  187. myFinallyCount = 0;
  188. myPending = new ArrayList<Pair<InstructionImpl, GroovyPsiElement>>();
  189. myInstructionNumber = 0;
  190. startNode(null);
  191. if (scope instanceof GrClosableBlock) {
  192. buildFlowForClosure((GrClosableBlock)scope);
  193. }
  194. else {
  195. scope.accept(this);
  196. }
  197. final InstructionImpl end = startNode(null);
  198. checkPending(end); //collect return edges
  199. return assertValidPsi(myInstructions.toArray(new Instruction[myInstructions.size()]));
  200. }
  201. public static Instruction[] assertValidPsi(Instruction[] instructions) {
  202. /*for (Instruction instruction : instructions) {
  203. PsiElement element = instruction.getElement();
  204. if (element != null && !element.isValid()) {
  205. throw new AssertionError("invalid element in dfa: " + element);
  206. }
  207. }*/
  208. return instructions;
  209. }
  210. private void buildFlowForClosure(final GrClosableBlock closure) {
  211. for (GrParameter parameter : closure.getAllParameters()) {
  212. addNode(new ReadWriteVariableInstruction(parameter.getName(), parameter, WRITE));
  213. }
  214. addNode(new ReadWriteVariableInstruction("owner", closure.getLBrace(), WRITE));
  215. PsiElement child = closure.getFirstChild();
  216. while (child != null) {
  217. if (child instanceof GroovyPsiElement) {
  218. ((GroovyPsiElement)child).accept(this);
  219. }
  220. child = child.getNextSibling();
  221. }
  222. final GrStatement[] statements = closure.getStatements();
  223. if (statements.length > 0) {
  224. handlePossibleReturn(statements[statements.length - 1]);
  225. }
  226. }
  227. private <T extends InstructionImpl> T addNode(T instruction) {
  228. instruction.setNumber(myInstructionNumber++);
  229. myInstructions.add(instruction);
  230. if (myHead != null) {
  231. addEdge(myHead, instruction);
  232. }
  233. myHead = instruction;
  234. return instruction;
  235. }
  236. private <T extends InstructionImpl> T addNodeAndCheckPending(T i) {
  237. addNode(i);
  238. checkPending(i);
  239. return i;
  240. }
  241. private static void addEdge(InstructionImpl begin, InstructionImpl end) {
  242. begin.addSuccessor(end);
  243. end.addPredecessor(begin);
  244. if (!(begin instanceof ReadWriteVariableInstruction || begin instanceof MixinTypeInstruction)) {
  245. end.addNegationsFrom(begin);
  246. }
  247. }
  248. public void visitClosure(GrClosableBlock closure) {
  249. //do not go inside closures except gstring injections
  250. if (closure.getParent() instanceof GrStringInjection) {
  251. super.visitClosure(closure);
  252. return;
  253. }
  254. Set<String> names = new HashSet<String>();
  255. ReadWriteVariableInstruction[] reads = ControlFlowBuilderUtil.getReadsWithoutPriorWrites(closure.getControlFlow(), false);
  256. for (ReadWriteVariableInstruction read : reads) {
  257. names.add(read.getVariableName());
  258. }
  259. for (String name : names) {
  260. addNodeAndCheckPending(new ReadWriteVariableInstruction(name, closure, READ));
  261. }
  262. addNodeAndCheckPending(new InstructionImpl(closure));
  263. }
  264. public void visitBreakStatement(GrBreakStatement breakStatement) {
  265. super.visitBreakStatement(breakStatement);
  266. final GrStatement target = breakStatement.findTargetStatement();
  267. if (target != null && myHead != null) {
  268. addPendingEdge(target, myHead);
  269. }
  270. interruptFlow();
  271. }
  272. public void visitContinueStatement(GrContinueStatement continueStatement) {
  273. super.visitContinueStatement(continueStatement);
  274. final GrStatement target = continueStatement.findTargetStatement();
  275. if (target != null && myHead != null) {
  276. final InstructionImpl instruction = findInstruction(target);
  277. if (instruction != null) {
  278. addEdge(myHead, instruction);
  279. }
  280. }
  281. interruptFlow();
  282. }
  283. public void visitReturnStatement(GrReturnStatement returnStatement) {
  284. boolean isNodeNeeded = myHead == null || myHead.getElement() != returnStatement;
  285. final GrExpression value = returnStatement.getReturnValue();
  286. if (value != null) value.accept(this);
  287. if (isNodeNeeded) {
  288. InstructionImpl returnInstruction = startNode(returnStatement);
  289. addPendingEdge(null, myHead);
  290. finishNode(returnInstruction);
  291. }
  292. else {
  293. addPendingEdge(null, myHead);
  294. }
  295. interruptFlow();
  296. }
  297. public void visitAssertStatement(GrAssertStatement assertStatement) {
  298. final GrExpression assertion = assertStatement.getAssertion();
  299. if (assertion != null) {
  300. myConditions.push(addNodeAndCheckPending(new ConditionInstruction(assertion)));
  301. assertion.accept(this);
  302. final InstructionImpl assertInstruction = startNode(assertStatement);
  303. GrExpression errorMessage = assertStatement.getErrorMessage();
  304. if (errorMessage != null) {
  305. errorMessage.accept(this);
  306. }
  307. final PsiType type = TypesUtil.createTypeByFQClassName("java.lang.AssertionError", assertStatement);
  308. ExceptionInfo info = findCatch(type);
  309. if (info != null) {
  310. info.myThrowers.add(assertInstruction);
  311. }
  312. else {
  313. addPendingEdge(null, assertInstruction);
  314. }
  315. finishNode(assertInstruction);
  316. }
  317. }
  318. public void visitThrowStatement(GrThrowStatement throwStatement) {
  319. final GrExpression exception = throwStatement.getException();
  320. if (exception == null) return;
  321. exception.accept(this);
  322. final InstructionImpl throwInstruction = new ThrowingInstruction(throwStatement);
  323. addNodeAndCheckPending(throwInstruction);
  324. interruptFlow();
  325. final PsiType type = getNominalTypeNoRecursion(exception);
  326. if (type != null) {
  327. ExceptionInfo info = findCatch(type);
  328. if (info != null) {
  329. info.myThrowers.add(throwInstruction);
  330. }
  331. else {
  332. addPendingEdge(null, throwInstruction);
  333. }
  334. }
  335. else {
  336. addPendingEdge(null, throwInstruction);
  337. }
  338. }
  339. private static PsiType getNominalTypeNoRecursion(final GrExpression exception) {
  340. return RecursionManager.doPreventingRecursion(exception, true, new NullableComputable<PsiType>() {
  341. @Override
  342. public PsiType compute() {
  343. return exception.getNominalType();
  344. }
  345. });
  346. }
  347. private void interruptFlow() {
  348. myHead = null;
  349. }
  350. @Nullable
  351. private ExceptionInfo findCatch(PsiType thrownType) {
  352. final Iterator<ExceptionInfo> iterator = myCaughtExceptionInfos.descendingIterator();
  353. while (iterator.hasNext()) {
  354. final ExceptionInfo info = iterator.next();
  355. final GrCatchClause clause = info.myClause;
  356. final GrParameter parameter = clause.getParameter();
  357. if (parameter != null) {
  358. final PsiType type = parameter.getType();
  359. if (type.isAssignableFrom(thrownType)) return info;
  360. }
  361. }
  362. return null;
  363. }
  364. public void visitLabeledStatement(GrLabeledStatement labeledStatement) {
  365. final InstructionImpl instruction = startNode(labeledStatement);
  366. super.visitLabeledStatement(labeledStatement);
  367. finishNode(instruction);
  368. }
  369. public void visitAssignmentExpression(GrAssignmentExpression expression) {
  370. GrExpression lValue = expression.getLValue();
  371. if (expression.getOperationToken() != mASSIGN) {
  372. if (lValue instanceof GrReferenceExpression) {
  373. String referenceName = ((GrReferenceExpression)lValue).getReferenceName();
  374. if (referenceName != null) {
  375. addNodeAndCheckPending(new ReadWriteVariableInstruction(referenceName, lValue, READ));
  376. }
  377. }
  378. }
  379. GrExpression rValue = expression.getRValue();
  380. if (rValue != null) {
  381. rValue.accept(this);
  382. lValue.accept(this);
  383. }
  384. }
  385. @Override
  386. public void visitParenthesizedExpression(GrParenthesizedExpression expression) {
  387. final GrExpression operand = expression.getOperand();
  388. if (operand != null) operand.accept(this);
  389. }
  390. @Override
  391. public void visitUnaryExpression(GrUnaryExpression expression) {
  392. final GrExpression operand = expression.getOperand();
  393. if (operand == null) return;
  394. if (expression.getOperationTokenType() != mLNOT) {
  395. operand.accept(this);
  396. visitCall(expression);
  397. return;
  398. }
  399. ConditionInstruction cond = new ConditionInstruction(expression);
  400. addNodeAndCheckPending(cond);
  401. registerCondition(cond);
  402. operand.accept(this);
  403. visitCall(expression);
  404. myConditions.removeFirstOccurrence(cond);
  405. List<GotoInstruction> negations = collectAndRemoveAllPendingNegations(expression);
  406. InstructionImpl head = myHead;
  407. addPendingEdge(expression, addNodeAndCheckPending(new PositiveGotoInstruction(expression, cond)));
  408. if (negations.isEmpty()) {
  409. myHead = head;
  410. }
  411. else {
  412. myHead = reduceAllNegationsIntoInstruction(expression, negations);
  413. }
  414. }
  415. @Nullable
  416. private InstructionImpl reduceAllNegationsIntoInstruction(GroovyPsiElement currentScope, List<? extends GotoInstruction> negations) {
  417. if (negations.size() > 1) {
  418. InstructionImpl instruction = addNode(new InstructionImpl(currentScope));
  419. for (GotoInstruction negation : negations) {
  420. addEdge(negation, instruction);
  421. }
  422. return instruction;
  423. }
  424. else if (negations.size() == 1) {
  425. return negations.get(0);
  426. }
  427. return null;
  428. }
  429. private List<GotoInstruction> collectAndRemoveAllPendingNegations(GroovyPsiElement currentScope) {
  430. List<GotoInstruction> negations = new ArrayList<GotoInstruction>();
  431. for (Iterator<Pair<InstructionImpl, GroovyPsiElement>> iterator = myPending.iterator(); iterator.hasNext(); ) {
  432. Pair<InstructionImpl, GroovyPsiElement> pair = iterator.next();
  433. InstructionImpl instruction = pair.first;
  434. GroovyPsiElement scope = pair.second;
  435. if (!PsiTreeUtil.isAncestor(scope, currentScope, true) && instruction instanceof GotoInstruction) {
  436. negations.add((GotoInstruction)instruction);
  437. iterator.remove();
  438. }
  439. }
  440. return negations;
  441. }
  442. @Override
  443. public void visitInstanceofExpression(GrInstanceOfExpression expression) {
  444. expression.getOperand().accept(this);
  445. processInstanceOf(expression);
  446. }
  447. public void visitReferenceExpression(GrReferenceExpression refExpr) {
  448. super.visitReferenceExpression(refExpr);
  449. if (refExpr.getQualifierExpression() == null) {
  450. String name = refExpr.getReferenceName();
  451. if (name == null) return;
  452. if (ControlFlowUtils.isIncOrDecOperand(refExpr)) {
  453. final InstructionImpl i = new ReadWriteVariableInstruction(name, refExpr, READ);
  454. addNodeAndCheckPending(i);
  455. addNode(new ReadWriteVariableInstruction(name, refExpr, WRITE));
  456. }
  457. else {
  458. final int type = PsiUtil.isLValue(refExpr) ? WRITE : READ;
  459. addNodeAndCheckPending(new ReadWriteVariableInstruction(name, refExpr, type));
  460. if (refExpr.getParent() instanceof GrArgumentList && refExpr.getParent().getParent() instanceof GrCall) {
  461. addNodeAndCheckPending(new ArgumentInstruction(refExpr));
  462. }
  463. }
  464. }
  465. else if (!(refExpr.getParent() instanceof GrCall)) {
  466. visitCall(refExpr);
  467. }
  468. }
  469. @Override
  470. public void visitMethodCallExpression(GrMethodCallExpression methodCallExpression) {
  471. super.visitMethodCallExpression(methodCallExpression);
  472. visitCall(methodCallExpression);
  473. }
  474. @Override
  475. public void visitApplicationStatement(GrApplicationStatement applicationStatement) {
  476. super.visitApplicationStatement(applicationStatement);
  477. visitCall(applicationStatement);
  478. }
  479. @Override
  480. public void visitConstructorInvocation(GrConstructorInvocation invocation) {
  481. super.visitConstructorInvocation(invocation);
  482. visitCall(invocation);
  483. }
  484. @Override
  485. public void visitNewExpression(GrNewExpression newExpression) {
  486. super.visitNewExpression(newExpression);
  487. visitCall(newExpression);
  488. }
  489. @Override
  490. public void visitBinaryExpression(GrBinaryExpression expression) {
  491. final GrExpression left = expression.getLeftOperand();
  492. final GrExpression right = expression.getRightOperand();
  493. final IElementType opType = expression.getOperationTokenType();
  494. if (ControlFlowBuilderUtil.isInstanceOfBinary(expression)) {
  495. expression.getLeftOperand().accept(this);
  496. processInstanceOf(expression);
  497. return;
  498. }
  499. if (opType != mLOR && opType != mLAND && opType!=kIN) {
  500. left.accept(this);
  501. if (right != null) {
  502. right.accept(this);
  503. }
  504. visitCall(expression);
  505. return;
  506. }
  507. ConditionInstruction condition = new ConditionInstruction(expression);
  508. addNodeAndCheckPending(condition);
  509. registerCondition(condition);
  510. left.accept(this);
  511. if (right == null) return;
  512. final List<GotoInstruction> negations = collectAndRemoveAllPendingNegations(expression);
  513. visitCall(expression);
  514. if (opType == mLAND) {
  515. for (GotoInstruction negation : negations) {
  516. addPendingEdge(expression, negation);
  517. }
  518. if (negations.isEmpty()) {
  519. InstructionImpl head = myHead;
  520. addNode(new NegatingGotoInstruction(expression, condition));
  521. handlePossibleReturn(expression);
  522. addPendingEdge(expression, myHead);
  523. myHead = head;
  524. }
  525. }
  526. else /*if (opType == mLOR)*/ {
  527. final InstructionImpl instruction = addNodeAndCheckPending(new InstructionImpl(expression));//collect all pending edges from left argument
  528. handlePossibleReturn(expression);
  529. addPendingEdge(expression, myHead);
  530. myHead = instruction;
  531. InstructionImpl head = reduceAllNegationsIntoInstruction(expression, negations);
  532. if (head != null) myHead = head;
  533. //addNode(new NegatingGotoInstruction(expression, myInstructionNumber++, condition));
  534. }
  535. myConditions.removeFirstOccurrence(condition);
  536. right.accept(this);
  537. }
  538. private void processInstanceOf(GrExpression expression) {
  539. ConditionInstruction cond = new ConditionInstruction(expression);
  540. addNodeAndCheckPending(cond);
  541. registerCondition(cond);
  542. addNode(new InstanceOfInstruction(expression, cond));
  543. NegatingGotoInstruction negation = new NegatingGotoInstruction(expression, cond);
  544. addNode(negation);
  545. addPendingEdge(expression, negation);
  546. myHead = cond;
  547. addNode(new InstanceOfInstruction(expression, cond));
  548. myConditions.removeFirstOccurrence(cond);
  549. }
  550. /**
  551. * Emulates throwing an exception from method call. Should be inserted into all places where method or closure is called, because it
  552. * can throw something unexpectedly
  553. */
  554. private void visitCall(GroovyPsiElement call) {
  555. //optimization: don't add call instruction if there is no catch or finally block in the context
  556. if (myCaughtExceptionInfos.size() <= 0 && myFinallyCount <= 0) {
  557. return;
  558. }
  559. final InstructionImpl instruction = new ThrowingInstruction(call);
  560. addNodeAndCheckPending(instruction);
  561. for (ExceptionInfo info : myCaughtExceptionInfos) {
  562. info.myThrowers.add(instruction);
  563. }
  564. if (myFinallyCount > 0) {
  565. addPendingEdge(null, instruction);
  566. }
  567. }
  568. public void visitIfStatement(GrIfStatement ifStatement) {
  569. InstructionImpl ifInstruction = startNode(ifStatement);
  570. final GrCondition condition = ifStatement.getCondition();
  571. final GrStatement thenBranch = ifStatement.getThenBranch();
  572. final GrStatement elseBranch = ifStatement.getElseBranch();
  573. InstructionImpl conditionEnd = null;
  574. InstructionImpl thenEnd = null;
  575. InstructionImpl elseEnd = null;
  576. if (condition != null) {
  577. condition.accept(this);
  578. conditionEnd = myHead;
  579. }
  580. List<GotoInstruction> negations = collectAndRemoveAllPendingNegations(ifStatement);
  581. if (thenBranch != null) {
  582. thenBranch.accept(this);
  583. handlePossibleReturn(thenBranch);
  584. thenEnd = myHead;
  585. interruptFlow();
  586. }
  587. myHead = reduceAllNegationsIntoInstruction(ifStatement, negations);
  588. if (myHead == null && conditionEnd != null) {
  589. myHead = conditionEnd;
  590. }
  591. if (elseBranch != null) {
  592. elseBranch.accept(this);
  593. handlePossibleReturn(elseBranch);
  594. elseEnd = myHead;
  595. interruptFlow();
  596. }
  597. if (thenBranch != null || elseBranch != null) {
  598. final InstructionImpl end = new IfEndInstruction(ifStatement);
  599. addNode(end);
  600. if (thenEnd != null) {
  601. addEdge(thenEnd, end);
  602. }
  603. if (elseEnd != null) {
  604. addEdge(elseEnd, end);
  605. }
  606. else if (elseBranch == null) {
  607. addEdge(conditionEnd != null ? conditionEnd : ifInstruction, end);
  608. }
  609. }
  610. finishNode(ifInstruction);
  611. }
  612. private void registerCondition(ConditionInstruction conditionStart) {
  613. for (ConditionInstruction condition : myConditions) {
  614. condition.addDependent(conditionStart);
  615. }
  616. myConditions.push(conditionStart);
  617. }
  618. private void acceptNullable(@Nullable GroovyPsiElement element) {
  619. if (element != null) {
  620. element.accept(this);
  621. }
  622. }
  623. public void visitForStatement(GrForStatement forStatement) {
  624. final GrForClause clause = forStatement.getClause();
  625. processForLoopInitializer(clause);
  626. InstructionImpl start = startNode(forStatement);
  627. addForLoopBreakingEdge(forStatement, clause);
  628. flushForeachLoopVariable(clause);
  629. final GrStatement body = forStatement.getBody();
  630. if (body != null) {
  631. InstructionImpl bodyInstruction = startNode(body);
  632. body.accept(this);
  633. finishNode(bodyInstruction);
  634. }
  635. checkPending(start); //check for breaks targeted here
  636. if (clause instanceof GrTraditionalForClause) {
  637. acceptNullable(((GrTraditionalForClause)clause).getUpdate());
  638. }
  639. if (myHead != null) addEdge(myHead, start); //loop
  640. interruptFlow();
  641. finishNode(start);
  642. }
  643. private void processForLoopInitializer(GrForClause clause) {
  644. GroovyPsiElement initializer = clause instanceof GrTraditionalForClause ? ((GrTraditionalForClause)clause).getInitialization() :
  645. clause instanceof GrForInClause ? ((GrForInClause)clause).getIteratedExpression() : null;
  646. acceptNullable(initializer);
  647. }
  648. private void addForLoopBreakingEdge(GrForStatement forStatement, GrForClause clause) {
  649. if (clause instanceof GrTraditionalForClause) {
  650. final GrExpression condition = ((GrTraditionalForClause)clause).getCondition();
  651. if (condition != null) {
  652. condition.accept(this);
  653. if (!alwaysTrue(condition)) {
  654. addPendingEdge(forStatement, myHead); //break cycle
  655. }
  656. }
  657. }
  658. else {
  659. addPendingEdge(forStatement, myHead); //break cycle
  660. }
  661. }
  662. private void flushForeachLoopVariable(GrForClause clause) {
  663. if (clause instanceof GrForInClause) {
  664. GrVariable variable = clause.getDeclaredVariable();
  665. if (variable != null) {
  666. addNodeAndCheckPending(new ReadWriteVariableInstruction(variable.getName(), variable, WRITE));
  667. }
  668. }
  669. }
  670. private void checkPending(InstructionImpl instruction) {
  671. final PsiElement element = instruction.getElement();
  672. if (element == null) {
  673. //add all
  674. for (Pair<InstructionImpl, GroovyPsiElement> pair : myPending) {
  675. addEdge(pair.getFirst(), instruction);
  676. }
  677. myPending.clear();
  678. }
  679. else {
  680. for (int i = myPending.size() - 1; i >= 0; i--) {
  681. final Pair<InstructionImpl, GroovyPsiElement> pair = myPending.get(i);
  682. final PsiElement scopeWhenToAdd = pair.getSecond();
  683. if (scopeWhenToAdd == null) continue;
  684. if (!PsiTreeUtil.isAncestor(scopeWhenToAdd, element, false)) {
  685. addEdge(pair.getFirst(), instruction);
  686. myPending.remove(i);
  687. }
  688. else {
  689. break;
  690. }
  691. }
  692. }
  693. }
  694. //add edge when instruction.getElement() is not contained in scopeWhenAdded
  695. private void addPendingEdge(@Nullable GroovyPsiElement scopeWhenAdded, InstructionImpl instruction) {
  696. if (instruction == null) return;
  697. int i = 0;
  698. if (scopeWhenAdded != null) {
  699. for (; i < myPending.size(); i++) {
  700. Pair<InstructionImpl, GroovyPsiElement> pair = myPending.get(i);
  701. final GroovyPsiElement currScope = pair.getSecond();
  702. if (currScope == null) continue;
  703. if (!PsiTreeUtil.isAncestor(currScope, scopeWhenAdded, true)) break;
  704. }
  705. }
  706. myPending.add(i, new Pair<InstructionImpl, GroovyPsiElement>(instruction, scopeWhenAdded));
  707. }
  708. public void visitWhileStatement(GrWhileStatement whileStatement) {
  709. final InstructionImpl instruction = startNode(whileStatement);
  710. final GrCondition condition = whileStatement.getCondition();
  711. if (condition != null) {
  712. condition.accept(this);
  713. }
  714. if (!alwaysTrue(condition)) {
  715. addPendingEdge(whileStatement, myHead); //break
  716. }
  717. final GrCondition body = whileStatement.getBody();
  718. if (body != null) {
  719. body.accept(this);
  720. }
  721. checkPending(instruction); //check for breaks targeted here
  722. if (myHead != null) addEdge(myHead, instruction); //loop
  723. interruptFlow();
  724. finishNode(instruction);
  725. }
  726. private boolean alwaysTrue(GroovyPsiElement condition) {
  727. return Boolean.TRUE.equals(myConstantEvaluator.computeConstantExpression(condition));
  728. }
  729. public void visitSwitchStatement(GrSwitchStatement switchStatement) {
  730. final GrCondition condition = switchStatement.getCondition();
  731. if (condition != null) {
  732. condition.accept(this);
  733. }
  734. final InstructionImpl instruction = startNode(switchStatement);
  735. final GrCaseSection[] sections = switchStatement.getCaseSections();
  736. if (!containsAllCases(switchStatement)) {
  737. addPendingEdge(switchStatement, instruction);
  738. }
  739. for (GrCaseSection section : sections) {
  740. myHead = instruction;
  741. section.accept(this);
  742. }
  743. finishNode(instruction);
  744. }
  745. @Override
  746. public void visitConditionalExpression(GrConditionalExpression expression) {
  747. GrExpression condition = expression.getCondition();
  748. GrExpression thenBranch = expression.getThenBranch();
  749. GrExpression elseBranch = expression.getElseBranch();
  750. condition.accept(this);
  751. InstructionImpl conditionEnd = myHead;
  752. List<GotoInstruction> negations = collectAndRemoveAllPendingNegations(expression);
  753. if (thenBranch != null) {
  754. thenBranch.accept(this);
  755. handlePossibleReturn(thenBranch);
  756. addPendingEdge(expression, myHead);
  757. }
  758. if (elseBranch != null) {
  759. InstructionImpl head = reduceAllNegationsIntoInstruction(expression, negations);
  760. myHead = head != null ? head : conditionEnd;
  761. elseBranch.accept(this);
  762. handlePossibleReturn(elseBranch);
  763. }
  764. }
  765. @Override
  766. public void visitElvisExpression(GrElvisExpression expression) {
  767. GrExpression condition = expression.getCondition();
  768. GrExpression elseBranch = expression.getElseBranch();
  769. condition.accept(this);
  770. List<GotoInstruction> negations = collectAndRemoveAllPendingNegations(expression);
  771. InstructionImpl head = myHead;
  772. handlePossibleReturn(condition);
  773. addPendingEdge(expression, myHead);
  774. myHead = head;
  775. if (elseBranch != null) {
  776. head = reduceAllNegationsIntoInstruction(expression, negations);
  777. if (head != null) myHead = head;
  778. elseBranch.accept(this);
  779. handlePossibleReturn(elseBranch);
  780. }
  781. }
  782. private static boolean containsAllCases(GrSwitchStatement statement) {
  783. final GrCaseSection[] sections = statement.getCaseSections();
  784. for (GrCaseSection section : sections) {
  785. if (section.getCaseLabel().isDefault()) return true;
  786. }
  787. final GrExpression condition = statement.getCondition();
  788. if (!(condition instanceof GrReferenceExpression)) return false;
  789. PsiType type = TypesUtil.unboxPrimitiveTypeWrapper(getNominalTypeNoRecursion(condition));
  790. if (type == null) return false;
  791. if (type instanceof PsiPrimitiveType) {
  792. if (type == PsiType.BOOLEAN) return sections.length == 2;
  793. if (type == PsiType.BYTE || type == PsiType.CHAR) return sections.length == 128;
  794. return false;
  795. }
  796. if (type instanceof PsiClassType) {
  797. final PsiClass resolved = ((PsiClassType)type).resolve();
  798. if (resolved != null && resolved.isEnum()) {
  799. int enumConstantCount = 0;
  800. final PsiField[] fields = resolved.getFields();
  801. for (PsiField field : fields) {
  802. if (field instanceof PsiEnumConstant) enumConstantCount++;
  803. }
  804. if (sections.length == enumConstantCount) return true;
  805. }
  806. }
  807. return false;
  808. }
  809. @Override
  810. public void visitCaseSection(GrCaseSection caseSection) {
  811. GrExpression value = caseSection.getCaseLabel().getValue();
  812. if (value != null) {
  813. value.accept(this);
  814. }
  815. final GrStatement[] statements = caseSection.getStatements();
  816. //infer 'may be return' position
  817. int i;
  818. for (i = statements.length - 1; i >= 0 && statements[i] instanceof GrBreakStatement; i--) {
  819. }
  820. for (int j = 0; j < statements.length; j++) {
  821. GrStatement statement = statements[j];
  822. statement.accept(this);
  823. if (j == i) handlePossibleReturn(statement);
  824. }
  825. }
  826. public void visitTryStatement(GrTryCatchStatement tryCatchStatement) {
  827. final GrOpenBlock tryBlock = tryCatchStatement.getTryBlock();
  828. final GrCatchClause[] catchClauses = tryCatchStatement.getCatchClauses();
  829. final GrFinallyClause finallyClause = tryCatchStatement.getFinallyClause();
  830. for (int i = catchClauses.length - 1; i >= 0; i--) {
  831. myCaughtExceptionInfos.push(new ExceptionInfo(catchClauses[i]));
  832. }
  833. if (finallyClause != null) myFinallyCount++;
  834. List<Pair<InstructionImpl, GroovyPsiElement>> oldPending = null;
  835. if (finallyClause != null) {
  836. oldPending = myPending;
  837. myPending = new ArrayList<Pair<InstructionImpl, GroovyPsiElement>>();
  838. }
  839. InstructionImpl tryBegin = startNode(tryBlock);
  840. tryBlock.accept(this);
  841. InstructionImpl tryEnd = myHead;
  842. finishNode(tryBegin);
  843. Set<Pair<InstructionImpl, GroovyPsiElement>> pendingAfterTry = new LinkedHashSet<Pair<InstructionImpl, GroovyPsiElement>>(myPending);
  844. @SuppressWarnings("unchecked")
  845. List<InstructionImpl>[] throwers = new List[catchClauses.length];
  846. for (int i = 0; i < catchClauses.length; i++) {
  847. throwers[i] = myCaughtExceptionInfos.pop().myThrowers;
  848. }
  849. InstructionImpl[] catches = new InstructionImpl[catchClauses.length];
  850. for (int i = 0; i < catchClauses.length; i++) {
  851. interruptFlow();
  852. final InstructionImpl catchBeg = startNode(catchClauses[i]);
  853. for (InstructionImpl thrower : throwers[i]) {
  854. addEdge(thrower, catchBeg);
  855. }
  856. final GrParameter parameter = catchClauses[i].getParameter();
  857. if (parameter != null) {
  858. addNode(new ReadWriteVariableInstruction(parameter.getName(), parameter, WRITE));
  859. }
  860. catchClauses[i].accept(this);
  861. catches[i] = myHead;
  862. finishNode(catchBeg);
  863. }
  864. pendingAfterTry.addAll(myPending);
  865. myPending = new ArrayList<Pair<InstructionImpl, GroovyPsiElement>>(pendingAfterTry);
  866. if (finallyClause != null) {
  867. myFinallyCount--;
  868. interruptFlow();
  869. final InstructionImpl finallyInstruction = startNode(finallyClause, false);
  870. Set<AfterCallInstruction> postCalls = new LinkedHashSet<AfterCallInstruction>();
  871. final List<Pair<InstructionImpl, GroovyPsiElement>> copy = myPending;
  872. myPending = new ArrayList<Pair<InstructionImpl, GroovyPsiElement>>();
  873. for (Pair<InstructionImpl, GroovyPsiElement> pair : copy) {
  874. postCalls.add(addCallNode(finallyInstruction, pair.getSecond(), pair.getFirst()));
  875. }
  876. if (tryEnd != null) {
  877. postCalls.add(addCallNode(finallyInstruction, tryCatchStatement, tryEnd));
  878. }
  879. for (InstructionImpl catchEnd : catches) {
  880. if (catchEnd != null) {
  881. postCalls.add(addCallNode(finallyInstruction, tryCatchStatement, catchEnd));
  882. }
  883. }
  884. //save added postcalls into separate list because we don't want returnInstruction grabbed their pending edges
  885. List<Pair<InstructionImpl, GroovyPsiElement>> pendingPostCalls = myPending;
  886. myPending = new ArrayList<Pair<InstructionImpl, GroovyPsiElement>>();
  887. myHead = finallyInstruction;
  888. finallyClause.accept(this);
  889. final ReturnInstruction returnInstruction = new ReturnInstruction(finallyClause);
  890. for (AfterCallInstruction postCall : postCalls) {
  891. postCall.setReturnInstruction(returnInstruction);
  892. addEdge(returnInstruction, postCall);
  893. }
  894. addNodeAndCheckPending(returnInstruction);
  895. interruptFlow();
  896. finishNode(finallyInstruction);
  897. assert oldPending != null;
  898. oldPending.addAll(pendingPostCalls);
  899. myPending = oldPending;
  900. }
  901. else {
  902. if (tryEnd != null) {
  903. addPendingEdge(tryCatchStatement, tryEnd);
  904. }
  905. for (InstructionImpl catchEnd : catches) {
  906. addPendingEdge(tryBlock, catchEnd);
  907. }
  908. }
  909. }
  910. private AfterCallInstruction addCallNode(InstructionImpl finallyInstruction, GroovyPsiElement scopeWhenAdded, InstructionImpl src) {
  911. interruptFlow();
  912. final CallInstruction call = new CallInstruction(finallyInstruction);
  913. addNode(call);
  914. addEdge(src, call);
  915. addEdge(call, finallyInstruction);
  916. AfterCallInstruction afterCall = new AfterCallInstruction(call);
  917. addNode(afterCall);
  918. addPendingEdge(scopeWhenAdded, afterCall);
  919. return afterCall;
  920. }
  921. private InstructionImpl startNode(@Nullable GroovyPsiElement element) {
  922. return startNode(element, true);
  923. }
  924. private InstructionImpl startNode(GroovyPsiElement element, boolean checkPending) {
  925. final InstructionImpl instruction = new InstructionImpl(element);
  926. addNode(instruction);
  927. if (checkPending) checkPending(instruction);
  928. myProcessingStack.push(instruction);
  929. return instruction;
  930. }
  931. private void finishNode(InstructionImpl instruction) {
  932. final InstructionImpl popped = myProcessingStack.pop();
  933. assert instruction.equals(popped);
  934. }
  935. public void visitField(GrField field) {
  936. }
  937. public void visitParameter(GrParameter parameter) {
  938. if (parameter.getParent() instanceof GrForClause) {
  939. visitVariable(parameter);
  940. }
  941. }
  942. public void visitMethod(GrMethod method) {
  943. }
  944. @Override
  945. public void visitClassInitializer(GrClassInitializer initializer) {
  946. }
  947. public void visitTypeDefinition(final GrTypeDefinition typeDefinition) {
  948. if (!(typeDefinition instanceof GrAnonymousClassDefinition)) return;
  949. final Set<String> vars = new HashSet<String>();
  950. typeDefinition.acceptChildren(new GroovyRecursiveElementVisitor() {
  951. private void collectVars(Instruction[] flow) {
  952. ReadWriteVariableInstruction[] reads = ControlFlowBuilderUtil.getReadsWithoutPriorWrites(flow, false);
  953. for (ReadWriteVariableInstruction instruction : reads) {
  954. vars.add(instruction.getVariableName());
  955. }
  956. }
  957. @Override
  958. public void visitField(GrField field) {
  959. GrExpression initializer = field.getInitializerGroovy();
  960. if (initializer != null) {
  961. Instruction[] flow = new ControlFlowBuilder(field.getProject()).buildControlFlow(initializer);
  962. collectVars(flow);
  963. }
  964. }
  965. @Override
  966. public void visitMethod(GrMethod method) {
  967. GrOpenBlock block = method.getBlock();
  968. if (block != null) {
  969. collectVars(block.getControlFlow());
  970. }
  971. }
  972. @Override
  973. public void visitClassInitializer(GrClassInitializer initializer) {
  974. GrOpenBlock block = initializer.getBlock();
  975. collectVars(block.getControlFlow());
  976. }
  977. });
  978. PsiField[] fields = typeDefinition.getAllFields();
  979. for (PsiField field : fields) {
  980. vars.remove(field.getName());
  981. }
  982. for (String var : vars) {
  983. addNodeAndCheckPending(new ReadWriteVariableInstruction(var, typeDefinition, READ));
  984. }
  985. addNodeAndCheckPending(new InstructionImpl(typeDefinition));
  986. }
  987. public void visitVariable(GrVariable variable) {
  988. super.visitVariable(variable);
  989. if (variable.getInitializerGroovy() != null ||
  990. variable.getParent() instanceof GrTupleDeclaration && ((GrTupleDeclaration)variable.getParent()).getInitializerGroovy() != null) {
  991. ReadWriteVariableInstruction writeInst = new ReadWriteVariableInstruction(variable.getName(), variable, WRITE);
  992. addNodeAndCheckPending(writeInst);
  993. }
  994. }
  995. @Nullable
  996. private InstructionImpl findInstruction(PsiElement element) {
  997. final Iterator<InstructionImpl> iterator = myProcessingStack.descendingIterator();
  998. while (iterator.hasNext()) {
  999. final InstructionImpl instruction = iterator.next();
  1000. if (element.equals(instruction.getElement())) return instruction;
  1001. }
  1002. return null;
  1003. }
  1004. }