public TypeBinding resolveType(BlockScope scope) {

    this.constant = Constant.NotAConstant;
    if (!checkAccess(scope.methodScope())) return null;
    ReferenceBinding enclosingReceiverType = scope.enclosingReceiverType();
    if (enclosingReceiverType.id == T_JavaLangObject) {
      scope.problemReporter().cannotUseSuperInJavaLangObject(this);
      return null;
    }
    return this.resolvedType = enclosingReceiverType.superclass();
  }
Exemplo n.º 2
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;
    }
  }
Exemplo n.º 3
0
  public void resolve(BlockScope upperScope) {

    // special scope for secret locals optimization.
    this.scope = new BlockScope(upperScope);

    BlockScope tryScope = new BlockScope(scope);
    BlockScope finallyScope = null;

    if (finallyBlock != null) {
      if (finallyBlock.isEmptyBlock()) {
        if ((finallyBlock.bits & UndocumentedEmptyBlockMASK) != 0) {
          scope
              .problemReporter()
              .undocumentedEmptyBlock(finallyBlock.sourceStart, finallyBlock.sourceEnd);
        }
      } else {
        finallyScope = new BlockScope(scope, false); // don't add it yet to parent scope

        // provision for returning and forcing the finally block to run
        MethodScope methodScope = scope.methodScope();

        // the type does not matter as long as it is not a base type
        if (!upperScope.compilerOptions().inlineJsrBytecode) {
          this.returnAddressVariable =
              new LocalVariableBinding(
                  SecretReturnName, upperScope.getJavaLangObject(), AccDefault, false);
          finallyScope.addLocalVariable(returnAddressVariable);
          this.returnAddressVariable.setConstant(NotAConstant); // not inlinable
        }
        this.subRoutineStartLabel = new Label();

        this.anyExceptionVariable =
            new LocalVariableBinding(
                SecretAnyHandlerName, scope.getJavaLangThrowable(), AccDefault, false);
        finallyScope.addLocalVariable(this.anyExceptionVariable);
        this.anyExceptionVariable.setConstant(NotAConstant); // not inlinable

        if (!methodScope.isInsideInitializer()) {
          MethodBinding methodBinding =
              ((AbstractMethodDeclaration) methodScope.referenceContext).binding;
          if (methodBinding != null) {
            TypeBinding methodReturnType = methodBinding.returnType;
            if (methodReturnType.id != T_void) {
              this.secretReturnValue =
                  new LocalVariableBinding(
                      SecretLocalDeclarationName, methodReturnType, AccDefault, false);
              finallyScope.addLocalVariable(this.secretReturnValue);
              this.secretReturnValue.setConstant(NotAConstant); // not inlinable
            }
          }
        }
        finallyBlock.resolveUsing(finallyScope);
        // force the finally scope to have variable positions shifted after its try scope and catch
        // ones
        finallyScope.shiftScopes =
            new BlockScope[catchArguments == null ? 1 : catchArguments.length + 1];
        finallyScope.shiftScopes[0] = tryScope;
      }
    }
    this.tryBlock.resolveUsing(tryScope);

    // arguments type are checked against JavaLangThrowable in resolveForCatch(..)
    if (this.catchBlocks != null) {
      int length = this.catchArguments.length;
      TypeBinding[] argumentTypes = new TypeBinding[length];
      boolean catchHasError = false;
      for (int i = 0; i < length; i++) {
        BlockScope catchScope = new BlockScope(scope);
        if (finallyScope != null) {
          finallyScope.shiftScopes[i + 1] = catchScope;
        }
        // side effect on catchScope in resolveForCatch(..)
        if ((argumentTypes[i] = catchArguments[i].resolveForCatch(catchScope)) == null) {
          catchHasError = true;
        }
        catchBlocks[i].resolveUsing(catchScope);
      }
      if (catchHasError) {
        return;
      }
      // Verify that the catch clause are ordered in the right way:
      // more specialized first.
      this.caughtExceptionTypes = new ReferenceBinding[length];
      for (int i = 0; i < length; i++) {
        caughtExceptionTypes[i] = (ReferenceBinding) argumentTypes[i];
        for (int j = 0; j < i; j++) {
          if (caughtExceptionTypes[i].isCompatibleWith(argumentTypes[j])) {
            scope
                .problemReporter()
                .wrongSequenceOfExceptionTypesError(
                    this, caughtExceptionTypes[i], i, argumentTypes[j]);
          }
        }
      }
    } else {
      caughtExceptionTypes = new ReferenceBinding[0];
    }

    if (finallyScope != null) {
      // add finallyScope as last subscope, so it can be shifted behind try/catch subscopes.
      // the shifting is necessary to achieve no overlay in between the finally scope and its
      // sibling in term of local variable positions.
      this.scope.addSubscope(finallyScope);
    }
  }
  public FlowInfo analyseCode(BlockScope currentScope, FlowContext flowContext, FlowInfo flowInfo) {
    this.breakLabel = new BranchLabel();
    this.continueLabel = new BranchLabel();
    LoopingFlowContext loopingContext =
        new LoopingFlowContext(
            flowContext, flowInfo, this, this.breakLabel, this.continueLabel, currentScope);

    Constant cst = this.condition.constant;
    boolean isConditionTrue = cst != Constant.NotAConstant && cst.booleanValue() == true;
    cst = this.condition.optimizedBooleanConstant();
    boolean isConditionOptimizedTrue = cst != Constant.NotAConstant && cst.booleanValue() == true;
    boolean isConditionOptimizedFalse = cst != Constant.NotAConstant && cst.booleanValue() == false;

    int previousMode = flowInfo.reachMode();

    UnconditionalFlowInfo actionInfo = flowInfo.nullInfoLessUnconditionalCopy();
    // we need to collect the contribution to nulls of the coming paths through the
    // loop, be they falling through normally or branched to break, continue labels
    // or catch blocks
    if ((this.action != null) && !this.action.isEmptyBlock()) {
      actionInfo =
          this.action.analyseCode(currentScope, loopingContext, actionInfo).unconditionalInits();

      // code generation can be optimized when no need to continue in the loop
      if ((actionInfo.tagBits & loopingContext.initsOnContinue.tagBits & FlowInfo.UNREACHABLE)
          != 0) {
        this.continueLabel = null;
      }
    }
    /* Reset reach mode, to address following scenario.
     *   final blank;
     *   do { if (true) break; else blank = 0; } while(false);
     *   blank = 1; // may be initialized already
     */
    actionInfo.setReachMode(previousMode);

    LoopingFlowContext condLoopContext;
    FlowInfo condInfo =
        this.condition.analyseCode(
            currentScope,
            (condLoopContext =
                new LoopingFlowContext(flowContext, flowInfo, this, null, null, currentScope)),
            (this.action == null
                    ? actionInfo
                    : (actionInfo.mergedWith(loopingContext.initsOnContinue)))
                .copy());
    this.preConditionInitStateIndex =
        currentScope.methodScope().recordInitializationStates(actionInfo);
    if (!isConditionOptimizedFalse && this.continueLabel != null) {
      loopingContext.complainOnDeferredFinalChecks(currentScope, condInfo);
      condLoopContext.complainOnDeferredFinalChecks(currentScope, condInfo);
      loopingContext.complainOnDeferredNullChecks(
          currentScope,
          flowInfo
              .unconditionalCopy()
              .addPotentialNullInfoFrom(condInfo.initsWhenTrue().unconditionalInits()));
      condLoopContext.complainOnDeferredNullChecks(
          currentScope,
          actionInfo.addPotentialNullInfoFrom(condInfo.initsWhenTrue().unconditionalInits()));
    }

    // end of loop
    FlowInfo mergedInfo =
        FlowInfo.mergedOptimizedBranches(
            (loopingContext.initsOnBreak.tagBits & FlowInfo.UNREACHABLE) != 0
                ? loopingContext.initsOnBreak
                : flowInfo.unconditionalCopy().addInitializationsFrom(loopingContext.initsOnBreak),
            // recover upstream null info
            isConditionOptimizedTrue,
            (condInfo.tagBits & FlowInfo.UNREACHABLE) == 0
                ? flowInfo.addInitializationsFrom(condInfo.initsWhenFalse())
                : condInfo,
            // recover null inits from before condition analysis
            false, // never consider opt false case for DO loop, since break can always occur
                   // (47776)
            !isConditionTrue /*do{}while(true); unreachable(); */);
    this.mergedInitStateIndex = currentScope.methodScope().recordInitializationStates(mergedInfo);
    return mergedInfo;
  }
  public FlowInfo analyseCode(BlockScope currentScope, FlowContext flowContext, FlowInfo flowInfo) {

    Constant cst = this.left.optimizedBooleanConstant();
    boolean isLeftOptimizedTrue = cst != Constant.NotAConstant && cst.booleanValue() == true;
    boolean isLeftOptimizedFalse = cst != Constant.NotAConstant && cst.booleanValue() == false;

    if (isLeftOptimizedFalse) {
      // FALSE || anything
      // need to be careful of scenario:
      //		(x || y) || !z, if passing the left info to the right, it would be swapped by the !
      FlowInfo mergedInfo =
          this.left.analyseCode(currentScope, flowContext, flowInfo).unconditionalInits();
      flowContext.expireNullCheckedFieldInfo();
      mergedInfo = this.right.analyseCode(currentScope, flowContext, mergedInfo);
      flowContext.expireNullCheckedFieldInfo();
      this.mergedInitStateIndex = currentScope.methodScope().recordInitializationStates(mergedInfo);
      return mergedInfo;
    }

    FlowInfo leftInfo = this.left.analyseCode(currentScope, flowContext, flowInfo);
    flowContext.expireNullCheckedFieldInfo();

    // need to be careful of scenario:
    //		(x || y) || !z, if passing the left info to the right, it would be swapped by the !
    FlowInfo rightInfo = leftInfo.initsWhenFalse().unconditionalCopy();
    this.rightInitStateIndex = currentScope.methodScope().recordInitializationStates(rightInfo);

    int previousMode = rightInfo.reachMode();
    if (isLeftOptimizedTrue) {
      if ((rightInfo.reachMode() & FlowInfo.UNREACHABLE) == 0) {
        currentScope.problemReporter().fakeReachable(this.right);
        rightInfo.setReachMode(FlowInfo.UNREACHABLE_OR_DEAD);
      }
    }
    rightInfo = this.right.analyseCode(currentScope, flowContext, rightInfo);
    flowContext.expireNullCheckedFieldInfo();
    if ((this.left.implicitConversion & TypeIds.UNBOXING) != 0) {
      this.left.checkNPE(currentScope, flowContext, flowInfo);
    }
    if ((this.right.implicitConversion & TypeIds.UNBOXING) != 0) {
      this.right.checkNPE(currentScope, flowContext, flowInfo);
    }
    // The definitely null variables in right info when true should not be missed out while merging
    // https://bugs.eclipse.org/bugs/show_bug.cgi?id=299900
    FlowInfo leftInfoWhenTrueForMerging =
        leftInfo
            .initsWhenTrue()
            .unconditionalCopy()
            .addPotentialInitializationsFrom(rightInfo.unconditionalInitsWithoutSideEffect());
    FlowInfo mergedInfo =
        FlowInfo.conditional(
            // merging two true initInfos for such a negative case: if ((t && (b = t)) || f) r = b;
            // // b may not have been initialized
            leftInfoWhenTrueForMerging
                .unconditionalInits()
                .mergedWith(
                    rightInfo.safeInitsWhenTrue().setReachMode(previousMode).unconditionalInits()),
            rightInfo.initsWhenFalse());
    this.mergedInitStateIndex = currentScope.methodScope().recordInitializationStates(mergedInfo);
    return mergedInfo;
  }
