Package org.eclipse.jdt.internal.compiler.flow

Source Code of org.eclipse.jdt.internal.compiler.flow.ExceptionHandlingFlowContext

/*******************************************************************************
* Copyright (c) 2000, 2013 IBM Corporation and others.
* All rights reserved. This program and the accompanying materials
* are made available under the terms of the Eclipse Public License v1.0
* which accompanies this distribution, and is available at
* http://www.eclipse.org/legal/epl-v10.html
*
* Contributors:
*     IBM Corporation - initial API and implementation
*     Stephan Herrmann - Contribution for
*                bug 345305 - [compiler][null] Compiler misidentifies a case of "variable can only be null"
*                bug 402993 - [null] Follow up of bug 401088: Missing warning about redundant null check
*******************************************************************************/
package org.eclipse.jdt.internal.compiler.flow;

import java.util.ArrayList;

import org.eclipse.jdt.internal.compiler.ast.AbstractMethodDeclaration;
import org.eclipse.jdt.internal.compiler.ast.ASTNode;
import org.eclipse.jdt.internal.compiler.ast.Argument;
import org.eclipse.jdt.internal.compiler.ast.UnionTypeReference;
import org.eclipse.jdt.internal.compiler.ast.SubRoutineStatement;
import org.eclipse.jdt.internal.compiler.ast.TryStatement;
import org.eclipse.jdt.internal.compiler.ast.TypeReference;
import org.eclipse.jdt.internal.compiler.codegen.ObjectCache;
import org.eclipse.jdt.internal.compiler.lookup.BlockScope;
import org.eclipse.jdt.internal.compiler.lookup.CatchParameterBinding;
import org.eclipse.jdt.internal.compiler.lookup.ExtraCompilerModifiers;
import org.eclipse.jdt.internal.compiler.lookup.MethodScope;
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.TypeIds;

/**
* Reflects the context of code analysis, keeping track of enclosing
*  try statements, exception handlers, etc...
*/
@SuppressWarnings({"rawtypes", "unchecked"})
public class ExceptionHandlingFlowContext extends FlowContext {

  public final static int BitCacheSize = 32; // 32 bits per int

  public ReferenceBinding[] handledExceptions;
  int[] isReached;
  int[] isNeeded;
  // WARNING: This is an array that maps to catch blocks, not caught exceptions (which could be more than catch blocks in a multi-catch block)
  UnconditionalFlowInfo[] initsOnExceptions;
  ObjectCache indexes = new ObjectCache();
  boolean isMethodContext;

  public UnconditionalFlowInfo initsOnReturn;
  public FlowContext initializationParent; // special parent relationship only for initialization purpose
 
  // for dealing with anonymous constructor thrown exceptions
  public ArrayList extendedExceptions;

  private static final Argument[] NO_ARGUMENTS = new Argument[0];
  public  Argument [] catchArguments;

