public void generateCode(ClassFile classFile) {

    classFile.generateMethodInfoHeader(this.binding);
    int methodAttributeOffset = classFile.contentsOffset;
    int attributeNumber = classFile.generateMethodInfoAttributes(this.binding);
    if ((!this.binding.isNative()) && (!this.binding.isAbstract())) {
      int codeAttributeOffset = classFile.contentsOffset;
      classFile.generateCodeAttributeHeader();
      CodeStream codeStream = classFile.codeStream;
      codeStream.reset(this, classFile);
      // initialize local positions
      this.scope.computeLocalVariablePositions(this.binding.isStatic() ? 0 : 1, codeStream);

      // arguments initialization for local variable debug attributes
      if (this.arguments != null) {
        for (int i = 0, max = this.arguments.length; i < max; i++) {
          LocalVariableBinding argBinding;
          codeStream.addVisibleLocalVariable(argBinding = this.arguments[i].binding);
          argBinding.recordInitializationStartPC(0);
        }
      }
      if (this.statements != null) {
        for (int i = 0, max = this.statements.length; i < max; i++)
          this.statements[i].generateCode(this.scope, codeStream);
      }
      // if a problem got reported during code gen, then trigger problem method creation
      if (this.ignoreFurtherInvestigation) {
        throw new AbortMethod(this.scope.referenceCompilationUnit().compilationResult, null);
      }
      if ((this.bits & ASTNode.NeedFreeReturn) != 0) {
        codeStream.return_();
      }
      // local variable attributes
      codeStream.exitUserScope(this.scope);
      codeStream.recordPositionsFrom(0, this.declarationSourceEnd);
      try {
        classFile.completeCodeAttribute(codeAttributeOffset);
      } catch (NegativeArraySizeException e) {
        throw new AbortMethod(this.scope.referenceCompilationUnit().compilationResult, null);
      }
      attributeNumber++;
    } else {
      checkArgumentsSize();
    }
    classFile.completeMethodInfo(this.binding, methodAttributeOffset, attributeNumber);
  }
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
  /**
   * Try statement code generation with or without jsr bytecode use post 1.5 target level, cannot
   * use jsr bytecode, must instead inline finally block returnAddress is only allocated if jsr is
   * allowed
   */
  public void generateCode(BlockScope currentScope, CodeStream codeStream) {
    if ((bits & IsReachableMASK) == 0) {
      return;
    }
    // in case the labels needs to be reinitialized
    // when the code generation is restarted in wide mode
    if (this.anyExceptionLabelsCount > 0) {
      this.anyExceptionLabels = NO_EXCEPTION_HANDLER;
      this.anyExceptionLabelsCount = 0;
    }
    int pc = codeStream.position;
    final int NO_FINALLY = 0; // no finally block
    final int FINALLY_SUBROUTINE =
        1; // finally is generated as a subroutine (using jsr/ret bytecodes)
    final int FINALLY_DOES_NOT_COMPLETE =
        2; // non returning finally is optimized with only one instance of finally block
    final int FINALLY_MUST_BE_INLINED =
        3; // finally block must be inlined since cannot use jsr/ret bytecodes >1.5
    int finallyMode;
    if (subRoutineStartLabel == null) {
      finallyMode = NO_FINALLY;
    } else {
      if (this.isSubRoutineEscaping) {
        finallyMode = FINALLY_DOES_NOT_COMPLETE;
      } else if (scope.compilerOptions().inlineJsrBytecode) {
        finallyMode = FINALLY_MUST_BE_INLINED;
      } else {
        finallyMode = FINALLY_SUBROUTINE;
      }
    }
    boolean requiresNaturalExit = false;
    // preparing exception labels
    int maxCatches;
    ExceptionLabel[] exceptionLabels =
        new ExceptionLabel[maxCatches = catchArguments == null ? 0 : catchArguments.length];
    for (int i = 0; i < maxCatches; i++) {
      exceptionLabels[i] = new ExceptionLabel(codeStream, catchArguments[i].binding.type);
    }
    if (subRoutineStartLabel != null) {
      subRoutineStartLabel.initialize(codeStream);
      this.enterAnyExceptionHandler(codeStream);
    }
    // generate the try block
    tryBlock.generateCode(scope, codeStream);
    boolean tryBlockHasSomeCode = codeStream.position != pc;
    // flag telling if some bytecodes were issued inside the try block

    // place end positions of user-defined exception labels
    if (tryBlockHasSomeCode) {
      // natural exit may require subroutine invocation (if finally != null)
      Label naturalExitLabel = new Label(codeStream);
      if (!tryBlockExit) {
        int position = codeStream.position;
        switch (finallyMode) {
          case FINALLY_SUBROUTINE:
          case FINALLY_MUST_BE_INLINED:
            requiresNaturalExit = true;
            // fall through
          case NO_FINALLY:
            codeStream.goto_(naturalExitLabel);
            break;
          case FINALLY_DOES_NOT_COMPLETE:
            codeStream.goto_(subRoutineStartLabel);
            break;
        }
        codeStream.updateLastRecordedEndPC(tryBlock.scope, position);
        // goto is tagged as part of the try block
      }
      for (int i = 0; i < maxCatches; i++) {
        exceptionLabels[i].placeEnd();
      }
      /* generate sequence of handler, all starting by storing the TOS (exception
      thrown) into their own catch variables, the one specified in the source
      that must denote the handled exception.
      */
      if (catchArguments != null) {
        for (int i = 0; i < maxCatches; i++) {
          // May loose some local variable initializations : affecting the local variable attributes
          if (preTryInitStateIndex != -1) {
            codeStream.removeNotDefinitelyAssignedVariables(currentScope, preTryInitStateIndex);
          }
          exceptionLabels[i].place();
          codeStream.incrStackSize(1);
          // optimizing the case where the exception variable is not actually used
          LocalVariableBinding catchVar;
          int varPC = codeStream.position;
          if ((catchVar = catchArguments[i].binding).resolvedPosition != -1) {
            codeStream.store(catchVar, false);
            catchVar.recordInitializationStartPC(codeStream.position);
            codeStream.addVisibleLocalVariable(catchVar);
          } else {
            codeStream.pop();
          }
          codeStream.recordPositionsFrom(varPC, catchArguments[i].sourceStart);
          // Keep track of the pcs at diverging point for computing the local attribute
          // since not passing the catchScope, the block generation will exitUserScope(catchScope)
          catchBlocks[i].generateCode(scope, codeStream);
          if (!catchExits[i]) {
            switch (finallyMode) {
              case FINALLY_SUBROUTINE:
              case FINALLY_MUST_BE_INLINED:
                requiresNaturalExit = true;
                // fall through
              case NO_FINALLY:
                codeStream.goto_(naturalExitLabel);
                break;
              case FINALLY_DOES_NOT_COMPLETE:
                codeStream.goto_(subRoutineStartLabel);
                break;
            }
          }
        }
      }
      this.exitAnyExceptionHandler();
      // extra handler for trailing natural exit (will be fixed up later on when natural exit is
      // generated below)
      ExceptionLabel naturalExitExceptionHandler =
          finallyMode == FINALLY_SUBROUTINE && requiresNaturalExit
              ? new ExceptionLabel(codeStream, null)
              : null;

      // addition of a special handler so as to ensure that any uncaught exception (or exception
      // thrown
      // inside catch blocks) will run the finally block
      int finallySequenceStartPC = codeStream.position;
      if (subRoutineStartLabel != null) {
        this.placeAllAnyExceptionHandlers();
        if (naturalExitExceptionHandler != null) naturalExitExceptionHandler.place();

        if (preTryInitStateIndex != -1) {
          // reset initialization state, as for a normal catch block
          codeStream.removeNotDefinitelyAssignedVariables(currentScope, preTryInitStateIndex);
        }

        codeStream.incrStackSize(1);
        switch (finallyMode) {
          case FINALLY_SUBROUTINE:
            codeStream.store(anyExceptionVariable, false);
            codeStream.jsr(subRoutineStartLabel);
            codeStream.recordPositionsFrom(finallySequenceStartPC, finallyBlock.sourceStart);
            int position = codeStream.position;
            codeStream.load(anyExceptionVariable);
            codeStream.athrow();
            codeStream.recordPositionsFrom(position, finallyBlock.sourceEnd);
            subRoutineStartLabel.place();
            codeStream.incrStackSize(1);
            position = codeStream.position;
            codeStream.store(returnAddressVariable, false);
            codeStream.recordPositionsFrom(position, finallyBlock.sourceStart);
            finallyBlock.generateCode(scope, codeStream);
            position = codeStream.position;
            codeStream.ret(returnAddressVariable.resolvedPosition);
            //						codeStream.updateLastRecordedEndPC(position);
            codeStream.recordPositionsFrom(position, finallyBlock.sourceEnd);
            // the ret bytecode is part of the subroutine
            break;
          case FINALLY_MUST_BE_INLINED:
            codeStream.store(anyExceptionVariable, false);
            codeStream.recordPositionsFrom(finallySequenceStartPC, finallyBlock.sourceStart);
            this.finallyBlock.generateCode(currentScope, codeStream);
            position = codeStream.position;
            codeStream.load(anyExceptionVariable);
            codeStream.athrow();
            subRoutineStartLabel.place();
            codeStream.recordPositionsFrom(position, finallyBlock.sourceEnd);
            break;
          case FINALLY_DOES_NOT_COMPLETE:
            codeStream.pop();
            subRoutineStartLabel.place();
            codeStream.recordPositionsFrom(finallySequenceStartPC, finallyBlock.sourceStart);
            finallyBlock.generateCode(scope, codeStream);
            break;
        }
        // will naturally fall into subsequent code after subroutine invocation
        naturalExitLabel.place();
        if (requiresNaturalExit) {
          switch (finallyMode) {
            case FINALLY_SUBROUTINE:
              int position = codeStream.position;
              // fix up natural exit handler
              naturalExitExceptionHandler.placeStart();
              codeStream.jsr(subRoutineStartLabel);
              naturalExitExceptionHandler.placeEnd();
              codeStream.recordPositionsFrom(position, finallyBlock.sourceEnd);
              break;
            case FINALLY_MUST_BE_INLINED:
              // May loose some local variable initializations : affecting the local variable
              // attributes
              // needed since any exception handler got inlined subroutine
              if (preTryInitStateIndex != -1) {
                codeStream.removeNotDefinitelyAssignedVariables(currentScope, preTryInitStateIndex);
              }
              // entire sequence for finally is associated to finally block
              finallyBlock.generateCode(scope, codeStream);
              break;
            case FINALLY_DOES_NOT_COMPLETE:
              break;
          }
        }
      } else {
        // no subroutine, simply position end label (natural exit == end)
        naturalExitLabel.place();
      }
    } else {
      // try block had no effect, only generate the body of the finally block if any
      if (subRoutineStartLabel != null) {
        finallyBlock.generateCode(scope, codeStream);
      }
    }
    // May loose some local variable initializations : affecting the local variable attributes
    if (mergedInitStateIndex != -1) {
      codeStream.removeNotDefinitelyAssignedVariables(currentScope, mergedInitStateIndex);
      codeStream.addDefinitelyAssignedVariables(currentScope, mergedInitStateIndex);
    }
    codeStream.recordPositionsFrom(pc, this.sourceStart);
  }
