Lines 48-53
Link Here
|
48 |
private static final int CLOSED_IN_NESTED_METHOD = 4; |
48 |
private static final int CLOSED_IN_NESTED_METHOD = 4; |
49 |
// a location independent issue has been reported already against this resource: |
49 |
// a location independent issue has been reported already against this resource: |
50 |
private static final int REPORTED = 8; |
50 |
private static final int REPORTED = 8; |
|
|
51 |
// a resource is wrapped in another resource: |
52 |
private static final int WRAPPED = 16; |
51 |
|
53 |
|
52 |
/** |
54 |
/** |
53 |
* Bitset of {@link #CLOSE_SEEN}, {@link #PASSED_TO_OUTSIDE}, {@link #CLOSED_IN_NESTED_METHOD} and {@link #REPORTED}. |
55 |
* Bitset of {@link #CLOSE_SEEN}, {@link #PASSED_TO_OUTSIDE}, {@link #CLOSED_IN_NESTED_METHOD} and {@link #REPORTED}. |
Lines 58-67
Link Here
|
58 |
|
60 |
|
59 |
public LocalVariableBinding originalBinding; // the real local being tracked |
61 |
public LocalVariableBinding originalBinding; // the real local being tracked |
60 |
|
62 |
|
61 |
HashMap recordedLocations; // initially null, ASTNode -> Integer |
63 |
HashMap recordedLocations; // initially null, ASTNode -> Integer |
|
|
64 |
|
65 |
public FakedTrackingVariable innerTracker; // chained tracking variable of a chained (wrapped) resource |
62 |
|
66 |
|
63 |
|
67 |
|
64 |
public FakedTrackingVariable(LocalVariableBinding original, Statement location) { |
68 |
public FakedTrackingVariable(LocalVariableBinding original, ASTNode location) { |
65 |
super(original.name, location.sourceStart, location.sourceEnd); |
69 |
super(original.name, location.sourceStart, location.sourceEnd); |
66 |
this.type = new SingleTypeReference( |
70 |
this.type = new SingleTypeReference( |
67 |
TypeConstants.OBJECT, |
71 |
TypeConstants.OBJECT, |
Lines 145-151
Link Here
|
145 |
// re-assigning from a fresh, mark as not-closed again: |
149 |
// re-assigning from a fresh, mark as not-closed again: |
146 |
flowInfo.markAsDefinitelyNull(previousTrackerBinding); |
150 |
flowInfo.markAsDefinitelyNull(previousTrackerBinding); |
147 |
} else { // 3. no re-use, create a fresh tracking variable: |
151 |
} else { // 3. no re-use, create a fresh tracking variable: |
148 |
local.closeTracker = new FakedTrackingVariable(local, location); |
152 |
rhsTrackVar = analyseCloseableExpression(local, location, rhs); |
|
|
153 |
if (rhsTrackVar == null) |
154 |
return; |
155 |
local.closeTracker = rhsTrackVar; |
149 |
// a fresh resource, mark as not-closed: |
156 |
// a fresh resource, mark as not-closed: |
150 |
flowInfo.markAsDefinitelyNull(local.closeTracker.binding); |
157 |
flowInfo.markAsDefinitelyNull(local.closeTracker.binding); |
151 |
// TODO(stephan): this might be useful, but I could not find a test case for it: |
158 |
// TODO(stephan): this might be useful, but I could not find a test case for it: |
Lines 153-158
Link Here
|
153 |
// flowContext.initsOnFinally.markAsDefinitelyNonNull(trackerBinding); |
160 |
// flowContext.initsOnFinally.markAsDefinitelyNonNull(trackerBinding); |
154 |
} |
161 |
} |
155 |
} |
162 |
} |
|
|
163 |
} |
164 |
/* analyse structure of a closeable expression, matching (chained) resources against our white lists. */ |
165 |
private static FakedTrackingVariable analyseCloseableExpression(LocalVariableBinding local, |
166 |
ASTNode location, Expression expression) |
167 |
{ |
168 |
if (expression.resolvedType instanceof ReferenceBinding) { |
169 |
ReferenceBinding resourceType = (ReferenceBinding) expression.resolvedType; |
170 |
|
171 |
if (resourceType.hasTypeBit(TypeIds.BitResourceFreeCloseable)) { |
172 |
// (a) resource-free closeable: -> null |
173 |
return null; |
174 |
} |
175 |
|
176 |
if (resourceType.hasTypeBit(TypeIds.BitWrapperCloseable)) { |
177 |
if (expression instanceof Assignment) |
178 |
expression = ((Assignment)expression).expression; |
179 |
if (expression instanceof AllocationExpression) { |
180 |
Expression[] args = ((AllocationExpression) expression).arguments; |
181 |
if (args != null && args.length == 1) { |
182 |
Expression arg = args[0]; |
183 |
if (arg instanceof Assignment) { |
184 |
Expression lhs = ((Assignment)arg).lhs; |
185 |
if (lhs instanceof SingleNameReference) { |
186 |
SingleNameReference lhsRef = (SingleNameReference) lhs; |
187 |
if (lhsRef.binding instanceof LocalVariableBinding) { |
188 |
FakedTrackingVariable innerTracker = analyseCloseableExpression(((LocalVariableBinding)lhsRef.binding), arg, ((Assignment) arg).expression); |
189 |
if (innerTracker != null) { |
190 |
// (b.1) wrapper with relevant inner: -> tracking var, linked to inner tracking var |
191 |
FakedTrackingVariable tracker = new FakedTrackingVariable(local, location); |
192 |
tracker.innerTracker = innerTracker; |
193 |
innerTracker.globalClosingState |= WRAPPED; |
194 |
return tracker; |
195 |
} |
196 |
} |
197 |
} |
198 |
// FIXME: if we ever get here, what should we do? |
199 |
} |
200 |
if (arg instanceof SingleNameReference) { |
201 |
SingleNameReference ref = (SingleNameReference) arg; |
202 |
if (ref.binding instanceof LocalVariableBinding) { |
203 |
LocalVariableBinding innerLocal = (LocalVariableBinding)ref.binding; |
204 |
FakedTrackingVariable innerTracker = innerLocal.closeTracker; |
205 |
if (innerTracker != null) |
206 |
innerTracker = analyseCloseableExpression(innerLocal, expression, arg); |
207 |
if (innerTracker != null) { |
208 |
// (b.2) wrapper with relevant inner: -> tracking var, linked to inner tracking var |
209 |
FakedTrackingVariable tracker = new FakedTrackingVariable(local, location); |
210 |
tracker.innerTracker = innerTracker; |
211 |
innerTracker.globalClosingState |= WRAPPED; |
212 |
return tracker; |
213 |
} |
214 |
} |
215 |
} else if (arg instanceof AllocationExpression) { |
216 |
ReferenceBinding innerType = (ReferenceBinding)arg.resolvedType; |
217 |
if (!innerType.hasTypeBit(TypeIds.BitResourceFreeCloseable|TypeIds.BitWrapperCloseable)) { |
218 |
// (c) wrapper alloc with direct nested alloc of regular: -> normal track var (no local represents inner) |
219 |
return new FakedTrackingVariable(local, location); |
220 |
} |
221 |
} |
222 |
} |
223 |
} |
224 |
// (d) wrapper with irrelevant inner: -> null |
225 |
return null; |
226 |
} |
227 |
} |
228 |
if (local.closeTracker != null) |
229 |
// (e): inner has already been analysed: -> re-use track var |
230 |
return local.closeTracker; |
231 |
// (f): normal resource: -> normal tracking var |
232 |
return new FakedTrackingVariable(local, location); |
156 |
} |
233 |
} |
157 |
|
234 |
|
158 |
/** Answer wither the given type binding is a subtype of java.lang.AutoCloseable. */ |
235 |
/** Answer wither the given type binding is a subtype of java.lang.AutoCloseable. */ |
Lines 182-188
Link Here
|
182 |
*/ |
259 |
*/ |
183 |
public static FlowInfo markPassedToOutside(BlockScope scope, Expression expression, FlowInfo flowInfo) { |
260 |
public static FlowInfo markPassedToOutside(BlockScope scope, Expression expression, FlowInfo flowInfo) { |
184 |
FakedTrackingVariable trackVar = getCloseTrackingVariable(expression); |
261 |
FakedTrackingVariable trackVar = getCloseTrackingVariable(expression); |
185 |
if (trackVar != null) { |
262 |
if (trackVar != null && trackVar.innerTracker == null) { // ignore when resource is passed to the ctor of a resource wrapper |
186 |
trackVar.globalClosingState |= PASSED_TO_OUTSIDE; |
263 |
trackVar.globalClosingState |= PASSED_TO_OUTSIDE; |
187 |
if (scope.methodScope() != trackVar.methodScope) |
264 |
if (scope.methodScope() != trackVar.methodScope) |
188 |
trackVar.globalClosingState |= CLOSED_IN_NESTED_METHOD; |
265 |
trackVar.globalClosingState |= CLOSED_IN_NESTED_METHOD; |
Lines 200-208
Link Here
|
200 |
this.recordedLocations.put(location, new Integer(nullStatus)); |
277 |
this.recordedLocations.put(location, new Integer(nullStatus)); |
201 |
} |
278 |
} |
202 |
|
279 |
|
203 |
public boolean reportRecordedErrors(Scope scope) { |
280 |
public boolean reportRecordedErrors(Scope scope, int innerStatus) { |
204 |
if (this.globalClosingState == 0) { |
281 |
if (this.globalClosingState == 0) { |
205 |
reportError(scope.problemReporter(), null, FlowInfo.NULL); |
282 |
reportError(scope.problemReporter(), null, FlowInfo.NULL, innerStatus); |
206 |
return true; |
283 |
return true; |
207 |
} |
284 |
} |
208 |
boolean hasReported = false; |
285 |
boolean hasReported = false; |
Lines 210-223
Link Here
|
210 |
Iterator locations = this.recordedLocations.entrySet().iterator(); |
287 |
Iterator locations = this.recordedLocations.entrySet().iterator(); |
211 |
while (locations.hasNext()) { |
288 |
while (locations.hasNext()) { |
212 |
Map.Entry entry = (Entry) locations.next(); |
289 |
Map.Entry entry = (Entry) locations.next(); |
213 |
reportError(scope.problemReporter(), (ASTNode)entry.getKey(), ((Integer)entry.getValue()).intValue()); |
290 |
reportError(scope.problemReporter(), (ASTNode)entry.getKey(), ((Integer)entry.getValue()).intValue(), 0); // FIXME: innerStatus |
214 |
hasReported = true; |
291 |
hasReported = true; |
215 |
} |
292 |
} |
216 |
} |
293 |
} |
217 |
return hasReported; |
294 |
return hasReported; |
218 |
} |
295 |
} |
219 |
|
296 |
|
220 |
public void reportError(ProblemReporter problemReporter, ASTNode location, int nullStatus) { |
297 |
public void reportError(ProblemReporter problemReporter, ASTNode location, int nullStatus, int innerStatus) { |
|
|
298 |
if (this.innerTracker != null && innerStatus == FlowInfo.NON_NULL) { |
299 |
return; // inner resource is closed, don't complain against the wrapper |
300 |
} |
301 |
if ((this.globalClosingState & WRAPPED) != 0) |
302 |
return; |
221 |
if (nullStatus == FlowInfo.NULL) { |
303 |
if (nullStatus == FlowInfo.NULL) { |
222 |
if ((this.globalClosingState & CLOSED_IN_NESTED_METHOD) != 0) |
304 |
if ((this.globalClosingState & CLOSED_IN_NESTED_METHOD) != 0) |
223 |
problemReporter.potentiallyUnclosedCloseable(this, location); |
305 |
problemReporter.potentiallyUnclosedCloseable(this, location); |
Lines 225-231
Link Here
|
225 |
problemReporter.unclosedCloseable(this, location); |
307 |
problemReporter.unclosedCloseable(this, location); |
226 |
} else if (nullStatus == FlowInfo.POTENTIALLY_NULL) { |
308 |
} else if (nullStatus == FlowInfo.POTENTIALLY_NULL) { |
227 |
problemReporter.potentiallyUnclosedCloseable(this, location); |
309 |
problemReporter.potentiallyUnclosedCloseable(this, location); |
228 |
} |
310 |
} |
229 |
} |
311 |
} |
230 |
|
312 |
|
231 |
public void reportExplicitClosing(ProblemReporter problemReporter) { |
313 |
public void reportExplicitClosing(ProblemReporter problemReporter) { |