Exemplo n.º 6
0
  public FlowInfo analyseCode(BlockScope currentScope, FlowContext flowContext, FlowInfo flowInfo) {
    this.preAssertInitStateIndex = currentScope.methodScope().recordInitializationStates(flowInfo);

    Constant cst = this.assertExpression.optimizedBooleanConstant();
    if ((this.assertExpression.implicitConversion & TypeIds.UNBOXING) != 0) {
      this.assertExpression.checkNPE(currentScope, flowContext, flowInfo);
    }
    boolean isOptimizedTrueAssertion = cst != Constant.NotAConstant && cst.booleanValue() == true;
    boolean isOptimizedFalseAssertion = cst != Constant.NotAConstant && cst.booleanValue() == false;

    flowContext.tagBits |= FlowContext.HIDE_NULL_COMPARISON_WARNING;
    FlowInfo conditionFlowInfo =
        this.assertExpression.analyseCode(currentScope, flowContext, flowInfo.copy());
    flowContext.extendTimeToLiveForNullCheckedField(1); // survive this assert as a Statement
    flowContext.tagBits &= ~FlowContext.HIDE_NULL_COMPARISON_WARNING;
    UnconditionalFlowInfo assertWhenTrueInfo =
        conditionFlowInfo.initsWhenTrue().unconditionalInits();
    FlowInfo assertInfo = conditionFlowInfo.initsWhenFalse();
    if (isOptimizedTrueAssertion) {
      assertInfo.setReachMode(FlowInfo.UNREACHABLE_OR_DEAD);
    }

    if (this.exceptionArgument != null) {
      // only gets evaluated when escaping - results are not taken into account
      FlowInfo exceptionInfo =
          this.exceptionArgument.analyseCode(currentScope, flowContext, assertInfo.copy());

      if (isOptimizedTrueAssertion) {
        currentScope.problemReporter().fakeReachable(this.exceptionArgument);
      } else {
        flowContext.checkExceptionHandlers(
            currentScope.getJavaLangAssertionError(), this, exceptionInfo, currentScope);
      }
    }

    if (!isOptimizedTrueAssertion) {
      // add the assert support in the clinit
      manageSyntheticAccessIfNecessary(currentScope, flowInfo);
    }
    // account for potential AssertionError:
    flowContext.recordAbruptExit();
    if (isOptimizedFalseAssertion) {
      return flowInfo; // if assertions are enabled, the following code will be unreachable
      // change this if we need to carry null analysis results of the assert
      // expression downstream
    } else {
      CompilerOptions compilerOptions = currentScope.compilerOptions();
      if (!compilerOptions.includeNullInfoFromAsserts) {
        // keep just the initializations info, don't include assert's null info
        // merge initialization info's and then add back the null info from flowInfo to
        // make sure that the empty null info of assertInfo doesnt change flowInfo's null info.
        return ((flowInfo.nullInfoLessUnconditionalCopy())
                .mergedWith(assertInfo.nullInfoLessUnconditionalCopy()))
            .addNullInfoFrom(flowInfo);
      }
      return flowInfo
          .mergedWith(assertInfo.nullInfoLessUnconditionalCopy())
          .addInitializationsFrom(assertWhenTrueInfo.discardInitializationInfo());
      // keep the merge from the initial code for the definite assignment
      // analysis, tweak the null part to influence nulls downstream
    }
  }
  public TypeBinding resolveType(BlockScope scope) {
    // Answer the signature type of the field.
    // constants are propaged when the field is final
    // and initialized with a (compile time) constant

    // always ignore receiver cast, since may affect constant pool reference
    boolean receiverCast = false;
    if (this.receiver instanceof CastExpression) {
      this.receiver.bits |= ASTNode.DisableUnnecessaryCastCheck; // will check later on
      receiverCast = true;
    }
    this.actualReceiverType = this.receiver.resolveType(scope);
    if (this.actualReceiverType == null) {
      this.constant = Constant.NotAConstant;
      return null;
    }
    if (receiverCast) {
      // due to change of declaring class with receiver type, only identity cast should be notified
      if (((CastExpression) this.receiver).expression.resolvedType == this.actualReceiverType) {
        scope.problemReporter().unnecessaryCast((CastExpression) this.receiver);
      }
    }
    // the case receiverType.isArrayType and token = 'length' is handled by the scope API
    FieldBinding fieldBinding =
        this.binding = scope.getField(this.actualReceiverType, this.token, this);
    if (!fieldBinding.isValidBinding()) {
      this.constant = Constant.NotAConstant;
      if (this.receiver.resolvedType instanceof ProblemReferenceBinding) {
        // problem already got signaled on receiver, do not report secondary problem
        return null;
      }
      // https://bugs.eclipse.org/bugs/show_bug.cgi?id=245007 avoid secondary errors in case of
      // missing super type for anonymous classes ...
      ReferenceBinding declaringClass = fieldBinding.declaringClass;
      boolean avoidSecondary =
          declaringClass != null
              && declaringClass.isAnonymousType()
              && declaringClass.superclass() instanceof MissingTypeBinding;
      if (!avoidSecondary) {
        scope.problemReporter().invalidField(this, this.actualReceiverType);
      }
      if (fieldBinding instanceof ProblemFieldBinding) {
        ProblemFieldBinding problemFieldBinding = (ProblemFieldBinding) fieldBinding;
        FieldBinding closestMatch = problemFieldBinding.closestMatch;
        switch (problemFieldBinding.problemId()) {
          case ProblemReasons.InheritedNameHidesEnclosingName:
          case ProblemReasons.NotVisible:
          case ProblemReasons.NonStaticReferenceInConstructorInvocation:
          case ProblemReasons.NonStaticReferenceInStaticContext:
            if (closestMatch != null) {
              fieldBinding = closestMatch;
            }
        }
      }
      if (!fieldBinding.isValidBinding()) {
        return null;
      }
    }
    // handle indirect inheritance thru variable secondary bound
    // receiver may receive generic cast, as part of implicit conversion
    TypeBinding oldReceiverType = this.actualReceiverType;
    this.actualReceiverType =
        this.actualReceiverType.getErasureCompatibleType(fieldBinding.declaringClass);
    this.receiver.computeConversion(scope, this.actualReceiverType, this.actualReceiverType);
    if (this.actualReceiverType != oldReceiverType
        && this.receiver.postConversionType(scope)
            != this
                .actualReceiverType) { // record need for explicit cast at codegen since receiver
                                       // could not handle it
      this.bits |= NeedReceiverGenericCast;
    }
    if (isFieldUseDeprecated(fieldBinding, scope, this.bits)) {
      scope.problemReporter().deprecatedField(fieldBinding, this);
    }
    boolean isImplicitThisRcv = this.receiver.isImplicitThis();
    this.constant = isImplicitThisRcv ? fieldBinding.constant() : Constant.NotAConstant;
    if (fieldBinding.isStatic()) {
      // static field accessed through receiver? legal but unoptimal (optional warning)
      if (!(isImplicitThisRcv
          || (this.receiver instanceof NameReference
              && (((NameReference) this.receiver).bits & Binding.TYPE) != 0))) {
        scope.problemReporter().nonStaticAccessToStaticField(this, fieldBinding);
      }
      ReferenceBinding declaringClass = this.binding.declaringClass;
      if (!isImplicitThisRcv
          && declaringClass != this.actualReceiverType
          && declaringClass.canBeSeenBy(scope)) {
        scope.problemReporter().indirectAccessToStaticField(this, fieldBinding);
      }
      // check if accessing enum static field in initializer
      if (declaringClass.isEnum()) {
        MethodScope methodScope = scope.methodScope();
        SourceTypeBinding sourceType = scope.enclosingSourceType();
        if (this.constant == Constant.NotAConstant
            && !methodScope.isStatic
            && (sourceType == declaringClass
                || sourceType.superclass == declaringClass) // enum constant body
            && methodScope.isInsideInitializerOrConstructor()) {
          scope.problemReporter().enumStaticFieldUsedDuringInitialization(this.binding, this);
        }
      }
    }
    TypeBinding fieldType = fieldBinding.type;
    if (fieldType != null) {
      if ((this.bits & ASTNode.IsStrictlyAssigned) == 0) {
        fieldType =
            fieldType.capture(scope, this.sourceEnd); // perform capture conversion if read access
      }
      this.resolvedType = fieldType;
      if ((fieldType.tagBits & TagBits.HasMissingType) != 0) {
        scope.problemReporter().invalidType(this, fieldType);
        return null;
      }
    }
    return fieldType;
  }
