1 /*******************************************************************************
2 * Copyright (c) 2000, 2011 IBM Corporation and others.
3 * All rights reserved. This program and the accompanying materials
4 * are made available under the terms of the Eclipse Public License v1.0
5 * which accompanies this distribution, and is available at
6 * http://www.eclipse.org/legal/epl-v10.html
9 * IBM Corporation - initial API and implementation
10 * Benjamin Muskalla <bmuskalla@eclipsesource.com> - [extract method] missing return type when code can throw exception - https://bugs.eclipse.org/bugs/show_bug.cgi?id=97413
11 *******************************************************************************/
12 package org.eclipse.jdt.internal.corext.refactoring.code.flow;
14 import java.util.ArrayList;
15 import java.util.HashSet;
16 import java.util.Iterator;
17 import java.util.List;
20 import org.eclipse.jdt.core.dom.ITypeBinding;
21 import org.eclipse.jdt.core.dom.IVariableBinding;
22 import org.eclipse.jdt.core.dom.SimpleName;
24 import org.eclipse.jdt.internal.corext.refactoring.code.ExtractMethodAnalyzer;
26 public abstract class FlowInfo {
28 // Return statement handling.
29 protected static final int NOT_POSSIBLE= 0;
30 protected static final int UNDEFINED= 1;
31 protected static final int NO_RETURN= 2;
32 protected static final int PARTIAL_RETURN= 3;
33 protected static final int VOID_RETURN= 4;
34 protected static final int VALUE_RETURN= 5;
35 protected static final int THROW= 6;
37 // Local access handling.
38 public static final int UNUSED= 1 << 0;
39 public static final int READ= 1 << 1;
40 public static final int READ_POTENTIAL= 1 << 2;
41 public static final int WRITE= 1 << 3;
42 public static final int WRITE_POTENTIAL= 1 << 4;
43 public static final int UNKNOWN= 1 << 5;
45 // Table to merge access modes for condition statements (e.g branch[x] || branch[y]).
46 private static final int[][] ACCESS_MODE_CONDITIONAL_TABLE= {
47 /* UNUSED READ READ_POTENTIAL WRTIE WRITE_POTENTIAL UNKNOWN */
48 /* UNUSED */ { UNUSED, READ_POTENTIAL, READ_POTENTIAL, WRITE_POTENTIAL, WRITE_POTENTIAL, UNKNOWN },
49 /* READ */ { READ_POTENTIAL, READ, READ_POTENTIAL, UNKNOWN, UNKNOWN, UNKNOWN },
50 /* READ_POTENTIAL */ { READ_POTENTIAL, READ_POTENTIAL, READ_POTENTIAL, UNKNOWN, UNKNOWN, UNKNOWN },
51 /* WRITE */ { WRITE_POTENTIAL, UNKNOWN, UNKNOWN, WRITE, WRITE_POTENTIAL, UNKNOWN },
52 /* WRITE_POTENTIAL */ { WRITE_POTENTIAL, UNKNOWN, UNKNOWN, WRITE_POTENTIAL, WRITE_POTENTIAL, UNKNOWN },
53 /* UNKNOWN */ { UNKNOWN, UNKNOWN, UNKNOWN, UNKNOWN, UNKNOWN, UNKNOWN }
56 // Table to change access mode if there is an open branch statement
57 private static final int[] ACCESS_MODE_OPEN_BRANCH_TABLE= {
58 /* UNUSED READ READ_POTENTIAL WRTIE WRITE_POTENTIAL UNKNOWN */
59 UNUSED, READ_POTENTIAL, READ_POTENTIAL, WRITE_POTENTIAL, WRITE_POTENTIAL, UNKNOWN
62 // Table to merge return modes for condition statements (y: fReturnKind, x: other.fReturnKind)
63 private static final int[][] RETURN_KIND_CONDITIONAL_TABLE = {
64 /* NOT_POSSIBLE UNDEFINED NO_RETURN PARTIAL_RETURN VOID_RETURN VALUE_RETURN THROW */
65 /* NOT_POSSIBLE */ { NOT_POSSIBLE, NOT_POSSIBLE, NOT_POSSIBLE, NOT_POSSIBLE, NOT_POSSIBLE, NOT_POSSIBLE, NOT_POSSIBLE },
66 /* UNDEFINED */ { NOT_POSSIBLE, UNDEFINED, NO_RETURN, PARTIAL_RETURN, VOID_RETURN, VALUE_RETURN, THROW },
67 /* NO_RETURN */ { NOT_POSSIBLE, NO_RETURN, NO_RETURN, PARTIAL_RETURN, PARTIAL_RETURN, PARTIAL_RETURN, NO_RETURN },
68 /* PARTIAL_RETURN */ { NOT_POSSIBLE, PARTIAL_RETURN, PARTIAL_RETURN, PARTIAL_RETURN, PARTIAL_RETURN, PARTIAL_RETURN, PARTIAL_RETURN },
69 /* VOID_RETURN */ { NOT_POSSIBLE, VOID_RETURN, PARTIAL_RETURN, PARTIAL_RETURN, VOID_RETURN, NOT_POSSIBLE, VOID_RETURN },
70 /* VALUE_RETURN */ { NOT_POSSIBLE, VALUE_RETURN, PARTIAL_RETURN, PARTIAL_RETURN, NOT_POSSIBLE, VALUE_RETURN, VALUE_RETURN },
71 /* THROW */ { NOT_POSSIBLE, THROW, NO_RETURN, PARTIAL_RETURN, VOID_RETURN, VALUE_RETURN, THROW }
74 // Table to merge return modes for sequential statements (y: fReturnKind, x: other.fReturnKind)
75 private static final int[][] RETURN_KIND_SEQUENTIAL_TABLE = {
76 /* NOT_POSSIBLE UNDEFINED NO_RETURN PARTIAL_RETURN VOID_RETURN VALUE_RETURN THROW */
77 /* NOT_POSSIBLE */ { NOT_POSSIBLE, NOT_POSSIBLE, NOT_POSSIBLE, NOT_POSSIBLE, NOT_POSSIBLE, NOT_POSSIBLE, NOT_POSSIBLE },
78 /* UNDEFINED */ { NOT_POSSIBLE, UNDEFINED, NO_RETURN, PARTIAL_RETURN, VOID_RETURN, VALUE_RETURN, THROW },
79 /* NO_RETURN */ { NOT_POSSIBLE, NO_RETURN, NO_RETURN, PARTIAL_RETURN, VOID_RETURN, VALUE_RETURN, THROW },
80 /* PARTIAL_RETURN */ { NOT_POSSIBLE, PARTIAL_RETURN, PARTIAL_RETURN, PARTIAL_RETURN, VOID_RETURN, VALUE_RETURN, VALUE_RETURN },
81 /* VOID_RETURN */ { NOT_POSSIBLE, VOID_RETURN, VOID_RETURN, PARTIAL_RETURN, VOID_RETURN, NOT_POSSIBLE, NOT_POSSIBLE },
82 /* VALUE_RETURN */ { NOT_POSSIBLE, VALUE_RETURN, VALUE_RETURN, PARTIAL_RETURN, NOT_POSSIBLE, VALUE_RETURN, NOT_POSSIBLE },
83 /* THROW */ { NOT_POSSIBLE, THROW, THROW, VALUE_RETURN, VOID_RETURN, VALUE_RETURN, THROW }
86 protected static final String UNLABELED = "@unlabeled"; //$NON-NLS-1$
87 protected static final IVariableBinding[] EMPTY_ARRAY= new IVariableBinding[0];
89 protected int fReturnKind;
90 protected int[] fAccessModes;
91 protected Set<String> fBranches;
92 //protected Set<ITypeBinding> fExceptions;
93 protected Set<ITypeBinding> fTypeVariables;
95 protected FlowInfo() {
99 protected FlowInfo(int returnKind) {
100 fReturnKind= returnKind;
103 //---- General Helpers ----------------------------------------------------------
105 protected void assignExecutionFlow(FlowInfo right) {
106 right.generated_5909308445650589946(this);
109 protected void assignAccessMode(FlowInfo right) {
110 right.generated_2737604558401151346(this);
113 protected void assign(FlowInfo right) {
114 assignExecutionFlow(right);
115 assignAccessMode(right);
118 protected void mergeConditional(FlowInfo info, FlowContext context) {
119 mergeAccessModeConditional(info, context);
120 info.generated_4203786444310502274(this);
123 protected void mergeSequential(FlowInfo info, FlowContext context) {
124 mergeAccessModeSequential(info, context);
125 info.generated_5384924323028097289(this);
130 //---- Return Kind ------------------------------------------------------------------
132 public void setNoReturn() {
133 fReturnKind= NO_RETURN;
136 public boolean isUndefined() {
137 return fReturnKind == UNDEFINED;
140 public boolean isNoReturn() {
141 return fReturnKind == NO_RETURN;
144 public boolean isPartialReturn() {
145 return fReturnKind == PARTIAL_RETURN;
148 public boolean isVoidReturn() {
149 return fReturnKind == VOID_RETURN;
152 public boolean isValueReturn() {
153 return fReturnKind == VALUE_RETURN;
156 public boolean isThrow() {
157 return fReturnKind == THROW;
160 public boolean isReturn() {
161 return fReturnKind == VOID_RETURN || fReturnKind == VALUE_RETURN;
164 //---- Branches -------------------------------------------------------------------------
166 public boolean branches() {
167 return fBranches != null && !fBranches.isEmpty();
170 protected Set<String> getBranches() {
174 protected void removeLabel(SimpleName label) {
175 if (fBranches != null) {
176 fBranches.remove(makeString(label));
177 if (fBranches.isEmpty())
182 protected static String makeString(SimpleName label) {
186 return label.getIdentifier();
189 //---- Type parameters -----------------------------------------------------------------
191 public ITypeBinding[] getTypeVariables() {
192 if (fTypeVariables == null)
193 return new ITypeBinding[0];
194 return fTypeVariables.toArray(new ITypeBinding[fTypeVariables.size()]);
197 protected void addTypeVariable(ITypeBinding typeParameter) {
198 if (fTypeVariables == null)
199 fTypeVariables= new HashSet<ITypeBinding>();
200 fTypeVariables.add(typeParameter);
203 private void mergeTypeVariablesSequential(FlowInfo otherInfo) {
204 otherInfo.generated_9177087240118677719(this);
207 private void mergeTypeVariablesConditional(FlowInfo otherInfo) {
208 otherInfo.generated_3576989528696038545(this);
213 //---- Execution flow -------------------------------------------------------------------
215 private void mergeExecutionFlowSequential(FlowInfo otherInfo) {
216 otherInfo.generated_1782832635089041599(this);
219 private void mergeExecutionFlowConditional(FlowInfo otherInfo) {
220 otherInfo.generated_8403552546052715200(this);
223 private void mergeBranches(FlowInfo otherInfo) {
224 otherInfo.generated_1347106970305352130(this);
227 private static <T> Set<T> mergeSets(Set<T> thisSet, Set<T> otherSet) {
228 if (otherSet != null) {
229 if (thisSet == null) {
232 Iterator<T> iter= otherSet.iterator();
233 while (iter.hasNext()) {
234 thisSet.add(iter.next());
241 //---- Local access handling --------------------------------------------------
244 * Returns an array of <code>IVariableBinding</code> that conform to the given
245 * access mode <code>mode</code>.
247 * @param context the flow context object used to compute this flow info
248 * @param mode the access type. Valid values are <code>READ</code>, <code>WRITE</code>,
249 * <code>UNKNOWN</code> and any combination of them.
250 * @return an array of local variable bindings conforming to the given type.
252 public IVariableBinding[] get(FlowContext context, int mode) {
253 List<IVariableBinding> result= new ArrayList<IVariableBinding>();
254 int[] locals= getAccessModes();
257 for (int i= 0; i < locals.length; i++) {
258 int accessMode= locals[i];
259 if ((accessMode & mode) != 0)
260 result.add(context.getLocalFromIndex(i));
262 return result.toArray(new IVariableBinding[result.size()]);
266 * Checks whether the given local variable binding has the given access
269 * @param context the flow context used during flow analysis
270 * @param local local variable of interest
271 * @param mode the access mode of the local variable
273 * @return <code>true</code> if the binding has the given access mode.
274 * <code>False</code> otherwise
276 public boolean hasAccessMode(FlowContext context, IVariableBinding local, int mode) {
277 boolean unusedMode= (mode & UNUSED) != 0;
278 if (fAccessModes == null && unusedMode)
280 int index= context.getIndexFromLocal(local);
283 return (fAccessModes[index] & mode) != 0;
287 * Returns the access mode of the local variable identified by the given binding.
289 * @param context the flow context used during flow analysis
290 * @param local the local variable of interest
291 * @return the access mode of the local variable
293 public int getAccessMode(FlowContext context, IVariableBinding local) {
294 if (fAccessModes == null)
296 int index= context.getIndexFromLocal(local);
299 return fAccessModes[index];
302 protected int[] getAccessModes() {
306 protected void clearAccessMode(IVariableBinding binding, FlowContext context) {
307 if (fAccessModes == null) // all are unused
309 fAccessModes[binding.getVariableId() - context.getStartingIndex()]= UNUSED;
312 protected void mergeAccessModeSequential(FlowInfo otherInfo, FlowContext context) {
313 if (!context.considerAccessMode())
316 int[] others= otherInfo.generated_8026203182823550563(this);
317 if (others == null) // others are all unused. So nothing to do
320 // Must not consider return kind since a return statement can't control execution flow
321 // inside a method. It always leaves the method.
323 for (int i= 0; i < others.length; i++)
324 others[i]= ACCESS_MODE_OPEN_BRANCH_TABLE[getIndex(others[i])];
327 if (fAccessModes == null) { // all current variables are unused
328 fAccessModes= others;
332 if (context.computeArguments()) {
333 handleComputeArguments(others);
334 } else if (context.computeReturnValues()) {
335 handleComputeReturnValues(others);
336 } else if (context.computeMerge()) {
337 handleMergeValues(others);
341 private void handleComputeReturnValues(int[] others) {
342 for (int i= 0; i < fAccessModes.length; i++) {
343 int accessmode= fAccessModes[i];
344 int othermode= others[i];
345 if (accessmode == WRITE)
347 if (accessmode == WRITE_POTENTIAL) {
348 if (othermode == WRITE)
349 fAccessModes[i]= WRITE;
353 if (others[i] != UNUSED)
354 fAccessModes[i]= othermode;
358 private void handleComputeArguments(int[] others) {
359 for (int i= 0; i < fAccessModes.length; i++) {
360 int accessMode= fAccessModes[i];
361 int otherMode= others[i];
362 if (accessMode == UNUSED) {
363 fAccessModes[i]= otherMode;
364 } else if (accessMode == WRITE_POTENTIAL && (otherMode == READ || otherMode == READ_POTENTIAL)) {
365 // Read always supersedes a potential write even if the read is potential as well
366 // (we have to consider the potential read as an argument then).
367 fAccessModes[i]= otherMode;
368 } else if (accessMode == WRITE_POTENTIAL && otherMode == WRITE) {
369 fAccessModes[i]= WRITE;
374 private void handleMergeValues(int[] others) {
375 for (int i= 0; i < fAccessModes.length; i++) {
376 fAccessModes[i]= ACCESS_MODE_CONDITIONAL_TABLE
377 [getIndex(fAccessModes[i])]
378 [getIndex(others[i])];
382 protected void createAccessModeArray(FlowContext context) {
383 fAccessModes= new int[context.getArrayLength()];
384 for (int i= 0; i < fAccessModes.length; i++) {
385 fAccessModes[i]= UNUSED;
389 protected void mergeAccessModeConditional(FlowInfo otherInfo, FlowContext context) {
390 if (!context.considerAccessMode())
393 int[] others= otherInfo.generated_2805630759121218078(this);
395 if (fAccessModes == null) {
397 fAccessModes= others;
399 createAccessModeArray(context);
402 if (others == null) {
403 for (int i= 0; i < fAccessModes.length; i++) {
404 int unused_index= getIndex(UNUSED);
405 fAccessModes[i]= ACCESS_MODE_CONDITIONAL_TABLE
406 [getIndex(fAccessModes[i])]
410 for (int i= 0; i < fAccessModes.length; i++) {
411 fAccessModes[i]= ACCESS_MODE_CONDITIONAL_TABLE
412 [getIndex(fAccessModes[i])]
413 [getIndex(others[i])];
419 protected void mergeEmptyCondition(FlowContext context) {
420 if (fReturnKind == VALUE_RETURN || fReturnKind == VOID_RETURN)
421 fReturnKind= PARTIAL_RETURN;
423 if (!context.considerAccessMode())
426 if (fAccessModes == null) {
427 createAccessModeArray(context);
431 int unused_index= getIndex(UNUSED);
432 for (int i= 0; i < fAccessModes.length; i++) {
433 fAccessModes[i]= ACCESS_MODE_CONDITIONAL_TABLE
434 [getIndex(fAccessModes[i])]
439 public void generated_3658011097861187265(ExtractMethodAnalyzer extractmethodanalyzer) {
440 extractmethodanalyzer.fMethodLocals= extractmethodanalyzer.removeSelectedDeclarations(get(extractmethodanalyzer.fInputFlowContext, FlowInfo.WRITE | FlowInfo.WRITE_POTENTIAL));
441 extractmethodanalyzer.fTypeVariables= extractmethodanalyzer.computeTypeVariables(getTypeVariables());
444 public void generated_5909308445650589946(FlowInfo flowinfo) {
445 flowinfo.fReturnKind= fReturnKind;
446 flowinfo.fBranches= fBranches;
449 public void generated_2737604558401151346(FlowInfo flowinfo) {
450 flowinfo.fAccessModes= fAccessModes;
453 public void generated_4203786444310502274(FlowInfo flowinfo) {
454 flowinfo.mergeExecutionFlowConditional(this);
455 flowinfo.mergeTypeVariablesConditional(this);
458 public void generated_5384924323028097289(FlowInfo flowinfo) {
459 flowinfo.mergeExecutionFlowSequential(this);
460 flowinfo.mergeTypeVariablesSequential(this);
463 public void generated_9177087240118677719(FlowInfo flowinfo) {
464 flowinfo.fTypeVariables= FlowInfo.mergeSets(flowinfo.fTypeVariables, fTypeVariables);
467 public void generated_3576989528696038545(FlowInfo flowinfo) {
468 flowinfo.fTypeVariables= FlowInfo.mergeSets(flowinfo.fTypeVariables, fTypeVariables);
471 public void generated_1782832635089041599(FlowInfo flowinfo) {
472 int other= fReturnKind;
473 if (flowinfo.branches() && other == FlowInfo.VALUE_RETURN)
474 other= FlowInfo.PARTIAL_RETURN;
475 flowinfo.fReturnKind= FlowInfo.RETURN_KIND_SEQUENTIAL_TABLE[flowinfo.fReturnKind][other];
476 flowinfo.mergeBranches(this);
479 public void generated_8403552546052715200(FlowInfo flowinfo) {
480 flowinfo.fReturnKind= FlowInfo.RETURN_KIND_CONDITIONAL_TABLE[flowinfo.fReturnKind][fReturnKind];
481 flowinfo.mergeBranches(this);
484 public void generated_1347106970305352130(FlowInfo flowinfo) {
485 flowinfo.fBranches= FlowInfo.mergeSets(flowinfo.fBranches, fBranches);
488 public int[] generated_8026203182823550563(FlowInfo flowinfo) {
489 int[] others= fAccessModes;
493 public int[] generated_2805630759121218078(FlowInfo flowinfo) {
494 int[] others= fAccessModes;
498 private static int getIndex(int accessMode) {
500 switch (accessMode) {
509 case WRITE_POTENTIAL: