+++ /dev/null
-/*******************************************************************************
- * Copyright (c) 2000, 2004 IBM Corporation and others.
- * All rights reserved. This program and the accompanying materials
- * are made available under the terms of the Common Public License v1.0
- * which accompanies this distribution, and is available at
- * http://www.eclipse.org/legal/cpl-v10.html
- *
- * Contributors:
- * IBM Corporation - initial API and implementation
- *******************************************************************************/
-package org.eclipse.jdt.internal.compiler.flow;
-
-import org.eclipse.jdt.core.compiler.CharOperation;
-import org.eclipse.jdt.internal.compiler.ast.AbstractMethodDeclaration;
-import org.eclipse.jdt.internal.compiler.ast.ASTNode;
-import org.eclipse.jdt.internal.compiler.ast.Reference;
-import org.eclipse.jdt.internal.compiler.ast.SubRoutineStatement;
-import org.eclipse.jdt.internal.compiler.ast.TryStatement;
-import org.eclipse.jdt.internal.compiler.codegen.Label;
-import org.eclipse.jdt.internal.compiler.lookup.BlockScope;
-import org.eclipse.jdt.internal.compiler.lookup.ReferenceBinding;
-import org.eclipse.jdt.internal.compiler.lookup.Scope;
-import org.eclipse.jdt.internal.compiler.lookup.TypeBinding;
-import org.eclipse.jdt.internal.compiler.lookup.TypeConstants;
-import org.eclipse.jdt.internal.compiler.lookup.VariableBinding;
-
-/**
- * Reflects the context of code analysis, keeping track of enclosing
- * try statements, exception handlers, etc...
- */
-public class FlowContext implements TypeConstants {
-
- public ASTNode associatedNode;
- public FlowContext parent;
-
- public final static FlowContext NotContinuableContext = new FlowContext(null, null);
-
- public FlowContext(FlowContext parent, ASTNode associatedNode) {
-
- this.parent = parent;
- this.associatedNode = associatedNode;
- }
-
- public Label breakLabel() {
-
- return null;
- }
-
- public void checkExceptionHandlers(
- TypeBinding[] raisedExceptions,
- ASTNode location,
- FlowInfo flowInfo,
- BlockScope scope) {
-
- // check that all the argument exception types are handled
- // JDK Compatible implementation - when an exception type is thrown,
- // all related catch blocks are marked as reachable... instead of those only
- // until the point where it is safely handled (Smarter - see comment at the end)
- int remainingCount; // counting the number of remaining unhandled exceptions
- int raisedCount; // total number of exceptions raised
- if ((raisedExceptions == null)
- || ((raisedCount = raisedExceptions.length) == 0))
- return;
- remainingCount = raisedCount;
-
- // duplicate the array of raised exceptions since it will be updated
- // (null replaces any handled exception)
- System.arraycopy(
- raisedExceptions,
- 0,
- (raisedExceptions = new TypeBinding[raisedCount]),
- 0,
- raisedCount);
- FlowContext traversedContext = this;
-
- while (traversedContext != null) {
- SubRoutineStatement sub;
- if (((sub = traversedContext.subRoutine()) != null) && sub.isSubRoutineEscaping()) {
- // traversing a non-returning subroutine means that all unhandled
- // exceptions will actually never get sent...
- return;
- }
- // filter exceptions that are locally caught from the innermost enclosing
- // try statement to the outermost ones.
- if (traversedContext instanceof ExceptionHandlingFlowContext) {
- ExceptionHandlingFlowContext exceptionContext =
- (ExceptionHandlingFlowContext) traversedContext;
- ReferenceBinding[] caughtExceptions;
- if ((caughtExceptions = exceptionContext.handledExceptions) != NoExceptions) {
- int caughtCount = caughtExceptions.length;
- boolean[] locallyCaught = new boolean[raisedCount]; // at most
-
- for (int caughtIndex = 0; caughtIndex < caughtCount; caughtIndex++) {
- ReferenceBinding caughtException = caughtExceptions[caughtIndex];
- for (int raisedIndex = 0; raisedIndex < raisedCount; raisedIndex++) {
- TypeBinding raisedException;
- if ((raisedException = raisedExceptions[raisedIndex]) != null) {
- int state = caughtException == null
- ? EqualOrMoreSpecific /* any exception */
- : Scope.compareTypes(raisedException, caughtException);
- switch (state) {
- case EqualOrMoreSpecific :
- exceptionContext.recordHandlingException(
- caughtException,
- flowInfo.unconditionalInits(),
- raisedException,
- location,
- locallyCaught[raisedIndex]);
- // was already definitely caught ?
- if (!locallyCaught[raisedIndex]) {
- locallyCaught[raisedIndex] = true;
- // remember that this exception has been definitely caught
- remainingCount--;
- }
- break;
- case MoreGeneric :
- exceptionContext.recordHandlingException(
- caughtException,
- flowInfo.unconditionalInits(),
- raisedException,
- location,
- false);
- // was not caught already per construction
- }
- }
- }
- }
- // remove locally caught exceptions from the remaining ones
- for (int i = 0; i < raisedCount; i++) {
- if (locallyCaught[i]) {
- raisedExceptions[i] = null; // removed from the remaining ones.
- }
- }
- }
- // method treatment for unchecked exceptions
- if (exceptionContext.isMethodContext) {
- for (int i = 0; i < raisedCount; i++) {
- TypeBinding raisedException;
- if ((raisedException = raisedExceptions[i]) != null) {
- if (raisedException.isCompatibleWith(scope.getJavaLangRuntimeException())
- || raisedException.isCompatibleWith(scope.getJavaLangError())) {
- remainingCount--;
- raisedExceptions[i] = null;
- }
- }
- }
- // anonymous constructors are allowed to throw any exceptions (their thrown exceptions
- // clause will be fixed up later as per JLS 8.6).
- if (exceptionContext.associatedNode instanceof AbstractMethodDeclaration){
- AbstractMethodDeclaration method = (AbstractMethodDeclaration)exceptionContext.associatedNode;
- if (method.isConstructor() && method.binding.declaringClass.isAnonymousType()){
-
- for (int i = 0; i < raisedCount; i++) {
- TypeBinding raisedException;
- if ((raisedException = raisedExceptions[i]) != null) {
- exceptionContext.mergeUnhandledException(raisedException);
- }
- }
- return; // no need to complain, will fix up constructor exceptions
- }
- }
- break; // not handled anywhere, thus jump to error handling
- }
- }
- if (remainingCount == 0)
- return;
-
- traversedContext.recordReturnFrom(flowInfo.unconditionalInits());
- if (traversedContext.associatedNode instanceof TryStatement){
- flowInfo = flowInfo.copy().addInitializationsFrom(((TryStatement) traversedContext.associatedNode).subRoutineInits);
- }
- traversedContext = traversedContext.parent;
- }
- // if reaches this point, then there are some remaining unhandled exception types.
- nextReport: for (int i = 0; i < raisedCount; i++) {
- TypeBinding exception;
- if ((exception = raisedExceptions[i]) != null) {
- // only one complaint if same exception declared to be thrown more than once
- for (int j = 0; j < i; j++) {
- if (raisedExceptions[j] == exception) continue nextReport; // already reported
- }
- scope.problemReporter().unhandledException(exception, location);
- }
- }
- }
-
- public void checkExceptionHandlers(
- TypeBinding raisedException,
- ASTNode location,
- FlowInfo flowInfo,
- BlockScope scope) {
-
- // LIGHT-VERSION OF THE EQUIVALENT WITH AN ARRAY OF EXCEPTIONS
- // check that all the argument exception types are handled
- // JDK Compatible implementation - when an exception type is thrown,
- // all related catch blocks are marked as reachable... instead of those only
- // until the point where it is safely handled (Smarter - see comment at the end)
- FlowContext traversedContext = this;
- while (traversedContext != null) {
- SubRoutineStatement sub;
- if (((sub = traversedContext.subRoutine()) != null) && sub.isSubRoutineEscaping()) {
- // traversing a non-returning subroutine means that all unhandled
- // exceptions will actually never get sent...
- return;
- }
-
- // filter exceptions that are locally caught from the innermost enclosing
- // try statement to the outermost ones.
- if (traversedContext instanceof ExceptionHandlingFlowContext) {
- ExceptionHandlingFlowContext exceptionContext =
- (ExceptionHandlingFlowContext) traversedContext;
- ReferenceBinding[] caughtExceptions;
- if ((caughtExceptions = exceptionContext.handledExceptions) != NoExceptions) {
- boolean definitelyCaught = false;
- for (int caughtIndex = 0, caughtCount = caughtExceptions.length;
- caughtIndex < caughtCount;
- caughtIndex++) {
- ReferenceBinding caughtException = caughtExceptions[caughtIndex];
- int state = caughtException == null
- ? EqualOrMoreSpecific /* any exception */
- : Scope.compareTypes(raisedException, caughtException);
- switch (state) {
- case EqualOrMoreSpecific :
- exceptionContext.recordHandlingException(
- caughtException,
- flowInfo.unconditionalInits(),
- raisedException,
- location,
- definitelyCaught);
- // was it already definitely caught ?
- definitelyCaught = true;
- break;
- case MoreGeneric :
- exceptionContext.recordHandlingException(
- caughtException,
- flowInfo.unconditionalInits(),
- raisedException,
- location,
- false);
- // was not caught already per construction
- }
- }
- if (definitelyCaught)
- return;
- }
- // method treatment for unchecked exceptions
- if (exceptionContext.isMethodContext) {
- if (raisedException.isCompatibleWith(scope.getJavaLangRuntimeException())
- || raisedException.isCompatibleWith(scope.getJavaLangError()))
- return;
-
- // anonymous constructors are allowed to throw any exceptions (their thrown exceptions
- // clause will be fixed up later as per JLS 8.6).
- if (exceptionContext.associatedNode instanceof AbstractMethodDeclaration){
- AbstractMethodDeclaration method = (AbstractMethodDeclaration)exceptionContext.associatedNode;
- if (method.isConstructor() && method.binding.declaringClass.isAnonymousType()){
-
- exceptionContext.mergeUnhandledException(raisedException);
- return; // no need to complain, will fix up constructor exceptions
- }
- }
- break; // not handled anywhere, thus jump to error handling
- }
- }
-
- traversedContext.recordReturnFrom(flowInfo.unconditionalInits());
- if (traversedContext.associatedNode instanceof TryStatement){
- flowInfo = flowInfo.copy().addInitializationsFrom(((TryStatement) traversedContext.associatedNode).subRoutineInits);
- }
- traversedContext = traversedContext.parent;
- }
- // if reaches this point, then there are some remaining unhandled exception types.
- scope.problemReporter().unhandledException(raisedException, location);
- }
-
- public Label continueLabel() {
-
- return null;
- }
-
- /*
- * lookup through break labels
- */
- public FlowContext getTargetContextForBreakLabel(char[] labelName) {
-
- FlowContext current = this, lastNonReturningSubRoutine = null;
- while (current != null) {
- if (current.isNonReturningContext()) {
- lastNonReturningSubRoutine = current;
- }
- char[] currentLabelName;
- if (((currentLabelName = current.labelName()) != null)
- && CharOperation.equals(currentLabelName, labelName)) {
- if (lastNonReturningSubRoutine == null)
- return current;
- return lastNonReturningSubRoutine;
- }
- current = current.parent;
- }
- // not found
- return null;
- }
-
- /*
- * lookup through continue labels
- */
- public FlowContext getTargetContextForContinueLabel(char[] labelName) {
-
- FlowContext current = this;
- FlowContext lastContinuable = null;
- FlowContext lastNonReturningSubRoutine = null;
-
- while (current != null) {
- if (current.isNonReturningContext()) {
- lastNonReturningSubRoutine = current;
- } else {
- if (current.isContinuable()) {
- lastContinuable = current;
- }
- }
-
- char[] currentLabelName;
- if ((currentLabelName = current.labelName()) != null && CharOperation.equals(currentLabelName, labelName)) {
-
- // matching label found
- if ((lastContinuable != null)
- && (current.associatedNode.concreteStatement() == lastContinuable.associatedNode)) {
-
- if (lastNonReturningSubRoutine == null) return lastContinuable;
- return lastNonReturningSubRoutine;
- }
- // label is found, but not a continuable location
- return NotContinuableContext;
- }
- current = current.parent;
- }
- // not found
- return null;
- }
-
- /*
- * lookup a default break through breakable locations
- */
- public FlowContext getTargetContextForDefaultBreak() {
-
- FlowContext current = this, lastNonReturningSubRoutine = null;
- while (current != null) {
- if (current.isNonReturningContext()) {
- lastNonReturningSubRoutine = current;
- }
- if (current.isBreakable() && current.labelName() == null) {
- if (lastNonReturningSubRoutine == null) return current;
- return lastNonReturningSubRoutine;
- }
- current = current.parent;
- }
- // not found
- return null;
- }
-
- /*
- * lookup a default continue amongst continuable locations
- */
- public FlowContext getTargetContextForDefaultContinue() {
-
- FlowContext current = this, lastNonReturningSubRoutine = null;
- while (current != null) {
- if (current.isNonReturningContext()) {
- lastNonReturningSubRoutine = current;
- }
- if (current.isContinuable()) {
- if (lastNonReturningSubRoutine == null)
- return current;
- return lastNonReturningSubRoutine;
- }
- current = current.parent;
- }
- // not found
- return null;
- }
-
- public String individualToString() {
-
- return "Flow context"; //$NON-NLS-1$
- }
-
- public FlowInfo initsOnBreak() {
-
- return FlowInfo.DEAD_END;
- }
-
- public UnconditionalFlowInfo initsOnReturn() {
-
- return FlowInfo.DEAD_END;
- }
-
- public boolean isBreakable() {
-
- return false;
- }
-
- public boolean isContinuable() {
-
- return false;
- }
-
- public boolean isNonReturningContext() {
-
- return false;
- }
-
- public boolean isSubRoutine() {
-
- return false;
- }
-
- public char[] labelName() {
-
- return null;
- }
-
- public void recordBreakFrom(FlowInfo flowInfo) {
- // default implementation: do nothing
- }
-
- public void recordContinueFrom(FlowInfo flowInfo) {
- // default implementation: do nothing
- }
-
- boolean recordFinalAssignment(
- VariableBinding variable,
- Reference finalReference) {
-
- return true; // keep going
- }
-
- public void recordReturnFrom(FlowInfo flowInfo) {
- // default implementation: do nothing
- }
-
- public void recordSettingFinal(
- VariableBinding variable,
- Reference finalReference,
- FlowInfo flowInfo) {
-
- if (!flowInfo.isReachable()) return;
-
- // for initialization inside looping statement that effectively loops
- FlowContext context = this;
- while (context != null) {
- if (!context.recordFinalAssignment(variable, finalReference)) {
- break; // no need to keep going
- }
- context = context.parent;
- }
- }
-
- void removeFinalAssignmentIfAny(Reference reference) {
- // default implementation: do nothing
- }
-
- public SubRoutineStatement subRoutine() {
-
- return null;
- }
-
- public String toString() {
-
- StringBuffer buffer = new StringBuffer();
- FlowContext current = this;
- int parentsCount = 0;
- while ((current = current.parent) != null) {
- parentsCount++;
- }
- FlowContext[] parents = new FlowContext[parentsCount + 1];
- current = this;
- int index = parentsCount;
- while (index >= 0) {
- parents[index--] = current;
- current = current.parent;
- }
- for (int i = 0; i < parentsCount; i++) {
- for (int j = 0; j < i; j++)
- buffer.append('\t');
- buffer.append(parents[i].individualToString()).append('\n');
- }
- buffer.append('*');
- for (int j = 0; j < parentsCount + 1; j++)
- buffer.append('\t');
- buffer.append(individualToString()).append('\n');
- return buffer.toString();
- }
-}