Exemplo n.º 8
0
  public TypeBinding resolveType(BlockScope scope) {

    this.constant = Constant.NotAConstant;
    if ((this.targetType = this.type.resolveType(scope, true /* check bounds*/)) == null)
      return null;

    /* https://bugs.eclipse.org/bugs/show_bug.cgi?id=320463
      https://bugs.eclipse.org/bugs/show_bug.cgi?id=312076
      JLS3 15.8.2 forbids the type named in the class literal expression from being a parameterized type.
      And the grammar in 18.1 disallows (where X and Y are some concrete types) constructs of the form
      Outer<X>.class, Outer<X>.Inner.class, Outer.Inner<X>.class, Outer<X>.Inner<Y>.class etc.
      Corollary wise, we should resolve the type of the class literal expression to be a raw type as
      class literals exist only for the raw underlying type.
    */
    LookupEnvironment environment = scope.environment();
    this.targetType =
        environment.convertToRawType(
            this.targetType, true /* force conversion of enclosing types*/);

    if (this.targetType.isArrayType()) {
      ArrayBinding arrayBinding = (ArrayBinding) this.targetType;
      TypeBinding leafComponentType = arrayBinding.leafComponentType;
      if (leafComponentType == TypeBinding.VOID) {
        scope.problemReporter().cannotAllocateVoidArray(this);
        return null;
      } else if (leafComponentType.isTypeVariable()) {
        scope
            .problemReporter()
            .illegalClassLiteralForTypeVariable((TypeVariableBinding) leafComponentType, this);
      }
    } else if (this.targetType.isTypeVariable()) {
      scope
          .problemReporter()
          .illegalClassLiteralForTypeVariable((TypeVariableBinding) this.targetType, this);
    }
    // {ObjectTeams: do we need a RoleClassLiteralAccess?
    if (this.targetType instanceof ReferenceBinding) {
      ReferenceBinding targetRef = (ReferenceBinding) this.targetType;
      if (targetRef.isRole()) {
        if (this.verbatim) {
          this.targetType =
              RoleTypeCreator.maybeWrapUnqualifiedRoleType(scope, this.targetType, this);
        } else {
          SourceTypeBinding site = scope.enclosingSourceType();
          if (scope.methodScope().isStatic // role class literal needs team instance
              && !site.isRole() // static role method are OK.
              && !RoleTypeBinding.isRoleWithExplicitAnchor(this.targetType)) // t.R.class?
          {
            scope.problemReporter().roleClassLiteralLacksTeamInstance(this, targetRef);
            return null;
          }
          ReferenceBinding teamBinding;
          if (RoleTypeBinding.isRoleWithExplicitAnchor(targetRef))
            teamBinding = targetRef.enclosingType();
          else teamBinding = TeamModel.findEnclosingTeamContainingRole(site, targetRef);
          if (teamBinding == null)
            scope.problemReporter().externalizedRoleClassLiteral(this, targetRef);
          else {
            TypeBinding methodType =
                RoleClassLiteralAccess.ensureGetClassMethod(
                    teamBinding.getTeamModel(),
                    targetRef.roleModel); // not affected by visibility check (for resilience)
            this.roleClassLiteralAccess = new RoleClassLiteralAccess(this, methodType);
            this.resolvedType = this.roleClassLiteralAccess.resolveType(scope);
          }
          return this.resolvedType;
        }
      }
    }
    // SH}
    ReferenceBinding classType = scope.getJavaLangClass();
    // https://bugs.eclipse.org/bugs/show_bug.cgi?id=328689
    if (scope.compilerOptions().sourceLevel >= ClassFileConstants.JDK1_5) {
      // Integer.class --> Class<Integer>, perform boxing of base types (int.class -->
      // Class<Integer>)
      TypeBinding boxedType = null;
      if (this.targetType.id == T_void) {
        boxedType = environment.getResolvedType(JAVA_LANG_VOID, scope);
      } else {
        boxedType = scope.boxing(this.targetType);
      }
      if (environment.usesNullTypeAnnotations())
        boxedType =
            environment.createAnnotatedType(
                boxedType, new AnnotationBinding[] {environment.getNonNullAnnotation()});
      this.resolvedType =
          environment.createParameterizedType(
              classType, new TypeBinding[] {boxedType}, null /*not a member*/);
    } else {
      this.resolvedType = classType;
    }
    return this.resolvedType;
  }
