Lines 43-59
public class FakedTrackingVariable extends LocalDeclaration {
Link Here
|
43 |
private static final int CLOSE_SEEN = 1; |
43 |
private static final int CLOSE_SEEN = 1; |
44 |
// the resource was passed to outside code (arg in method/ctor call or as a return value from this method): |
44 |
// the resource was passed to outside code (arg in method/ctor call or as a return value from this method): |
45 |
private static final int PASSED_TO_OUTSIDE = 2; |
45 |
private static final int PASSED_TO_OUTSIDE = 2; |
|
|
46 |
// the resource was obtained from the outside (argument of the current method, or via a field read) |
47 |
private static final int OBTAINED_FROM_OUTSIDE = 4; |
46 |
// If close() is invoked from a nested method (inside a local type) report remaining problems only as potential: |
48 |
// If close() is invoked from a nested method (inside a local type) report remaining problems only as potential: |
47 |
private static final int CLOSED_IN_NESTED_METHOD = 4; |
49 |
private static final int CLOSED_IN_NESTED_METHOD = 8; |
48 |
// a location independent issue has been reported already against this resource: |
50 |
// a location independent issue has been reported already against this resource: |
49 |
private static final int REPORTED = 8; |
51 |
private static final int REPORTED = 16; |
50 |
// a resource is wrapped in another resource: |
52 |
// a resource is wrapped in another resource: |
51 |
private static final int WRAPPED = 16; |
53 |
private static final int WRAPPED = 32; |
52 |
|
54 |
|
53 |
private static final int DOUBT_MASK = CLOSE_SEEN | PASSED_TO_OUTSIDE | CLOSED_IN_NESTED_METHOD | REPORTED; // not WRAPPED |
55 |
private static final int DOUBT_MASK = CLOSE_SEEN | PASSED_TO_OUTSIDE | OBTAINED_FROM_OUTSIDE | CLOSED_IN_NESTED_METHOD | REPORTED; // not WRAPPED |
54 |
|
56 |
|
55 |
/** |
57 |
/** |
56 |
* Bitset of {@link #CLOSE_SEEN}, {@link #PASSED_TO_OUTSIDE}, {@link #CLOSED_IN_NESTED_METHOD}, {@link #REPORTED} and {@link #WRAPPED}. |
58 |
* Bitset of {@link #CLOSE_SEEN}, {@link #PASSED_TO_OUTSIDE}, {@link #OBTAINED_FROM_OUTSIDE}, {@link #CLOSED_IN_NESTED_METHOD}, {@link #REPORTED} and {@link #WRAPPED}. |
57 |
*/ |
59 |
*/ |
58 |
private int globalClosingState = 0; |
60 |
private int globalClosingState = 0; |
59 |
|
61 |
|
Lines 116-133
public class FakedTrackingVariable extends LocalDeclaration {
Link Here
|
116 |
* @return a new {@link FakedTrackingVariable} or null. |
118 |
* @return a new {@link FakedTrackingVariable} or null. |
117 |
*/ |
119 |
*/ |
118 |
public static FakedTrackingVariable getCloseTrackingVariable(Expression expression) { |
120 |
public static FakedTrackingVariable getCloseTrackingVariable(Expression expression) { |
|
|
121 |
while (true) { |
122 |
if (expression instanceof CastExpression) |
123 |
expression = ((CastExpression) expression).expression; |
124 |
else if (expression instanceof Assignment) |
125 |
expression = ((Assignment) expression).expression; |
126 |
else |
127 |
break; |
128 |
} |
119 |
if (expression instanceof SingleNameReference) { |
129 |
if (expression instanceof SingleNameReference) { |
120 |
SingleNameReference name = (SingleNameReference) expression; |
130 |
SingleNameReference name = (SingleNameReference) expression; |
121 |
if (name.binding instanceof LocalVariableBinding) { |
131 |
if (name.binding instanceof LocalVariableBinding) { |
122 |
LocalVariableBinding local = (LocalVariableBinding)name.binding; |
132 |
LocalVariableBinding local = (LocalVariableBinding)name.binding; |
123 |
if (local.closeTracker != null) |
133 |
if (local.closeTracker != null) |
124 |
return local.closeTracker; |
134 |
return local.closeTracker; |
125 |
if (local.isParameter() || !isAutoCloseable(expression.resolvedType)) |
135 |
if (!isAnyCloseable(expression.resolvedType)) |
126 |
return null; |
136 |
return null; |
127 |
// tracking var doesn't yet exist. This happens in finally block |
137 |
// tracking var doesn't yet exist. This happens in finally block |
128 |
// which is analyzed before the corresponding try block |
138 |
// which is analyzed before the corresponding try block |
129 |
Statement location = local.declaration; |
139 |
Statement location = local.declaration; |
130 |
return local.closeTracker = new FakedTrackingVariable(local, location); |
140 |
local.closeTracker = new FakedTrackingVariable(local, location); |
|
|
141 |
if (local.isParameter()) { |
142 |
local.closeTracker.globalClosingState |= OBTAINED_FROM_OUTSIDE; |
143 |
// status of this tracker is now UNKNOWN |
144 |
} |
145 |
return local.closeTracker; |
131 |
} |
146 |
} |
132 |
} else if (expression instanceof AllocationExpression) { |
147 |
} else if (expression instanceof AllocationExpression) { |
133 |
// return any preliminary tracking variable from analyseCloseableAllocation |
148 |
// return any preliminary tracking variable from analyseCloseableAllocation |
Lines 152-158
public class FakedTrackingVariable extends LocalDeclaration {
Link Here
|
152 |
if (rhs instanceof AllocationExpression) { |
167 |
if (rhs instanceof AllocationExpression) { |
153 |
closeTracker = local.closeTracker; |
168 |
closeTracker = local.closeTracker; |
154 |
if (closeTracker == null) { |
169 |
if (closeTracker == null) { |
155 |
if (isAutoCloseable(rhs.resolvedType)) { |
170 |
if (isAnyCloseable(rhs.resolvedType)) { |
156 |
closeTracker = new FakedTrackingVariable(local, location); |
171 |
closeTracker = new FakedTrackingVariable(local, location); |
157 |
} |
172 |
} |
158 |
} |
173 |
} |
Lines 178-184
public class FakedTrackingVariable extends LocalDeclaration {
Link Here
|
178 |
} else if (((ReferenceBinding)allocation.resolvedType).hasTypeBit(TypeIds.BitWrapperCloseable)) { |
193 |
} else if (((ReferenceBinding)allocation.resolvedType).hasTypeBit(TypeIds.BitWrapperCloseable)) { |
179 |
if (allocation.arguments != null && allocation.arguments.length > 0) { |
194 |
if (allocation.arguments != null && allocation.arguments.length > 0) { |
180 |
// find the wrapped resource represented by its tracking var: |
195 |
// find the wrapped resource represented by its tracking var: |
181 |
FakedTrackingVariable innerTracker = analyseCloseableAllocationArgument(scope, flowInfo, allocation, allocation.arguments[0]); |
196 |
FakedTrackingVariable innerTracker = findCloseTracker(scope, flowInfo, allocation, allocation.arguments[0]); |
182 |
if (innerTracker != null) { |
197 |
if (innerTracker != null) { |
183 |
if (innerTracker == allocation.closeTracker) |
198 |
if (innerTracker == allocation.closeTracker) |
184 |
return; // self wrap (res = new Res(res)) -> neither change (here) nor remove (below) |
199 |
return; // self wrap (res = new Res(res)) -> neither change (here) nor remove (below) |
Lines 212-218
public class FakedTrackingVariable extends LocalDeclaration {
Link Here
|
212 |
} |
227 |
} |
213 |
|
228 |
|
214 |
/** Find an existing tracking variable for the argument of an allocation for a resource wrapper. */ |
229 |
/** Find an existing tracking variable for the argument of an allocation for a resource wrapper. */ |
215 |
public static FakedTrackingVariable analyseCloseableAllocationArgument(BlockScope scope, FlowInfo flowInfo, AllocationExpression allocation, Expression arg) |
230 |
private static FakedTrackingVariable findCloseTracker(BlockScope scope, FlowInfo flowInfo, AllocationExpression allocation, Expression arg) |
216 |
{ |
231 |
{ |
217 |
while (arg instanceof Assignment) { |
232 |
while (arg instanceof Assignment) { |
218 |
Assignment assign = (Assignment)arg; |
233 |
Assignment assign = (Assignment)arg; |
Lines 276-289
public class FakedTrackingVariable extends LocalDeclaration {
Link Here
|
276 |
// keep close-status of RHS unchanged across this assignment |
291 |
// keep close-status of RHS unchanged across this assignment |
277 |
} else if (previousTracker != null) { // 2. re-use tracking variable from the LHS? |
292 |
} else if (previousTracker != null) { // 2. re-use tracking variable from the LHS? |
278 |
// re-assigning from a fresh value, mark as not-closed again: |
293 |
// re-assigning from a fresh value, mark as not-closed again: |
279 |
flowInfo.markAsDefinitelyNull(previousTracker.binding); |
294 |
if ((previousTracker.globalClosingState & (PASSED_TO_OUTSIDE|OBTAINED_FROM_OUTSIDE)) == 0) |
|
|
295 |
flowInfo.markAsDefinitelyNull(previousTracker.binding); |
280 |
local.closeTracker = analyseCloseableExpression(flowInfo, local, location, rhs, previousTracker); |
296 |
local.closeTracker = analyseCloseableExpression(flowInfo, local, location, rhs, previousTracker); |
281 |
} else { // 3. no re-use, create a fresh tracking variable: |
297 |
} else { // 3. no re-use, create a fresh tracking variable: |
282 |
rhsTrackVar = analyseCloseableExpression(flowInfo, local, location, rhs, null); |
298 |
rhsTrackVar = analyseCloseableExpression(flowInfo, local, location, rhs, null); |
283 |
if (rhsTrackVar != null) { |
299 |
if (rhsTrackVar != null) { |
284 |
local.closeTracker = rhsTrackVar; |
300 |
local.closeTracker = rhsTrackVar; |
285 |
// a fresh resource, mark as not-closed: |
301 |
// a fresh resource, mark as not-closed: |
286 |
if ((rhsTrackVar.globalClosingState & PASSED_TO_OUTSIDE) == 0) |
302 |
if ((rhsTrackVar.globalClosingState & (PASSED_TO_OUTSIDE|OBTAINED_FROM_OUTSIDE)) == 0) |
287 |
flowInfo.markAsDefinitelyNull(rhsTrackVar.binding); |
303 |
flowInfo.markAsDefinitelyNull(rhsTrackVar.binding); |
288 |
// TODO(stephan): this might be useful, but I could not find a test case for it: |
304 |
// TODO(stephan): this might be useful, but I could not find a test case for it: |
289 |
// if (flowContext.initsOnFinally != null) |
305 |
// if (flowContext.initsOnFinally != null) |
Lines 333-347
public class FakedTrackingVariable extends LocalDeclaration {
Link Here
|
333 |
// we *might* be responsible for the resource obtained |
349 |
// we *might* be responsible for the resource obtained |
334 |
FakedTrackingVariable tracker = new FakedTrackingVariable(local, location); |
350 |
FakedTrackingVariable tracker = new FakedTrackingVariable(local, location); |
335 |
tracker.globalClosingState |= PASSED_TO_OUTSIDE; |
351 |
tracker.globalClosingState |= PASSED_TO_OUTSIDE; |
336 |
flowInfo.markPotentiallyNullBit(tracker.binding); |
352 |
flowInfo.markPotentiallyNullBit(tracker.binding); // shed some doubt |
337 |
return tracker; |
353 |
return tracker; |
338 |
} else if (expression instanceof FieldReference |
354 |
} else if (expression instanceof FieldReference |
339 |
|| expression instanceof QualifiedNameReference) |
355 |
|| expression instanceof QualifiedNameReference) |
340 |
{ |
356 |
{ |
341 |
// responsibility for this resource probably lies at a higher level |
357 |
// responsibility for this resource probably lies at a higher level |
342 |
FakedTrackingVariable tracker = new FakedTrackingVariable(local, location); |
358 |
FakedTrackingVariable tracker = new FakedTrackingVariable(local, location); |
343 |
tracker.globalClosingState |= PASSED_TO_OUTSIDE; |
359 |
tracker.globalClosingState |= OBTAINED_FROM_OUTSIDE; |
344 |
flowInfo.markPotentiallyNonNullBit(tracker.binding); |
360 |
// leave state as UNKNOWN, the bit OBTAINED_FROM_OUTSIDE will prevent spurious warnings |
345 |
return tracker; |
361 |
return tracker; |
346 |
} |
362 |
} |
347 |
|
363 |
|
Lines 385-391
public class FakedTrackingVariable extends LocalDeclaration {
Link Here
|
385 |
} |
401 |
} |
386 |
|
402 |
|
387 |
/** Answer wither the given type binding is a subtype of java.lang.AutoCloseable. */ |
403 |
/** Answer wither the given type binding is a subtype of java.lang.AutoCloseable. */ |
388 |
public static boolean isAutoCloseable(TypeBinding typeBinding) { |
404 |
public static boolean isAnyCloseable(TypeBinding typeBinding) { |
389 |
return typeBinding instanceof ReferenceBinding |
405 |
return typeBinding instanceof ReferenceBinding |
390 |
&& ((ReferenceBinding)typeBinding).hasTypeBit(TypeIds.BitAutoCloseable|TypeIds.BitCloseable); |
406 |
&& ((ReferenceBinding)typeBinding).hasTypeBit(TypeIds.BitAutoCloseable|TypeIds.BitCloseable); |
391 |
} |
407 |
} |
Lines 544-550
public class FakedTrackingVariable extends LocalDeclaration {
Link Here
|
544 |
} |
560 |
} |
545 |
|
561 |
|
546 |
public void reportExplicitClosing(ProblemReporter problemReporter) { |
562 |
public void reportExplicitClosing(ProblemReporter problemReporter) { |
547 |
if ((this.globalClosingState & REPORTED) == 0) { |
563 |
if ((this.globalClosingState & (OBTAINED_FROM_OUTSIDE|REPORTED)) == 0) { // can't use t-w-r for OBTAINED_FROM_OUTSIDE |
548 |
this.globalClosingState |= REPORTED; |
564 |
this.globalClosingState |= REPORTED; |
549 |
problemReporter.explicitlyClosedAutoCloseable(this); |
565 |
problemReporter.explicitlyClosedAutoCloseable(this); |
550 |
} |
566 |
} |