예제 #1
0
  public FlowInfo analyseCode(BlockScope currentScope, FlowContext flowContext, FlowInfo flowInfo) {

    // Consider the try block and catch block so as to compute the intersection of initializations
    // and
    // the minimum exit relative depth amongst all of them. Then consider the subroutine, and append
    // its
    // initialization to the try/catch ones, if the subroutine completes normally. If the subroutine
    // does not
    // complete, then only keep this result for the rest of the analysis

    // process the finally block (subroutine) - create a context for the subroutine

    preTryInitStateIndex = currentScope.methodScope().recordInitializationStates(flowInfo);

    if (anyExceptionVariable != null) {
      anyExceptionVariable.useFlag = LocalVariableBinding.USED;
    }
    if (returnAddressVariable != null) { // TODO (philippe) if subroutine is escaping, unused
      returnAddressVariable.useFlag = LocalVariableBinding.USED;
    }
    InsideSubRoutineFlowContext insideSubContext;
    FinallyFlowContext finallyContext;
    UnconditionalFlowInfo subInfo;
    if (subRoutineStartLabel == null) {
      // no finally block
      insideSubContext = null;
      finallyContext = null;
      subInfo = null;
    } else {
      // analyse finally block first
      insideSubContext = new InsideSubRoutineFlowContext(flowContext, this);
      subInfo =
          finallyBlock
              .analyseCode(
                  currentScope,
                  finallyContext = new FinallyFlowContext(flowContext, finallyBlock),
                  flowInfo.copy().unconditionalInits().discardNullRelatedInitializations())
              .unconditionalInits();
      if (subInfo == FlowInfo.DEAD_END) {
        isSubRoutineEscaping = true;
        scope.problemReporter().finallyMustCompleteNormally(finallyBlock);
      }
      this.subRoutineInits = subInfo;
    }
    // process the try block in a context handling the local exceptions.
    ExceptionHandlingFlowContext handlingContext =
        new ExceptionHandlingFlowContext(
            insideSubContext == null ? flowContext : insideSubContext,
            tryBlock,
            caughtExceptionTypes,
            scope,
            flowInfo.unconditionalInits());

    FlowInfo tryInfo;
    if (tryBlock.isEmptyBlock()) {
      tryInfo = flowInfo;
      tryBlockExit = false;
    } else {
      tryInfo = tryBlock.analyseCode(currentScope, handlingContext, flowInfo.copy());
      tryBlockExit = !tryInfo.isReachable();
    }

    // check unreachable catch blocks
    handlingContext.complainIfUnusedExceptionHandlers(scope, this);

    // process the catch blocks - computing the minimal exit depth amongst try/catch
    if (catchArguments != null) {
      int catchCount;
      catchExits = new boolean[catchCount = catchBlocks.length];
      for (int i = 0; i < catchCount; i++) {
        // keep track of the inits that could potentially have led to this exception handler (for
        // final assignments diagnosis)
        FlowInfo catchInfo =
            flowInfo
                .copy()
                .unconditionalInits()
                .addPotentialInitializationsFrom(
                    handlingContext.initsOnException(caughtExceptionTypes[i]).unconditionalInits())
                .addPotentialInitializationsFrom(tryInfo.unconditionalInits())
                .addPotentialInitializationsFrom(handlingContext.initsOnReturn);

        // catch var is always set
        LocalVariableBinding catchArg = catchArguments[i].binding;
        FlowContext catchContext = insideSubContext == null ? flowContext : insideSubContext;
        catchInfo.markAsDefinitelyAssigned(catchArg);
        catchInfo.markAsDefinitelyNonNull(catchArg);
        /*
        "If we are about to consider an unchecked exception handler, potential inits may have occured inside
        the try block that need to be detected , e.g.
        try { x = 1; throwSomething();} catch(Exception e){ x = 2} "
        "(uncheckedExceptionTypes notNil and: [uncheckedExceptionTypes at: index])
        ifTrue: [catchInits addPotentialInitializationsFrom: tryInits]."
        */
        if (tryBlock.statements == null) {
          catchInfo.setReachMode(FlowInfo.UNREACHABLE);
        }
        catchInfo = catchBlocks[i].analyseCode(currentScope, catchContext, catchInfo);
        catchExits[i] = !catchInfo.isReachable();
        tryInfo = tryInfo.mergedWith(catchInfo.unconditionalInits());
      }
    }
    if (subRoutineStartLabel == null) {
      mergedInitStateIndex = currentScope.methodScope().recordInitializationStates(tryInfo);
      return tryInfo;
    }

    // we also need to check potential multiple assignments of final variables inside the finally
    // block
    // need to include potential inits from returns inside the try/catch parts - 1GK2AOF
    finallyContext.complainOnDeferredChecks(
        tryInfo.isReachable()
            ? (tryInfo.addPotentialInitializationsFrom(insideSubContext.initsOnReturn))
            : insideSubContext.initsOnReturn,
        currentScope);
    if (subInfo == FlowInfo.DEAD_END) {
      mergedInitStateIndex = currentScope.methodScope().recordInitializationStates(subInfo);
      return subInfo;
    } else {
      FlowInfo mergedInfo = tryInfo.addInitializationsFrom(subInfo);
      mergedInitStateIndex = currentScope.methodScope().recordInitializationStates(mergedInfo);
      return mergedInfo;
    }
  }