Exemplo n.º 9
0
  public FlowInfo analyseCode(BlockScope currentScope, FlowContext flowContext, FlowInfo flowInfo) {
    boolean nonStatic = !this.binding.isStatic();
    boolean wasInsideAssert =
        ((flowContext.tagBits & FlowContext.HIDE_NULL_COMPARISON_WARNING) != 0);
    flowInfo =
        this.receiver
            .analyseCode(currentScope, flowContext, flowInfo, nonStatic)
            .unconditionalInits();
    // recording the closing of AutoCloseable resources:
    boolean analyseResources = currentScope.compilerOptions().analyseResourceLeaks;
    if (analyseResources && CharOperation.equals(TypeConstants.CLOSE, this.selector)) {
      FakedTrackingVariable trackingVariable =
          FakedTrackingVariable.getCloseTrackingVariable(this.receiver);
      if (trackingVariable
          != null) { // null happens if receiver is not a local variable or not an AutoCloseable
        if (trackingVariable.methodScope == currentScope.methodScope()) {
          trackingVariable.markClose(flowInfo, flowContext);
        } else {
          trackingVariable.markClosedInNestedMethod();
        }
      }
    }
    if (nonStatic) {
      this.receiver.checkNPE(currentScope, flowContext, flowInfo);
      // https://bugs.eclipse.org/bugs/show_bug.cgi?id=318682
      if (this.receiver.isThis()) {
        // accessing non-static method without an object
        currentScope.resetDeclaringClassMethodStaticFlag(this.binding.declaringClass);
      }
    } else if (this.receiver.isThis()) {
      if ((this.receiver.bits & ASTNode.IsImplicitThis) == 0) {
        // explicit this receiver, not allowed in static context
        currentScope.resetEnclosingMethodStaticFlag();
      }
    }

    FlowInfo conditionFlowInfo;
    if (this.arguments != null) {
      int length = this.arguments.length;
      for (int i = 0; i < length; i++) {
        Expression argument = this.arguments[i];
        if ((argument.implicitConversion & TypeIds.UNBOXING) != 0) {
          argument.checkNPE(currentScope, flowContext, flowInfo);
        }
        if (this.receiver.resolvedType != null
            && this.receiver.resolvedType.id == TypeIds.T_OrgEclipseCoreRuntimeAssert
            && argument.resolvedType != null
            && argument.resolvedType.id == TypeIds.T_boolean) {
          Constant cst = argument.optimizedBooleanConstant();
          boolean isOptimizedTrueAssertion =
              cst != Constant.NotAConstant && cst.booleanValue() == true;
          boolean isOptimizedFalseAssertion =
              cst != Constant.NotAConstant && cst.booleanValue() == false;
          flowContext.tagBits |= FlowContext.HIDE_NULL_COMPARISON_WARNING;
          conditionFlowInfo = argument.analyseCode(currentScope, flowContext, flowInfo.copy());
          if (!wasInsideAssert) {
            flowContext.tagBits &= ~FlowContext.HIDE_NULL_COMPARISON_WARNING;
          }
          UnconditionalFlowInfo assertWhenTrueInfo =
              conditionFlowInfo.initsWhenTrue().unconditionalInits();
          FlowInfo assertInfo = conditionFlowInfo.initsWhenFalse();
          if (isOptimizedTrueAssertion) {
            assertInfo.setReachMode(FlowInfo.UNREACHABLE_OR_DEAD);
          }
          if (!isOptimizedFalseAssertion) {
            // if assertion is not false for sure, only then it makes sense to carry the flow info
            // ahead.
            // if the code does reach ahead, it means the assert didn't cause an exit, and so
            // the expression inside it shouldn't change the prior flowinfo
            // viz. org.eclipse.core.runtime.Assert.isLegal(false && o != null)

            // keep the merge from the initial code for the definite assignment
            // analysis, tweak the null part to influence nulls downstream
            flowInfo =
                flowInfo
                    .mergedWith(assertInfo.nullInfoLessUnconditionalCopy())
                    .addInitializationsFrom(assertWhenTrueInfo.discardInitializationInfo());
          }
        } else {
          flowInfo = argument.analyseCode(currentScope, flowContext, flowInfo).unconditionalInits();
        }
        if (analyseResources) {
          // if argument is an AutoCloseable insert info that it *may* be closed (by the target
          // method, i.e.)
          flowInfo =
              FakedTrackingVariable.markPassedToOutside(currentScope, argument, flowInfo, false);
        }
      }
      analyseArguments(currentScope, flowContext, flowInfo, this.binding, this.arguments);
    }
    ReferenceBinding[] thrownExceptions;
    if ((thrownExceptions = this.binding.thrownExceptions) != Binding.NO_EXCEPTIONS) {
      if ((this.bits & ASTNode.Unchecked) != 0 && this.genericTypeArguments == null) {
        // https://bugs.eclipse.org/bugs/show_bug.cgi?id=277643, align with javac on JLS 15.12.2.6
        thrownExceptions =
            currentScope.environment().convertToRawTypes(this.binding.thrownExceptions, true, true);
      }
      // must verify that exceptions potentially thrown by this expression are caught in the method
      flowContext.checkExceptionHandlers(thrownExceptions, this, flowInfo.copy(), currentScope);
      // TODO (maxime) the copy above is needed because of a side effect into
      //               checkExceptionHandlers; consider protecting there instead of here;
      //               NullReferenceTest#test0510
    }
    manageSyntheticAccessIfNecessary(currentScope, flowInfo);
    return flowInfo;
  }