Exemplo n.º 4
0
 /** Figures if @Deprecated annotation is specified, do not resolve entire annotations. */
 public static void resolveDeprecatedAnnotations(
     BlockScope scope, Annotation[] annotations, Binding recipient) {
   if (recipient != null) {
     int kind = recipient.kind();
     if (annotations != null) {
       int length;
       if ((length = annotations.length) >= 0) {
         switch (kind) {
           case Binding.PACKAGE:
             PackageBinding packageBinding = (PackageBinding) recipient;
             if ((packageBinding.tagBits & TagBits.DeprecatedAnnotationResolved) != 0) return;
             break;
           case Binding.TYPE:
           case Binding.GENERIC_TYPE:
             ReferenceBinding type = (ReferenceBinding) recipient;
             if ((type.tagBits & TagBits.DeprecatedAnnotationResolved) != 0) return;
             break;
           case Binding.METHOD:
             MethodBinding method = (MethodBinding) recipient;
             if ((method.tagBits & TagBits.DeprecatedAnnotationResolved) != 0) return;
             break;
           case Binding.FIELD:
             FieldBinding field = (FieldBinding) recipient;
             if ((field.tagBits & TagBits.DeprecatedAnnotationResolved) != 0) return;
             break;
           case Binding.LOCAL:
             LocalVariableBinding local = (LocalVariableBinding) recipient;
             if ((local.tagBits & TagBits.DeprecatedAnnotationResolved) != 0) return;
             break;
           default:
             return;
         }
         for (int i = 0; i < length; i++) {
           TypeReference annotationTypeRef = annotations[i].type;
           // only resolve type name if 'Deprecated' last token
           if (!CharOperation.equals(
               TypeConstants.JAVA_LANG_DEPRECATED[2], annotationTypeRef.getLastToken())) return;
           TypeBinding annotationType = annotations[i].type.resolveType(scope);
           if (annotationType != null
               && annotationType.isValidBinding()
               && annotationType.id == TypeIds.T_JavaLangDeprecated) {
             switch (kind) {
               case Binding.PACKAGE:
                 PackageBinding packageBinding = (PackageBinding) recipient;
                 packageBinding.tagBits |=
                     (TagBits.AnnotationDeprecated | TagBits.DeprecatedAnnotationResolved);
                 return;
               case Binding.TYPE:
               case Binding.GENERIC_TYPE:
               case Binding.TYPE_PARAMETER:
                 ReferenceBinding type = (ReferenceBinding) recipient;
                 type.tagBits |=
                     (TagBits.AnnotationDeprecated | TagBits.DeprecatedAnnotationResolved);
                 return;
               case Binding.METHOD:
                 MethodBinding method = (MethodBinding) recipient;
                 method.tagBits |=
                     (TagBits.AnnotationDeprecated | TagBits.DeprecatedAnnotationResolved);
                 return;
               case Binding.FIELD:
                 FieldBinding field = (FieldBinding) recipient;
                 field.tagBits |=
                     (TagBits.AnnotationDeprecated | TagBits.DeprecatedAnnotationResolved);
                 return;
               case Binding.LOCAL:
                 LocalVariableBinding local = (LocalVariableBinding) recipient;
                 local.tagBits |=
                     (TagBits.AnnotationDeprecated | TagBits.DeprecatedAnnotationResolved);
                 return;
               default:
                 return;
             }
           }
         }
       }
     }
     switch (kind) {
       case Binding.PACKAGE:
         PackageBinding packageBinding = (PackageBinding) recipient;
         packageBinding.tagBits |= TagBits.DeprecatedAnnotationResolved;
         return;
       case Binding.TYPE:
       case Binding.GENERIC_TYPE:
       case Binding.TYPE_PARAMETER:
         ReferenceBinding type = (ReferenceBinding) recipient;
         type.tagBits |= TagBits.DeprecatedAnnotationResolved;
         return;
       case Binding.METHOD:
         MethodBinding method = (MethodBinding) recipient;
         method.tagBits |= TagBits.DeprecatedAnnotationResolved;
         return;
       case Binding.FIELD:
         FieldBinding field = (FieldBinding) recipient;
         field.tagBits |= TagBits.DeprecatedAnnotationResolved;
         return;
       case Binding.LOCAL:
         LocalVariableBinding local = (LocalVariableBinding) recipient;
         local.tagBits |= TagBits.DeprecatedAnnotationResolved;
         return;
       default:
         return;
     }
   }
 }
