1 /*******************************************************************************
2 * Copyright (c) 2000, 2001, 2002 International Business Machines Corp. and others.
3 * All rights reserved. This program and the accompanying materials
4 * are made available under the terms of the Common Public License v0.5
5 * which accompanies this distribution, and is available at
6 * http://www.eclipse.org/legal/cpl-v05.html
9 * IBM Corporation - initial API and implementation
10 ******************************************************************************/
11 package net.sourceforge.phpdt.internal.compiler.ast;
13 import net.sourceforge.phpdt.internal.compiler.IAbstractSyntaxTreeVisitor;
14 import net.sourceforge.phpdt.internal.compiler.codegen.CodeStream;
15 import net.sourceforge.phpdt.internal.compiler.codegen.ExceptionLabel;
16 import net.sourceforge.phpdt.internal.compiler.codegen.Label;
17 import net.sourceforge.phpdt.internal.compiler.flow.ExceptionHandlingFlowContext;
18 import net.sourceforge.phpdt.internal.compiler.flow.FinallyFlowContext;
19 import net.sourceforge.phpdt.internal.compiler.flow.FlowContext;
20 import net.sourceforge.phpdt.internal.compiler.flow.FlowInfo;
21 import net.sourceforge.phpdt.internal.compiler.flow.InsideSubRoutineFlowContext;
22 import net.sourceforge.phpdt.internal.compiler.flow.UnconditionalFlowInfo;
23 import net.sourceforge.phpdt.internal.compiler.lookup.BlockScope;
24 import net.sourceforge.phpdt.internal.compiler.lookup.LocalVariableBinding;
25 import net.sourceforge.phpdt.internal.compiler.lookup.MethodBinding;
26 import net.sourceforge.phpdt.internal.compiler.lookup.MethodScope;
27 import net.sourceforge.phpdt.internal.compiler.lookup.ReferenceBinding;
28 import net.sourceforge.phpdt.internal.compiler.lookup.TypeBinding;
30 public class TryStatement extends Statement {
32 public Block tryBlock;
33 public Block[] catchBlocks;
34 public Argument[] catchArguments;
35 public Block finallyBlock;
38 public boolean subRoutineCannotReturn = true;
39 // should rename into subRoutineComplete to be set to false by default
41 ReferenceBinding[] caughtExceptionTypes;
44 public int[] preserveExceptionHandler;
46 Label subRoutineStartLabel;
47 public LocalVariableBinding anyExceptionVariable,
48 returnAddressVariable,
51 public final static char[] SecretReturnName = " returnAddress".toCharArray(); //$NON-NLS-1$
52 public final static char[] SecretAnyHandlerName = " anyExceptionHandler".toCharArray(); //$NON-NLS-1$
53 public static final char[] SecretLocalDeclarationName = " returnValue".toCharArray(); //$NON-NLS-1$
55 // for local variables table attributes
56 int preTryInitStateIndex = -1;
57 int mergedInitStateIndex = -1;
59 public FlowInfo analyseCode(
60 BlockScope currentScope,
61 FlowContext flowContext,
64 // Consider the try block and catch block so as to compute the intersection of initializations and
65 // the minimum exit relative depth amongst all of them. Then consider the subroutine, and append its
66 // initialization to the try/catch ones, if the subroutine completes normally. If the subroutine does not
67 // complete, then only keep this result for the rest of the analysis
69 // process the finally block (subroutine) - create a context for the subroutine
71 preTryInitStateIndex =
72 currentScope.methodScope().recordInitializationStates(flowInfo);
74 if (anyExceptionVariable != null) {
75 anyExceptionVariable.used = true;
77 if (returnAddressVariable != null) {
78 returnAddressVariable.used = true;
80 InsideSubRoutineFlowContext insideSubContext;
81 FinallyFlowContext finallyContext;
82 UnconditionalFlowInfo subInfo;
83 if (subRoutineStartLabel == null) {
85 insideSubContext = null;
86 finallyContext = null;
89 // analyse finally block first
90 insideSubContext = new InsideSubRoutineFlowContext(flowContext, this);
95 finallyContext = new FinallyFlowContext(flowContext, finallyBlock),
97 .unconditionalInits();
98 if (!((subInfo == FlowInfo.DeadEnd) || subInfo.isFakeReachable())) {
99 subRoutineCannotReturn = false;
102 // process the try block in a context handling the local exceptions.
103 ExceptionHandlingFlowContext handlingContext =
104 new ExceptionHandlingFlowContext(
105 insideSubContext == null ? flowContext : insideSubContext,
107 caughtExceptionTypes,
109 flowInfo.unconditionalInits());
112 if (tryBlock.statements == null) {
114 tryBlockExit = false;
116 tryInfo = tryBlock.analyseCode(currentScope, handlingContext, flowInfo.copy());
117 tryBlockExit = (tryInfo == FlowInfo.DeadEnd) || tryInfo.isFakeReachable();
120 // check unreachable catch blocks
121 handlingContext.complainIfUnusedExceptionHandlers(catchBlocks, scope, this);
123 // process the catch blocks - computing the minimal exit depth amongst try/catch
124 if (catchArguments != null) {
126 catchExits = new boolean[catchCount = catchBlocks.length];
127 for (int i = 0; i < catchCount; i++) {
128 // keep track of the inits that could potentially have led to this exception handler (for final assignments diagnosis)
133 .unconditionalInits()
134 .addPotentialInitializationsFrom(
135 handlingContext.initsOnException(caughtExceptionTypes[i]).unconditionalInits())
136 .addPotentialInitializationsFrom(tryInfo.unconditionalInits())
137 .addPotentialInitializationsFrom(handlingContext.initsOnReturn);
139 // catch var is always set
140 catchInfo.markAsDefinitelyAssigned(catchArguments[i].binding);
142 "If we are about to consider an unchecked exception handler, potential inits may have occured inside
143 the try block that need to be detected , e.g.
144 try { x = 1; throwSomething();} catch(Exception e){ x = 2} "
145 "(uncheckedExceptionTypes notNil and: [uncheckedExceptionTypes at: index])
146 ifTrue: [catchInits addPotentialInitializationsFrom: tryInits]."
148 if (tryBlock.statements == null) {
149 catchInfo.markAsFakeReachable(true);
152 catchBlocks[i].analyseCode(
154 insideSubContext == null ? flowContext : insideSubContext,
157 ((catchInfo == FlowInfo.DeadEnd) || catchInfo.isFakeReachable());
158 tryInfo = tryInfo.mergedWith(catchInfo.unconditionalInits());
161 if (subRoutineStartLabel == null) {
162 mergedInitStateIndex =
163 currentScope.methodScope().recordInitializationStates(tryInfo);
167 // we also need to check potential multiple assignments of final variables inside the finally block
168 // need to include potential inits from returns inside the try/catch parts - 1GK2AOF
169 tryInfo.addPotentialInitializationsFrom(insideSubContext.initsOnReturn);
170 finallyContext.complainOnRedundantFinalAssignments(tryInfo, currentScope);
171 if (subInfo == FlowInfo.DeadEnd) {
172 mergedInitStateIndex =
173 currentScope.methodScope().recordInitializationStates(subInfo);
176 FlowInfo mergedInfo = tryInfo.addInitializationsFrom(subInfo);
177 mergedInitStateIndex =
178 currentScope.methodScope().recordInitializationStates(mergedInfo);
183 public boolean cannotReturn() {
185 return subRoutineCannotReturn;
189 * Try statement code generation
192 public void generateCode(BlockScope currentScope, CodeStream codeStream) {
194 if ((bits & IsReachableMASK) == 0) {
197 if (tryBlock.isEmptyBlock()) {
198 if (subRoutineStartLabel != null) {
199 // since not passing the finallyScope, the block generation will exitUserScope(finallyScope)
200 finallyBlock.generateCode(scope, codeStream);
202 // May loose some local variable initializations : affecting the local variable attributes
203 if (mergedInitStateIndex != -1) {
204 codeStream.removeNotDefinitelyAssignedVariables(
206 mergedInitStateIndex);
208 // no local bytecode produced so no need for position remembering
211 int pc = codeStream.position;
212 Label endLabel = new Label(codeStream);
213 boolean requiresNaturalJsr = false;
215 // preparing exception labels
217 ExceptionLabel[] exceptionLabels =
218 new ExceptionLabel[maxCatches =
219 catchArguments == null ? 0 : catchArguments.length];
220 for (int i = 0; i < maxCatches; i++) {
221 boolean preserveCurrentHandler =
222 (preserveExceptionHandler[i
223 / ExceptionHandlingFlowContext.BitCacheSize]
224 & (1 << (i % ExceptionHandlingFlowContext.BitCacheSize)))
226 if (preserveCurrentHandler) {
230 (ReferenceBinding) catchArguments[i].binding.type);
233 ExceptionLabel anyExceptionLabel = null;
234 if (subRoutineStartLabel != null) {
235 subRoutineStartLabel.codeStream = codeStream;
236 anyExceptionLabel = new ExceptionLabel(codeStream, null);
238 // generate the try block
239 tryBlock.generateCode(scope, codeStream);
240 boolean tryBlockHasSomeCode = codeStream.position != pc;
241 // flag telling if some bytecodes were issued inside the try block
243 // natural exit: only if necessary
244 boolean nonReturningSubRoutine =
245 (subRoutineStartLabel != null) && subRoutineCannotReturn;
246 if ((!tryBlockExit) && tryBlockHasSomeCode) {
247 int position = codeStream.position;
248 if (nonReturningSubRoutine) {
249 codeStream.goto_(subRoutineStartLabel);
251 requiresNaturalJsr = true;
252 codeStream.goto_(endLabel);
254 codeStream.updateLastRecordedEndPC(position);
255 //goto is tagged as part of the try block
257 // place end positions of user-defined exception labels
258 if (tryBlockHasSomeCode) {
259 for (int i = 0; i < maxCatches; i++) {
260 boolean preserveCurrentHandler =
261 (preserveExceptionHandler[i
262 / ExceptionHandlingFlowContext.BitCacheSize]
263 & (1 << (i % ExceptionHandlingFlowContext.BitCacheSize)))
265 if (preserveCurrentHandler) {
266 exceptionLabels[i].placeEnd();
269 /* generate sequence of handler, all starting by storing the TOS (exception
270 thrown) into their own catch variables, the one specified in the source
271 that must denote the handled exception.
273 if (catchArguments == null) {
274 if (anyExceptionLabel != null) {
275 anyExceptionLabel.placeEnd();
278 for (int i = 0; i < maxCatches; i++) {
279 boolean preserveCurrentHandler =
280 (preserveExceptionHandler[i
281 / ExceptionHandlingFlowContext.BitCacheSize]
282 & (1 << (i % ExceptionHandlingFlowContext.BitCacheSize)))
284 if (preserveCurrentHandler) {
285 // May loose some local variable initializations : affecting the local variable attributes
286 if (preTryInitStateIndex != -1) {
287 codeStream.removeNotDefinitelyAssignedVariables(
289 preTryInitStateIndex);
291 exceptionLabels[i].place();
292 codeStream.incrStackSize(1);
293 // optimizing the case where the exception variable is not actually used
294 LocalVariableBinding catchVar;
295 int varPC = codeStream.position;
296 if ((catchVar = catchArguments[i].binding).resolvedPosition != -1) {
297 codeStream.store(catchVar, false);
298 catchVar.recordInitializationStartPC(codeStream.position);
299 codeStream.addVisibleLocalVariable(catchVar);
303 codeStream.recordPositionsFrom(varPC, catchArguments[i].sourceStart);
304 // Keep track of the pcs at diverging point for computing the local attribute
305 // since not passing the catchScope, the block generation will exitUserScope(catchScope)
306 catchBlocks[i].generateCode(scope, codeStream);
308 if (i == maxCatches - 1) {
309 if (anyExceptionLabel != null) {
310 anyExceptionLabel.placeEnd();
312 if (subRoutineStartLabel != null) {
313 if (!catchExits[i] && preserveCurrentHandler) {
314 requiresNaturalJsr = true;
315 codeStream.goto_(endLabel);
319 if (!catchExits[i] && preserveCurrentHandler) {
320 if (nonReturningSubRoutine) {
321 codeStream.goto_(subRoutineStartLabel);
323 requiresNaturalJsr = true;
324 codeStream.goto_(endLabel);
330 // addition of a special handler so as to ensure that any uncaught exception (or exception thrown
331 // inside catch blocks) will run the finally block
332 int finallySequenceStartPC = codeStream.position;
333 if (subRoutineStartLabel != null) {
334 // the additional handler is doing: jsr finallyBlock and rethrow TOS-exception
335 anyExceptionLabel.place();
337 if (preTryInitStateIndex != -1) {
338 // reset initialization state, as for a normal catch block
339 codeStream.removeNotDefinitelyAssignedVariables(
341 preTryInitStateIndex);
344 codeStream.incrStackSize(1);
345 if (nonReturningSubRoutine) {
347 // "if subroutine cannot return, no need to jsr/jump to subroutine since it will be entered in sequence
349 codeStream.store(anyExceptionVariable, false);
350 codeStream.jsr(subRoutineStartLabel);
351 codeStream.load(anyExceptionVariable);
355 // end of catch sequence, place label that will correspond to the finally block beginning, or end of statement
357 if (subRoutineStartLabel != null) {
358 if (nonReturningSubRoutine) {
359 requiresNaturalJsr = false;
361 Label veryEndLabel = new Label(codeStream);
362 if (requiresNaturalJsr) {
363 codeStream.jsr(subRoutineStartLabel);
364 codeStream.goto_(veryEndLabel);
366 subRoutineStartLabel.place();
367 if (!nonReturningSubRoutine) {
368 codeStream.incrStackSize(1);
369 codeStream.store(returnAddressVariable, false);
371 codeStream.recordPositionsFrom(
372 finallySequenceStartPC,
373 finallyBlock.sourceStart);
374 // entire sequence for finally is associated to finally block
375 finallyBlock.generateCode(scope, codeStream);
376 if (!nonReturningSubRoutine) {
377 int position = codeStream.position;
378 codeStream.ret(returnAddressVariable.resolvedPosition);
379 codeStream.updateLastRecordedEndPC(position);
380 // the ret bytecode is part of the subroutine
382 if (requiresNaturalJsr) {
383 veryEndLabel.place();
387 // try block had no effect, only generate the body of the finally block if any
388 if (subRoutineStartLabel != null) {
389 finallyBlock.generateCode(scope, codeStream);
392 // May loose some local variable initializations : affecting the local variable attributes
393 if (mergedInitStateIndex != -1) {
394 codeStream.removeNotDefinitelyAssignedVariables(
396 mergedInitStateIndex);
397 codeStream.addDefinitelyAssignedVariables(currentScope, mergedInitStateIndex);
399 codeStream.recordPositionsFrom(pc, this.sourceStart);
402 public void resolve(BlockScope upperScope) {
404 // special scope for secret locals optimization.
405 this.scope = new BlockScope(upperScope);
407 BlockScope tryScope = new BlockScope(scope);
408 BlockScope finallyScope = null;
410 if (finallyBlock != null
411 && finallyBlock.statements != null) {
413 finallyScope = new BlockScope(scope, false); // don't add it yet to parent scope
415 // provision for returning and forcing the finally block to run
416 MethodScope methodScope = scope.methodScope();
418 // the type does not matter as long as its not a normal base type
419 this.returnAddressVariable =
420 new LocalVariableBinding(SecretReturnName, upperScope.getJavaLangObject(), AccDefault, false);
421 finallyScope.addLocalVariable(returnAddressVariable);
422 this.returnAddressVariable.constant = NotAConstant; // not inlinable
423 this.subRoutineStartLabel = new Label();
425 this.anyExceptionVariable =
426 new LocalVariableBinding(SecretAnyHandlerName, scope.getJavaLangThrowable(), AccDefault, false);
427 finallyScope.addLocalVariable(this.anyExceptionVariable);
428 this.anyExceptionVariable.constant = NotAConstant; // not inlinable
430 if (!methodScope.isInsideInitializer()) {
431 MethodBinding methodBinding =
432 ((AbstractMethodDeclaration) methodScope.referenceContext).binding;
433 if (methodBinding != null) {
434 TypeBinding methodReturnType = methodBinding.returnType;
435 if (methodReturnType.id != T_void) {
436 this.secretReturnValue =
437 new LocalVariableBinding(
438 SecretLocalDeclarationName,
442 finallyScope.addLocalVariable(this.secretReturnValue);
443 this.secretReturnValue.constant = NotAConstant; // not inlinable
447 finallyBlock.resolveUsing(finallyScope);
448 // force the finally scope to have variable positions shifted after its try scope and catch ones
449 finallyScope.shiftScopes = new BlockScope[catchArguments == null ? 1 : catchArguments.length+1];
450 finallyScope.shiftScopes[0] = tryScope;
452 this.tryBlock.resolveUsing(tryScope);
454 // arguments type are checked against JavaLangThrowable in resolveForCatch(..)
455 if (this.catchBlocks != null) {
456 int length = this.catchArguments.length;
457 TypeBinding[] argumentTypes = new TypeBinding[length];
458 for (int i = 0; i < length; i++) {
459 BlockScope catchScope = new BlockScope(scope);
460 if (finallyScope != null){
461 finallyScope.shiftScopes[i+1] = catchScope;
463 // side effect on catchScope in resolveForCatch(..)
464 if ((argumentTypes[i] = catchArguments[i].resolveForCatch(catchScope)) == null)
466 catchBlocks[i].resolveUsing(catchScope);
469 // Verify that the catch clause are ordered in the right way:
470 // more specialized first.
471 this.caughtExceptionTypes = new ReferenceBinding[length];
472 for (int i = 0; i < length; i++) {
473 caughtExceptionTypes[i] = (ReferenceBinding) argumentTypes[i];
474 for (int j = 0; j < i; j++) {
475 if (BlockScope.areTypesCompatible(caughtExceptionTypes[i], argumentTypes[j])) {
476 scope.problemReporter().wrongSequenceOfExceptionTypesError(this, i, j);
482 caughtExceptionTypes = new ReferenceBinding[0];
485 if (finallyScope != null){
486 // add finallyScope as last subscope, so it can be shifted behind try/catch subscopes.
487 // the shifting is necessary to achieve no overlay in between the finally scope and its
488 // sibling in term of local variable positions.
489 this.scope.addSubscope(finallyScope);
493 public String toString(int tab) {
494 String s = tabString(tab);
496 s = s + "try "; //$NON-NLS-1$
497 if (tryBlock == Block.None)
498 s = s + "{}"; //$NON-NLS-1$
500 s = s + "\n" + tryBlock.toString(tab + 1); //$NON-NLS-1$
503 if (catchBlocks != null)
504 for (int i = 0; i < catchBlocks.length; i++)
505 s = s + "\n" + tabString(tab) + "catch (" //$NON-NLS-2$ //$NON-NLS-1$
506 +catchArguments[i].toString(0) + ") " //$NON-NLS-1$
507 +catchBlocks[i].toString(tab + 1);
509 if (finallyBlock != null) {
510 if (finallyBlock == Block.None)
511 s = s + "\n" + tabString(tab) + "finally {}"; //$NON-NLS-2$ //$NON-NLS-1$
513 s = s + "\n" + tabString(tab) + "finally\n" + //$NON-NLS-2$ //$NON-NLS-1$
514 finallyBlock.toString(tab + 1);
520 public void traverse(
521 IAbstractSyntaxTreeVisitor visitor,
522 BlockScope blockScope) {
524 if (visitor.visit(this, blockScope)) {
525 tryBlock.traverse(visitor, scope);
526 if (catchArguments != null) {
527 for (int i = 0, max = catchBlocks.length; i < max; i++) {
528 catchArguments[i].traverse(visitor, scope);
529 catchBlocks[i].traverse(visitor, scope);
532 if (finallyBlock != null)
533 finallyBlock.traverse(visitor, scope);
535 visitor.endVisit(this, blockScope);