Exemplo n.º 10
0
  public TypeBinding resolveType(BlockScope scope) {
    // Answer the signature return type
    // Base type promotion

    this.constant = Constant.NotAConstant;
    boolean receiverCast = false, argsContainCast = false;
    if (this.receiver instanceof CastExpression) {
      this.receiver.bits |= ASTNode.DisableUnnecessaryCastCheck; // will check later on
      receiverCast = true;
    }
    this.actualReceiverType = this.receiver.resolveType(scope);
    boolean receiverIsType =
        this.receiver instanceof NameReference
            && (((NameReference) this.receiver).bits & Binding.TYPE) != 0;
    if (receiverCast && this.actualReceiverType != null) {
      // due to change of declaring class with receiver type, only identity cast should be notified
      if (((CastExpression) this.receiver).expression.resolvedType == this.actualReceiverType) {
        scope.problemReporter().unnecessaryCast((CastExpression) this.receiver);
      }
    }
    // resolve type arguments (for generic constructor call)
    if (this.typeArguments != null) {
      int length = this.typeArguments.length;
      boolean argHasError =
          scope.compilerOptions().sourceLevel
              < ClassFileConstants.JDK1_5; // typeChecks all arguments
      this.genericTypeArguments = new TypeBinding[length];
      for (int i = 0; i < length; i++) {
        TypeReference typeReference = this.typeArguments[i];
        if ((this.genericTypeArguments[i] =
                typeReference.resolveType(scope, true /* check bounds*/))
            == null) {
          argHasError = true;
        }
        if (argHasError && typeReference instanceof Wildcard) {
          scope.problemReporter().illegalUsageOfWildcard(typeReference);
        }
      }
      if (argHasError) {
        if (this.arguments != null) { // still attempt to resolve arguments
          for (int i = 0, max = this.arguments.length; i < max; i++) {
            this.arguments[i].resolveType(scope);
          }
        }
        return null;
      }
    }
    // will check for null after args are resolved
    TypeBinding[] argumentTypes = Binding.NO_PARAMETERS;
    if (this.arguments != null) {
      boolean argHasError = false; // typeChecks all arguments
      int length = this.arguments.length;
      argumentTypes = new TypeBinding[length];
      for (int i = 0; i < length; i++) {
        Expression argument = this.arguments[i];
        if (argument instanceof CastExpression) {
          argument.bits |= ASTNode.DisableUnnecessaryCastCheck; // will check later on
          argsContainCast = true;
        }
        if ((argumentTypes[i] = argument.resolveType(scope)) == null) {
          argHasError = true;
        }
      }
      if (argHasError) {
        if (this.actualReceiverType instanceof ReferenceBinding) {
          //  record a best guess, for clients who need hint about possible method match
          TypeBinding[] pseudoArgs = new TypeBinding[length];
          for (int i = length; --i >= 0; )
            pseudoArgs[i] =
                argumentTypes[i] == null
                    ? TypeBinding.NULL
                    : argumentTypes[i]; // replace args with errors with null type
          this.binding =
              this.receiver.isImplicitThis()
                  ? scope.getImplicitMethod(this.selector, pseudoArgs, this)
                  : scope.findMethod(
                      (ReferenceBinding) this.actualReceiverType, this.selector, pseudoArgs, this);
          if (this.binding != null && !this.binding.isValidBinding()) {
            MethodBinding closestMatch = ((ProblemMethodBinding) this.binding).closestMatch;
            // record the closest match, for clients who may still need hint about possible method
            // match
            if (closestMatch != null) {
              if (closestMatch.original().typeVariables
                  != Binding.NO_TYPE_VARIABLES) { // generic method
                // shouldn't return generic method outside its context, rather convert it to raw
                // method (175409)
                closestMatch =
                    scope
                        .environment()
                        .createParameterizedGenericMethod(
                            closestMatch.original(), (RawTypeBinding) null);
              }
              this.binding = closestMatch;
              MethodBinding closestMatchOriginal = closestMatch.original();
              if (closestMatchOriginal.isOrEnclosedByPrivateType()
                  && !scope.isDefinedInMethod(closestMatchOriginal)) {
                // ignore cases where method is used from within inside itself (e.g. direct
                // recursions)
                closestMatchOriginal.modifiers |= ExtraCompilerModifiers.AccLocallyUsed;
              }
            }
          }
        }
        return null;
      }
    }
    if (this.actualReceiverType == null) {
      return null;
    }
    // base type cannot receive any message
    if (this.actualReceiverType.isBaseType()) {
      scope.problemReporter().errorNoMethodFor(this, this.actualReceiverType, argumentTypes);
      return null;
    }
    this.binding =
        this.receiver.isImplicitThis()
            ? scope.getImplicitMethod(this.selector, argumentTypes, this)
            : scope.getMethod(this.actualReceiverType, this.selector, argumentTypes, this);
    if (!this.binding.isValidBinding()) {
      if (this.binding.declaringClass == null) {
        if (this.actualReceiverType instanceof ReferenceBinding) {
          this.binding.declaringClass = (ReferenceBinding) this.actualReceiverType;
        } else {
          scope.problemReporter().errorNoMethodFor(this, this.actualReceiverType, argumentTypes);
          return null;
        }
      }
      // https://bugs.eclipse.org/bugs/show_bug.cgi?id=245007 avoid secondary errors in case of
      // missing super type for anonymous classes ...
      ReferenceBinding declaringClass = this.binding.declaringClass;
      boolean avoidSecondary =
          declaringClass != null
              && declaringClass.isAnonymousType()
              && declaringClass.superclass() instanceof MissingTypeBinding;
      if (!avoidSecondary) scope.problemReporter().invalidMethod(this, this.binding);
      MethodBinding closestMatch = ((ProblemMethodBinding) this.binding).closestMatch;
      switch (this.binding.problemId()) {
        case ProblemReasons.Ambiguous:
          break; // no resilience on ambiguous
        case ProblemReasons.NotVisible:
        case ProblemReasons.NonStaticReferenceInConstructorInvocation:
        case ProblemReasons.NonStaticReferenceInStaticContext:
        case ProblemReasons.ReceiverTypeNotVisible:
        case ProblemReasons.ParameterBoundMismatch:
          // only steal returnType in cases listed above
          if (closestMatch != null) this.resolvedType = closestMatch.returnType;
          break;
      }
      // record the closest match, for clients who may still need hint about possible method match
      if (closestMatch != null) {
        this.binding = closestMatch;
        MethodBinding closestMatchOriginal = closestMatch.original();
        if (closestMatchOriginal.isOrEnclosedByPrivateType()
            && !scope.isDefinedInMethod(closestMatchOriginal)) {
          // ignore cases where method is used from within inside itself (e.g. direct recursions)
          closestMatchOriginal.modifiers |= ExtraCompilerModifiers.AccLocallyUsed;
        }
      }
      return (this.resolvedType != null
              && (this.resolvedType.tagBits & TagBits.HasMissingType) == 0)
          ? this.resolvedType
          : null;
    }
    final CompilerOptions compilerOptions = scope.compilerOptions();
    if (compilerOptions.complianceLevel <= ClassFileConstants.JDK1_6
        && this.binding.isPolymorphic()) {
      scope.problemReporter().polymorphicMethodNotBelow17(this);
      return null;
    }

    if (((this.bits & ASTNode.InsideExpressionStatement) != 0) && this.binding.isPolymorphic()) {
      // we only set the return type to be void if this method invocation is used inside an
      // expression statement
      this.binding =
          scope
              .environment()
              .updatePolymorphicMethodReturnType(
                  (PolymorphicMethodBinding) this.binding, TypeBinding.VOID);
    }
    if ((this.binding.tagBits & TagBits.HasMissingType) != 0) {
      scope.problemReporter().missingTypeInMethod(this, this.binding);
    }
    if (!this.binding.isStatic()) {
      // the "receiver" must not be a type
      if (receiverIsType) {
        scope.problemReporter().mustUseAStaticMethod(this, this.binding);
        if (this.actualReceiverType.isRawType()
            && (this.receiver.bits & ASTNode.IgnoreRawTypeCheck) == 0
            && compilerOptions.getSeverity(CompilerOptions.RawTypeReference)
                != ProblemSeverities.Ignore) {
          scope.problemReporter().rawTypeReference(this.receiver, this.actualReceiverType);
        }
      } else {
        // handle indirect inheritance thru variable secondary bound
        // receiver may receive generic cast, as part of implicit conversion
        TypeBinding oldReceiverType = this.actualReceiverType;
        this.actualReceiverType =
            this.actualReceiverType.getErasureCompatibleType(this.binding.declaringClass);
        this.receiver.computeConversion(scope, this.actualReceiverType, this.actualReceiverType);
        if (this.actualReceiverType != oldReceiverType
            && this.receiver.postConversionType(scope)
                != this
                    .actualReceiverType) { // record need for explicit cast at codegen since
                                           // receiver could not handle it
          this.bits |= NeedReceiverGenericCast;
        }
      }
    } else {
      // static message invoked through receiver? legal but unoptimal (optional warning).
      if (!(this.receiver.isImplicitThis() || this.receiver.isSuper() || receiverIsType)) {
        scope.problemReporter().nonStaticAccessToStaticMethod(this, this.binding);
      }
      if (!this.receiver.isImplicitThis()
          && this.binding.declaringClass != this.actualReceiverType) {
        scope.problemReporter().indirectAccessToStaticMethod(this, this.binding);
      }
    }
    if (checkInvocationArguments(
        scope,
        this.receiver,
        this.actualReceiverType,
        this.binding,
        this.arguments,
        argumentTypes,
        argsContainCast,
        this)) {
      this.bits |= ASTNode.Unchecked;
    }

    // -------message send that are known to fail at compile time-----------
    if (this.binding.isAbstract()) {
      if (this.receiver.isSuper()) {
        scope.problemReporter().cannotDireclyInvokeAbstractMethod(this, this.binding);
      }
      // abstract private methods cannot occur nor abstract static............
    }
    if (isMethodUseDeprecated(this.binding, scope, true))
      scope.problemReporter().deprecatedMethod(this.binding, this);

    // from 1.5 source level on, array#clone() returns the array type (but binding still shows
    // Object)
    if (this.binding == scope.environment().arrayClone
        && compilerOptions.sourceLevel >= ClassFileConstants.JDK1_5) {
      this.resolvedType = this.actualReceiverType;
    } else {
      TypeBinding returnType;
      if ((this.bits & ASTNode.Unchecked) != 0 && this.genericTypeArguments == null) {
        // https://bugs.eclipse.org/bugs/show_bug.cgi?id=277643, align with javac on JLS 15.12.2.6
        returnType = this.binding.returnType;
        if (returnType != null) {
          returnType = scope.environment().convertToRawType(returnType.erasure(), true);
        }
      } else {
        returnType = this.binding.returnType;
        if (returnType != null) {
          returnType = returnType.capture(scope, this.sourceEnd);
        }
      }
      this.resolvedType = returnType;
    }
    if (this.receiver.isSuper()
        && compilerOptions.getSeverity(CompilerOptions.OverridingMethodWithoutSuperInvocation)
            != ProblemSeverities.Ignore) {
      final ReferenceContext referenceContext = scope.methodScope().referenceContext;
      if (referenceContext instanceof AbstractMethodDeclaration) {
        final AbstractMethodDeclaration abstractMethodDeclaration =
            (AbstractMethodDeclaration) referenceContext;
        MethodBinding enclosingMethodBinding = abstractMethodDeclaration.binding;
        if (enclosingMethodBinding.isOverriding()
            && CharOperation.equals(this.binding.selector, enclosingMethodBinding.selector)
            && this.binding.areParametersEqual(enclosingMethodBinding)) {
          abstractMethodDeclaration.bits |= ASTNode.OverridingMethodWithSupercall;
        }
      }
    }
    if (this.typeArguments != null
        && this.binding.original().typeVariables == Binding.NO_TYPE_VARIABLES) {
      scope
          .problemReporter()
          .unnecessaryTypeArgumentsForMethodInvocation(
              this.binding, this.genericTypeArguments, this.typeArguments);
    }
    return (this.resolvedType.tagBits & TagBits.HasMissingType) == 0 ? this.resolvedType : null;
  }