Exemplo n.º 5
0
 /**
  * Resolve annotations, and check duplicates, answers combined tagBits for recognized standard
  * annotations
  */
 public static void resolveAnnotations(
     BlockScope scope, Annotation[] sourceAnnotations, Binding recipient) {
   AnnotationBinding[] annotations = null;
   int length = sourceAnnotations == null ? 0 : sourceAnnotations.length;
   if (recipient != null) {
     switch (recipient.kind()) {
       case Binding.PACKAGE:
         PackageBinding packageBinding = (PackageBinding) recipient;
         if ((packageBinding.tagBits & TagBits.AnnotationResolved) != 0) return;
         packageBinding.tagBits |=
             (TagBits.AnnotationResolved | TagBits.DeprecatedAnnotationResolved);
         break;
       case Binding.TYPE:
       case Binding.GENERIC_TYPE:
         ReferenceBinding type = (ReferenceBinding) recipient;
         if ((type.tagBits & TagBits.AnnotationResolved) != 0) return;
         type.tagBits |= (TagBits.AnnotationResolved | TagBits.DeprecatedAnnotationResolved);
         if (length > 0) {
           annotations = new AnnotationBinding[length];
           type.setAnnotations(annotations);
         }
         break;
       case Binding.METHOD:
         MethodBinding method = (MethodBinding) recipient;
         if ((method.tagBits & TagBits.AnnotationResolved) != 0) return;
         method.tagBits |= (TagBits.AnnotationResolved | TagBits.DeprecatedAnnotationResolved);
         if (length > 0) {
           annotations = new AnnotationBinding[length];
           method.setAnnotations(annotations);
         }
         break;
       case Binding.FIELD:
         FieldBinding field = (FieldBinding) recipient;
         if ((field.tagBits & TagBits.AnnotationResolved) != 0) return;
         field.tagBits |= (TagBits.AnnotationResolved | TagBits.DeprecatedAnnotationResolved);
         if (length > 0) {
           annotations = new AnnotationBinding[length];
           field.setAnnotations(annotations);
         }
         break;
       case Binding.LOCAL:
         LocalVariableBinding local = (LocalVariableBinding) recipient;
         if ((local.tagBits & TagBits.AnnotationResolved) != 0) return;
         local.tagBits |= (TagBits.AnnotationResolved | TagBits.DeprecatedAnnotationResolved);
         if (length > 0) {
           annotations = new AnnotationBinding[length];
           local.setAnnotations(annotations);
         }
         break;
       default:
         return;
     }
   }
   if (sourceAnnotations == null) return;
   for (int i = 0; i < length; i++) {
     Annotation annotation = sourceAnnotations[i];
     final Binding annotationRecipient = annotation.recipient;
     if (annotationRecipient != null && recipient != null) {
       // only local and field can share annnotations
       switch (recipient.kind()) {
         case Binding.FIELD:
           FieldBinding field = (FieldBinding) recipient;
           field.tagBits = ((FieldBinding) annotationRecipient).tagBits;
           break;
         case Binding.LOCAL:
           LocalVariableBinding local = (LocalVariableBinding) recipient;
           local.tagBits = ((LocalVariableBinding) annotationRecipient).tagBits;
           break;
       }
       if (annotations != null) {
         // need to fill the instances array
         annotations[0] = annotation.getCompilerAnnotation();
         for (int j = 1; j < length; j++) {
           Annotation annot = sourceAnnotations[j];
           annotations[j] = annot.getCompilerAnnotation();
         }
       }
       return;
     } else {
       annotation.recipient = recipient;
       annotation.resolveType(scope);
       // null if receiver is a package binding
       if (annotations != null) {
         annotations[i] = annotation.getCompilerAnnotation();
       }
     }
   }
   // check duplicate annotations
   if (annotations != null) {
     AnnotationBinding[] distinctAnnotations =
         annotations; // only copy after 1st duplicate is detected
     for (int i = 0; i < length; i++) {
       AnnotationBinding annotation = distinctAnnotations[i];
       if (annotation == null) continue;
       TypeBinding annotationType = annotation.getAnnotationType();
       boolean foundDuplicate = false;
       for (int j = i + 1; j < length; j++) {
         AnnotationBinding otherAnnotation = distinctAnnotations[j];
         if (otherAnnotation == null) continue;
         if (otherAnnotation.getAnnotationType() == annotationType) {
           foundDuplicate = true;
           if (distinctAnnotations == annotations) {
             System.arraycopy(
                 distinctAnnotations,
                 0,
                 distinctAnnotations = new AnnotationBinding[length],
                 0,
                 length);
           }
           distinctAnnotations[j] = null; // report it only once
           scope.problemReporter().duplicateAnnotation(sourceAnnotations[j]);
         }
       }
       if (foundDuplicate) {
         scope.problemReporter().duplicateAnnotation(sourceAnnotations[i]);
       }
     }
   }
 }
