blob: 426698c29cd01607b2c61cffb2a0ab8f1d05059f [file] [log] [blame]
package org.eclipse.jdt.internal.compiler.flow;
/*
* (c) Copyright IBM Corp. 2000, 2001.
* All Rights Reserved.
*/
import org.eclipse.jdt.internal.compiler.*;
import org.eclipse.jdt.internal.compiler.ast.*;
import org.eclipse.jdt.internal.compiler.lookup.*;
import org.eclipse.jdt.internal.compiler.problem.*;
import org.eclipse.jdt.internal.compiler.codegen.*;
/**
* Reflects the context of code analysis, keeping track of enclosing
* try statements, exception handlers, etc...
*/
public class ExceptionHandlingFlowContext extends FlowContext {
ReferenceBinding[] handledExceptions;
public final static int BitCacheSize = 32; // 32 bits per int
int[] isReached;
int[] isNeeded;
UnconditionalFlowInfo[] initsOnExceptions;
ObjectCache indexes = new ObjectCache();
boolean isMethodContext;
public ExceptionHandlingFlowContext(
FlowContext parent,
AstNode associatedNode,
ReferenceBinding[] handledExceptions,
BlockScope scope,
UnconditionalFlowInfo flowInfo) {
super(parent, associatedNode);
isMethodContext = scope == scope.methodScope();
/*
// for a method, append the unchecked exceptions to the handled exceptions collection
if (scope.methodScope() == scope) {
int length;
System.arraycopy(
handledExceptions,
0,
(handledExceptions =
new ReferenceBinding[(length = handledExceptions.length) + 2]),
0,
length);
handledExceptions[length] = scope.getJavaLangRuntimeException();
handledExceptions[length + 1] = scope.getJavaLangError();
}
*/
this.handledExceptions = handledExceptions;
int count = handledExceptions.length, cacheSize = (count / BitCacheSize) + 1;
isReached = new int[cacheSize]; // none is reached by default
isNeeded = new int[cacheSize]; // none is needed by default
initsOnExceptions = new UnconditionalFlowInfo[count];
for (int i = 0; i < count; i++) {
indexes.put(handledExceptions[i], i); // key type -> value index
boolean isUnchecked =
(scope.compareUncheckedException(handledExceptions[i]) != NotRelated);
int cacheIndex = i / BitCacheSize, bitMask = 1 << (i % BitCacheSize);
if (isUnchecked) {
isReached[cacheIndex] |= bitMask;
initsOnExceptions[i] = flowInfo.copy().unconditionalInits();
} else {
initsOnExceptions[i] = FlowInfo.DeadEnd;
}
}
System.arraycopy(isReached, 0, isNeeded, 0, cacheSize);
}
public void complainIfUnusedExceptionHandlers(AstNode[] exceptionHandlers, BlockScope scope, TryStatement tryStatement) {
// report errors for unreachable exception handlers
for (int i = 0, count = handledExceptions.length; i < count; i++) {
int index = indexes.get(handledExceptions[i]);
int cacheIndex = index / BitCacheSize;
int bitMask = 1 << (index % BitCacheSize);
if ((isReached[cacheIndex] & bitMask) == 0) {
scope.problemReporter().unreachableExceptionHandler(
handledExceptions[index],
exceptionHandlers[index]);
} else {
if ((isNeeded[cacheIndex] & bitMask) == 0) {
scope.problemReporter().maskedExceptionHandler(
handledExceptions[index],
exceptionHandlers[index]);
}
}
}
// will optimized out unnecessary catch block during code gen
tryStatement.preserveExceptionHandler = isNeeded;
}
public String individualToString() {
StringBuffer buffer = new StringBuffer("Exception flow context"/*nonNLS*/);
int length = handledExceptions.length;
for (int i = 0; i < length; i++) {
int cacheIndex = i / BitCacheSize;
int bitMask = 1 << (i % BitCacheSize);
buffer.append('[').append(handledExceptions[i].readableName());
if ((isReached[cacheIndex] & bitMask) != 0) {
if ((isNeeded[cacheIndex] & bitMask) == 0) {
buffer.append("-masked"/*nonNLS*/);
} else {
buffer.append("-reached"/*nonNLS*/);
}
} else {
buffer.append("-not reached"/*nonNLS*/);
}
buffer.append('-').append(initsOnExceptions[i].toString()).append(']');
}
return buffer.toString();
}
public UnconditionalFlowInfo initsOnException(ReferenceBinding exceptionType) {
int index;
if ((index = indexes.get(exceptionType)) < 0) {
return FlowInfo.DeadEnd;
}
return initsOnExceptions[index];
}
public void recordHandlingException(ReferenceBinding exceptionType, UnconditionalFlowInfo flowInfo, TypeBinding raisedException, AstNode invocationSite, boolean wasAlreadyDefinitelyCaught) {
int index = indexes.get(exceptionType);
// if already flagged as being reached (unchecked exception handler)
int cacheIndex = index / BitCacheSize;
int bitMask = 1 << (index % BitCacheSize);
if (!wasAlreadyDefinitelyCaught) {
this.isNeeded[cacheIndex] |= bitMask;
}
this.isReached[cacheIndex] |= bitMask;
initsOnExceptions[index] =
initsOnExceptions[index] == FlowInfo.DeadEnd
? flowInfo.copy().unconditionalInits()
: initsOnExceptions[index].mergedWith(flowInfo);
}
}