0001: /*******************************************************************************
0002: * Copyright (c) 2000, 2007 IBM Corporation and others.
0003: * All rights reserved. This program and the accompanying materials
0004: * are made available under the terms of the Eclipse Public License v1.0
0005: * which accompanies this distribution, and is available at
0006: * http://www.eclipse.org/legal/epl-v10.html
0007: *
0008: * Contributors:
0009: * IBM Corporation - initial API and implementation
0010: *******************************************************************************/package org.eclipse.jdt.internal.compiler.ast;
0011:
0012: import org.eclipse.jdt.internal.compiler.ASTVisitor;
0013: import org.eclipse.jdt.internal.compiler.classfmt.ClassFileConstants;
0014: import org.eclipse.jdt.internal.compiler.codegen.*;
0015: import org.eclipse.jdt.internal.compiler.flow.*;
0016: import org.eclipse.jdt.internal.compiler.impl.Constant;
0017: import org.eclipse.jdt.internal.compiler.lookup.*;
0018:
0019: public class TryStatement extends SubRoutineStatement {
0020:
0021: private final static char[] SECRET_RETURN_ADDRESS_NAME = " returnAddress".toCharArray(); //$NON-NLS-1$
0022: private final static char[] SECRET_ANY_HANDLER_NAME = " anyExceptionHandler".toCharArray(); //$NON-NLS-1$
0023: private final static char[] SECRET_RETURN_VALUE_NAME = " returnValue".toCharArray(); //$NON-NLS-1$
0024:
0025: public Block tryBlock;
0026: public Block[] catchBlocks;
0027:
0028: public Argument[] catchArguments;
0029:
0030: // should rename into subRoutineComplete to be set to false by default
0031:
0032: public Block finallyBlock;
0033: BlockScope scope;
0034:
0035: public UnconditionalFlowInfo subRoutineInits;
0036: ReferenceBinding[] caughtExceptionTypes;
0037: boolean[] catchExits;
0038:
0039: BranchLabel subRoutineStartLabel;
0040: public LocalVariableBinding anyExceptionVariable,
0041: returnAddressVariable, secretReturnValue;
0042:
0043: ExceptionLabel[] declaredExceptionLabels; // only set while generating code
0044:
0045: // for inlining/optimizing JSR instructions
0046: private Object[] reusableJSRTargets;
0047: private BranchLabel[] reusableJSRSequenceStartLabels;
0048: private int[] reusableJSRStateIndexes;
0049: private int reusableJSRTargetsCount = 0;
0050:
0051: private final static int NO_FINALLY = 0; // no finally block
0052: private final static int FINALLY_SUBROUTINE = 1; // finally is generated as a subroutine (using jsr/ret bytecodes)
0053: private final static int FINALLY_DOES_NOT_COMPLETE = 2; // non returning finally is optimized with only one instance of finally block
0054: private final static int FINALLY_INLINE = 3; // finally block must be inlined since cannot use jsr/ret bytecodes >1.5
0055:
0056: // for local variables table attributes
0057: int mergedInitStateIndex = -1;
0058: int preTryInitStateIndex = -1;
0059: int naturalExitMergeInitStateIndex = -1;
0060: int[] catchExitInitStateIndexes;
0061:
0062: public FlowInfo analyseCode(BlockScope currentScope,
0063: FlowContext flowContext, FlowInfo flowInfo) {
0064:
0065: // Consider the try block and catch block so as to compute the intersection of initializations and
0066: // the minimum exit relative depth amongst all of them. Then consider the subroutine, and append its
0067: // initialization to the try/catch ones, if the subroutine completes normally. If the subroutine does not
0068: // complete, then only keep this result for the rest of the analysis
0069:
0070: // process the finally block (subroutine) - create a context for the subroutine
0071:
0072: this .preTryInitStateIndex = currentScope.methodScope()
0073: .recordInitializationStates(flowInfo);
0074:
0075: if (this .anyExceptionVariable != null) {
0076: this .anyExceptionVariable.useFlag = LocalVariableBinding.USED;
0077: }
0078: if (this .returnAddressVariable != null) { // TODO (philippe) if subroutine is escaping, unused
0079: this .returnAddressVariable.useFlag = LocalVariableBinding.USED;
0080: }
0081: if (this .subRoutineStartLabel == null) {
0082: // no finally block -- this is a simplified copy of the else part
0083: // process the try block in a context handling the local exceptions.
0084: ExceptionHandlingFlowContext handlingContext = new ExceptionHandlingFlowContext(
0085: flowContext, this , this .caughtExceptionTypes,
0086: this .scope, flowInfo.unconditionalInits());
0087: handlingContext.initsOnFinally = new NullInfoRegistry(
0088: flowInfo.unconditionalInits());
0089: // only try blocks initialize that member - may consider creating a
0090: // separate class if needed
0091:
0092: FlowInfo tryInfo;
0093: if (this .tryBlock.isEmptyBlock()) {
0094: tryInfo = flowInfo;
0095: } else {
0096: tryInfo = this .tryBlock.analyseCode(currentScope,
0097: handlingContext, flowInfo.copy());
0098: if ((tryInfo.tagBits & FlowInfo.UNREACHABLE) != 0)
0099: this .bits |= ASTNode.IsTryBlockExiting;
0100: }
0101:
0102: // check unreachable catch blocks
0103: handlingContext.complainIfUnusedExceptionHandlers(
0104: this .scope, this );
0105:
0106: // process the catch blocks - computing the minimal exit depth amongst try/catch
0107: if (this .catchArguments != null) {
0108: int catchCount;
0109: this .catchExits = new boolean[catchCount = this .catchBlocks.length];
0110: this .catchExitInitStateIndexes = new int[catchCount];
0111: for (int i = 0; i < catchCount; i++) {
0112: // keep track of the inits that could potentially have led to this exception handler (for final assignments diagnosis)
0113: FlowInfo catchInfo;
0114: if (this .caughtExceptionTypes[i]
0115: .isUncheckedException(true)) {
0116: catchInfo = handlingContext.initsOnFinally
0117: .mitigateNullInfoOf(flowInfo
0118: .unconditionalCopy()
0119: .addPotentialInitializationsFrom(
0120: handlingContext
0121: .initsOnException(this .caughtExceptionTypes[i]))
0122: .addPotentialInitializationsFrom(
0123: tryInfo)
0124: .addPotentialInitializationsFrom(
0125: handlingContext.initsOnReturn));
0126: } else {
0127: catchInfo = flowInfo
0128: .unconditionalCopy()
0129: .addPotentialInitializationsFrom(
0130: handlingContext
0131: .initsOnException(this .caughtExceptionTypes[i]))
0132: .addPotentialInitializationsFrom(
0133: tryInfo
0134: .nullInfoLessUnconditionalCopy())
0135: // remove null info to protect point of
0136: // exception null info
0137: .addPotentialInitializationsFrom(
0138: handlingContext.initsOnReturn
0139: .nullInfoLessUnconditionalCopy());
0140: }
0141:
0142: // catch var is always set
0143: LocalVariableBinding catchArg = this .catchArguments[i].binding;
0144: catchInfo.markAsDefinitelyAssigned(catchArg);
0145: catchInfo.markAsDefinitelyNonNull(catchArg);
0146: /*
0147: "If we are about to consider an unchecked exception handler, potential inits may have occured inside
0148: the try block that need to be detected , e.g.
0149: try { x = 1; throwSomething();} catch(Exception e){ x = 2} "
0150: "(uncheckedExceptionTypes notNil and: [uncheckedExceptionTypes at: index])
0151: ifTrue: [catchInits addPotentialInitializationsFrom: tryInits]."
0152: */
0153: if (this .tryBlock.statements == null) {
0154: catchInfo.setReachMode(FlowInfo.UNREACHABLE);
0155: }
0156: catchInfo = this .catchBlocks[i].analyseCode(
0157: currentScope, flowContext, catchInfo);
0158: this .catchExitInitStateIndexes[i] = currentScope
0159: .methodScope().recordInitializationStates(
0160: catchInfo);
0161: this .catchExits[i] = (catchInfo.tagBits & FlowInfo.UNREACHABLE) != 0;
0162: tryInfo = tryInfo.mergedWith(catchInfo
0163: .unconditionalInits());
0164: }
0165: }
0166: this .mergedInitStateIndex = currentScope.methodScope()
0167: .recordInitializationStates(tryInfo);
0168:
0169: // chain up null info registry
0170: if (flowContext.initsOnFinally != null) {
0171: flowContext.initsOnFinally
0172: .add(handlingContext.initsOnFinally);
0173: }
0174:
0175: return tryInfo;
0176: } else {
0177: InsideSubRoutineFlowContext insideSubContext;
0178: FinallyFlowContext finallyContext;
0179: UnconditionalFlowInfo subInfo;
0180: // analyse finally block first
0181: insideSubContext = new InsideSubRoutineFlowContext(
0182: flowContext, this );
0183:
0184: subInfo = this .finallyBlock.analyseCode(
0185: currentScope,
0186: finallyContext = new FinallyFlowContext(
0187: flowContext, this .finallyBlock),
0188: flowInfo.nullInfoLessUnconditionalCopy())
0189: .unconditionalInits();
0190: if (subInfo == FlowInfo.DEAD_END) {
0191: this .bits |= ASTNode.IsSubRoutineEscaping;
0192: this .scope.problemReporter()
0193: .finallyMustCompleteNormally(this .finallyBlock);
0194: }
0195: this .subRoutineInits = subInfo;
0196: // process the try block in a context handling the local exceptions.
0197: ExceptionHandlingFlowContext handlingContext = new ExceptionHandlingFlowContext(
0198: insideSubContext, this , this .caughtExceptionTypes,
0199: this .scope, flowInfo.unconditionalInits());
0200: handlingContext.initsOnFinally = new NullInfoRegistry(
0201: flowInfo.unconditionalInits());
0202: // only try blocks initialize that member - may consider creating a
0203: // separate class if needed
0204:
0205: FlowInfo tryInfo;
0206: if (this .tryBlock.isEmptyBlock()) {
0207: tryInfo = flowInfo;
0208: } else {
0209: tryInfo = this .tryBlock.analyseCode(currentScope,
0210: handlingContext, flowInfo.copy());
0211: if ((tryInfo.tagBits & FlowInfo.UNREACHABLE) != 0)
0212: this .bits |= ASTNode.IsTryBlockExiting;
0213: }
0214:
0215: // check unreachable catch blocks
0216: handlingContext.complainIfUnusedExceptionHandlers(
0217: this .scope, this );
0218:
0219: // process the catch blocks - computing the minimal exit depth amongst try/catch
0220: if (this .catchArguments != null) {
0221: int catchCount;
0222: this .catchExits = new boolean[catchCount = this .catchBlocks.length];
0223: this .catchExitInitStateIndexes = new int[catchCount];
0224: for (int i = 0; i < catchCount; i++) {
0225: // keep track of the inits that could potentially have led to this exception handler (for final assignments diagnosis)
0226: FlowInfo catchInfo;
0227: if (this .caughtExceptionTypes[i]
0228: .isUncheckedException(true)) {
0229: catchInfo = handlingContext.initsOnFinally
0230: .mitigateNullInfoOf(flowInfo
0231: .unconditionalCopy()
0232: .addPotentialInitializationsFrom(
0233: handlingContext
0234: .initsOnException(this .caughtExceptionTypes[i]))
0235: .addPotentialInitializationsFrom(
0236: tryInfo)
0237: .addPotentialInitializationsFrom(
0238: handlingContext.initsOnReturn));
0239: } else {
0240: catchInfo = flowInfo
0241: .unconditionalCopy()
0242: .addPotentialInitializationsFrom(
0243: handlingContext
0244: .initsOnException(this .caughtExceptionTypes[i]))
0245: .addPotentialInitializationsFrom(
0246: tryInfo
0247: .nullInfoLessUnconditionalCopy())
0248: // remove null info to protect point of
0249: // exception null info
0250: .addPotentialInitializationsFrom(
0251: handlingContext.initsOnReturn
0252: .nullInfoLessUnconditionalCopy());
0253: }
0254:
0255: // catch var is always set
0256: LocalVariableBinding catchArg = this .catchArguments[i].binding;
0257: catchInfo.markAsDefinitelyAssigned(catchArg);
0258: catchInfo.markAsDefinitelyNonNull(catchArg);
0259: /*
0260: "If we are about to consider an unchecked exception handler, potential inits may have occured inside
0261: the try block that need to be detected , e.g.
0262: try { x = 1; throwSomething();} catch(Exception e){ x = 2} "
0263: "(uncheckedExceptionTypes notNil and: [uncheckedExceptionTypes at: index])
0264: ifTrue: [catchInits addPotentialInitializationsFrom: tryInits]."
0265: */
0266: if (this .tryBlock.statements == null) {
0267: catchInfo.setReachMode(FlowInfo.UNREACHABLE);
0268: }
0269: catchInfo = this .catchBlocks[i].analyseCode(
0270: currentScope, insideSubContext, catchInfo);
0271: this .catchExitInitStateIndexes[i] = currentScope
0272: .methodScope().recordInitializationStates(
0273: catchInfo);
0274: this .catchExits[i] = (catchInfo.tagBits & FlowInfo.UNREACHABLE) != 0;
0275: tryInfo = tryInfo.mergedWith(catchInfo
0276: .unconditionalInits());
0277: }
0278: }
0279: // we also need to check potential multiple assignments of final variables inside the finally block
0280: // need to include potential inits from returns inside the try/catch parts - 1GK2AOF
0281: finallyContext
0282: .complainOnDeferredChecks(
0283: handlingContext.initsOnFinally
0284: .mitigateNullInfoOf((tryInfo.tagBits & FlowInfo.UNREACHABLE) == 0 ? flowInfo
0285: .unconditionalCopy()
0286: .addPotentialInitializationsFrom(
0287: tryInfo).
0288: // lighten the influence of the try block, which may have
0289: // exited at any point
0290: addPotentialInitializationsFrom(
0291: insideSubContext.initsOnReturn)
0292: : insideSubContext.initsOnReturn),
0293: currentScope);
0294:
0295: // chain up null info registry
0296: if (flowContext.initsOnFinally != null) {
0297: flowContext.initsOnFinally
0298: .add(handlingContext.initsOnFinally);
0299: }
0300:
0301: this .naturalExitMergeInitStateIndex = currentScope
0302: .methodScope().recordInitializationStates(tryInfo);
0303: if (subInfo == FlowInfo.DEAD_END) {
0304: this .mergedInitStateIndex = currentScope.methodScope()
0305: .recordInitializationStates(subInfo);
0306: return subInfo;
0307: } else {
0308: FlowInfo mergedInfo = tryInfo
0309: .addInitializationsFrom(subInfo);
0310: this .mergedInitStateIndex = currentScope.methodScope()
0311: .recordInitializationStates(mergedInfo);
0312: return mergedInfo;
0313: }
0314: }
0315: }
0316:
0317: public ExceptionLabel enterAnyExceptionHandler(CodeStream codeStream) {
0318: if (this .subRoutineStartLabel == null)
0319: return null;
0320: return super .enterAnyExceptionHandler(codeStream);
0321: }
0322:
0323: public void enterDeclaredExceptionHandlers(CodeStream codeStream) {
0324: for (int i = 0, length = this .declaredExceptionLabels == null ? 0
0325: : this .declaredExceptionLabels.length; i < length; i++) {
0326: this .declaredExceptionLabels[i].placeStart();
0327: }
0328: }
0329:
0330: public void exitAnyExceptionHandler() {
0331: if (this .subRoutineStartLabel == null)
0332: return;
0333: super .exitAnyExceptionHandler();
0334: }
0335:
0336: public void exitDeclaredExceptionHandlers(CodeStream codeStream) {
0337: for (int i = 0, length = this .declaredExceptionLabels == null ? 0
0338: : this .declaredExceptionLabels.length; i < length; i++) {
0339: this .declaredExceptionLabels[i].placeEnd();
0340: }
0341: }
0342:
0343: private int finallyMode() {
0344: if (this .subRoutineStartLabel == null) {
0345: return NO_FINALLY;
0346: } else if (isSubRoutineEscaping()) {
0347: return FINALLY_DOES_NOT_COMPLETE;
0348: } else if (scope.compilerOptions().inlineJsrBytecode) {
0349: return FINALLY_INLINE;
0350: } else {
0351: return FINALLY_SUBROUTINE;
0352: }
0353: }
0354:
0355: /**
0356: * Try statement code generation with or without jsr bytecode use
0357: * post 1.5 target level, cannot use jsr bytecode, must instead inline finally block
0358: * returnAddress is only allocated if jsr is allowed
0359: */
0360: public void generateCode(BlockScope currentScope,
0361: CodeStream codeStream) {
0362: if ((this .bits & ASTNode.IsReachable) == 0) {
0363: return;
0364: }
0365: boolean isStackMapFrameCodeStream = codeStream instanceof StackMapFrameCodeStream;
0366: // in case the labels needs to be reinitialized
0367: // when the code generation is restarted in wide mode
0368: this .anyExceptionLabel = null;
0369: this .reusableJSRTargets = null;
0370: this .reusableJSRSequenceStartLabels = null;
0371: this .reusableJSRTargetsCount = 0;
0372:
0373: int pc = codeStream.position;
0374: int finallyMode = finallyMode();
0375:
0376: boolean requiresNaturalExit = false;
0377: // preparing exception labels
0378: int maxCatches = this .catchArguments == null ? 0
0379: : this .catchArguments.length;
0380: ExceptionLabel[] exceptionLabels;
0381: if (maxCatches > 0) {
0382: exceptionLabels = new ExceptionLabel[maxCatches];
0383: for (int i = 0; i < maxCatches; i++) {
0384: ExceptionLabel exceptionLabel = new ExceptionLabel(
0385: codeStream, this .catchArguments[i].binding.type);
0386: exceptionLabel.placeStart();
0387: exceptionLabels[i] = exceptionLabel;
0388: }
0389: } else {
0390: exceptionLabels = null;
0391: }
0392: if (this .subRoutineStartLabel != null) {
0393: this .subRoutineStartLabel.initialize(codeStream);
0394: this .enterAnyExceptionHandler(codeStream);
0395: }
0396: // generate the try block
0397: try {
0398: this .declaredExceptionLabels = exceptionLabels;
0399: this .tryBlock.generateCode(this .scope, codeStream);
0400: } finally {
0401: this .declaredExceptionLabels = null;
0402: }
0403: boolean tryBlockHasSomeCode = codeStream.position != pc;
0404: // flag telling if some bytecodes were issued inside the try block
0405:
0406: // place end positions of user-defined exception labels
0407: if (tryBlockHasSomeCode) {
0408: // natural exit may require subroutine invocation (if finally != null)
0409: BranchLabel naturalExitLabel = new BranchLabel(codeStream);
0410: BranchLabel postCatchesFinallyLabel = null;
0411: for (int i = 0; i < maxCatches; i++) {
0412: exceptionLabels[i].placeEnd();
0413: }
0414: if ((this .bits & ASTNode.IsTryBlockExiting) == 0) {
0415: int position = codeStream.position;
0416: switch (finallyMode) {
0417: case FINALLY_SUBROUTINE:
0418: case FINALLY_INLINE:
0419: requiresNaturalExit = true;
0420: if (this .naturalExitMergeInitStateIndex != -1) {
0421: codeStream
0422: .removeNotDefinitelyAssignedVariables(
0423: currentScope,
0424: this .naturalExitMergeInitStateIndex);
0425: codeStream.addDefinitelyAssignedVariables(
0426: currentScope,
0427: this .naturalExitMergeInitStateIndex);
0428: }
0429: codeStream.goto_(naturalExitLabel);
0430: break;
0431: case NO_FINALLY:
0432: if (this .naturalExitMergeInitStateIndex != -1) {
0433: codeStream
0434: .removeNotDefinitelyAssignedVariables(
0435: currentScope,
0436: this .naturalExitMergeInitStateIndex);
0437: codeStream.addDefinitelyAssignedVariables(
0438: currentScope,
0439: this .naturalExitMergeInitStateIndex);
0440: }
0441: codeStream.goto_(naturalExitLabel);
0442: break;
0443: case FINALLY_DOES_NOT_COMPLETE:
0444: codeStream.goto_(this .subRoutineStartLabel);
0445: break;
0446: }
0447: codeStream.updateLastRecordedEndPC(this .tryBlock.scope,
0448: position);
0449: //goto is tagged as part of the try block
0450: }
0451: /* generate sequence of handler, all starting by storing the TOS (exception
0452: thrown) into their own catch variables, the one specified in the source
0453: that must denote the handled exception.
0454: */
0455: this .exitAnyExceptionHandler();
0456: if (this .catchArguments != null) {
0457: postCatchesFinallyLabel = new BranchLabel(codeStream);
0458:
0459: for (int i = 0; i < maxCatches; i++) {
0460: /*
0461: * This should not happen. For consistency purpose, if the exception label is never used
0462: * we also don't generate the corresponding catch block, otherwise we have some
0463: * unreachable bytecodes
0464: */
0465: if (exceptionLabels[i].count == 0)
0466: continue;
0467: enterAnyExceptionHandler(codeStream);
0468: // May loose some local variable initializations : affecting the local variable attributes
0469: if (this .preTryInitStateIndex != -1) {
0470: codeStream
0471: .removeNotDefinitelyAssignedVariables(
0472: currentScope,
0473: this .preTryInitStateIndex);
0474: codeStream
0475: .addDefinitelyAssignedVariables(
0476: currentScope,
0477: this .preTryInitStateIndex);
0478: }
0479: codeStream
0480: .pushExceptionOnStack(exceptionLabels[i].exceptionType);
0481: exceptionLabels[i].place();
0482: // optimizing the case where the exception variable is not actually used
0483: LocalVariableBinding catchVar;
0484: int varPC = codeStream.position;
0485: if ((catchVar = this .catchArguments[i].binding).resolvedPosition != -1) {
0486: codeStream.store(catchVar, false);
0487: catchVar
0488: .recordInitializationStartPC(codeStream.position);
0489: codeStream.addVisibleLocalVariable(catchVar);
0490: } else {
0491: codeStream.pop();
0492: }
0493: codeStream.recordPositionsFrom(varPC,
0494: this .catchArguments[i].sourceStart);
0495: // Keep track of the pcs at diverging point for computing the local attribute
0496: // since not passing the catchScope, the block generation will exitUserScope(catchScope)
0497: this .catchBlocks[i].generateCode(this .scope,
0498: codeStream);
0499: this .exitAnyExceptionHandler();
0500: if (!this .catchExits[i]) {
0501: switch (finallyMode) {
0502: case FINALLY_INLINE:
0503: // inlined finally here can see all merged variables
0504: if (isStackMapFrameCodeStream) {
0505: ((StackMapFrameCodeStream) codeStream)
0506: .pushStateIndex(this .naturalExitMergeInitStateIndex);
0507: }
0508: if (this .catchExitInitStateIndexes[i] != -1) {
0509: codeStream
0510: .removeNotDefinitelyAssignedVariables(
0511: currentScope,
0512: this .catchExitInitStateIndexes[i]);
0513: codeStream
0514: .addDefinitelyAssignedVariables(
0515: currentScope,
0516: this .catchExitInitStateIndexes[i]);
0517: }
0518: // entire sequence for finally is associated to finally block
0519: this .finallyBlock.generateCode(this .scope,
0520: codeStream);
0521: codeStream.goto_(postCatchesFinallyLabel);
0522: if (isStackMapFrameCodeStream) {
0523: ((StackMapFrameCodeStream) codeStream)
0524: .popStateIndex();
0525: }
0526: break;
0527: case FINALLY_SUBROUTINE:
0528: requiresNaturalExit = true;
0529: // fall through
0530: case NO_FINALLY:
0531: if (this .naturalExitMergeInitStateIndex != -1) {
0532: codeStream
0533: .removeNotDefinitelyAssignedVariables(
0534: currentScope,
0535: this .naturalExitMergeInitStateIndex);
0536: codeStream
0537: .addDefinitelyAssignedVariables(
0538: currentScope,
0539: this .naturalExitMergeInitStateIndex);
0540: }
0541: codeStream.goto_(naturalExitLabel);
0542: break;
0543: case FINALLY_DOES_NOT_COMPLETE:
0544: codeStream.goto_(this .subRoutineStartLabel);
0545: break;
0546: }
0547: }
0548: }
0549: }
0550: // extra handler for trailing natural exit (will be fixed up later on when natural exit is generated below)
0551: ExceptionLabel naturalExitExceptionHandler = requiresNaturalExit
0552: && (finallyMode == FINALLY_SUBROUTINE) ? new ExceptionLabel(
0553: codeStream, null)
0554: : null;
0555:
0556: // addition of a special handler so as to ensure that any uncaught exception (or exception thrown
0557: // inside catch blocks) will run the finally block
0558: int finallySequenceStartPC = codeStream.position;
0559: if (this .subRoutineStartLabel != null
0560: && this .anyExceptionLabel.count != 0) {
0561: codeStream.pushExceptionOnStack(this .scope
0562: .getJavaLangThrowable());
0563: if (this .preTryInitStateIndex != -1) {
0564: // reset initialization state, as for a normal catch block
0565: codeStream.removeNotDefinitelyAssignedVariables(
0566: currentScope, this .preTryInitStateIndex);
0567: codeStream.addDefinitelyAssignedVariables(
0568: currentScope, this .preTryInitStateIndex);
0569: }
0570: this .placeAllAnyExceptionHandler();
0571: if (naturalExitExceptionHandler != null)
0572: naturalExitExceptionHandler.place();
0573:
0574: switch (finallyMode) {
0575: case FINALLY_SUBROUTINE:
0576: // any exception handler
0577: codeStream.store(this .anyExceptionVariable, false);
0578: codeStream.jsr(this .subRoutineStartLabel);
0579: codeStream.recordPositionsFrom(
0580: finallySequenceStartPC,
0581: this .finallyBlock.sourceStart);
0582: int position = codeStream.position;
0583: codeStream
0584: .throwAnyException(this .anyExceptionVariable);
0585: codeStream.recordPositionsFrom(position,
0586: this .finallyBlock.sourceEnd);
0587: // subroutine
0588: this .subRoutineStartLabel.place();
0589: codeStream.pushExceptionOnStack(this .scope
0590: .getJavaLangThrowable());
0591: position = codeStream.position;
0592: codeStream.store(this .returnAddressVariable, false);
0593: codeStream.recordPositionsFrom(position,
0594: this .finallyBlock.sourceStart);
0595: this .finallyBlock.generateCode(this .scope,
0596: codeStream);
0597: position = codeStream.position;
0598: codeStream
0599: .ret(this .returnAddressVariable.resolvedPosition);
0600: codeStream.recordPositionsFrom(position,
0601: this .finallyBlock.sourceEnd);
0602: // the ret bytecode is part of the subroutine
0603: break;
0604: case FINALLY_INLINE:
0605: // any exception handler
0606: codeStream.store(this .anyExceptionVariable, false);
0607: codeStream.addVariable(this .anyExceptionVariable);
0608: codeStream.recordPositionsFrom(
0609: finallySequenceStartPC,
0610: this .finallyBlock.sourceStart);
0611: // subroutine
0612: this .finallyBlock.generateCode(currentScope,
0613: codeStream);
0614: position = codeStream.position;
0615: codeStream
0616: .throwAnyException(this .anyExceptionVariable);
0617: codeStream
0618: .removeVariable(this .anyExceptionVariable);
0619: if (this .preTryInitStateIndex != -1) {
0620: codeStream
0621: .removeNotDefinitelyAssignedVariables(
0622: currentScope,
0623: this .preTryInitStateIndex);
0624: }
0625: this .subRoutineStartLabel.place();
0626: codeStream.recordPositionsFrom(position,
0627: this .finallyBlock.sourceEnd);
0628: break;
0629: case FINALLY_DOES_NOT_COMPLETE:
0630: // any exception handler
0631: codeStream.pop();
0632: this .subRoutineStartLabel.place();
0633: codeStream.recordPositionsFrom(
0634: finallySequenceStartPC,
0635: this .finallyBlock.sourceStart);
0636: // subroutine
0637: this .finallyBlock.generateCode(this .scope,
0638: codeStream);
0639: break;
0640: }
0641:
0642: // will naturally fall into subsequent code after subroutine invocation
0643: if (requiresNaturalExit) {
0644: switch (finallyMode) {
0645: case FINALLY_SUBROUTINE:
0646: naturalExitLabel.place();
0647: int position = codeStream.position;
0648: naturalExitExceptionHandler.placeStart();
0649: codeStream.jsr(this .subRoutineStartLabel);
0650: naturalExitExceptionHandler.placeEnd();
0651: codeStream.recordPositionsFrom(position,
0652: this .finallyBlock.sourceEnd);
0653: break;
0654: case FINALLY_INLINE:
0655: // inlined finally here can see all merged variables
0656: if (isStackMapFrameCodeStream) {
0657: ((StackMapFrameCodeStream) codeStream)
0658: .pushStateIndex(this .naturalExitMergeInitStateIndex);
0659: }
0660: if (this .naturalExitMergeInitStateIndex != -1) {
0661: codeStream
0662: .removeNotDefinitelyAssignedVariables(
0663: currentScope,
0664: this .naturalExitMergeInitStateIndex);
0665: codeStream
0666: .addDefinitelyAssignedVariables(
0667: currentScope,
0668: this .naturalExitMergeInitStateIndex);
0669: }
0670: naturalExitLabel.place();
0671: // entire sequence for finally is associated to finally block
0672: this .finallyBlock.generateCode(this .scope,
0673: codeStream);
0674: if (postCatchesFinallyLabel != null) {
0675: position = codeStream.position;
0676: // entire sequence for finally is associated to finally block
0677: codeStream.goto_(postCatchesFinallyLabel);
0678: codeStream.recordPositionsFrom(position,
0679: this .finallyBlock.sourceEnd);
0680: }
0681: if (isStackMapFrameCodeStream) {
0682: ((StackMapFrameCodeStream) codeStream)
0683: .popStateIndex();
0684: }
0685: break;
0686: case FINALLY_DOES_NOT_COMPLETE:
0687: break;
0688: default:
0689: naturalExitLabel.place();
0690: break;
0691: }
0692: }
0693: if (postCatchesFinallyLabel != null) {
0694: postCatchesFinallyLabel.place();
0695: }
0696: } else {
0697: // no subroutine, simply position end label (natural exit == end)
0698: naturalExitLabel.place();
0699: }
0700: } else {
0701: // try block had no effect, only generate the body of the finally block if any
0702: if (this .subRoutineStartLabel != null) {
0703: this .finallyBlock.generateCode(this .scope, codeStream);
0704: }
0705: }
0706: // May loose some local variable initializations : affecting the local variable attributes
0707: if (this .mergedInitStateIndex != -1) {
0708: codeStream.removeNotDefinitelyAssignedVariables(
0709: currentScope, this .mergedInitStateIndex);
0710: codeStream.addDefinitelyAssignedVariables(currentScope,
0711: this .mergedInitStateIndex);
0712: }
0713: codeStream.recordPositionsFrom(pc, this .sourceStart);
0714: }
0715:
0716: /**
0717: * @see SubRoutineStatement#generateSubRoutineInvocation(BlockScope, CodeStream, Object, int, LocalVariableBinding)
0718: */
0719: public boolean generateSubRoutineInvocation(
0720: BlockScope currentScope, CodeStream codeStream,
0721: Object targetLocation, int stateIndex,
0722: LocalVariableBinding secretLocal) {
0723:
0724: boolean isStackMapFrameCodeStream = codeStream instanceof StackMapFrameCodeStream;
0725: int finallyMode = finallyMode();
0726: switch (finallyMode) {
0727: case FINALLY_DOES_NOT_COMPLETE:
0728: codeStream.goto_(this .subRoutineStartLabel);
0729: return true;
0730:
0731: case NO_FINALLY:
0732: exitDeclaredExceptionHandlers(codeStream);
0733: return false;
0734: }
0735: // optimize subroutine invocation sequences, using the targetLocation (if any)
0736: if (targetLocation != null) {
0737: boolean reuseTargetLocation = true;
0738: if (this .reusableJSRTargetsCount > 0) {
0739: nextReusableTarget: for (int i = 0, count = this .reusableJSRTargetsCount; i < count; i++) {
0740: Object reusableJSRTarget = this .reusableJSRTargets[i];
0741: differentTarget: {
0742: if (targetLocation == reusableJSRTarget)
0743: break differentTarget;
0744: if (targetLocation instanceof Constant
0745: && reusableJSRTarget instanceof Constant
0746: && ((Constant) targetLocation)
0747: .hasSameValue((Constant) reusableJSRTarget)) {
0748: break differentTarget;
0749: }
0750: // cannot reuse current target
0751: continue nextReusableTarget;
0752: }
0753: // current target has been used in the past, simply branch to its label
0754: if ((this .reusableJSRStateIndexes[i] != stateIndex)
0755: && finallyMode == FINALLY_INLINE
0756: && isStackMapFrameCodeStream) {
0757: reuseTargetLocation = false;
0758: break nextReusableTarget;
0759: } else {
0760: codeStream
0761: .goto_(this .reusableJSRSequenceStartLabels[i]);
0762: return true;
0763: }
0764: }
0765: } else {
0766: this .reusableJSRTargets = new Object[3];
0767: this .reusableJSRSequenceStartLabels = new BranchLabel[3];
0768: this .reusableJSRStateIndexes = new int[3];
0769: }
0770: if (reuseTargetLocation) {
0771: if (this .reusableJSRTargetsCount == this .reusableJSRTargets.length) {
0772: System
0773: .arraycopy(
0774: this .reusableJSRTargets,
0775: 0,
0776: this .reusableJSRTargets = new Object[2 * this .reusableJSRTargetsCount],
0777: 0, this .reusableJSRTargetsCount);
0778: System
0779: .arraycopy(
0780: this .reusableJSRSequenceStartLabels,
0781: 0,
0782: this .reusableJSRSequenceStartLabels = new BranchLabel[2 * this .reusableJSRTargetsCount],
0783: 0, this .reusableJSRTargetsCount);
0784: System
0785: .arraycopy(
0786: this .reusableJSRStateIndexes,
0787: 0,
0788: this .reusableJSRStateIndexes = new int[2 * this .reusableJSRTargetsCount],
0789: 0, this .reusableJSRTargetsCount);
0790: }
0791: this .reusableJSRTargets[this .reusableJSRTargetsCount] = targetLocation;
0792: BranchLabel reusableJSRSequenceStartLabel = new BranchLabel(
0793: codeStream);
0794: reusableJSRSequenceStartLabel.place();
0795: this .reusableJSRStateIndexes[this .reusableJSRTargetsCount] = stateIndex;
0796: this .reusableJSRSequenceStartLabels[this .reusableJSRTargetsCount++] = reusableJSRSequenceStartLabel;
0797: }
0798: }
0799: if (finallyMode == FINALLY_INLINE) {
0800: if (isStackMapFrameCodeStream) {
0801: ((StackMapFrameCodeStream) codeStream)
0802: .pushStateIndex(stateIndex);
0803: if (this .naturalExitMergeInitStateIndex != -1
0804: || stateIndex != -1) {
0805: // reset initialization state, as for a normal catch block
0806: codeStream.removeNotDefinitelyAssignedVariables(
0807: currentScope,
0808: this .naturalExitMergeInitStateIndex);
0809: codeStream.addDefinitelyAssignedVariables(
0810: currentScope,
0811: this .naturalExitMergeInitStateIndex);
0812: }
0813: } else {
0814: if (this .naturalExitMergeInitStateIndex != -1) {
0815: // reset initialization state, as for a normal catch block
0816: codeStream.removeNotDefinitelyAssignedVariables(
0817: currentScope,
0818: this .naturalExitMergeInitStateIndex);
0819: codeStream.addDefinitelyAssignedVariables(
0820: currentScope,
0821: this .naturalExitMergeInitStateIndex);
0822: }
0823: }
0824: if (secretLocal != null) {
0825: codeStream.addVariable(secretLocal);
0826: }
0827: // cannot use jsr bytecode, then simply inline the subroutine
0828: // inside try block, ensure to deactivate all catch block exception handlers while inlining finally block
0829: exitAnyExceptionHandler();
0830: exitDeclaredExceptionHandlers(codeStream);
0831: this .finallyBlock.generateCode(currentScope, codeStream);
0832: if (isStackMapFrameCodeStream) {
0833: ((StackMapFrameCodeStream) codeStream).popStateIndex();
0834: }
0835: } else {
0836: // classic subroutine invocation, distinguish case of non-returning subroutine
0837: codeStream.jsr(this .subRoutineStartLabel);
0838: exitAnyExceptionHandler();
0839: exitDeclaredExceptionHandlers(codeStream);
0840: }
0841: return false;
0842: }
0843:
0844: public boolean isSubRoutineEscaping() {
0845: return (this .bits & ASTNode.IsSubRoutineEscaping) != 0;
0846: }
0847:
0848: public StringBuffer printStatement(int indent, StringBuffer output) {
0849: printIndent(indent, output).append("try \n"); //$NON-NLS-1$
0850: this .tryBlock.printStatement(indent + 1, output);
0851:
0852: //catches
0853: if (this .catchBlocks != null)
0854: for (int i = 0; i < this .catchBlocks.length; i++) {
0855: output.append('\n');
0856: printIndent(indent, output).append("catch ("); //$NON-NLS-1$
0857: this .catchArguments[i].print(0, output).append(") "); //$NON-NLS-1$
0858: this .catchBlocks[i].printStatement(indent + 1, output);
0859: }
0860: //finally
0861: if (this .finallyBlock != null) {
0862: output.append('\n');
0863: printIndent(indent, output).append("finally\n"); //$NON-NLS-1$
0864: this .finallyBlock.printStatement(indent + 1, output);
0865: }
0866: return output;
0867: }
0868:
0869: public void resolve(BlockScope upperScope) {
0870: // special scope for secret locals optimization.
0871: this .scope = new BlockScope(upperScope);
0872:
0873: BlockScope tryScope = new BlockScope(this .scope);
0874: BlockScope finallyScope = null;
0875:
0876: if (this .finallyBlock != null) {
0877: if (this .finallyBlock.isEmptyBlock()) {
0878: if ((this .finallyBlock.bits & ASTNode.UndocumentedEmptyBlock) != 0) {
0879: this .scope.problemReporter()
0880: .undocumentedEmptyBlock(
0881: this .finallyBlock.sourceStart,
0882: this .finallyBlock.sourceEnd);
0883: }
0884: } else {
0885: finallyScope = new BlockScope(this .scope, false); // don't add it yet to parent scope
0886:
0887: // provision for returning and forcing the finally block to run
0888: MethodScope methodScope = this .scope.methodScope();
0889:
0890: // the type does not matter as long as it is not a base type
0891: if (!upperScope.compilerOptions().inlineJsrBytecode) {
0892: this .returnAddressVariable = new LocalVariableBinding(
0893: TryStatement.SECRET_RETURN_ADDRESS_NAME,
0894: upperScope.getJavaLangObject(),
0895: ClassFileConstants.AccDefault, false);
0896: finallyScope
0897: .addLocalVariable(this .returnAddressVariable);
0898: this .returnAddressVariable
0899: .setConstant(Constant.NotAConstant); // not inlinable
0900: }
0901: this .subRoutineStartLabel = new BranchLabel();
0902:
0903: this .anyExceptionVariable = new LocalVariableBinding(
0904: TryStatement.SECRET_ANY_HANDLER_NAME,
0905: this .scope.getJavaLangThrowable(),
0906: ClassFileConstants.AccDefault, false);
0907: finallyScope
0908: .addLocalVariable(this .anyExceptionVariable);
0909: this .anyExceptionVariable
0910: .setConstant(Constant.NotAConstant); // not inlinable
0911:
0912: if (!methodScope.isInsideInitializer()) {
0913: MethodBinding methodBinding = ((AbstractMethodDeclaration) methodScope.referenceContext).binding;
0914: if (methodBinding != null) {
0915: TypeBinding methodReturnType = methodBinding.returnType;
0916: if (methodReturnType.id != TypeIds.T_void) {
0917: this .secretReturnValue = new LocalVariableBinding(
0918: TryStatement.SECRET_RETURN_VALUE_NAME,
0919: methodReturnType,
0920: ClassFileConstants.AccDefault,
0921: false);
0922: finallyScope
0923: .addLocalVariable(this .secretReturnValue);
0924: this .secretReturnValue
0925: .setConstant(Constant.NotAConstant); // not inlinable
0926: }
0927: }
0928: }
0929: this .finallyBlock.resolveUsing(finallyScope);
0930: // force the finally scope to have variable positions shifted after its try scope and catch ones
0931: finallyScope.shiftScopes = new BlockScope[this .catchArguments == null ? 1
0932: : this .catchArguments.length + 1];
0933: finallyScope.shiftScopes[0] = tryScope;
0934: }
0935: }
0936: this .tryBlock.resolveUsing(tryScope);
0937:
0938: // arguments type are checked against JavaLangThrowable in resolveForCatch(..)
0939: if (this .catchBlocks != null) {
0940: int length = this .catchArguments.length;
0941: TypeBinding[] argumentTypes = new TypeBinding[length];
0942: boolean catchHasError = false;
0943: for (int i = 0; i < length; i++) {
0944: BlockScope catchScope = new BlockScope(this .scope);
0945: if (finallyScope != null) {
0946: finallyScope.shiftScopes[i + 1] = catchScope;
0947: }
0948: // side effect on catchScope in resolveForCatch(..)
0949: if ((argumentTypes[i] = this .catchArguments[i]
0950: .resolveForCatch(catchScope)) == null) {
0951: catchHasError = true;
0952: }
0953: this .catchBlocks[i].resolveUsing(catchScope);
0954: }
0955: if (catchHasError) {
0956: return;
0957: }
0958: // Verify that the catch clause are ordered in the right way:
0959: // more specialized first.
0960: this .caughtExceptionTypes = new ReferenceBinding[length];
0961: for (int i = 0; i < length; i++) {
0962: this .caughtExceptionTypes[i] = (ReferenceBinding) argumentTypes[i];
0963: for (int j = 0; j < i; j++) {
0964: if (this .caughtExceptionTypes[i]
0965: .isCompatibleWith(argumentTypes[j])) {
0966: this .scope.problemReporter()
0967: .wrongSequenceOfExceptionTypesError(
0968: this ,
0969: this .caughtExceptionTypes[i],
0970: i, argumentTypes[j]);
0971: }
0972: }
0973: }
0974: } else {
0975: this .caughtExceptionTypes = new ReferenceBinding[0];
0976: }
0977:
0978: if (finallyScope != null) {
0979: // add finallyScope as last subscope, so it can be shifted behind try/catch subscopes.
0980: // the shifting is necessary to achieve no overlay in between the finally scope and its
0981: // sibling in term of local variable positions.
0982: this .scope.addSubscope(finallyScope);
0983: }
0984: }
0985:
0986: public void traverse(ASTVisitor visitor, BlockScope blockScope) {
0987: if (visitor.visit(this , blockScope)) {
0988: this .tryBlock.traverse(visitor, this .scope);
0989: if (this .catchArguments != null) {
0990: for (int i = 0, max = this.catchBlocks.length; i < max; i++) {
0991: this.catchArguments[i]
0992: .traverse(visitor, this.scope);
0993: this.catchBlocks[i].traverse(visitor, this.scope);
0994: }
0995: }
0996: if (this.finallyBlock != null)
0997: this.finallyBlock.traverse(visitor, this.scope);
0998: }
0999: visitor.endVisit(this, blockScope);
1000: }
1001: }
|