Exemplo n.º 11
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

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

    if (this.anyExceptionVariable != null) {
      this.anyExceptionVariable.useFlag = LocalVariableBinding.USED;
    }
    if (this.returnAddressVariable != null) { // TODO (philippe) if subroutine is escaping, unused
      this.returnAddressVariable.useFlag = LocalVariableBinding.USED;
    }
    if (this.subRoutineStartLabel == null) {
      // no finally block -- this is a simplified copy of the else part
      // process the try block in a context handling the local exceptions.
      ExceptionHandlingFlowContext handlingContext =
          new ExceptionHandlingFlowContext(
              flowContext,
              this,
              this.caughtExceptionTypes,
              null,
              this.scope,
              flowInfo.unconditionalInits());
      handlingContext.initsOnFinally = new NullInfoRegistry(flowInfo.unconditionalInits());
      // only try blocks initialize that member - may consider creating a
      // separate class if needed

      FlowInfo tryInfo;
      if (this.tryBlock.isEmptyBlock()) {
        tryInfo = flowInfo;
      } else {
        tryInfo = this.tryBlock.analyseCode(currentScope, handlingContext, flowInfo.copy());
        if ((tryInfo.tagBits & FlowInfo.UNREACHABLE) != 0) this.bits |= ASTNode.IsTryBlockExiting;
      }

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

      // process the catch blocks - computing the minimal exit depth amongst try/catch
      if (this.catchArguments != null) {
        int catchCount;
        this.catchExits = new boolean[catchCount = this.catchBlocks.length];
        this.catchExitInitStateIndexes = new int[catchCount];
        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;
          if (this.caughtExceptionTypes[i].isUncheckedException(true)) {
            catchInfo =
                handlingContext.initsOnFinally.mitigateNullInfoOf(
                    flowInfo
                        .unconditionalCopy()
                        .addPotentialInitializationsFrom(
                            handlingContext.initsOnException(this.caughtExceptionTypes[i]))
                        .addPotentialInitializationsFrom(tryInfo)
                        .addPotentialInitializationsFrom(handlingContext.initsOnReturn));
          } else {
            FlowInfo initsOnException =
                handlingContext.initsOnException(this.caughtExceptionTypes[i]);
            catchInfo =
                flowInfo
                    .nullInfoLessUnconditionalCopy()
                    .addPotentialInitializationsFrom(initsOnException)
                    .addNullInfoFrom(
                        initsOnException) // null info only from here, this is the only way to enter
                                          // the catch block
                    .addPotentialInitializationsFrom(tryInfo.nullInfoLessUnconditionalCopy())
                    .addPotentialInitializationsFrom(
                        handlingContext.initsOnReturn.nullInfoLessUnconditionalCopy());
          }

          // catch var is always set
          LocalVariableBinding catchArg = this.catchArguments[i].binding;
          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 (this.tryBlock.statements == null) {
            catchInfo.setReachMode(FlowInfo.UNREACHABLE);
          }
          catchInfo = this.catchBlocks[i].analyseCode(currentScope, flowContext, catchInfo);
          this.catchExitInitStateIndexes[i] =
              currentScope.methodScope().recordInitializationStates(catchInfo);
          this.catchExits[i] = (catchInfo.tagBits & FlowInfo.UNREACHABLE) != 0;
          tryInfo = tryInfo.mergedWith(catchInfo.unconditionalInits());
        }
      }
      this.mergedInitStateIndex = currentScope.methodScope().recordInitializationStates(tryInfo);

      // chain up null info registry
      if (flowContext.initsOnFinally != null) {
        flowContext.initsOnFinally.add(handlingContext.initsOnFinally);
      }

      return tryInfo;
    } else {
      InsideSubRoutineFlowContext insideSubContext;
      FinallyFlowContext finallyContext;
      UnconditionalFlowInfo subInfo;
      // analyse finally block first
      insideSubContext = new InsideSubRoutineFlowContext(flowContext, this);

      subInfo =
          this.finallyBlock
              .analyseCode(
                  currentScope,
                  finallyContext = new FinallyFlowContext(flowContext, this.finallyBlock),
                  flowInfo.nullInfoLessUnconditionalCopy())
              .unconditionalInits();
      if (subInfo == FlowInfo.DEAD_END) {
        this.bits |= ASTNode.IsSubRoutineEscaping;
        this.scope.problemReporter().finallyMustCompleteNormally(this.finallyBlock);
      }
      this.subRoutineInits = subInfo;
      // process the try block in a context handling the local exceptions.
      ExceptionHandlingFlowContext handlingContext =
          new ExceptionHandlingFlowContext(
              insideSubContext,
              this,
              this.caughtExceptionTypes,
              null,
              this.scope,
              flowInfo.unconditionalInits());
      handlingContext.initsOnFinally = new NullInfoRegistry(flowInfo.unconditionalInits());
      // only try blocks initialize that member - may consider creating a
      // separate class if needed

      FlowInfo tryInfo;
      if (this.tryBlock.isEmptyBlock()) {
        tryInfo = flowInfo;
      } else {
        tryInfo = this.tryBlock.analyseCode(currentScope, handlingContext, flowInfo.copy());
        if ((tryInfo.tagBits & FlowInfo.UNREACHABLE) != 0) this.bits |= ASTNode.IsTryBlockExiting;
      }

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

      // process the catch blocks - computing the minimal exit depth amongst try/catch
      if (this.catchArguments != null) {
        int catchCount;
        this.catchExits = new boolean[catchCount = this.catchBlocks.length];
        this.catchExitInitStateIndexes = new int[catchCount];
        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;
          if (this.caughtExceptionTypes[i].isUncheckedException(true)) {
            catchInfo =
                handlingContext.initsOnFinally.mitigateNullInfoOf(
                    flowInfo
                        .unconditionalCopy()
                        .addPotentialInitializationsFrom(
                            handlingContext.initsOnException(this.caughtExceptionTypes[i]))
                        .addPotentialInitializationsFrom(tryInfo)
                        .addPotentialInitializationsFrom(handlingContext.initsOnReturn));
          } else {
            FlowInfo initsOnException =
                handlingContext.initsOnException(this.caughtExceptionTypes[i]);
            catchInfo =
                flowInfo
                    .nullInfoLessUnconditionalCopy()
                    .addPotentialInitializationsFrom(initsOnException)
                    .addNullInfoFrom(
                        initsOnException) // null info only from here, this is the only way to enter
                                          // the catch block
                    .addPotentialInitializationsFrom(tryInfo.nullInfoLessUnconditionalCopy())
                    .addPotentialInitializationsFrom(
                        handlingContext.initsOnReturn.nullInfoLessUnconditionalCopy());
          }

          // catch var is always set
          LocalVariableBinding catchArg = this.catchArguments[i].binding;
          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 (this.tryBlock.statements == null) {
            catchInfo.setReachMode(FlowInfo.UNREACHABLE);
          }
          catchInfo = this.catchBlocks[i].analyseCode(currentScope, insideSubContext, catchInfo);
          this.catchExitInitStateIndexes[i] =
              currentScope.methodScope().recordInitializationStates(catchInfo);
          this.catchExits[i] = (catchInfo.tagBits & FlowInfo.UNREACHABLE) != 0;
          tryInfo = tryInfo.mergedWith(catchInfo.unconditionalInits());
        }
      }
      // 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(
          handlingContext.initsOnFinally.mitigateNullInfoOf(
              (tryInfo.tagBits & FlowInfo.UNREACHABLE) == 0
                  ? flowInfo
                      .unconditionalCopy()
                      .addPotentialInitializationsFrom(tryInfo)
                      .
                      // lighten the influence of the try block, which may have
                      // exited at any point
                      addPotentialInitializationsFrom(insideSubContext.initsOnReturn)
                  : insideSubContext.initsOnReturn),
          currentScope);

      // chain up null info registry
      if (flowContext.initsOnFinally != null) {
        flowContext.initsOnFinally.add(handlingContext.initsOnFinally);
      }

      this.naturalExitMergeInitStateIndex =
          currentScope.methodScope().recordInitializationStates(tryInfo);
      if (subInfo == FlowInfo.DEAD_END) {
        this.mergedInitStateIndex = currentScope.methodScope().recordInitializationStates(subInfo);
        return subInfo;
      } else {
        FlowInfo mergedInfo = tryInfo.addInitializationsFrom(subInfo);
        this.mergedInitStateIndex =
            currentScope.methodScope().recordInitializationStates(mergedInfo);
        return mergedInfo;
      }
    }
  }