  private int[] exceptionToCatchBlockMap;

public ExceptionHandlingFlowContext(
      FlowContext parent,
      ASTNode associatedNode,
      ReferenceBinding[] handledExceptions,
      FlowContext initializationParent,
      BlockScope scope,
      UnconditionalFlowInfo flowInfo) {
  this(parent, associatedNode, handledExceptions, null, NO_ARGUMENTS, initializationParent, scope, flowInfo);
}
public ExceptionHandlingFlowContext(
    FlowContext parent,
    TryStatement tryStatement,
    ReferenceBinding[] handledExceptions,
    int [] exceptionToCatchBlockMap,
    FlowContext initializationParent,
    BlockScope scope,
    FlowInfo flowInfo) {
  this(parent, tryStatement, handledExceptions, exceptionToCatchBlockMap,
      tryStatement.catchArguments, initializationParent, scope, flowInfo.unconditionalInits());
  this.initsOnFinally = flowInfo.unconditionalCopy();
}
ExceptionHandlingFlowContext(
    FlowContext parent,
    ASTNode associatedNode,
    ReferenceBinding[] handledExceptions,
    int [] exceptionToCatchBlockMap,
    Argument [] catchArguments,
    FlowContext initializationParent,
    BlockScope scope,
    UnconditionalFlowInfo flowInfo) {

  super(parent, associatedNode);
  this.isMethodContext = scope == scope.methodScope();
  this.handledExceptions = handledExceptions;
  this.catchArguments = catchArguments;
  this.exceptionToCatchBlockMap = exceptionToCatchBlockMap;
  int count = handledExceptions.length, cacheSize = (count / ExceptionHandlingFlowContext.BitCacheSize) + 1;
  this.isReached = new int[cacheSize]; // none is reached by default
  this.isNeeded = new int[cacheSize]; // none is needed by default
  this.initsOnExceptions = new UnconditionalFlowInfo[count];
  boolean markExceptionsAndThrowableAsReached =
    !this.isMethodContext || scope.compilerOptions().reportUnusedDeclaredThrownExceptionExemptExceptionAndThrowable;
  for (int i = 0; i < count; i++) {
    ReferenceBinding handledException = handledExceptions[i];
    int catchBlock = this.exceptionToCatchBlockMap != null? this.exceptionToCatchBlockMap[i] : i;
    this.indexes.put(handledException, i); // key type  -> value index
    if (handledException.isUncheckedException(true)) {
      if (markExceptionsAndThrowableAsReached ||
          handledException.id != TypeIds.T_JavaLangThrowable &&
          handledException.id != TypeIds.T_JavaLangException) {
        this.isReached[i / ExceptionHandlingFlowContext.BitCacheSize] |= 1 << (i % ExceptionHandlingFlowContext.BitCacheSize);
      }
      this.initsOnExceptions[catchBlock] = flowInfo.unconditionalCopy();
    } else {
      this.initsOnExceptions[catchBlock] = FlowInfo.DEAD_END;
    }
  }
  if (!this.isMethodContext) {
    System.arraycopy(this.isReached, 0, this.isNeeded, 0, cacheSize);
  }
  this.initsOnReturn = FlowInfo.DEAD_END;
  this.initializationParent = initializationParent;
}

public void complainIfUnusedExceptionHandlers(AbstractMethodDeclaration method) {
  MethodScope scope = method.scope;
  // can optionally skip overriding methods
  if ((method.binding.modifiers & (ExtraCompilerModifiers.AccOverriding | ExtraCompilerModifiers.AccImplementing)) != 0
          && !scope.compilerOptions().reportUnusedDeclaredThrownExceptionWhenOverriding) {
      return;
  }

  // report errors for unreachable exception handlers
  TypeBinding[] docCommentReferences = null;
  int docCommentReferencesLength = 0;
  if (scope.compilerOptions().
        reportUnusedDeclaredThrownExceptionIncludeDocCommentReference &&
      method.javadoc != null &&
      method.javadoc.exceptionReferences != null &&
      (docCommentReferencesLength = method.javadoc.exceptionReferences.length) > 0) {
    docCommentReferences = new TypeBinding[docCommentReferencesLength];
    for (int i = 0; i < docCommentReferencesLength; i++) {
      docCommentReferences[i] = method.javadoc.exceptionReferences[i].resolvedType;
    }
  }
  nextHandledException: for (int i = 0, count = this.handledExceptions.length; i < count; i++) {
    int index = this.indexes.get(this.handledExceptions[i]);
    if ((this.isReached[index / ExceptionHandlingFlowContext.BitCacheSize] & 1 << (index % ExceptionHandlingFlowContext.BitCacheSize)) == 0) {
      for (int j = 0; j < docCommentReferencesLength; j++) {
        if (TypeBinding.equalsEquals(docCommentReferences[j], this.handledExceptions[i])) {
          continue nextHandledException;
        }
      }
      scope.problemReporter().unusedDeclaredThrownException(
        this.handledExceptions[index],
        method,
        method.thrownExceptions[index]);
    }
  }
}

public void complainIfUnusedExceptionHandlers(BlockScope scope,TryStatement tryStatement) {
  // report errors for unreachable exception handlers
  for (int index = 0, count = this.handledExceptions.length; index < count; index++) {
    int cacheIndex = index / ExceptionHandlingFlowContext.BitCacheSize;
    int bitMask = 1 << (index % ExceptionHandlingFlowContext.BitCacheSize);
    if ((this.isReached[cacheIndex] & bitMask) == 0) {
      scope.problemReporter().unreachableCatchBlock(
        this.handledExceptions[index],
        getExceptionType(index));
    } else {
      if ((this.isNeeded[cacheIndex] & bitMask) == 0) {
        scope.problemReporter().hiddenCatchBlock(
          this.handledExceptions[index],
          getExceptionType(index));
      }
    }
  }
}

private ASTNode getExceptionType(int index) { 
  if (this.exceptionToCatchBlockMap == null) {
    return this.catchArguments[index].type;
  }
  int catchBlock = this.exceptionToCatchBlockMap[index];
  ASTNode node = this.catchArguments[catchBlock].type;
  if (node instanceof UnionTypeReference) {
    TypeReference[] typeRefs = ((UnionTypeReference)node).typeReferences;
    for (int i = 0, len = typeRefs.length; i < len; i++) {
      TypeReference typeRef = typeRefs[i];
      if (TypeBinding.equalsEquals(typeRef.resolvedType, this.handledExceptions[index])) return typeRef;
   
  }
  return node;
}



public String individualToString() {
  StringBuffer buffer = new StringBuffer("Exception flow context"); //$NON-NLS-1$
  int length = this.handledExceptions.length;
  for (int i = 0; i < length; i++) {
    int cacheIndex = i / ExceptionHandlingFlowContext.BitCacheSize;
    int bitMask = 1 << (i % ExceptionHandlingFlowContext.BitCacheSize);
    buffer.append('[').append(this.handledExceptions[i].readableName());
    if ((this.isReached[cacheIndex] & bitMask) != 0) {
      if ((this.isNeeded[cacheIndex] & bitMask) == 0) {
        buffer.append("-masked"); //$NON-NLS-1$
      } else {
        buffer.append("-reached"); //$NON-NLS-1$
      }
    } else {
      buffer.append("-not reached"); //$NON-NLS-1$
    }
    int catchBlock = this.exceptionToCatchBlockMap != null? this.exceptionToCatchBlockMap[i] : i;
    buffer.append('-').append(this.initsOnExceptions[catchBlock].toString()).append(']');
  }
  buffer.append("[initsOnReturn -").append(this.initsOnReturn.toString()).append(']'); //$NON-NLS-1$
  return buffer.toString();
}

// WARNING: index is the catch block index as in the program order, before any normalization is
// applied for multi catch
public UnconditionalFlowInfo initsOnException(int index) {
  return this.initsOnExceptions[index];
}

public UnconditionalFlowInfo initsOnReturn(){
  return this.initsOnReturn;
}

/*
* Compute a merged list of unhandled exception types (keeping only the most generic ones).
* This is necessary to add synthetic thrown exceptions for anonymous type constructors (JLS 8.6).
*/
public void mergeUnhandledException(TypeBinding newException){
  if (this.extendedExceptions == null){
    this.extendedExceptions = new ArrayList(5);
    for (int i = 0; i < this.handledExceptions.length; i++){
      this.extendedExceptions.add(this.handledExceptions[i]);
    }
  }
  boolean isRedundant = false;

  for(int i = this.extendedExceptions.size()-1; i >= 0; i--){
    switch(Scope.compareTypes(newException, (TypeBinding)this.extendedExceptions.get(i))){
      case Scope.MORE_GENERIC :
        this.extendedExceptions.remove(i);
        break;
      case Scope.EQUAL_OR_MORE_SPECIFIC :
        isRedundant = true;
        break;
      case Scope.NOT_RELATED :
        break;
    }
  }
  if (!isRedundant){
    this.extendedExceptions.add(newException);
  }
}

public void recordHandlingException(
    ReferenceBinding exceptionType,
    UnconditionalFlowInfo flowInfo,
    TypeBinding raisedException,
    TypeBinding caughtException,
    ASTNode invocationSite,
    boolean wasAlreadyDefinitelyCaught) {

  int index = this.indexes.get(exceptionType);
  int cacheIndex = index / ExceptionHandlingFlowContext.BitCacheSize;
  int bitMask = 1 << (index % ExceptionHandlingFlowContext.BitCacheSize);
  if (!wasAlreadyDefinitelyCaught) {
    this.isNeeded[cacheIndex] |= bitMask;
  }
  this.isReached[cacheIndex] |= bitMask;
  int catchBlock = this.exceptionToCatchBlockMap != null? this.exceptionToCatchBlockMap[index] : index;
  if (caughtException != null && this.catchArguments != null && this.catchArguments.length > 0 && !wasAlreadyDefinitelyCaught) {
    CatchParameterBinding catchParameter = (CatchParameterBinding) this.catchArguments[catchBlock].binding;
    catchParameter.setPreciseType(caughtException);
  }
  this.initsOnExceptions[catchBlock] =
    (this.initsOnExceptions[catchBlock].tagBits & FlowInfo.UNREACHABLE) == 0 ?
      this.initsOnExceptions[catchBlock].mergedWith(flowInfo):
      flowInfo.unconditionalCopy();
}

public void recordReturnFrom(UnconditionalFlowInfo flowInfo) {
  if ((flowInfo.tagBits & FlowInfo.UNREACHABLE_OR_DEAD) == 0) {
    if ((this.initsOnReturn.tagBits & FlowInfo.UNREACHABLE_OR_DEAD) == 0) {
      this.initsOnReturn = this.initsOnReturn.mergedWith(flowInfo);
    }
    else {
      this.initsOnReturn = (UnconditionalFlowInfo) flowInfo.copy();
    }
  }
}

/**
* Exception handlers (with no finally block) are also included with subroutine
* only once (in case parented with true InsideSubRoutineFlowContext).
* Standard management of subroutines need to also operate on intermediate
* exception handlers.
* @see org.eclipse.jdt.internal.compiler.flow.FlowContext#subroutine()
*/
public SubRoutineStatement subroutine() {
  if (this.associatedNode instanceof SubRoutineStatement) {
    // exception handler context may be child of InsideSubRoutineFlowContext, which maps to same handler
    if (this.parent.subroutine() == this.associatedNode)
      return null;
    return (SubRoutineStatement) this.associatedNode;
  }
  return null;
}
}
TOP

Related Classes of org.eclipse.jdt.internal.compiler.flow.ExceptionHandlingFlowContext

TOP
Copyright © 2018 www.massapi.com. All rights reserved.
All source code are property of their respective owners. Java is a trademark of Sun Microsystems, Inc and owned by ORACLE Inc. Contact coftware#gmail.com.