KickJava   Java API By Example, From Geeks To Geeks.

Java > Open Source Codes > org > eclipse > jdt > internal > compiler > ast > TryStatement


1 /*******************************************************************************
2  * Copyright (c) 2000, 2007 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
7  *
8  * Contributors:
9  * IBM Corporation - initial API and implementation
10  *******************************************************************************/

11 package org.eclipse.jdt.internal.compiler.ast;
12
13 import org.eclipse.jdt.internal.compiler.ASTVisitor;
14 import org.eclipse.jdt.internal.compiler.classfmt.ClassFileConstants;
15 import org.eclipse.jdt.internal.compiler.codegen.*;
16 import org.eclipse.jdt.internal.compiler.flow.*;
17 import org.eclipse.jdt.internal.compiler.impl.Constant;
18 import org.eclipse.jdt.internal.compiler.lookup.*;
19
20 public class TryStatement extends SubRoutineStatement {
21     
22     private final static char[] SECRET_RETURN_ADDRESS_NAME = " returnAddress".toCharArray(); //$NON-NLS-1$
23
private final static char[] SECRET_ANY_HANDLER_NAME = " anyExceptionHandler".toCharArray(); //$NON-NLS-1$
24
private final static char[] SECRET_RETURN_VALUE_NAME = " returnValue".toCharArray(); //$NON-NLS-1$
25

26     public Block tryBlock;
27     public Block[] catchBlocks;
28
29     public Argument[] catchArguments;
30     
31     // should rename into subRoutineComplete to be set to false by default
32

33     public Block finallyBlock;
34     BlockScope scope;
35
36     public UnconditionalFlowInfo subRoutineInits;
37     ReferenceBinding[] caughtExceptionTypes;
38     boolean[] catchExits;
39     
40     BranchLabel subRoutineStartLabel;
41     public LocalVariableBinding anyExceptionVariable,
42         returnAddressVariable,
43         secretReturnValue;
44     
45     ExceptionLabel[] declaredExceptionLabels; // only set while generating code
46

47     // for inlining/optimizing JSR instructions
48
private Object JavaDoc[] reusableJSRTargets;
49     private BranchLabel[] reusableJSRSequenceStartLabels;
50     private int[] reusableJSRStateIndexes;
51     private int reusableJSRTargetsCount = 0;
52
53     private final static int NO_FINALLY = 0; // no finally block
54
private final static int FINALLY_SUBROUTINE = 1; // finally is generated as a subroutine (using jsr/ret bytecodes)
55
private final static int FINALLY_DOES_NOT_COMPLETE = 2; // non returning finally is optimized with only one instance of finally block
56
private final static int FINALLY_INLINE = 3; // finally block must be inlined since cannot use jsr/ret bytecodes >1.5
57

58     // for local variables table attributes
59
int mergedInitStateIndex = -1;
60     int preTryInitStateIndex = -1;
61     int naturalExitMergeInitStateIndex = -1;
62     int[] catchExitInitStateIndexes;
63
64 public FlowInfo analyseCode(BlockScope currentScope, FlowContext flowContext, FlowInfo flowInfo) {
65
66     // Consider the try block and catch block so as to compute the intersection of initializations and
67
// the minimum exit relative depth amongst all of them. Then consider the subroutine, and append its
68
// initialization to the try/catch ones, if the subroutine completes normally. If the subroutine does not
69
// complete, then only keep this result for the rest of the analysis
70

71     // process the finally block (subroutine) - create a context for the subroutine
72

73     this.preTryInitStateIndex =
74         currentScope.methodScope().recordInitializationStates(flowInfo);
75
76     if (this.anyExceptionVariable != null) {
77         this.anyExceptionVariable.useFlag = LocalVariableBinding.USED;
78     }
79     if (this.returnAddressVariable != null) { // TODO (philippe) if subroutine is escaping, unused
80
this.returnAddressVariable.useFlag = LocalVariableBinding.USED;
81     }
82     if (this.subRoutineStartLabel == null) {
83         // no finally block -- this is a simplified copy of the else part
84
// process the try block in a context handling the local exceptions.
85
ExceptionHandlingFlowContext handlingContext =
86             new ExceptionHandlingFlowContext(
87                 flowContext,
88                 this,
89                 this.caughtExceptionTypes,
90                 this.scope,
91                 flowInfo.unconditionalInits());
92         handlingContext.initsOnFinally =
93             new NullInfoRegistry(flowInfo.unconditionalInits());
94         // only try blocks initialize that member - may consider creating a
95
// separate class if needed
96

97         FlowInfo tryInfo;
98         if (this.tryBlock.isEmptyBlock()) {
99             tryInfo = flowInfo;
100         } else {
101             tryInfo = this.tryBlock.analyseCode(currentScope, handlingContext, flowInfo.copy());
102             if ((tryInfo.tagBits & FlowInfo.UNREACHABLE) != 0)
103                 this.bits |= ASTNode.IsTryBlockExiting;
104         }
105
106         // check unreachable catch blocks
107
handlingContext.complainIfUnusedExceptionHandlers(this.scope, this);
108
109         // process the catch blocks - computing the minimal exit depth amongst try/catch
110
if (this.catchArguments != null) {
111             int catchCount;
112             this.catchExits = new boolean[catchCount = this.catchBlocks.length];
113             this.catchExitInitStateIndexes = new int[catchCount];
114             for (int i = 0; i < catchCount; i++) {
115                 // keep track of the inits that could potentially have led to this exception handler (for final assignments diagnosis)
116
FlowInfo catchInfo;
117                 if (this.caughtExceptionTypes[i].isUncheckedException(true)) {
118                     catchInfo =
119                         handlingContext.initsOnFinally.mitigateNullInfoOf(
120                             flowInfo.unconditionalCopy().
121                                 addPotentialInitializationsFrom(
122                                     handlingContext.initsOnException(
123                                         this.caughtExceptionTypes[i])).
124                                 addPotentialInitializationsFrom(tryInfo).
125                                 addPotentialInitializationsFrom(
126                                     handlingContext.initsOnReturn));
127                 } else {
128                     catchInfo =
129                         flowInfo.unconditionalCopy().
130                             addPotentialInitializationsFrom(
131                                 handlingContext.initsOnException(
132                                     this.caughtExceptionTypes[i]))
133                             .addPotentialInitializationsFrom(
134                                 tryInfo.nullInfoLessUnconditionalCopy())
135                                 // remove null info to protect point of
136
// exception null info
137
.addPotentialInitializationsFrom(
138                                 handlingContext.initsOnReturn.
139                                     nullInfoLessUnconditionalCopy());
140                 }
141
142                 // catch var is always set
143
LocalVariableBinding catchArg = this.catchArguments[i].binding;
144                 catchInfo.markAsDefinitelyAssigned(catchArg);
145                 catchInfo.markAsDefinitelyNonNull(catchArg);
146                 /*
147                 "If we are about to consider an unchecked exception handler, potential inits may have occured inside
148                 the try block that need to be detected , e.g.
149                 try { x = 1; throwSomething();} catch(Exception e){ x = 2} "
150                 "(uncheckedExceptionTypes notNil and: [uncheckedExceptionTypes at: index])
151                 ifTrue: [catchInits addPotentialInitializationsFrom: tryInits]."
152                 */

153                 if (this.tryBlock.statements == null) {
154                     catchInfo.setReachMode(FlowInfo.UNREACHABLE);
155                 }
156                 catchInfo =
157                     this.catchBlocks[i].analyseCode(
158                         currentScope,
159                         flowContext,
160                         catchInfo);
161                 this.catchExitInitStateIndexes[i] = currentScope.methodScope().recordInitializationStates(catchInfo);
162                 this.catchExits[i] =
163                     (catchInfo.tagBits & FlowInfo.UNREACHABLE) != 0;
164                 tryInfo = tryInfo.mergedWith(catchInfo.unconditionalInits());
165             }
166         }
167         this.mergedInitStateIndex =
168             currentScope.methodScope().recordInitializationStates(tryInfo);
169         
170         // chain up null info registry
171
if (flowContext.initsOnFinally != null) {
172             flowContext.initsOnFinally.add(handlingContext.initsOnFinally);
173         }
174         
175         return tryInfo;
176     } else {
177         InsideSubRoutineFlowContext insideSubContext;
178         FinallyFlowContext finallyContext;
179         UnconditionalFlowInfo subInfo;
180         // analyse finally block first
181
insideSubContext = new InsideSubRoutineFlowContext(flowContext, this);
182
183         subInfo =
184             this.finallyBlock
185                 .analyseCode(
186                     currentScope,
187                     finallyContext = new FinallyFlowContext(flowContext, this.finallyBlock),
188                     flowInfo.nullInfoLessUnconditionalCopy())
189                 .unconditionalInits();
190         if (subInfo == FlowInfo.DEAD_END) {
191             this.bits |= ASTNode.IsSubRoutineEscaping;
192             this.scope.problemReporter().finallyMustCompleteNormally(this.finallyBlock);
193         }
194         this.subRoutineInits = subInfo;
195         // process the try block in a context handling the local exceptions.
196
ExceptionHandlingFlowContext handlingContext =
197             new ExceptionHandlingFlowContext(
198                 insideSubContext,
199                 this,
200                 this.caughtExceptionTypes,
201                 this.scope,
202                 flowInfo.unconditionalInits());
203         handlingContext.initsOnFinally =
204             new NullInfoRegistry(flowInfo.unconditionalInits());
205         // only try blocks initialize that member - may consider creating a
206
// separate class if needed
207

208         FlowInfo tryInfo;
209         if (this.tryBlock.isEmptyBlock()) {
210             tryInfo = flowInfo;
211         } else {
212             tryInfo = this.tryBlock.analyseCode(currentScope, handlingContext, flowInfo.copy());
213             if ((tryInfo.tagBits & FlowInfo.UNREACHABLE) != 0)
214                 this.bits |= ASTNode.IsTryBlockExiting;
215         }
216
217         // check unreachable catch blocks
218
handlingContext.complainIfUnusedExceptionHandlers(this.scope, this);
219
220         // process the catch blocks - computing the minimal exit depth amongst try/catch
221
if (this.catchArguments != null) {
222             int catchCount;
223             this.catchExits = new boolean[catchCount = this.catchBlocks.length];
224             this.catchExitInitStateIndexes = new int[catchCount];
225             for (int i = 0; i < catchCount; i++) {
226                 // keep track of the inits that could potentially have led to this exception handler (for final assignments diagnosis)
227
FlowInfo catchInfo;
228                 if (this.caughtExceptionTypes[i].isUncheckedException(true)) {
229                     catchInfo =
230                         handlingContext.initsOnFinally.mitigateNullInfoOf(
231                             flowInfo.unconditionalCopy().
232                                 addPotentialInitializationsFrom(
233                                     handlingContext.initsOnException(
234                                         this.caughtExceptionTypes[i])).
235                                 addPotentialInitializationsFrom(tryInfo).
236                                 addPotentialInitializationsFrom(
237                                     handlingContext.initsOnReturn));
238                 }else {
239                     catchInfo =
240                         flowInfo.unconditionalCopy()
241                             .addPotentialInitializationsFrom(
242                                 handlingContext.initsOnException(
243                                     this.caughtExceptionTypes[i]))
244                                     .addPotentialInitializationsFrom(
245                                 tryInfo.nullInfoLessUnconditionalCopy())
246                                 // remove null info to protect point of
247
// exception null info
248
.addPotentialInitializationsFrom(
249                                     handlingContext.initsOnReturn.
250                                     nullInfoLessUnconditionalCopy());
251                 }
252
253                 // catch var is always set
254
LocalVariableBinding catchArg = this.catchArguments[i].binding;
255                 catchInfo.markAsDefinitelyAssigned(catchArg);
256                 catchInfo.markAsDefinitelyNonNull(catchArg);
257                 /*
258                 "If we are about to consider an unchecked exception handler, potential inits may have occured inside
259                 the try block that need to be detected , e.g.
260                 try { x = 1; throwSomething();} catch(Exception e){ x = 2} "
261                 "(uncheckedExceptionTypes notNil and: [uncheckedExceptionTypes at: index])
262                 ifTrue: [catchInits addPotentialInitializationsFrom: tryInits]."
263                 */

264                 if (this.tryBlock.statements == null) {
265                     catchInfo.setReachMode(FlowInfo.UNREACHABLE);
266                 }
267                 catchInfo =
268                     this.catchBlocks[i].analyseCode(
269                         currentScope,
270                         insideSubContext,
271                         catchInfo);
272                 this.catchExitInitStateIndexes[i] = currentScope.methodScope().recordInitializationStates(catchInfo);
273                 this.catchExits[i] =
274                     (catchInfo.tagBits & FlowInfo.UNREACHABLE) != 0;
275                 tryInfo = tryInfo.mergedWith(catchInfo.unconditionalInits());
276             }
277         }
278         // we also need to check potential multiple assignments of final variables inside the finally block
279
// need to include potential inits from returns inside the try/catch parts - 1GK2AOF
280
finallyContext.complainOnDeferredChecks(
281             handlingContext.initsOnFinally.mitigateNullInfoOf(
282                 (tryInfo.tagBits & FlowInfo.UNREACHABLE) == 0 ?
283                     flowInfo.unconditionalCopy().
284                     addPotentialInitializationsFrom(tryInfo).
285                         // lighten the influence of the try block, which may have
286
// exited at any point
287
addPotentialInitializationsFrom(insideSubContext.initsOnReturn) :
288                     insideSubContext.initsOnReturn),
289             currentScope);
290
291         // chain up null info registry
292
if (flowContext.initsOnFinally != null) {
293             flowContext.initsOnFinally.add(handlingContext.initsOnFinally);
294         }
295
296         this.naturalExitMergeInitStateIndex =
297             currentScope.methodScope().recordInitializationStates(tryInfo);
298         if (subInfo == FlowInfo.DEAD_END) {
299             this.mergedInitStateIndex =
300                 currentScope.methodScope().recordInitializationStates(subInfo);
301             return subInfo;
302         } else {
303             FlowInfo mergedInfo = tryInfo.addInitializationsFrom(subInfo);
304             this.mergedInitStateIndex =
305                 currentScope.methodScope().recordInitializationStates(mergedInfo);
306             return mergedInfo;
307         }
308     }
309 }
310
311 public ExceptionLabel enterAnyExceptionHandler(CodeStream codeStream) {
312     if (this.subRoutineStartLabel == null)
313         return null;
314     return super.enterAnyExceptionHandler(codeStream);
315 }
316
317 public void enterDeclaredExceptionHandlers(CodeStream codeStream) {
318     for (int i = 0, length = this.declaredExceptionLabels == null ? 0 : this.declaredExceptionLabels.length; i < length; i++) {
319         this.declaredExceptionLabels[i].placeStart();
320     }
321 }
322
323 public void exitAnyExceptionHandler() {
324     if (this.subRoutineStartLabel == null)
325         return;
326     super.exitAnyExceptionHandler();
327 }
328
329 public void exitDeclaredExceptionHandlers(CodeStream codeStream) {
330     for (int i = 0, length = this.declaredExceptionLabels == null ? 0 : this.declaredExceptionLabels.length; i < length; i++) {
331         this.declaredExceptionLabels[i].placeEnd();
332     }
333 }
334
335 private int finallyMode() {
336     if (this.subRoutineStartLabel == null) {
337         return NO_FINALLY;
338     } else if (isSubRoutineEscaping()) {
339         return FINALLY_DOES_NOT_COMPLETE;
340     } else if (scope.compilerOptions().inlineJsrBytecode) {
341         return FINALLY_INLINE;
342     } else {
343         return FINALLY_SUBROUTINE;
344     }
345 }
346 /**
347  * Try statement code generation with or without jsr bytecode use
348  * post 1.5 target level, cannot use jsr bytecode, must instead inline finally block
349  * returnAddress is only allocated if jsr is allowed
350  */

351 public void generateCode(BlockScope currentScope, CodeStream codeStream) {
352     if ((this.bits & ASTNode.IsReachable) == 0) {
353         return;
354     }
355     boolean isStackMapFrameCodeStream = codeStream instanceof StackMapFrameCodeStream;
356     // in case the labels needs to be reinitialized
357
// when the code generation is restarted in wide mode
358
this.anyExceptionLabel = null;
359     this.reusableJSRTargets = null;
360     this.reusableJSRSequenceStartLabels = null;
361     this.reusableJSRTargetsCount = 0;
362
363     int pc = codeStream.position;
364     int finallyMode = finallyMode();
365     
366     boolean requiresNaturalExit = false;
367     // preparing exception labels
368
int maxCatches = this.catchArguments == null ? 0 : this.catchArguments.length;
369     ExceptionLabel[] exceptionLabels;
370     if (maxCatches > 0) {
371         exceptionLabels = new ExceptionLabel[maxCatches];
372         for (int i = 0; i < maxCatches; i++) {
373             ExceptionLabel exceptionLabel = new ExceptionLabel(codeStream, this.catchArguments[i].binding.type);
374             exceptionLabel.placeStart();
375             exceptionLabels[i] = exceptionLabel;
376         }
377     } else {
378         exceptionLabels = null;
379     }
380     if (this.subRoutineStartLabel != null) {
381         this.subRoutineStartLabel.initialize(codeStream);
382         this.enterAnyExceptionHandler(codeStream);
383     }
384     // generate the try block
385
try {
386         this.declaredExceptionLabels = exceptionLabels;
387         this.tryBlock.generateCode(this.scope, codeStream);
388     } finally {
389         this.declaredExceptionLabels = null;
390     }
391     boolean tryBlockHasSomeCode = codeStream.position != pc;
392     // flag telling if some bytecodes were issued inside the try block
393

394     // place end positions of user-defined exception labels
395
if (tryBlockHasSomeCode) {
396         // natural exit may require subroutine invocation (if finally != null)
397
BranchLabel naturalExitLabel = new BranchLabel(codeStream);
398         BranchLabel postCatchesFinallyLabel = null;
399         for (int i = 0; i < maxCatches; i++) {
400             exceptionLabels[i].placeEnd();
401         }
402         if ((this.bits & ASTNode.IsTryBlockExiting) == 0) {
403             int position = codeStream.position;
404             switch(finallyMode) {
405                 case FINALLY_SUBROUTINE :
406                 case FINALLY_INLINE :
407                     requiresNaturalExit = true;
408                     if (this.naturalExitMergeInitStateIndex != -1) {
409                         codeStream.removeNotDefinitelyAssignedVariables(currentScope, this.naturalExitMergeInitStateIndex);
410                         codeStream.addDefinitelyAssignedVariables(currentScope, this.naturalExitMergeInitStateIndex);
411                     }
412                     codeStream.goto_(naturalExitLabel);
413                     break;
414                 case NO_FINALLY :
415                     if (this.naturalExitMergeInitStateIndex != -1) {
416                         codeStream.removeNotDefinitelyAssignedVariables(currentScope, this.naturalExitMergeInitStateIndex);
417                         codeStream.addDefinitelyAssignedVariables(currentScope, this.naturalExitMergeInitStateIndex);
418                     }
419                     codeStream.goto_(naturalExitLabel);
420                     break;
421                 case FINALLY_DOES_NOT_COMPLETE :
422                     codeStream.goto_(this.subRoutineStartLabel);
423                     break;
424             }
425             codeStream.updateLastRecordedEndPC(this.tryBlock.scope, position);
426             //goto is tagged as part of the try block
427
}
428         /* generate sequence of handler, all starting by storing the TOS (exception
429         thrown) into their own catch variables, the one specified in the source
430         that must denote the handled exception.
431         */

432         this.exitAnyExceptionHandler();
433         if (this.catchArguments != null) {
434             postCatchesFinallyLabel = new BranchLabel(codeStream);
435             
436             for (int i = 0; i < maxCatches; i++) {
437                 enterAnyExceptionHandler(codeStream);
438                 // May loose some local variable initializations : affecting the local variable attributes
439
if (this.preTryInitStateIndex != -1) {
440                     codeStream.removeNotDefinitelyAssignedVariables(currentScope, this.preTryInitStateIndex);
441                     codeStream.addDefinitelyAssignedVariables(currentScope, this.preTryInitStateIndex);
442                 }
443                 codeStream.pushOnStack(exceptionLabels[i].exceptionType);
444                 exceptionLabels[i].place();
445                 // optimizing the case where the exception variable is not actually used
446
LocalVariableBinding catchVar;
447                 int varPC = codeStream.position;
448                 if ((catchVar = this.catchArguments[i].binding).resolvedPosition != -1) {
449                     codeStream.store(catchVar, false);
450                     catchVar.recordInitializationStartPC(codeStream.position);
451                     codeStream.addVisibleLocalVariable(catchVar);
452                 } else {
453                     codeStream.pop();
454                 }
455                 codeStream.recordPositionsFrom(varPC, this.catchArguments[i].sourceStart);
456                 // Keep track of the pcs at diverging point for computing the local attribute
457
// since not passing the catchScope, the block generation will exitUserScope(catchScope)
458
this.catchBlocks[i].generateCode(this.scope, codeStream);
459                 this.exitAnyExceptionHandler();
460                 if (!this.catchExits[i]) {
461                     switch(finallyMode) {
462                         case FINALLY_INLINE :
463                             // inlined finally here can see all merged variables
464
if (isStackMapFrameCodeStream) {
465                                 ((StackMapFrameCodeStream) codeStream).pushStateIndex(this.naturalExitMergeInitStateIndex);
466                             }
467                             if (this.catchExitInitStateIndexes[i] != -1) {
468                                 codeStream.removeNotDefinitelyAssignedVariables(currentScope, this.catchExitInitStateIndexes[i]);
469                                 codeStream.addDefinitelyAssignedVariables(currentScope, this.catchExitInitStateIndexes[i]);
470                             }
471                             // entire sequence for finally is associated to finally block
472
this.finallyBlock.generateCode(this.scope, codeStream);
473                             codeStream.goto_(postCatchesFinallyLabel);
474                             if (isStackMapFrameCodeStream) {
475                                 ((StackMapFrameCodeStream) codeStream).popStateIndex();
476                             }
477                             break;
478                         case FINALLY_SUBROUTINE :
479                             requiresNaturalExit = true;
480                             // fall through
481
case NO_FINALLY :
482                             if (this.naturalExitMergeInitStateIndex != -1) {
483                                 codeStream.removeNotDefinitelyAssignedVariables(currentScope, this.naturalExitMergeInitStateIndex);
484                                 codeStream.addDefinitelyAssignedVariables(currentScope, this.naturalExitMergeInitStateIndex);
485                             }
486                             codeStream.goto_(naturalExitLabel);
487                             break;
488                         case FINALLY_DOES_NOT_COMPLETE :
489                             codeStream.goto_(this.subRoutineStartLabel);
490                             break;
491                     }
492                 }
493             }
494         }
495         // extra handler for trailing natural exit (will be fixed up later on when natural exit is generated below)
496
ExceptionLabel naturalExitExceptionHandler = requiresNaturalExit && (finallyMode == FINALLY_SUBROUTINE)
497                     ? new ExceptionLabel(codeStream, null)
498                     : null;
499
500         // addition of a special handler so as to ensure that any uncaught exception (or exception thrown
501
// inside catch blocks) will run the finally block
502
int finallySequenceStartPC = codeStream.position;
503         if (this.subRoutineStartLabel != null) {
504             codeStream.pushOnStack(this.scope.getJavaLangThrowable());
505             if (this.preTryInitStateIndex != -1) {
506                 // reset initialization state, as for a normal catch block
507
codeStream.removeNotDefinitelyAssignedVariables(currentScope, this.preTryInitStateIndex);
508                 codeStream.addDefinitelyAssignedVariables(currentScope, this.preTryInitStateIndex);
509             }
510             this.placeAllAnyExceptionHandler();
511             if (naturalExitExceptionHandler != null) naturalExitExceptionHandler.place();
512             
513             switch(finallyMode) {
514                 case FINALLY_SUBROUTINE :
515                     // any exception handler
516
codeStream.store(this.anyExceptionVariable, false);
517                     codeStream.jsr(this.subRoutineStartLabel);
518                     codeStream.recordPositionsFrom(finallySequenceStartPC, this.finallyBlock.sourceStart);
519                     int position = codeStream.position;
520                     codeStream.throwAnyException(this.anyExceptionVariable);
521                     codeStream.recordPositionsFrom(position, this.finallyBlock.sourceEnd);
522                     // subroutine
523
this.subRoutineStartLabel.place();
524                     codeStream.pushOnStack(this.scope.getJavaLangThrowable());
525                     position = codeStream.position;
526                     codeStream.store(this.returnAddressVariable, false);
527                     codeStream.recordPositionsFrom(position, this.finallyBlock.sourceStart);
528                     this.finallyBlock.generateCode(this.scope, codeStream);
529                     position = codeStream.position;
530                     codeStream.ret(this.returnAddressVariable.resolvedPosition);
531                     codeStream.recordPositionsFrom(
532                         position,
533                         this.finallyBlock.sourceEnd);
534                     // the ret bytecode is part of the subroutine
535
break;
536                 case FINALLY_INLINE :
537                     // any exception handler
538
codeStream.store(this.anyExceptionVariable, false);
539                     codeStream.recordPositionsFrom(finallySequenceStartPC, this.finallyBlock.sourceStart);
540                     // subroutine
541
this.finallyBlock.generateCode(currentScope, codeStream);
542                     position = codeStream.position;
543                     codeStream.throwAnyException(this.anyExceptionVariable);
544                     if (this.preTryInitStateIndex != -1) {
545                         codeStream.removeNotDefinitelyAssignedVariables(currentScope, this.preTryInitStateIndex);
546                     }
547                     this.subRoutineStartLabel.place();
548                     codeStream.recordPositionsFrom(position, this.finallyBlock.sourceEnd);
549                     break;
550                 case FINALLY_DOES_NOT_COMPLETE :
551                     // any exception handler
552
codeStream.pop();
553                     this.subRoutineStartLabel.place();
554                     codeStream.recordPositionsFrom(finallySequenceStartPC, this.finallyBlock.sourceStart);
555                     // subroutine
556
this.finallyBlock.generateCode(this.scope, codeStream);
557                     break;
558             }
559             
560             // will naturally fall into subsequent code after subroutine invocation
561
if (requiresNaturalExit) {
562                 switch(finallyMode) {
563                     case FINALLY_SUBROUTINE :
564                         naturalExitLabel.place();
565                         int position = codeStream.position;
566                         naturalExitExceptionHandler.placeStart();
567                         codeStream.jsr(this.subRoutineStartLabel);
568                         naturalExitExceptionHandler.placeEnd();
569                         codeStream.recordPositionsFrom(
570                             position,
571                             this.finallyBlock.sourceEnd);
572                         break;
573                     case FINALLY_INLINE :
574                         // inlined finally here can see all merged variables
575
if (isStackMapFrameCodeStream) {
576                             ((StackMapFrameCodeStream) codeStream).pushStateIndex(this.naturalExitMergeInitStateIndex);
577                         }
578                         if (this.naturalExitMergeInitStateIndex != -1) {
579                             codeStream.removeNotDefinitelyAssignedVariables(currentScope, this.naturalExitMergeInitStateIndex);
580                             codeStream.addDefinitelyAssignedVariables(currentScope, this.naturalExitMergeInitStateIndex);
581                         }
582                         naturalExitLabel.place();
583                         // entire sequence for finally is associated to finally block
584
this.finallyBlock.generateCode(this.scope, codeStream);
585                         if (postCatchesFinallyLabel != null) {
586                             position = codeStream.position;
587                             // entire sequence for finally is associated to finally block
588
codeStream.goto_(postCatchesFinallyLabel);
589                             codeStream.recordPositionsFrom(
590                                     position,
591                                     this.finallyBlock.sourceEnd);
592                         }
593                         if (isStackMapFrameCodeStream) {
594                             ((StackMapFrameCodeStream) codeStream).popStateIndex();
595                         }
596                         break;
597                     case FINALLY_DOES_NOT_COMPLETE :
598                         break;
599                     default :
600                         naturalExitLabel.place();
601                         break;
602                 }
603             }
604             if (postCatchesFinallyLabel != null) {
605                 postCatchesFinallyLabel.place();
606             }
607         } else {
608             // no subroutine, simply position end label (natural exit == end)
609
naturalExitLabel.place();
610         }
611     } else {
612         // try block had no effect, only generate the body of the finally block if any
613
if (this.subRoutineStartLabel != null) {
614             this.finallyBlock.generateCode(this.scope, codeStream);
615         }
616     }
617     // May loose some local variable initializations : affecting the local variable attributes
618
if (this.mergedInitStateIndex != -1) {
619         codeStream.removeNotDefinitelyAssignedVariables(currentScope, this.mergedInitStateIndex);
620         codeStream.addDefinitelyAssignedVariables(currentScope, this.mergedInitStateIndex);
621     }
622     codeStream.recordPositionsFrom(pc, this.sourceStart);
623 }
624
625 /**
626  * @see SubRoutineStatement#generateSubRoutineInvocation(BlockScope, CodeStream, Object, int, LocalVariableBinding)
627  */

628 public boolean generateSubRoutineInvocation(BlockScope currentScope, CodeStream codeStream, Object JavaDoc targetLocation, int stateIndex, LocalVariableBinding secretLocal) {
629
630     boolean isStackMapFrameCodeStream = codeStream instanceof StackMapFrameCodeStream;
631     int finallyMode = finallyMode();
632     switch(finallyMode) {
633         case FINALLY_DOES_NOT_COMPLETE :
634             codeStream.goto_(this.subRoutineStartLabel);
635             return true;
636
637         case NO_FINALLY :
638             exitDeclaredExceptionHandlers(codeStream);
639             return false;
640     }
641     // optimize subroutine invocation sequences, using the targetLocation (if any)
642
if (targetLocation != null) {
643         boolean reuseTargetLocation = true;
644         if (this.reusableJSRTargetsCount > 0) {
645             nextReusableTarget: for (int i = 0, count = this.reusableJSRTargetsCount; i < count; i++) {
646                 Object JavaDoc reusableJSRTarget = this.reusableJSRTargets[i];
647                 differentTarget: {
648                     if (targetLocation == reusableJSRTarget)
649                         break differentTarget;
650                     if (targetLocation instanceof Constant
651                             && reusableJSRTarget instanceof Constant
652                             && ((Constant)targetLocation).hasSameValue((Constant) reusableJSRTarget)) {
653                         break differentTarget;
654                     }
655                     // cannot reuse current target
656
continue nextReusableTarget;
657                 }
658                 // current target has been used in the past, simply branch to its label
659
if ((this.reusableJSRStateIndexes[i] != stateIndex) && finallyMode == FINALLY_INLINE && isStackMapFrameCodeStream) {
660                     reuseTargetLocation = false;
661                     break nextReusableTarget;
662                 } else {
663                     codeStream.goto_(this.reusableJSRSequenceStartLabels[i]);
664                     return true;
665                 }
666             }
667         } else {
668             this.reusableJSRTargets = new Object JavaDoc[3];
669             this.reusableJSRSequenceStartLabels = new BranchLabel[3];
670             this.reusableJSRStateIndexes = new int[3];
671         }
672         if (reuseTargetLocation) {
673             if (this.reusableJSRTargetsCount == this.reusableJSRTargets.length) {
674                 System.arraycopy(this.reusableJSRTargets, 0, this.reusableJSRTargets = new Object JavaDoc[2*this.reusableJSRTargetsCount], 0, this.reusableJSRTargetsCount);
675                 System.arraycopy(this.reusableJSRSequenceStartLabels, 0, this.reusableJSRSequenceStartLabels = new BranchLabel[2*this.reusableJSRTargetsCount], 0, this.reusableJSRTargetsCount);
676                 System.arraycopy(this.reusableJSRStateIndexes, 0, this.reusableJSRStateIndexes = new int[2*this.reusableJSRTargetsCount], 0, this.reusableJSRTargetsCount);
677             }
678             this.reusableJSRTargets[this.reusableJSRTargetsCount] = targetLocation;
679             BranchLabel reusableJSRSequenceStartLabel = new BranchLabel(codeStream);
680             reusableJSRSequenceStartLabel.place();
681             this.reusableJSRStateIndexes[this.reusableJSRTargetsCount] = stateIndex;
682             this.reusableJSRSequenceStartLabels[this.reusableJSRTargetsCount++] = reusableJSRSequenceStartLabel;
683         }
684     }
685     if (finallyMode == FINALLY_INLINE) {
686         if (isStackMapFrameCodeStream) {
687             ((StackMapFrameCodeStream) codeStream).pushStateIndex(stateIndex);
688             if (this.naturalExitMergeInitStateIndex != -1 || stateIndex != -1) {
689                 // reset initialization state, as for a normal catch block
690
codeStream.removeNotDefinitelyAssignedVariables(currentScope, this.naturalExitMergeInitStateIndex);
691                 codeStream.addDefinitelyAssignedVariables(currentScope, this.naturalExitMergeInitStateIndex);
692             }
693         } else {
694             if (this.naturalExitMergeInitStateIndex != -1) {
695                 // reset initialization state, as for a normal catch block
696
codeStream.removeNotDefinitelyAssignedVariables(currentScope, this.naturalExitMergeInitStateIndex);
697                 codeStream.addDefinitelyAssignedVariables(currentScope, this.naturalExitMergeInitStateIndex);
698             }
699         }
700         if (secretLocal != null) {
701             codeStream.addVariable(secretLocal);
702         }
703         // cannot use jsr bytecode, then simply inline the subroutine
704
// inside try block, ensure to deactivate all catch block exception handlers while inlining finally block
705
exitAnyExceptionHandler();
706         exitDeclaredExceptionHandlers(codeStream);
707         this.finallyBlock.generateCode(currentScope, codeStream);
708         if (isStackMapFrameCodeStream) {
709             ((StackMapFrameCodeStream) codeStream).popStateIndex();
710         }
711     } else {
712         // classic subroutine invocation, distinguish case of non-returning subroutine
713
codeStream.jsr(this.subRoutineStartLabel);
714         exitAnyExceptionHandler();
715         exitDeclaredExceptionHandlers(codeStream);
716     }
717     return false;
718 }
719 public boolean isSubRoutineEscaping() {
720     return (this.bits & ASTNode.IsSubRoutineEscaping) != 0;
721 }
722
723 public StringBuffer JavaDoc printStatement(int indent, StringBuffer JavaDoc output) {
724     printIndent(indent, output).append("try \n"); //$NON-NLS-1$
725
this.tryBlock.printStatement(indent + 1, output);
726
727     //catches
728
if (this.catchBlocks != null)
729         for (int i = 0; i < this.catchBlocks.length; i++) {
730                 output.append('\n');
731                 printIndent(indent, output).append("catch ("); //$NON-NLS-1$
732
this.catchArguments[i].print(0, output).append(") "); //$NON-NLS-1$
733
this.catchBlocks[i].printStatement(indent + 1, output);
734         }
735     //finally
736
if (this.finallyBlock != null) {
737         output.append('\n');
738         printIndent(indent, output).append("finally\n"); //$NON-NLS-1$
739
this.finallyBlock.printStatement(indent + 1, output);
740     }
741     return output;
742 }
743
744 public void resolve(BlockScope upperScope) {
745     // special scope for secret locals optimization.
746
this.scope = new BlockScope(upperScope);
747
748     BlockScope tryScope = new BlockScope(this.scope);
749     BlockScope finallyScope = null;
750     
751     if (this.finallyBlock != null) {
752         if (this.finallyBlock.isEmptyBlock()) {
753             if ((this.finallyBlock.bits & ASTNode.UndocumentedEmptyBlock) != 0) {
754                 this.scope.problemReporter().undocumentedEmptyBlock(this.finallyBlock.sourceStart, this.finallyBlock.sourceEnd);
755             }
756         } else {
757             finallyScope = new BlockScope(this.scope, false); // don't add it yet to parent scope
758

759             // provision for returning and forcing the finally block to run
760
MethodScope methodScope = this.scope.methodScope();
761
762             // the type does not matter as long as it is not a base type
763
if (!upperScope.compilerOptions().inlineJsrBytecode) {
764                 this.returnAddressVariable =
765                     new LocalVariableBinding(TryStatement.SECRET_RETURN_ADDRESS_NAME, upperScope.getJavaLangObject(), ClassFileConstants.AccDefault, false);
766                 finallyScope.addLocalVariable(this.returnAddressVariable);
767                 this.returnAddressVariable.setConstant(Constant.NotAConstant); // not inlinable
768
}
769             this.subRoutineStartLabel = new BranchLabel();
770
771             this.anyExceptionVariable =
772                 new LocalVariableBinding(TryStatement.SECRET_ANY_HANDLER_NAME, this.scope.getJavaLangThrowable(), ClassFileConstants.AccDefault, false);
773             finallyScope.addLocalVariable(this.anyExceptionVariable);
774             this.anyExceptionVariable.setConstant(Constant.NotAConstant); // not inlinable
775

776             if (!methodScope.isInsideInitializer()) {
777                 MethodBinding methodBinding =
778                     ((AbstractMethodDeclaration) methodScope.referenceContext).binding;
779                 if (methodBinding != null) {
780                     TypeBinding methodReturnType = methodBinding.returnType;
781                     if (methodReturnType.id != TypeIds.T_void) {
782                         this.secretReturnValue =
783                             new LocalVariableBinding(
784                                 TryStatement.SECRET_RETURN_VALUE_NAME,
785                                 methodReturnType,
786                                 ClassFileConstants.AccDefault,
787                                 false);
788                         finallyScope.addLocalVariable(this.secretReturnValue);
789                         this.secretReturnValue.setConstant(Constant.NotAConstant); // not inlinable
790
}
791                 }
792             }
793             this.finallyBlock.resolveUsing(finallyScope);
794             // force the finally scope to have variable positions shifted after its try scope and catch ones
795
finallyScope.shiftScopes = new BlockScope[this.catchArguments == null ? 1 : this.catchArguments.length+1];
796             finallyScope.shiftScopes[0] = tryScope;
797         }
798     }
799     this.tryBlock.resolveUsing(tryScope);
800
801     // arguments type are checked against JavaLangThrowable in resolveForCatch(..)
802
if (this.catchBlocks != null) {
803         int length = this.catchArguments.length;
804         TypeBinding[] argumentTypes = new TypeBinding[length];
805         boolean catchHasError = false;
806         for (int i = 0; i < length; i++) {
807             BlockScope catchScope = new BlockScope(this.scope);
808             if (finallyScope != null){
809                 finallyScope.shiftScopes[i+1] = catchScope;
810             }
811             // side effect on catchScope in resolveForCatch(..)
812
if ((argumentTypes[i] = this.catchArguments[i].resolveForCatch(catchScope)) == null) {
813                 catchHasError = true;
814             }
815             this.catchBlocks[i].resolveUsing(catchScope);
816         }
817         if (catchHasError) {
818             return;
819         }
820         // Verify that the catch clause are ordered in the right way:
821
// more specialized first.
822
this.caughtExceptionTypes = new ReferenceBinding[length];
823         for (int i = 0; i < length; i++) {
824             this.caughtExceptionTypes[i] = (ReferenceBinding) argumentTypes[i];
825             for (int j = 0; j < i; j++) {
826                 if (this.caughtExceptionTypes[i].isCompatibleWith(argumentTypes[j])) {
827                     this.scope.problemReporter().wrongSequenceOfExceptionTypesError(this, this.caughtExceptionTypes[i], i, argumentTypes[j]);
828                 }
829             }
830         }
831     } else {
832         this.caughtExceptionTypes = new ReferenceBinding[0];
833     }
834     
835     if (finallyScope != null){
836         // add finallyScope as last subscope, so it can be shifted behind try/catch subscopes.
837
// the shifting is necessary to achieve no overlay in between the finally scope and its
838
// sibling in term of local variable positions.
839
this.scope.addSubscope(finallyScope);
840     }
841 }
842
843 public void traverse(ASTVisitor visitor, BlockScope blockScope) {
844     if (visitor.visit(this, blockScope)) {
845         this.tryBlock.traverse(visitor, this.scope);
846         if (this.catchArguments != null) {
847             for (int i = 0, max = this.catchBlocks.length; i < max; i++) {
848                 this.catchArguments[i].traverse(visitor, this.scope);
849                 this.catchBlocks[i].traverse(visitor, this.scope);
850             }
851         }
852         if (this.finallyBlock != null)
853             this.finallyBlock.traverse(visitor, this.scope);
854     }
855     visitor.endVisit(this, blockScope);
856 }
857 }
858
Popular Tags