Exemplo n.º 6
0
  /**
   * Try statement code generation with or without jsr bytecode use post 1.5 target level, cannot
   * use jsr bytecode, must instead inline finally block returnAddress is only allocated if jsr is
   * allowed
   */
  public void generateCode(BlockScope currentScope, CodeStream codeStream) {
    if ((this.bits & ASTNode.IsReachable) == 0) {
      return;
    }
    boolean isStackMapFrameCodeStream = codeStream instanceof StackMapFrameCodeStream;
    // in case the labels needs to be reinitialized
    // when the code generation is restarted in wide mode
    this.anyExceptionLabel = null;
    this.reusableJSRTargets = null;
    this.reusableJSRSequenceStartLabels = null;
    this.reusableJSRTargetsCount = 0;

    int pc = codeStream.position;
    int finallyMode = finallyMode();

    boolean requiresNaturalExit = false;
    // preparing exception labels
    int maxCatches = this.catchArguments == null ? 0 : this.catchArguments.length;
    ExceptionLabel[] exceptionLabels;
    if (maxCatches > 0) {
      exceptionLabels = new ExceptionLabel[maxCatches];
      for (int i = 0; i < maxCatches; i++) {
        ExceptionLabel exceptionLabel =
            new ExceptionLabel(codeStream, this.catchArguments[i].binding.type);
        exceptionLabel.placeStart();
        exceptionLabels[i] = exceptionLabel;
      }
    } else {
      exceptionLabels = null;
    }
    if (this.subRoutineStartLabel != null) {
      this.subRoutineStartLabel.initialize(codeStream);
      enterAnyExceptionHandler(codeStream);
    }
    // generate the try block
    try {
      this.declaredExceptionLabels = exceptionLabels;
      this.tryBlock.generateCode(this.scope, codeStream);
    } finally {
      this.declaredExceptionLabels = null;
    }
    boolean tryBlockHasSomeCode = codeStream.position != pc;
    // flag telling if some bytecodes were issued inside the try block

    // place end positions of user-defined exception labels
    if (tryBlockHasSomeCode) {
      // natural exit may require subroutine invocation (if finally != null)
      BranchLabel naturalExitLabel = new BranchLabel(codeStream);
      BranchLabel postCatchesFinallyLabel = null;
      for (int i = 0; i < maxCatches; i++) {
        exceptionLabels[i].placeEnd();
      }
      if ((this.bits & ASTNode.IsTryBlockExiting) == 0) {
        int position = codeStream.position;
        switch (finallyMode) {
          case FINALLY_SUBROUTINE:
          case FINALLY_INLINE:
            requiresNaturalExit = true;
            if (this.naturalExitMergeInitStateIndex != -1) {
              codeStream.removeNotDefinitelyAssignedVariables(
                  currentScope, this.naturalExitMergeInitStateIndex);
              codeStream.addDefinitelyAssignedVariables(
                  currentScope, this.naturalExitMergeInitStateIndex);
            }
            codeStream.goto_(naturalExitLabel);
            break;
          case NO_FINALLY:
            if (this.naturalExitMergeInitStateIndex != -1) {
              codeStream.removeNotDefinitelyAssignedVariables(
                  currentScope, this.naturalExitMergeInitStateIndex);
              codeStream.addDefinitelyAssignedVariables(
                  currentScope, this.naturalExitMergeInitStateIndex);
            }
            codeStream.goto_(naturalExitLabel);
            break;
          case FINALLY_DOES_NOT_COMPLETE:
            codeStream.goto_(this.subRoutineStartLabel);
            break;
        }
        codeStream.updateLastRecordedEndPC(this.tryBlock.scope, position);
        // goto is tagged as part of the try block
      }
      /* generate sequence of handler, all starting by storing the TOS (exception
      thrown) into their own catch variables, the one specified in the source
      that must denote the handled exception.
      */
      exitAnyExceptionHandler();
      if (this.catchArguments != null) {
        postCatchesFinallyLabel = new BranchLabel(codeStream);

        for (int i = 0; i < maxCatches; i++) {
          /*
           * This should not happen. For consistency purpose, if the exception label is never used
           * we also don't generate the corresponding catch block, otherwise we have some
           * unreachable bytecodes
           */
          if (exceptionLabels[i].count == 0) continue;
          enterAnyExceptionHandler(codeStream);
          // May loose some local variable initializations : affecting the local variable attributes
          if (this.preTryInitStateIndex != -1) {
            codeStream.removeNotDefinitelyAssignedVariables(
                currentScope, this.preTryInitStateIndex);
            codeStream.addDefinitelyAssignedVariables(currentScope, this.preTryInitStateIndex);
          }
          codeStream.pushExceptionOnStack(exceptionLabels[i].exceptionType);
          exceptionLabels[i].place();
          // optimizing the case where the exception variable is not actually used
          LocalVariableBinding catchVar;
          int varPC = codeStream.position;
          if ((catchVar = this.catchArguments[i].binding).resolvedPosition != -1) {
            codeStream.store(catchVar, false);
            catchVar.recordInitializationStartPC(codeStream.position);
            codeStream.addVisibleLocalVariable(catchVar);
          } else {
            codeStream.pop();
          }
          codeStream.recordPositionsFrom(varPC, this.catchArguments[i].sourceStart);
          // Keep track of the pcs at diverging point for computing the local attribute
          // since not passing the catchScope, the block generation will exitUserScope(catchScope)
          this.catchBlocks[i].generateCode(this.scope, codeStream);
          exitAnyExceptionHandler();
          if (!this.catchExits[i]) {
            switch (finallyMode) {
              case FINALLY_INLINE:
                // inlined finally here can see all merged variables
                if (isStackMapFrameCodeStream) {
                  ((StackMapFrameCodeStream) codeStream)
                      .pushStateIndex(this.naturalExitMergeInitStateIndex);
                }
                if (this.catchExitInitStateIndexes[i] != -1) {
                  codeStream.removeNotDefinitelyAssignedVariables(
                      currentScope, this.catchExitInitStateIndexes[i]);
                  codeStream.addDefinitelyAssignedVariables(
                      currentScope, this.catchExitInitStateIndexes[i]);
                }
                // entire sequence for finally is associated to finally block
                this.finallyBlock.generateCode(this.scope, codeStream);
                codeStream.goto_(postCatchesFinallyLabel);
                if (isStackMapFrameCodeStream) {
                  ((StackMapFrameCodeStream) codeStream).popStateIndex();
                }
                break;
              case FINALLY_SUBROUTINE:
                requiresNaturalExit = true;
                // $FALL-THROUGH$
              case NO_FINALLY:
                if (this.naturalExitMergeInitStateIndex != -1) {
                  codeStream.removeNotDefinitelyAssignedVariables(
                      currentScope, this.naturalExitMergeInitStateIndex);
                  codeStream.addDefinitelyAssignedVariables(
                      currentScope, this.naturalExitMergeInitStateIndex);
                }
                codeStream.goto_(naturalExitLabel);
                break;
              case FINALLY_DOES_NOT_COMPLETE:
                codeStream.goto_(this.subRoutineStartLabel);
                break;
            }
          }
        }
      }
      // extra handler for trailing natural exit (will be fixed up later on when natural exit is
      // generated below)
      ExceptionLabel naturalExitExceptionHandler =
          requiresNaturalExit && (finallyMode == FINALLY_SUBROUTINE)
              ? new ExceptionLabel(codeStream, null)
              : null;

      // addition of a special handler so as to ensure that any uncaught exception (or exception
      // thrown
      // inside catch blocks) will run the finally block
      int finallySequenceStartPC = codeStream.position;
      if (this.subRoutineStartLabel != null && this.anyExceptionLabel.count != 0) {
        codeStream.pushExceptionOnStack(this.scope.getJavaLangThrowable());
        if (this.preTryInitStateIndex != -1) {
          // reset initialization state, as for a normal catch block
          codeStream.removeNotDefinitelyAssignedVariables(currentScope, this.preTryInitStateIndex);
          codeStream.addDefinitelyAssignedVariables(currentScope, this.preTryInitStateIndex);
        }
        placeAllAnyExceptionHandler();
        if (naturalExitExceptionHandler != null) naturalExitExceptionHandler.place();

        switch (finallyMode) {
          case FINALLY_SUBROUTINE:
            // any exception handler
            codeStream.store(this.anyExceptionVariable, false);
            codeStream.jsr(this.subRoutineStartLabel);
            codeStream.recordPositionsFrom(finallySequenceStartPC, this.finallyBlock.sourceStart);
            int position = codeStream.position;
            codeStream.throwAnyException(this.anyExceptionVariable);
            codeStream.recordPositionsFrom(position, this.finallyBlock.sourceEnd);
            // subroutine
            this.subRoutineStartLabel.place();
            codeStream.pushExceptionOnStack(this.scope.getJavaLangThrowable());
            position = codeStream.position;
            codeStream.store(this.returnAddressVariable, false);
            codeStream.recordPositionsFrom(position, this.finallyBlock.sourceStart);
            this.finallyBlock.generateCode(this.scope, codeStream);
            position = codeStream.position;
            codeStream.ret(this.returnAddressVariable.resolvedPosition);
            codeStream.recordPositionsFrom(position, this.finallyBlock.sourceEnd);
            // the ret bytecode is part of the subroutine
            break;
          case FINALLY_INLINE:
            // any exception handler
            codeStream.store(this.anyExceptionVariable, false);
            codeStream.addVariable(this.anyExceptionVariable);
            codeStream.recordPositionsFrom(finallySequenceStartPC, this.finallyBlock.sourceStart);
            // subroutine
            this.finallyBlock.generateCode(currentScope, codeStream);
            position = codeStream.position;
            codeStream.throwAnyException(this.anyExceptionVariable);
            codeStream.removeVariable(this.anyExceptionVariable);
            if (this.preTryInitStateIndex != -1) {
              codeStream.removeNotDefinitelyAssignedVariables(
                  currentScope, this.preTryInitStateIndex);
            }
            this.subRoutineStartLabel.place();
            codeStream.recordPositionsFrom(position, this.finallyBlock.sourceEnd);
            break;
          case FINALLY_DOES_NOT_COMPLETE:
            // any exception handler
            codeStream.pop();
            this.subRoutineStartLabel.place();
            codeStream.recordPositionsFrom(finallySequenceStartPC, this.finallyBlock.sourceStart);
            // subroutine
            this.finallyBlock.generateCode(this.scope, codeStream);
            break;
        }

        // will naturally fall into subsequent code after subroutine invocation
        if (requiresNaturalExit) {
          switch (finallyMode) {
            case FINALLY_SUBROUTINE:
              naturalExitLabel.place();
              int position = codeStream.position;
              naturalExitExceptionHandler.placeStart();
              codeStream.jsr(this.subRoutineStartLabel);
              naturalExitExceptionHandler.placeEnd();
              codeStream.recordPositionsFrom(position, this.finallyBlock.sourceEnd);
              break;
            case FINALLY_INLINE:
              // inlined finally here can see all merged variables
              if (isStackMapFrameCodeStream) {
                ((StackMapFrameCodeStream) codeStream)
                    .pushStateIndex(this.naturalExitMergeInitStateIndex);
              }
              if (this.naturalExitMergeInitStateIndex != -1) {
                codeStream.removeNotDefinitelyAssignedVariables(
                    currentScope, this.naturalExitMergeInitStateIndex);
                codeStream.addDefinitelyAssignedVariables(
                    currentScope, this.naturalExitMergeInitStateIndex);
              }
              naturalExitLabel.place();
              // entire sequence for finally is associated to finally block
              this.finallyBlock.generateCode(this.scope, codeStream);
              if (postCatchesFinallyLabel != null) {
                position = codeStream.position;
                // entire sequence for finally is associated to finally block
                codeStream.goto_(postCatchesFinallyLabel);
                codeStream.recordPositionsFrom(position, this.finallyBlock.sourceEnd);
              }
              if (isStackMapFrameCodeStream) {
                ((StackMapFrameCodeStream) codeStream).popStateIndex();
              }
              break;
            case FINALLY_DOES_NOT_COMPLETE:
              break;
            default:
              naturalExitLabel.place();
              break;
          }
        }
        if (postCatchesFinallyLabel != null) {
          postCatchesFinallyLabel.place();
        }
      } else {
        // no subroutine, simply position end label (natural exit == end)
        naturalExitLabel.place();
      }
    } else {
      // try block had no effect, only generate the body of the finally block if any
      if (this.subRoutineStartLabel != null) {
        this.finallyBlock.generateCode(this.scope, codeStream);
      }
    }
    // May loose some local variable initializations : affecting the local variable attributes
    if (this.mergedInitStateIndex != -1) {
      codeStream.removeNotDefinitelyAssignedVariables(currentScope, this.mergedInitStateIndex);
      codeStream.addDefinitelyAssignedVariables(currentScope, this.mergedInitStateIndex);
    }
    codeStream.recordPositionsFrom(pc, this.sourceStart);
  }