Package org.eclipse.jdt.internal.compiler

Source Code of org.eclipse.jdt.internal.compiler.ClassFile

/*******************************************************************************
* Copyright (c) 2000, 2011 IBM Corporation and others.
* All rights reserved. This program and the accompanying materials
* are made available under the terms of the Eclipse Public License v1.0
* which accompanies this distribution, and is available at
* http://www.eclipse.org/legal/epl-v10.html
*
* Contributors:
*     IBM Corporation - initial API and implementation
*******************************************************************************/
package org.eclipse.jdt.internal.compiler;

import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
import java.util.Comparator;
import java.util.HashSet;
import java.util.List;
import java.util.Set;

import org.eclipse.jdt.core.compiler.CategorizedProblem;
import org.eclipse.jdt.core.compiler.CharOperation;
import org.eclipse.jdt.core.compiler.IProblem;
import org.eclipse.jdt.internal.compiler.ast.ASTNode;
import org.eclipse.jdt.internal.compiler.ast.AbstractMethodDeclaration;
import org.eclipse.jdt.internal.compiler.ast.Annotation;
import org.eclipse.jdt.internal.compiler.ast.AnnotationMethodDeclaration;
import org.eclipse.jdt.internal.compiler.ast.Argument;
import org.eclipse.jdt.internal.compiler.ast.ArrayInitializer;
import org.eclipse.jdt.internal.compiler.ast.ClassLiteralAccess;
import org.eclipse.jdt.internal.compiler.ast.Expression;
import org.eclipse.jdt.internal.compiler.ast.FieldDeclaration;
import org.eclipse.jdt.internal.compiler.ast.MemberValuePair;
import org.eclipse.jdt.internal.compiler.ast.MethodDeclaration;
import org.eclipse.jdt.internal.compiler.ast.NormalAnnotation;
import org.eclipse.jdt.internal.compiler.ast.QualifiedNameReference;
import org.eclipse.jdt.internal.compiler.ast.SingleMemberAnnotation;
import org.eclipse.jdt.internal.compiler.ast.SingleNameReference;
import org.eclipse.jdt.internal.compiler.ast.TypeDeclaration;
import org.eclipse.jdt.internal.compiler.classfmt.ClassFileConstants;
import org.eclipse.jdt.internal.compiler.codegen.AttributeNamesConstants;
import org.eclipse.jdt.internal.compiler.codegen.CodeStream;
import org.eclipse.jdt.internal.compiler.codegen.ConstantPool;
import org.eclipse.jdt.internal.compiler.codegen.ExceptionLabel;
import org.eclipse.jdt.internal.compiler.codegen.Opcodes;
import org.eclipse.jdt.internal.compiler.codegen.StackMapFrame;
import org.eclipse.jdt.internal.compiler.codegen.StackMapFrameCodeStream;
import org.eclipse.jdt.internal.compiler.codegen.StackMapFrameCodeStream.ExceptionMarker;
import org.eclipse.jdt.internal.compiler.codegen.StackMapFrameCodeStream.StackDepthMarker;
import org.eclipse.jdt.internal.compiler.codegen.StackMapFrameCodeStream.StackMarker;
import org.eclipse.jdt.internal.compiler.codegen.VerificationTypeInfo;
import org.eclipse.jdt.internal.compiler.impl.CompilerOptions;
import org.eclipse.jdt.internal.compiler.impl.Constant;
import org.eclipse.jdt.internal.compiler.impl.StringConstant;
import org.eclipse.jdt.internal.compiler.lookup.Binding;
import org.eclipse.jdt.internal.compiler.lookup.FieldBinding;
import org.eclipse.jdt.internal.compiler.lookup.LocalTypeBinding;
import org.eclipse.jdt.internal.compiler.lookup.LocalVariableBinding;
import org.eclipse.jdt.internal.compiler.lookup.LookupEnvironment;
import org.eclipse.jdt.internal.compiler.lookup.MethodBinding;
import org.eclipse.jdt.internal.compiler.lookup.ReferenceBinding;
import org.eclipse.jdt.internal.compiler.lookup.SourceTypeBinding;
import org.eclipse.jdt.internal.compiler.lookup.SyntheticArgumentBinding;
import org.eclipse.jdt.internal.compiler.lookup.SyntheticMethodBinding;
import org.eclipse.jdt.internal.compiler.lookup.TagBits;
import org.eclipse.jdt.internal.compiler.lookup.TypeBinding;
import org.eclipse.jdt.internal.compiler.lookup.TypeConstants;
import org.eclipse.jdt.internal.compiler.lookup.TypeIds;
import org.eclipse.jdt.internal.compiler.lookup.TypeVariableBinding;
import org.eclipse.jdt.internal.compiler.problem.ProblemSeverities;
import org.eclipse.jdt.internal.compiler.problem.ShouldNotImplement;
import org.eclipse.jdt.internal.compiler.util.Messages;
import org.eclipse.jdt.internal.compiler.util.Util;

/**
* Represents a class file wrapper on bytes, it is aware of its actual
* type name.
*
* Public APIs are listed below:
*
* byte[] getBytes();
*    Answer the actual bytes of the class file
*
* char[][] getCompoundName();
*     Answer the compound name of the class file.
*     For example, {{java}, {util}, {Hashtable}}.
*
* byte[] getReducedBytes();
*     Answer a smaller byte format, which is only contains some structural
*      information. Those bytes are decodable with a regular class file reader,
*      such as DietClassFileReader
*/
public class ClassFile implements TypeConstants, TypeIds {

  private byte[] bytes;
  public CodeStream codeStream;
  public ConstantPool constantPool;

  public int constantPoolOffset;

  // the header contains all the bytes till the end of the constant pool
  public byte[] contents;

  public int contentsOffset;

  protected boolean creatingProblemType;

  public ClassFile enclosingClassFile;
  public byte[] header;
  // that collection contains all the remaining bytes of the .class file
  public int headerOffset;
  public Set innerClassesBindings;
  public int methodCount;
  public int methodCountOffset;
  // pool managment
  boolean isShared = false;
  // used to generate private access methods
  // debug and stack map attributes
  public int produceAttributes;
  public SourceTypeBinding referenceBinding;
  public boolean isNestedType;
  public long targetJDK;

  public List missingTypes = null;

  public Set visitedTypes;

  public static final int INITIAL_CONTENTS_SIZE = 400;
  public static final int INITIAL_HEADER_SIZE = 1500;
  public static final int INNER_CLASSES_SIZE = 5;

  /**
   * INTERNAL USE-ONLY
   * Request the creation of a ClassFile compatible representation of a problematic type
   *
   * @param typeDeclaration org.eclipse.jdt.internal.compiler.ast.TypeDeclaration
   * @param unitResult org.eclipse.jdt.internal.compiler.CompilationUnitResult
   */
  public static void createProblemType(TypeDeclaration typeDeclaration, CompilationResult unitResult) {
    SourceTypeBinding typeBinding = typeDeclaration.binding;
    ClassFile classFile = ClassFile.getNewInstance(typeBinding);
    classFile.initialize(typeBinding, null, true);

    if (typeBinding.hasMemberTypes()) {
      // see bug 180109
      ReferenceBinding[] members = typeBinding.memberTypes;
      for (int i = 0, l = members.length; i < l; i++)
        classFile.recordInnerClasses(members[i]);
    }
    // TODO (olivier) handle cases where a field cannot be generated (name too long)
    // TODO (olivier) handle too many methods
    // inner attributes
    if (typeBinding.isNestedType()) {
      classFile.recordInnerClasses(typeBinding);
    }
    TypeVariableBinding[] typeVariables = typeBinding.typeVariables();
    for (int i = 0, max = typeVariables.length; i < max; i++) {
      TypeVariableBinding typeVariableBinding = typeVariables[i];
      if ((typeVariableBinding.tagBits & TagBits.ContainsNestedTypeReferences) != 0) {
        Util.recordNestedType(classFile, typeVariableBinding);
      }
    }
    // add its fields
    FieldBinding[] fields = typeBinding.fields();
    if ((fields != null) && (fields != Binding.NO_FIELDS)) {
      classFile.addFieldInfos();
    } else {
      // we have to set the number of fields to be equals to 0
      classFile.contents[classFile.contentsOffset++] = 0;
      classFile.contents[classFile.contentsOffset++] = 0;
    }
    // leave some space for the methodCount
    classFile.setForMethodInfos();
    // add its user defined methods
    int problemsLength;
    CategorizedProblem[] problems = unitResult.getErrors();
    if (problems == null) {
      problems = new CategorizedProblem[0];
    }
    CategorizedProblem[] problemsCopy = new CategorizedProblem[problemsLength = problems.length];
    System.arraycopy(problems, 0, problemsCopy, 0, problemsLength);

    AbstractMethodDeclaration[] methodDecls = typeDeclaration.methods;
    if (methodDecls != null) {
      if (typeBinding.isInterface()) {
        // we cannot create problem methods for an interface. So we have to generate a clinit
        // which should contain all the problem
        classFile.addProblemClinit(problemsCopy);
        for (int i = 0, length = methodDecls.length; i < length; i++) {
          AbstractMethodDeclaration methodDecl = methodDecls[i];
          MethodBinding method = methodDecl.binding;
          if (method == null || method.isConstructor()) continue;
          method.modifiers = ClassFileConstants.AccPublic | ClassFileConstants.AccAbstract;
          classFile.addAbstractMethod(methodDecl, method);
        }
      } else {
        for (int i = 0, length = methodDecls.length; i < length; i++) {
          AbstractMethodDeclaration methodDecl = methodDecls[i];
          MethodBinding method = methodDecl.binding;
          if (method == null) continue;
          if (method.isConstructor()) {
            classFile.addProblemConstructor(methodDecl, method, problemsCopy);
          } else if (method.isAbstract()) {
            classFile.addAbstractMethod(methodDecl, method);
          } else {
            classFile.addProblemMethod(methodDecl, method, problemsCopy);
          }
        }
      }
      // add abstract methods
      classFile.addDefaultAbstractMethods();
    }

    // propagate generation of (problem) member types
    if (typeDeclaration.memberTypes != null) {
      for (int i = 0, max = typeDeclaration.memberTypes.length; i < max; i++) {
        TypeDeclaration memberType = typeDeclaration.memberTypes[i];
        if (memberType.binding != null) {
          ClassFile.createProblemType(memberType, unitResult);
        }
      }
    }
    classFile.addAttributes();
    unitResult.record(typeBinding.constantPoolName(), classFile);
  }
  public static ClassFile getNewInstance(SourceTypeBinding typeBinding) {
    LookupEnvironment env = typeBinding.scope.environment();
    return env.classFilePool.acquire(typeBinding);
  }
  /**
   * INTERNAL USE-ONLY
   * This methods creates a new instance of the receiver.
   */
  protected ClassFile() {
    // default constructor for subclasses
  }

  public ClassFile(SourceTypeBinding typeBinding) {
    // default constructor for subclasses
    this.constantPool = new ConstantPool(this);
    final CompilerOptions options = typeBinding.scope.compilerOptions();
    this.targetJDK = options.targetJDK;
    this.produceAttributes = options.produceDebugAttributes;
    this.referenceBinding = typeBinding;
    this.isNestedType = typeBinding.isNestedType();
    if (this.targetJDK >= ClassFileConstants.JDK1_6) {
      this.produceAttributes |= ClassFileConstants.ATTR_STACK_MAP_TABLE;
      this.codeStream = new StackMapFrameCodeStream(this);
    } else if (this.targetJDK == ClassFileConstants.CLDC_1_1) {
      this.targetJDK = ClassFileConstants.JDK1_1; // put back 45.3
      this.produceAttributes |= ClassFileConstants.ATTR_STACK_MAP;
      this.codeStream = new StackMapFrameCodeStream(this);
    } else {
      this.codeStream = new CodeStream(this);
    }
    initByteArrays();
  }

  /**
   * INTERNAL USE-ONLY
   * Generate the byte for a problem method info that correspond to a bogus method.
   *
   * @param method org.eclipse.jdt.internal.compiler.ast.AbstractMethodDeclaration
   * @param methodBinding org.eclipse.jdt.internal.compiler.nameloopkup.MethodBinding
   */
  public void addAbstractMethod(
      AbstractMethodDeclaration method,
      MethodBinding methodBinding) {

    this.generateMethodInfoHeader(methodBinding);
    int methodAttributeOffset = this.contentsOffset;
    int attributeNumber = this.generateMethodInfoAttributes(methodBinding);
    completeMethodInfo(methodBinding, methodAttributeOffset, attributeNumber);
  }

  /**
   * INTERNAL USE-ONLY
   * This methods generate all the attributes for the receiver.
   * For a class they could be:
   * - source file attribute
   * - inner classes attribute
   * - deprecated attribute
   */
  public void addAttributes() {
    // update the method count
    this.contents[this.methodCountOffset++] = (byte) (this.methodCount >> 8);
    this.contents[this.methodCountOffset] = (byte) this.methodCount;

    int attributesNumber = 0;
    // leave two bytes for the number of attributes and store the current offset
    int attributeOffset = this.contentsOffset;
    this.contentsOffset += 2;

    // source attribute
    if ((this.produceAttributes & ClassFileConstants.ATTR_SOURCE) != 0) {
      String fullFileName =
        new String(this.referenceBinding.scope.referenceCompilationUnit().getFileName());
      fullFileName = fullFileName.replace('\\', '/');
      int lastIndex = fullFileName.lastIndexOf('/');
      if (lastIndex != -1) {
        fullFileName = fullFileName.substring(lastIndex + 1, fullFileName.length());
      }
      attributesNumber += generateSourceAttribute(fullFileName);
    }
    // Deprecated attribute
    if (this.referenceBinding.isDeprecated()) {
      // check that there is enough space to write all the bytes for the field info corresponding
      // to the @fieldBinding
      attributesNumber += generateDeprecatedAttribute();
    }
    // add signature attribute
    char[] genericSignature = this.referenceBinding.genericSignature();
    if (genericSignature != null) {
      attributesNumber += generateSignatureAttribute(genericSignature);
    }
    if (this.targetJDK >= ClassFileConstants.JDK1_5
        && this.referenceBinding.isNestedType()
        && !this.referenceBinding.isMemberType()) {
      // add enclosing method attribute (1.5 mode only)
      attributesNumber += generateEnclosingMethodAttribute();
    }
    if (this.targetJDK >= ClassFileConstants.JDK1_4) {
      TypeDeclaration typeDeclaration = this.referenceBinding.scope.referenceContext;
      if (typeDeclaration != null) {
        final Annotation[] annotations = typeDeclaration.annotations;
        if (annotations != null) {
          attributesNumber += generateRuntimeAnnotations(annotations);
        }
      }
    }

    if (this.referenceBinding.isHierarchyInconsistent()) {
      ReferenceBinding superclass = this.referenceBinding.superclass;
      if (superclass != null) {
        this.missingTypes = superclass.collectMissingTypes(this.missingTypes);
      }
      ReferenceBinding[] superInterfaces = this.referenceBinding.superInterfaces();
      for (int i = 0, max = superInterfaces.length; i < max; i++) {
        this.missingTypes = superInterfaces[i].collectMissingTypes(this.missingTypes);
      }
      attributesNumber += generateHierarchyInconsistentAttribute();
    }
    // Inner class attribute
    int numberOfInnerClasses = this.innerClassesBindings == null ? 0 : this.innerClassesBindings.size();
    if (numberOfInnerClasses != 0) {
      ReferenceBinding[] innerClasses = new ReferenceBinding[numberOfInnerClasses];
      this.innerClassesBindings.toArray(innerClasses);
      Arrays.sort(innerClasses, new Comparator() {
        public int compare(Object o1, Object o2) {
          TypeBinding binding1 = (TypeBinding) o1;
          TypeBinding binding2 = (TypeBinding) o2;
          return CharOperation.compareTo(binding1.constantPoolName(), binding2.constantPoolName());
        }
      });
      attributesNumber += generateInnerClassAttribute(numberOfInnerClasses, innerClasses);
    }
    if (this.missingTypes != null) {
      generateMissingTypesAttribute();
      attributesNumber++;
    }
    // update the number of attributes
    if (attributeOffset + 2 >= this.contents.length) {
      resizeContents(2);
    }
    this.contents[attributeOffset++] = (byte) (attributesNumber >> 8);
    this.contents[attributeOffset] = (byte) attributesNumber;

    // resynchronize all offsets of the classfile
    this.header = this.constantPool.poolContent;
    this.headerOffset = this.constantPool.currentOffset;
    int constantPoolCount = this.constantPool.currentIndex;
    this.header[this.constantPoolOffset++] = (byte) (constantPoolCount >> 8);
    this.header[this.constantPoolOffset] = (byte) constantPoolCount;
  }
  /**
   * INTERNAL USE-ONLY
   * This methods generate all the default abstract method infos that correpond to
   * the abstract methods inherited from superinterfaces.
   */
  public void addDefaultAbstractMethods() { // default abstract methods
    MethodBinding[] defaultAbstractMethods =
      this.referenceBinding.getDefaultAbstractMethods();
    for (int i = 0, max = defaultAbstractMethods.length; i < max; i++) {
      MethodBinding methodBinding = defaultAbstractMethods[i];
      generateMethodInfoHeader(methodBinding);
      int methodAttributeOffset = this.contentsOffset;
      int attributeNumber = generateMethodInfoAttributes(methodBinding);
      completeMethodInfo(methodBinding, methodAttributeOffset, attributeNumber);
    }
  }

  private int addFieldAttributes(FieldBinding fieldBinding, int fieldAttributeOffset) {
    int attributesNumber = 0;
    // 4.7.2 only static constant fields get a ConstantAttribute
    // Generate the constantValueAttribute
    Constant fieldConstant = fieldBinding.constant();
    if (fieldConstant != Constant.NotAConstant){
      attributesNumber += generateConstantValueAttribute(fieldConstant, fieldBinding, fieldAttributeOffset);
    }
    if (this.targetJDK < ClassFileConstants.JDK1_5 && fieldBinding.isSynthetic()) {
      attributesNumber += generateSyntheticAttribute();
    }
    if (fieldBinding.isDeprecated()) {
      attributesNumber += generateDeprecatedAttribute();
    }
    // add signature attribute
    char[] genericSignature = fieldBinding.genericSignature();
    if (genericSignature != null) {
      attributesNumber += generateSignatureAttribute(genericSignature);
    }
    if (this.targetJDK >= ClassFileConstants.JDK1_4) {
      FieldDeclaration fieldDeclaration = fieldBinding.sourceField();
      if (fieldDeclaration != null) {
        Annotation[] annotations = fieldDeclaration.annotations;
        if (annotations != null) {
          attributesNumber += generateRuntimeAnnotations(annotations);
        }
      }
    }
    if ((fieldBinding.tagBits & TagBits.HasMissingType) != 0) {
      this.missingTypes = fieldBinding.type.collectMissingTypes(this.missingTypes);
    }
    return attributesNumber;
  }

  /**
   * INTERNAL USE-ONLY
   * This methods generates the bytes for the given field binding
   * @param fieldBinding the given field binding
   */
  private void addFieldInfo(FieldBinding fieldBinding) {
    // check that there is enough space to write all the bytes for the field info corresponding
    // to the @fieldBinding
    if (this.contentsOffset + 8 >= this.contents.length) {
      resizeContents(8);
    }
    // Now we can generate all entries into the byte array
    // First the accessFlags
    int accessFlags = fieldBinding.getAccessFlags();
    if (this.targetJDK < ClassFileConstants.JDK1_5) {
      // pre 1.5, synthetic was an attribute, not a modifier
      accessFlags &= ~ClassFileConstants.AccSynthetic;
    }
    this.contents[this.contentsOffset++] = (byte) (accessFlags >> 8);
    this.contents[this.contentsOffset++] = (byte) accessFlags;
    // Then the nameIndex
    int nameIndex = this.constantPool.literalIndex(fieldBinding.name);
    this.contents[this.contentsOffset++] = (byte) (nameIndex >> 8);
    this.contents[this.contentsOffset++] = (byte) nameIndex;
    // Then the descriptorIndex
    int descriptorIndex = this.constantPool.literalIndex(fieldBinding.type);
    this.contents[this.contentsOffset++] = (byte) (descriptorIndex >> 8);
    this.contents[this.contentsOffset++] = (byte) descriptorIndex;
    int fieldAttributeOffset = this.contentsOffset;
    int attributeNumber = 0;
    // leave some space for the number of attributes
    this.contentsOffset += 2;
    attributeNumber += addFieldAttributes(fieldBinding, fieldAttributeOffset);
    if (this.contentsOffset + 2 >= this.contents.length) {
      resizeContents(2);
    }
    this.contents[fieldAttributeOffset++] = (byte) (attributeNumber >> 8);
    this.contents[fieldAttributeOffset] = (byte) attributeNumber;
  }

  /**
   * INTERNAL USE-ONLY
   * This methods generate all the fields infos for the receiver.
   * This includes:
   * - a field info for each defined field of that class
   * - a field info for each synthetic field (e.g. this$0)
   */
  /**
   * INTERNAL USE-ONLY
   * This methods generate all the fields infos for the receiver.
   * This includes:
   * - a field info for each defined field of that class
   * - a field info for each synthetic field (e.g. this$0)
   */
  public void addFieldInfos() {
    SourceTypeBinding currentBinding = this.referenceBinding;
    FieldBinding[] syntheticFields = currentBinding.syntheticFields();
    int fieldCount =   currentBinding.fieldCount() + (syntheticFields == null ? 0 : syntheticFields.length);

    // write the number of fields
    if (fieldCount > 0xFFFF) {
      this.referenceBinding.scope.problemReporter().tooManyFields(this.referenceBinding.scope.referenceType());
    }
    this.contents[this.contentsOffset++] = (byte) (fieldCount >> 8);
    this.contents[this.contentsOffset++] = (byte) fieldCount;

    FieldDeclaration[] fieldDecls = currentBinding.scope.referenceContext.fields;
    for (int i = 0, max = fieldDecls == null ? 0 : fieldDecls.length; i < max; i++) {
      FieldDeclaration fieldDecl = fieldDecls[i];
      if (fieldDecl.binding != null) {
        addFieldInfo(fieldDecl.binding);
      }
    }

    if (syntheticFields != null) {
      for (int i = 0, max = syntheticFields.length; i < max; i++) {
        addFieldInfo(syntheticFields[i]);
      }
    }
  }

  private void addMissingAbstractProblemMethod(MethodDeclaration methodDeclaration, MethodBinding methodBinding, CategorizedProblem problem, CompilationResult compilationResult) {
    // always clear the strictfp/native/abstract bit for a problem method
    generateMethodInfoHeader(methodBinding, methodBinding.modifiers & ~(ClassFileConstants.AccStrictfp | ClassFileConstants.AccNative | ClassFileConstants.AccAbstract));
    int methodAttributeOffset = this.contentsOffset;
    int attributeNumber = generateMethodInfoAttributes(methodBinding);

    // Code attribute
    attributeNumber++;

    int codeAttributeOffset = this.contentsOffset;
    generateCodeAttributeHeader();
    StringBuffer buffer = new StringBuffer(25);
    buffer.append("\t"  + problem.getMessage() + "\n" ); //$NON-NLS-1$ //$NON-NLS-2$
    buffer.insert(0, Messages.compilation_unresolvedProblem);
    String problemString = buffer.toString();

    this.codeStream.init(this);
    this.codeStream.preserveUnusedLocals = true;
    this.codeStream.initializeMaxLocals(methodBinding);

    // return codeStream.generateCodeAttributeForProblemMethod(comp.options.runtimeExceptionNameForCompileError, "")
    this.codeStream.generateCodeAttributeForProblemMethod(problemString);

    completeCodeAttributeForMissingAbstractProblemMethod(
      methodBinding,
      codeAttributeOffset,
      compilationResult.getLineSeparatorPositions(),
      problem.getSourceLineNumber());

    completeMethodInfo(methodBinding, methodAttributeOffset, attributeNumber);
  }

  /**
   * INTERNAL USE-ONLY
   * Generate the byte for a problem clinit method info that correspond to a boggus method.
   *
   * @param problems org.eclipse.jdt.internal.compiler.problem.Problem[]
   */
  public void addProblemClinit(CategorizedProblem[] problems) {
    generateMethodInfoHeaderForClinit();
    // leave two spaces for the number of attributes
    this.contentsOffset -= 2;
    int attributeOffset = this.contentsOffset;
    this.contentsOffset += 2;
    int attributeNumber = 0;

    int codeAttributeOffset = this.contentsOffset;
    generateCodeAttributeHeader();
    this.codeStream.resetForProblemClinit(this);
    String problemString = "" ; //$NON-NLS-1$
    int problemLine = 0;
    if (problems != null) {
      int max = problems.length;
      StringBuffer buffer = new StringBuffer(25);
      int count = 0;
      for (int i = 0; i < max; i++) {
        CategorizedProblem problem = problems[i];
        if ((problem != null) && (problem.isError())) {
          buffer.append("\t"  +problem.getMessage() + "\n" ); //$NON-NLS-1$ //$NON-NLS-2$
          count++;
          if (problemLine == 0) {
            problemLine = problem.getSourceLineNumber();
          }
          problems[i] = null;
        }
      } // insert the top line afterwards, once knowing how many problems we have to consider
      if (count > 1) {
        buffer.insert(0, Messages.compilation_unresolvedProblems);
      } else {
        buffer.insert(0, Messages.compilation_unresolvedProblem);
      }
      problemString = buffer.toString();
    }

    // return codeStream.generateCodeAttributeForProblemMethod(comp.options.runtimeExceptionNameForCompileError, "")
    this.codeStream.generateCodeAttributeForProblemMethod(problemString);
    attributeNumber++; // code attribute
    completeCodeAttributeForClinit(
      codeAttributeOffset,
      problemLine);
    if (this.contentsOffset + 2 >= this.contents.length) {
      resizeContents(2);
    }
    this.contents[attributeOffset++] = (byte) (attributeNumber >> 8);
    this.contents[attributeOffset] = (byte) attributeNumber;
  }

  /**
   * INTERNAL USE-ONLY
   * Generate the byte for a problem method info that correspond to a boggus constructor.
   *
   * @param method org.eclipse.jdt.internal.compiler.ast.AbstractMethodDeclaration
   * @param methodBinding org.eclipse.jdt.internal.compiler.nameloopkup.MethodBinding
   * @param problems org.eclipse.jdt.internal.compiler.problem.Problem[]
   */
  public void addProblemConstructor(
    AbstractMethodDeclaration method,
    MethodBinding methodBinding,
    CategorizedProblem[] problems) {

    // always clear the strictfp/native/abstract bit for a problem method
    generateMethodInfoHeader(methodBinding, methodBinding.modifiers & ~(ClassFileConstants.AccStrictfp | ClassFileConstants.AccNative | ClassFileConstants.AccAbstract));
    int methodAttributeOffset = this.contentsOffset;
    int attributesNumber = generateMethodInfoAttributes(methodBinding);

    // Code attribute
    attributesNumber++;
    int codeAttributeOffset = this.contentsOffset;
    generateCodeAttributeHeader();
    this.codeStream.reset(method, this);
    String problemString = "" ; //$NON-NLS-1$
    int problemLine = 0;
    if (problems != null) {
      int max = problems.length;
      StringBuffer buffer = new StringBuffer(25);
      int count = 0;
      for (int i = 0; i < max; i++) {
        CategorizedProblem problem = problems[i];
        if ((problem != null) && (problem.isError())) {
          buffer.append("\t"  +problem.getMessage() + "\n" ); //$NON-NLS-1$ //$NON-NLS-2$
          count++;
          if (problemLine == 0) {
            problemLine = problem.getSourceLineNumber();
          }
        }
      } // insert the top line afterwards, once knowing how many problems we have to consider
      if (count > 1) {
        buffer.insert(0, Messages.compilation_unresolvedProblems);
      } else {
        buffer.insert(0, Messages.compilation_unresolvedProblem);
      }
      problemString = buffer.toString();
    }

    // return codeStream.generateCodeAttributeForProblemMethod(comp.options.runtimeExceptionNameForCompileError, "")
    this.codeStream.generateCodeAttributeForProblemMethod(problemString);
    completeCodeAttributeForProblemMethod(
      method,
      methodBinding,
      codeAttributeOffset,
      ((SourceTypeBinding) methodBinding.declaringClass)
        .scope
        .referenceCompilationUnit()
        .compilationResult
        .getLineSeparatorPositions(),
      problemLine);
    completeMethodInfo(methodBinding, methodAttributeOffset, attributesNumber);
  }
  /**
   * INTERNAL USE-ONLY
   * Generate the byte for a problem method info that correspond to a boggus constructor.
   * Reset the position inside the contents byte array to the savedOffset.
   *
   * @param method org.eclipse.jdt.internal.compiler.ast.AbstractMethodDeclaration
   * @param methodBinding org.eclipse.jdt.internal.compiler.nameloopkup.MethodBinding
   * @param problems org.eclipse.jdt.internal.compiler.problem.Problem[]
   * @param savedOffset <CODE>int</CODE>
   */
  public void addProblemConstructor(
    AbstractMethodDeclaration method,
    MethodBinding methodBinding,
    CategorizedProblem[] problems,
    int savedOffset) {
    // we need to move back the contentsOffset to the value at the beginning of the method
    this.contentsOffset = savedOffset;
    this.methodCount--; // we need to remove the method that causes the problem
    addProblemConstructor(method, methodBinding, problems);
  }
  /**
   * INTERNAL USE-ONLY
   * Generate the byte for a problem method info that correspond to a boggus method.
   *
   * @param method org.eclipse.jdt.internal.compiler.ast.AbstractMethodDeclaration
   * @param methodBinding org.eclipse.jdt.internal.compiler.nameloopkup.MethodBinding
   * @param problems org.eclipse.jdt.internal.compiler.problem.Problem[]
   */
  public void addProblemMethod(
    AbstractMethodDeclaration method,
    MethodBinding methodBinding,
    CategorizedProblem[] problems) {
    if (methodBinding.isAbstract() && methodBinding.declaringClass.isInterface()) {
      method.abort(ProblemSeverities.AbortType, null);
    }
    // always clear the strictfp/native/abstract bit for a problem method
    generateMethodInfoHeader(methodBinding, methodBinding.modifiers & ~(ClassFileConstants.AccStrictfp | ClassFileConstants.AccNative | ClassFileConstants.AccAbstract));
    int methodAttributeOffset = this.contentsOffset;
    int attributesNumber = generateMethodInfoAttributes(methodBinding);

    // Code attribute
    attributesNumber++;

    int codeAttributeOffset = this.contentsOffset;
    generateCodeAttributeHeader();
    this.codeStream.reset(method, this);
    String problemString = "" ; //$NON-NLS-1$
    int problemLine = 0;
    if (problems != null) {
      int max = problems.length;
      StringBuffer buffer = new StringBuffer(25);
      int count = 0;
      for (int i = 0; i < max; i++) {
        CategorizedProblem problem = problems[i];
        if ((problem != null)
          && (problem.isError())
          && (problem.getSourceStart() >= method.declarationSourceStart)
          && (problem.getSourceEnd() <= method.declarationSourceEnd)) {
          buffer.append("\t"  +problem.getMessage() + "\n" ); //$NON-NLS-1$ //$NON-NLS-2$
          count++;
          if (problemLine == 0) {
            problemLine = problem.getSourceLineNumber();
          }
          problems[i] = null;
        }
      } // insert the top line afterwards, once knowing how many problems we have to consider
      if (count > 1) {
        buffer.insert(0, Messages.compilation_unresolvedProblems);
      } else {
        buffer.insert(0, Messages.compilation_unresolvedProblem);
      }
      problemString = buffer.toString();
    }

    // return codeStream.generateCodeAttributeForProblemMethod(comp.options.runtimeExceptionNameForCompileError, "")
    this.codeStream.generateCodeAttributeForProblemMethod(problemString);
    completeCodeAttributeForProblemMethod(
      method,
      methodBinding,
      codeAttributeOffset,
      ((SourceTypeBinding) methodBinding.declaringClass)
        .scope
        .referenceCompilationUnit()
        .compilationResult
        .getLineSeparatorPositions(),
      problemLine);
    completeMethodInfo(methodBinding, methodAttributeOffset, attributesNumber);
  }

  /**
   * INTERNAL USE-ONLY
   * Generate the byte for a problem method info that correspond to a boggus method.
   * Reset the position inside the contents byte array to the savedOffset.
   *
   * @param method org.eclipse.jdt.internal.compiler.ast.AbstractMethodDeclaration
   * @param methodBinding org.eclipse.jdt.internal.compiler.nameloopkup.MethodBinding
   * @param problems org.eclipse.jdt.internal.compiler.problem.Problem[]
   * @param savedOffset <CODE>int</CODE>
   */
  public void addProblemMethod(
    AbstractMethodDeclaration method,
    MethodBinding methodBinding,
    CategorizedProblem[] problems,
    int savedOffset) {
    // we need to move back the contentsOffset to the value at the beginning of the method
    this.contentsOffset = savedOffset;
    this.methodCount--; // we need to remove the method that causes the problem
    addProblemMethod(method, methodBinding, problems);
  }

  /**
   * INTERNAL USE-ONLY
   * Generate the byte for all the special method infos.
   * They are:
   * - synthetic access methods
   * - default abstract methods
   */
  public void addSpecialMethods() {

    // add all methods (default abstract methods and synthetic)

    // default abstract methods
    generateMissingAbstractMethods(this.referenceBinding.scope.referenceType().missingAbstractMethods, this.referenceBinding.scope.referenceCompilationUnit().compilationResult);

    MethodBinding[] defaultAbstractMethods = this.referenceBinding.getDefaultAbstractMethods();
    for (int i = 0, max = defaultAbstractMethods.length; i < max; i++) {
      MethodBinding methodBinding = defaultAbstractMethods[i];
      generateMethodInfoHeader(methodBinding);
      int methodAttributeOffset = this.contentsOffset;
      int attributeNumber = generateMethodInfoAttributes(methodBinding);
      completeMethodInfo(methodBinding, methodAttributeOffset, attributeNumber);
    }
    // add synthetic methods infos
    SyntheticMethodBinding[] syntheticMethods = this.referenceBinding.syntheticMethods();
    if (syntheticMethods != null) {
      for (int i = 0, max = syntheticMethods.length; i < max; i++) {
        SyntheticMethodBinding syntheticMethod = syntheticMethods[i];
        switch (syntheticMethod.purpose) {
          case SyntheticMethodBinding.FieldReadAccess :
          case SyntheticMethodBinding.SuperFieldReadAccess :
            // generate a method info to emulate an reading access to
            // a non-accessible field
            addSyntheticFieldReadAccessMethod(syntheticMethod);
            break;
          case SyntheticMethodBinding.FieldWriteAccess :
          case SyntheticMethodBinding.SuperFieldWriteAccess :
            // generate a method info to emulate an writing access to
            // a non-accessible field
            addSyntheticFieldWriteAccessMethod(syntheticMethod);
            break;
          case SyntheticMethodBinding.MethodAccess :
          case SyntheticMethodBinding.SuperMethodAccess :
          case SyntheticMethodBinding.BridgeMethod :
            // generate a method info to emulate an access to a non-accessible method / super-method or bridge method
            addSyntheticMethodAccessMethod(syntheticMethod);
            break;
          case SyntheticMethodBinding.ConstructorAccess :
            // generate a method info to emulate an access to a non-accessible constructor
            addSyntheticConstructorAccessMethod(syntheticMethod);
            break;
          case SyntheticMethodBinding.EnumValues :
            // generate a method info to define <enum>#values()
            addSyntheticEnumValuesMethod(syntheticMethod);
            break;
          case SyntheticMethodBinding.EnumValueOf :
            // generate a method info to define <enum>#valueOf(String)
            addSyntheticEnumValueOfMethod(syntheticMethod);
            break;
          case SyntheticMethodBinding.SwitchTable :
            // generate a method info to define the switch table synthetic method
            addSyntheticSwitchTable(syntheticMethod);
            break;
          case SyntheticMethodBinding.TooManyEnumsConstants :
            addSyntheticEnumInitializationMethod(syntheticMethod);
        }
      }
    }
  }

  /**
   * INTERNAL USE-ONLY
   * Generate the bytes for a synthetic method that provides an access to a private constructor.
   *
   * @param methodBinding org.eclipse.jdt.internal.compiler.nameloopkup.SyntheticAccessMethodBinding
   */
  public void addSyntheticConstructorAccessMethod(SyntheticMethodBinding methodBinding) {
    generateMethodInfoHeader(methodBinding);
    int methodAttributeOffset = this.contentsOffset;
    // this will add exception attribute, synthetic attribute, deprecated attribute,...
    int attributeNumber = generateMethodInfoAttributes(methodBinding);
    // Code attribute
    int codeAttributeOffset = this.contentsOffset;
    attributeNumber++; // add code attribute
    generateCodeAttributeHeader();
    this.codeStream.init(this);
    this.codeStream.generateSyntheticBodyForConstructorAccess(methodBinding);
    completeCodeAttributeForSyntheticMethod(
      methodBinding,
      codeAttributeOffset,
      ((SourceTypeBinding) methodBinding.declaringClass)
        .scope
        .referenceCompilationUnit()
        .compilationResult
        .getLineSeparatorPositions());
    // update the number of attributes
    this.contents[methodAttributeOffset++] = (byte) (attributeNumber >> 8);
    this.contents[methodAttributeOffset] = (byte) attributeNumber;
  }

  /**
   * INTERNAL USE-ONLY
   *  Generate the bytes for a synthetic method that implements Enum#valueOf(String) for a given enum type
   *
   * @param methodBinding org.eclipse.jdt.internal.compiler.nameloopkup.SyntheticAccessMethodBinding
   */
  public void addSyntheticEnumValueOfMethod(SyntheticMethodBinding methodBinding) {
    generateMethodInfoHeader(methodBinding);
    int methodAttributeOffset = this.contentsOffset;
    // this will add exception attribute, synthetic attribute, deprecated attribute,...
    int attributeNumber = generateMethodInfoAttributes(methodBinding);
    // Code attribute
    int codeAttributeOffset = this.contentsOffset;
    attributeNumber++; // add code attribute
    generateCodeAttributeHeader();
    this.codeStream.init(this);
    this.codeStream.generateSyntheticBodyForEnumValueOf(methodBinding);
    completeCodeAttributeForSyntheticMethod(
      methodBinding,
      codeAttributeOffset,
      ((SourceTypeBinding) methodBinding.declaringClass)
        .scope
        .referenceCompilationUnit()
        .compilationResult
        .getLineSeparatorPositions());
    // update the number of attributes
    this.contents[methodAttributeOffset++] = (byte) (attributeNumber >> 8);
    this.contents[methodAttributeOffset] = (byte) attributeNumber;
  }

  /**
   * INTERNAL USE-ONLY
   *  Generate the bytes for a synthetic method that implements Enum#values() for a given enum type
   *
   * @param methodBinding org.eclipse.jdt.internal.compiler.nameloopkup.SyntheticAccessMethodBinding
   */
  public void addSyntheticEnumValuesMethod(SyntheticMethodBinding methodBinding) {
    generateMethodInfoHeader(methodBinding);
    int methodAttributeOffset = this.contentsOffset;
    // this will add exception attribute, synthetic attribute, deprecated attribute,...
    int attributeNumber = generateMethodInfoAttributes(methodBinding);
    // Code attribute
    int codeAttributeOffset = this.contentsOffset;
    attributeNumber++; // add code attribute
    generateCodeAttributeHeader();
    this.codeStream.init(this);
    this.codeStream.generateSyntheticBodyForEnumValues(methodBinding);
    completeCodeAttributeForSyntheticMethod(
      methodBinding,
      codeAttributeOffset,
      ((SourceTypeBinding) methodBinding.declaringClass)
        .scope
        .referenceCompilationUnit()
        .compilationResult
        .getLineSeparatorPositions());
    // update the number of attributes
    this.contents[methodAttributeOffset++] = (byte) (attributeNumber >> 8);
    this.contents[methodAttributeOffset] = (byte) attributeNumber;
  }

  public void addSyntheticEnumInitializationMethod(SyntheticMethodBinding methodBinding) {
    generateMethodInfoHeader(methodBinding);
    int methodAttributeOffset = this.contentsOffset;
    // this will add exception attribute, synthetic attribute, deprecated attribute,...
    int attributeNumber = generateMethodInfoAttributes(methodBinding);
    // Code attribute
    int codeAttributeOffset = this.contentsOffset;
    attributeNumber++; // add code attribute
    generateCodeAttributeHeader();
    this.codeStream.init(this);
    this.codeStream.generateSyntheticBodyForEnumInitializationMethod(methodBinding);
    completeCodeAttributeForSyntheticMethod(
      methodBinding,
      codeAttributeOffset,
      ((SourceTypeBinding) methodBinding.declaringClass)
        .scope
        .referenceCompilationUnit()
        .compilationResult
        .getLineSeparatorPositions());
    // update the number of attributes
    this.contents[methodAttributeOffset++] = (byte) (attributeNumber >> 8);
    this.contents[methodAttributeOffset] = (byte) attributeNumber;
  }
  /**
   * INTERNAL USE-ONLY
   * Generate the byte for a problem method info that correspond to a synthetic method that
   * generate an read access to a private field.
   *
   * @param methodBinding org.eclipse.jdt.internal.compiler.nameloopkup.SyntheticAccessMethodBinding
   */
  public void addSyntheticFieldReadAccessMethod(SyntheticMethodBinding methodBinding) {
    generateMethodInfoHeader(methodBinding);
    int methodAttributeOffset = this.contentsOffset;
    // this will add exception attribute, synthetic attribute, deprecated attribute,...
    int attributeNumber = generateMethodInfoAttributes(methodBinding);
    // Code attribute
    int codeAttributeOffset = this.contentsOffset;
    attributeNumber++; // add code attribute
    generateCodeAttributeHeader();
    this.codeStream.init(this);
    this.codeStream.generateSyntheticBodyForFieldReadAccess(methodBinding);
    completeCodeAttributeForSyntheticMethod(
      methodBinding,
      codeAttributeOffset,
      ((SourceTypeBinding) methodBinding.declaringClass)
        .scope
        .referenceCompilationUnit()
        .compilationResult
        .getLineSeparatorPositions());
    // update the number of attributes
    this.contents[methodAttributeOffset++] = (byte) (attributeNumber >> 8);
    this.contents[methodAttributeOffset] = (byte) attributeNumber;
  }

  /**
   * INTERNAL USE-ONLY
   * Generate the byte for a problem method info that correspond to a synthetic method that
   * generate an write access to a private field.
   *
   * @param methodBinding org.eclipse.jdt.internal.compiler.nameloopkup.SyntheticAccessMethodBinding
   */
  public void addSyntheticFieldWriteAccessMethod(SyntheticMethodBinding methodBinding) {
    generateMethodInfoHeader(methodBinding);
    int methodAttributeOffset = this.contentsOffset;
    // this will add exception attribute, synthetic attribute, deprecated attribute,...
    int attributeNumber = generateMethodInfoAttributes(methodBinding);
    // Code attribute
    int codeAttributeOffset = this.contentsOffset;
    attributeNumber++; // add code attribute
    generateCodeAttributeHeader();
    this.codeStream.init(this);
    this.codeStream.generateSyntheticBodyForFieldWriteAccess(methodBinding);
    completeCodeAttributeForSyntheticMethod(
      methodBinding,
      codeAttributeOffset,
      ((SourceTypeBinding) methodBinding.declaringClass)
        .scope
        .referenceCompilationUnit()
        .compilationResult
        .getLineSeparatorPositions());
    // update the number of attributes
    this.contents[methodAttributeOffset++] = (byte) (attributeNumber >> 8);
    this.contents[methodAttributeOffset] = (byte) attributeNumber;
  }

  /**
   * INTERNAL USE-ONLY
   * Generate the bytes for a synthetic method that provides access to a private method.
   *
   * @param methodBinding org.eclipse.jdt.internal.compiler.nameloopkup.SyntheticAccessMethodBinding
   */
  public void addSyntheticMethodAccessMethod(SyntheticMethodBinding methodBinding) {
    generateMethodInfoHeader(methodBinding);
    int methodAttributeOffset = this.contentsOffset;
    // this will add exception attribute, synthetic attribute, deprecated attribute,...
    int attributeNumber = generateMethodInfoAttributes(methodBinding);
    // Code attribute
    int codeAttributeOffset = this.contentsOffset;
    attributeNumber++; // add code attribute
    generateCodeAttributeHeader();
    this.codeStream.init(this);
    this.codeStream.generateSyntheticBodyForMethodAccess(methodBinding);
    completeCodeAttributeForSyntheticMethod(
      methodBinding,
      codeAttributeOffset,
      ((SourceTypeBinding) methodBinding.declaringClass)
        .scope
        .referenceCompilationUnit()
        .compilationResult
        .getLineSeparatorPositions());
    // update the number of attributes
    this.contents[methodAttributeOffset++] = (byte) (attributeNumber >> 8);
    this.contents[methodAttributeOffset] = (byte) attributeNumber;
  }

  public void addSyntheticSwitchTable(SyntheticMethodBinding methodBinding) {
    generateMethodInfoHeader(methodBinding);
    int methodAttributeOffset = this.contentsOffset;
    // this will add exception attribute, synthetic attribute, deprecated attribute,...
    int attributeNumber = generateMethodInfoAttributes(methodBinding);
    // Code attribute
    int codeAttributeOffset = this.contentsOffset;
    attributeNumber++; // add code attribute
    generateCodeAttributeHeader();
    this.codeStream.init(this);
    this.codeStream.generateSyntheticBodyForSwitchTable(methodBinding);
    completeCodeAttributeForSyntheticMethod(
      true,
      methodBinding,
      codeAttributeOffset,
      ((SourceTypeBinding) methodBinding.declaringClass)
        .scope
        .referenceCompilationUnit()
        .compilationResult
        .getLineSeparatorPositions());
    // update the number of attributes
    this.contents[methodAttributeOffset++] = (byte) (attributeNumber >> 8);
    this.contents[methodAttributeOffset] = (byte) attributeNumber;
  }

  /**
   * INTERNAL USE-ONLY
   * That method completes the creation of the code attribute by setting
   * - the attribute_length
   * - max_stack
   * - max_locals
   * - code_length
   * - exception table
   * - and debug attributes if necessary.
   *
   * @param codeAttributeOffset <CODE>int</CODE>
   */
  public void completeCodeAttribute(int codeAttributeOffset) {
    // reinitialize the localContents with the byte modified by the code stream
    this.contents = this.codeStream.bCodeStream;
    int localContentsOffset = this.codeStream.classFileOffset;
    // codeAttributeOffset is the position inside localContents byte array before we started to write
    // any information about the codeAttribute
    // That means that to write the attribute_length you need to offset by 2 the value of codeAttributeOffset
    // to get the right position, 6 for the max_stack etc...
    int code_length = this.codeStream.position;
    if (code_length > 65535) {
      this.codeStream.methodDeclaration.scope.problemReporter().bytecodeExceeds64KLimit(
        this.codeStream.methodDeclaration);
    }
    if (localContentsOffset + 20 >= this.contents.length) {
      resizeContents(20);
    }
    int max_stack = this.codeStream.stackMax;
    this.contents[codeAttributeOffset + 6] = (byte) (max_stack >> 8);
    this.contents[codeAttributeOffset + 7] = (byte) max_stack;
    int max_locals = this.codeStream.maxLocals;
    this.contents[codeAttributeOffset + 8] = (byte) (max_locals >> 8);
    this.contents[codeAttributeOffset + 9] = (byte) max_locals;
    this.contents[codeAttributeOffset + 10] = (byte) (code_length >> 24);
    this.contents[codeAttributeOffset + 11] = (byte) (code_length >> 16);
    this.contents[codeAttributeOffset + 12] = (byte) (code_length >> 8);
    this.contents[codeAttributeOffset + 13] = (byte) code_length;

    boolean addStackMaps = (this.produceAttributes & ClassFileConstants.ATTR_STACK_MAP_TABLE) != 0;
    // write the exception table
    ExceptionLabel[] exceptionLabels = this.codeStream.exceptionLabels;
    int exceptionHandlersCount = 0; // each label holds one handler per range (start/end contiguous)
    for (int i = 0, length = this.codeStream.exceptionLabelsCounter; i < length; i++) {
      exceptionHandlersCount += this.codeStream.exceptionLabels[i].getCount() / 2;
    }
    int exSize = exceptionHandlersCount * 8 + 2;
    if (exSize + localContentsOffset >= this.contents.length) {
      resizeContents(exSize);
    }
    // there is no exception table, so we need to offset by 2 the current offset and move
    // on the attribute generation
    this.contents[localContentsOffset++] = (byte) (exceptionHandlersCount >> 8);
    this.contents[localContentsOffset++] = (byte) exceptionHandlersCount;
    for (int i = 0, max = this.codeStream.exceptionLabelsCounter; i < max; i++) {
      ExceptionLabel exceptionLabel = exceptionLabels[i];
      if (exceptionLabel != null) {
        int iRange = 0, maxRange = exceptionLabel.getCount();
        if ((maxRange & 1) != 0) {
          this.codeStream.methodDeclaration.scope.problemReporter().abortDueToInternalError(
              Messages.bind(Messages.abort_invalidExceptionAttribute, new String(this.codeStream.methodDeclaration.selector)),
              this.codeStream.methodDeclaration);
        }
        while (iRange < maxRange) {
          int start = exceptionLabel.ranges[iRange++]; // even ranges are start positions
          this.contents[localContentsOffset++] = (byte) (start >> 8);
          this.contents[localContentsOffset++] = (byte) start;
          int end = exceptionLabel.ranges[iRange++]; // odd ranges are end positions
          this.contents[localContentsOffset++] = (byte) (end >> 8);
          this.contents[localContentsOffset++] = (byte) end;
          int handlerPC = exceptionLabel.position;
          if (addStackMaps) {
            StackMapFrameCodeStream stackMapFrameCodeStream = (StackMapFrameCodeStream) this.codeStream;
            stackMapFrameCodeStream.addFramePosition(handlerPC);
//            stackMapFrameCodeStream.addExceptionMarker(handlerPC, exceptionLabel.exceptionType);
          }
          this.contents[localContentsOffset++] = (byte) (handlerPC >> 8);
          this.contents[localContentsOffset++] = (byte) handlerPC;
          if (exceptionLabel.exceptionType == null) {
            // any exception handler
            this.contents[localContentsOffset++] = 0;
            this.contents[localContentsOffset++] = 0;
          } else {
            int nameIndex;
            if (exceptionLabel.exceptionType == TypeBinding.NULL) {
              /* represents ClassNotFoundException, see class literal access*/
              nameIndex = this.constantPool.literalIndexForType(ConstantPool.JavaLangClassNotFoundExceptionConstantPoolName);
            } else {
              nameIndex = this.constantPool.literalIndexForType(exceptionLabel.exceptionType);
            }
            this.contents[localContentsOffset++] = (byte) (nameIndex >> 8);
            this.contents[localContentsOffset++] = (byte) nameIndex;
          }
        }
      }
    }
    // debug attributes
    int codeAttributeAttributeOffset = localContentsOffset;
    int attributesNumber = 0;
    // leave two bytes for the attribute_length
    localContentsOffset += 2;
    if (localContentsOffset + 2 >= this.contents.length) {
      resizeContents(2);
    }

    this.contentsOffset = localContentsOffset;

    // first we handle the linenumber attribute
    if ((this.produceAttributes & ClassFileConstants.ATTR_LINES) != 0) {
      attributesNumber += generateLineNumberAttribute();
    }
    // then we do the local variable attribute
    if ((this.produceAttributes & ClassFileConstants.ATTR_VARS) != 0) {
      final boolean methodDeclarationIsStatic = this.codeStream.methodDeclaration.isStatic();
      attributesNumber += generateLocalVariableTableAttribute(code_length, methodDeclarationIsStatic, false);
    }

    if (addStackMaps) {
      attributesNumber += generateStackMapTableAttribute(
          this.codeStream.methodDeclaration.binding,
          code_length,
          codeAttributeOffset,
          max_locals,
          false);
    }

    if ((this.produceAttributes & ClassFileConstants.ATTR_STACK_MAP) != 0) {
      attributesNumber += generateStackMapAttribute(
          this.codeStream.methodDeclaration.binding,
          code_length,
          codeAttributeOffset,
          max_locals,
          false);
    }

    this.contents[codeAttributeAttributeOffset++] = (byte) (attributesNumber >> 8);
    this.contents[codeAttributeAttributeOffset] = (byte) attributesNumber;

    // update the attribute length
    int codeAttributeLength = this.contentsOffset - (codeAttributeOffset + 6);
    this.contents[codeAttributeOffset + 2] = (byte) (codeAttributeLength >> 24);
    this.contents[codeAttributeOffset + 3] = (byte) (codeAttributeLength >> 16);
    this.contents[codeAttributeOffset + 4] = (byte) (codeAttributeLength >> 8);
    this.contents[codeAttributeOffset + 5] = (byte) codeAttributeLength;
  }

  /**
   * INTERNAL USE-ONLY
   * That method completes the creation of the code attribute by setting
   * - the attribute_length
   * - max_stack
   * - max_locals
   * - code_length
   * - exception table
   * - and debug attributes if necessary.
   *
   * @param codeAttributeOffset <CODE>int</CODE>
   */
  public void completeCodeAttributeForClinit(int codeAttributeOffset) {
    // reinitialize the contents with the byte modified by the code stream
    this.contents = this.codeStream.bCodeStream;
    int localContentsOffset = this.codeStream.classFileOffset;
    // codeAttributeOffset is the position inside contents byte array before we started to write
    // any information about the codeAttribute
    // That means that to write the attribute_length you need to offset by 2 the value of codeAttributeOffset
    // to get the right position, 6 for the max_stack etc...
    int code_length = this.codeStream.position;
    if (code_length > 65535) {
      this.codeStream.methodDeclaration.scope.problemReporter().bytecodeExceeds64KLimit(
        this.codeStream.methodDeclaration.scope.referenceType());
    }
    if (localContentsOffset + 20 >= this.contents.length) {
      resizeContents(20);
    }
    int max_stack = this.codeStream.stackMax;
    this.contents[codeAttributeOffset + 6] = (byte) (max_stack >> 8);
    this.contents[codeAttributeOffset + 7] = (byte) max_stack;
    int max_locals = this.codeStream.maxLocals;
    this.contents[codeAttributeOffset + 8] = (byte) (max_locals >> 8);
    this.contents[codeAttributeOffset + 9] = (byte) max_locals;
    this.contents[codeAttributeOffset + 10] = (byte) (code_length >> 24);
    this.contents[codeAttributeOffset + 11] = (byte) (code_length >> 16);
    this.contents[codeAttributeOffset + 12] = (byte) (code_length >> 8);
    this.contents[codeAttributeOffset + 13] = (byte) code_length;

    boolean addStackMaps = (this.produceAttributes & ClassFileConstants.ATTR_STACK_MAP_TABLE) != 0;
    // write the exception table
    ExceptionLabel[] exceptionLabels = this.codeStream.exceptionLabels;
    int exceptionHandlersCount = 0; // each label holds one handler per range (start/end contiguous)
    for (int i = 0, length = this.codeStream.exceptionLabelsCounter; i < length; i++) {
      exceptionHandlersCount += this.codeStream.exceptionLabels[i].getCount() / 2;
    }
    int exSize = exceptionHandlersCount * 8 + 2;
    if (exSize + localContentsOffset >= this.contents.length) {
      resizeContents(exSize);
    }
    // there is no exception table, so we need to offset by 2 the current offset and move
    // on the attribute generation
    this.contents[localContentsOffset++] = (byte) (exceptionHandlersCount >> 8);
    this.contents[localContentsOffset++] = (byte) exceptionHandlersCount;
    for (int i = 0, max = this.codeStream.exceptionLabelsCounter; i < max; i++) {
      ExceptionLabel exceptionLabel = exceptionLabels[i];
      if (exceptionLabel != null) {
        int iRange = 0, maxRange = exceptionLabel.getCount();
        if ((maxRange & 1) != 0) {
          this.codeStream.methodDeclaration.scope.problemReporter().abortDueToInternalError(
              Messages.bind(Messages.abort_invalidExceptionAttribute, new String(this.codeStream.methodDeclaration.selector)),
              this.codeStream.methodDeclaration);
        }
        while  (iRange < maxRange) {
          int start = exceptionLabel.ranges[iRange++]; // even ranges are start positions
          this.contents[localContentsOffset++] = (byte) (start >> 8);
          this.contents[localContentsOffset++] = (byte) start;
          int end = exceptionLabel.ranges[iRange++]; // odd ranges are end positions
          this.contents[localContentsOffset++] = (byte) (end >> 8);
          this.contents[localContentsOffset++] = (byte) end;
          int handlerPC = exceptionLabel.position;
          this.contents[localContentsOffset++] = (byte) (handlerPC >> 8);
          this.contents[localContentsOffset++] = (byte) handlerPC;
          if (addStackMaps) {
            StackMapFrameCodeStream stackMapFrameCodeStream = (StackMapFrameCodeStream) this.codeStream;
            stackMapFrameCodeStream.addFramePosition(handlerPC);
//            stackMapFrameCodeStream.addExceptionMarker(handlerPC, exceptionLabel.exceptionType);
          }
          if (exceptionLabel.exceptionType == null) {
            // any exception handler
            this.contents[localContentsOffset++] = 0;
            this.contents[localContentsOffset++] = 0;
          } else {
            int nameIndex;
            if (exceptionLabel.exceptionType == TypeBinding.NULL) {
              /* represents denote ClassNotFoundException, see class literal access*/
              nameIndex = this.constantPool.literalIndexForType(ConstantPool.JavaLangClassNotFoundExceptionConstantPoolName);
            } else {
              nameIndex = this.constantPool.literalIndexForType(exceptionLabel.exceptionType);
            }
            this.contents[localContentsOffset++] = (byte) (nameIndex >> 8);
            this.contents[localContentsOffset++] = (byte) nameIndex;
          }
        }
      }
    }
    // debug attributes
    int codeAttributeAttributeOffset = localContentsOffset;
    int attributesNumber = 0;
    // leave two bytes for the attribute_length
    localContentsOffset += 2;
    if (localContentsOffset + 2 >= this.contents.length) {
      resizeContents(2);
    }

    this.contentsOffset = localContentsOffset;

    // first we handle the linenumber attribute
    if ((this.produceAttributes & ClassFileConstants.ATTR_LINES) != 0) {
      attributesNumber += generateLineNumberAttribute();
    }
    // then we do the local variable attribute
    if ((this.produceAttributes & ClassFileConstants.ATTR_VARS) != 0) {
      attributesNumber += generateLocalVariableTableAttribute(code_length, true, false);
    }

    if ((this.produceAttributes & ClassFileConstants.ATTR_STACK_MAP_TABLE) != 0) {
      attributesNumber += generateStackMapTableAttribute(
          null,
          code_length,
          codeAttributeOffset,
          max_locals,
          true);
    }

    if ((this.produceAttributes & ClassFileConstants.ATTR_STACK_MAP) != 0) {
      attributesNumber += generateStackMapAttribute(
          null,
          code_length,
          codeAttributeOffset,
          max_locals,
          true);
    }

    // update the number of attributes
    // ensure first that there is enough space available inside the contents array
    if (codeAttributeAttributeOffset + 2 >= this.contents.length) {
      resizeContents(2);
    }
    this.contents[codeAttributeAttributeOffset++] = (byte) (attributesNumber >> 8);
    this.contents[codeAttributeAttributeOffset] = (byte) attributesNumber;
    // update the attribute length
    int codeAttributeLength = this.contentsOffset - (codeAttributeOffset + 6);
    this.contents[codeAttributeOffset + 2] = (byte) (codeAttributeLength >> 24);
    this.contents[codeAttributeOffset + 3] = (byte) (codeAttributeLength >> 16);
    this.contents[codeAttributeOffset + 4] = (byte) (codeAttributeLength >> 8);
    this.contents[codeAttributeOffset + 5] = (byte) codeAttributeLength;
  }

  /**
   * INTERNAL USE-ONLY
   * That method completes the creation of the code attribute by setting
   * - the attribute_length
   * - max_stack
   * - max_locals
   * - code_length
   * - exception table
   * - and debug attributes if necessary.
   */
  public void completeCodeAttributeForClinit(
      int codeAttributeOffset,
      int problemLine) {
    // reinitialize the contents with the byte modified by the code stream
    this.contents = this.codeStream.bCodeStream;
    int localContentsOffset = this.codeStream.classFileOffset;
    // codeAttributeOffset is the position inside contents byte array before we started to write
    // any information about the codeAttribute
    // That means that to write the attribute_length you need to offset by 2 the value of codeAttributeOffset
    // to get the right position, 6 for the max_stack etc...
    int code_length = this.codeStream.position;
    if (code_length > 65535) {
      this.codeStream.methodDeclaration.scope.problemReporter().bytecodeExceeds64KLimit(
        this.codeStream.methodDeclaration.scope.referenceType());
    }
    if (localContentsOffset + 20 >= this.contents.length) {
      resizeContents(20);
    }
    int max_stack = this.codeStream.stackMax;
    this.contents[codeAttributeOffset + 6] = (byte) (max_stack >> 8);
    this.contents[codeAttributeOffset + 7] = (byte) max_stack;
    int max_locals = this.codeStream.maxLocals;
    this.contents[codeAttributeOffset + 8] = (byte) (max_locals >> 8);
    this.contents[codeAttributeOffset + 9] = (byte) max_locals;
    this.contents[codeAttributeOffset + 10] = (byte) (code_length >> 24);
    this.contents[codeAttributeOffset + 11] = (byte) (code_length >> 16);
    this.contents[codeAttributeOffset + 12] = (byte) (code_length >> 8);
    this.contents[codeAttributeOffset + 13] = (byte) code_length;

    // write the exception table
    this.contents[localContentsOffset++] = 0;
    this.contents[localContentsOffset++] = 0;

    // debug attributes
    int codeAttributeAttributeOffset = localContentsOffset;
    int attributesNumber = 0; // leave two bytes for the attribute_length
    localContentsOffset += 2; // first we handle the linenumber attribute
    if (localContentsOffset + 2 >= this.contents.length) {
      resizeContents(2);
    }

    this.contentsOffset = localContentsOffset;
    // first we handle the linenumber attribute
    if ((this.produceAttributes & ClassFileConstants.ATTR_LINES) != 0) {
      attributesNumber += generateLineNumberAttribute(problemLine);
    }
    localContentsOffset = this.contentsOffset;
    // then we do the local variable attribute
    if ((this.produceAttributes & ClassFileConstants.ATTR_VARS) != 0) {
      int localVariableNameIndex =
        this.constantPool.literalIndex(AttributeNamesConstants.LocalVariableTableName);
      if (localContentsOffset + 8 >= this.contents.length) {
        resizeContents(8);
      }
      this.contents[localContentsOffset++] = (byte) (localVariableNameIndex >> 8);
      this.contents[localContentsOffset++] = (byte) localVariableNameIndex;
      this.contents[localContentsOffset++] = 0;
      this.contents[localContentsOffset++] = 0;
      this.contents[localContentsOffset++] = 0;
      this.contents[localContentsOffset++] = 2;
      this.contents[localContentsOffset++] = 0;
      this.contents[localContentsOffset++] = 0;
      attributesNumber++;
    }

    this.contentsOffset = localContentsOffset;

    if ((this.produceAttributes & ClassFileConstants.ATTR_STACK_MAP_TABLE) != 0) {
      attributesNumber += generateStackMapTableAttribute(
          null,
          code_length,
          codeAttributeOffset,
          max_locals,
          true);
    }

    if ((this.produceAttributes & ClassFileConstants.ATTR_STACK_MAP) != 0) {
      attributesNumber += generateStackMapAttribute(
          null,
          code_length,
          codeAttributeOffset,
          max_locals,
          true);
    }

    // update the number of attributes
    // ensure first that there is enough space available inside the contents array
    if (codeAttributeAttributeOffset + 2 >= this.contents.length) {
      resizeContents(2);
    }
    this.contents[codeAttributeAttributeOffset++] = (byte) (attributesNumber >> 8);
    this.contents[codeAttributeAttributeOffset] = (byte) attributesNumber;
    // update the attribute length
    int codeAttributeLength = this.contentsOffset - (codeAttributeOffset + 6);
    this.contents[codeAttributeOffset + 2] = (byte) (codeAttributeLength >> 24);
    this.contents[codeAttributeOffset + 3] = (byte) (codeAttributeLength >> 16);
    this.contents[codeAttributeOffset + 4] = (byte) (codeAttributeLength >> 8);
    this.contents[codeAttributeOffset + 5] = (byte) codeAttributeLength;
  }


  /**
   *
   */
  public void completeCodeAttributeForMissingAbstractProblemMethod(
      MethodBinding binding,
      int codeAttributeOffset,
      int[] startLineIndexes,
      int problemLine) {
    // reinitialize the localContents with the byte modified by the code stream
    this.contents = this.codeStream.bCodeStream;
    int localContentsOffset = this.codeStream.classFileOffset;
    // codeAttributeOffset is the position inside localContents byte array before we started to write// any information about the codeAttribute// That means that to write the attribute_length you need to offset by 2 the value of codeAttributeOffset// to get the right position, 6 for the max_stack etc...
    int max_stack = this.codeStream.stackMax;
    this.contents[codeAttributeOffset + 6] = (byte) (max_stack >> 8);
    this.contents[codeAttributeOffset + 7] = (byte) max_stack;
    int max_locals = this.codeStream.maxLocals;
    this.contents[codeAttributeOffset + 8] = (byte) (max_locals >> 8);
    this.contents[codeAttributeOffset + 9] = (byte) max_locals;
    int code_length = this.codeStream.position;
    this.contents[codeAttributeOffset + 10] = (byte) (code_length >> 24);
    this.contents[codeAttributeOffset + 11] = (byte) (code_length >> 16);
    this.contents[codeAttributeOffset + 12] = (byte) (code_length >> 8);
    this.contents[codeAttributeOffset + 13] = (byte) code_length;
    // write the exception table
    if (localContentsOffset + 50 >= this.contents.length) {
      resizeContents(50);
    }
    this.contents[localContentsOffset++] = 0;
    this.contents[localContentsOffset++] = 0;
    // debug attributes
    int codeAttributeAttributeOffset = localContentsOffset;
    int attributesNumber = 0; // leave two bytes for the attribute_length
    localContentsOffset += 2; // first we handle the linenumber attribute
    if (localContentsOffset + 2 >= this.contents.length) {
      resizeContents(2);
    }

    this.contentsOffset = localContentsOffset;
    if ((this.produceAttributes & ClassFileConstants.ATTR_LINES) != 0) {
      if (problemLine == 0) {
        problemLine = Util.getLineNumber(binding.sourceStart(), startLineIndexes, 0, startLineIndexes.length-1);
      }
      attributesNumber += generateLineNumberAttribute(problemLine);
    }

    if ((this.produceAttributes & ClassFileConstants.ATTR_STACK_MAP_TABLE) != 0) {
      attributesNumber += generateStackMapTableAttribute(
          binding,
          code_length,
          codeAttributeOffset,
          max_locals,
          false);
    }

    if ((this.produceAttributes & ClassFileConstants.ATTR_STACK_MAP) != 0) {
      attributesNumber += generateStackMapAttribute(
          binding,
          code_length,
          codeAttributeOffset,
          max_locals,
          false);
    }

    // then we do the local variable attribute
    // update the number of attributes// ensure first that there is enough space available inside the localContents array
    if (codeAttributeAttributeOffset + 2 >= this.contents.length) {
      resizeContents(2);
    }
    this.contents[codeAttributeAttributeOffset++] = (byte) (attributesNumber >> 8);
    this.contents[codeAttributeAttributeOffset] = (byte) attributesNumber;
    // update the attribute length
    int codeAttributeLength = this.contentsOffset - (codeAttributeOffset + 6);
    this.contents[codeAttributeOffset + 2] = (byte) (codeAttributeLength >> 24);
    this.contents[codeAttributeOffset + 3] = (byte) (codeAttributeLength >> 16);
    this.contents[codeAttributeOffset + 4] = (byte) (codeAttributeLength >> 8);
    this.contents[codeAttributeOffset + 5] = (byte) codeAttributeLength;
  }

  /**
   * INTERNAL USE-ONLY
   * That method completes the creation of the code attribute by setting
   * - the attribute_length
   * - max_stack
   * - max_locals
   * - code_length
   * - exception table
   * - and debug attributes if necessary.
   *
   * @param codeAttributeOffset <CODE>int</CODE>
   */
  public void completeCodeAttributeForProblemMethod(
      AbstractMethodDeclaration method,
      MethodBinding binding,
      int codeAttributeOffset,
      int[] startLineIndexes,
      int problemLine) {
    // reinitialize the localContents with the byte modified by the code stream
    this.contents = this.codeStream.bCodeStream;
    int localContentsOffset = this.codeStream.classFileOffset;
    // codeAttributeOffset is the position inside localContents byte array before we started to write// any information about the codeAttribute// That means that to write the attribute_length you need to offset by 2 the value of codeAttributeOffset// to get the right position, 6 for the max_stack etc...
    int max_stack = this.codeStream.stackMax;
    this.contents[codeAttributeOffset + 6] = (byte) (max_stack >> 8);
    this.contents[codeAttributeOffset + 7] = (byte) max_stack;
    int max_locals = this.codeStream.maxLocals;
    this.contents[codeAttributeOffset + 8] = (byte) (max_locals >> 8);
    this.contents[codeAttributeOffset + 9] = (byte) max_locals;
    int code_length = this.codeStream.position;
    this.contents[codeAttributeOffset + 10] = (byte) (code_length >> 24);
    this.contents[codeAttributeOffset + 11] = (byte) (code_length >> 16);
    this.contents[codeAttributeOffset + 12] = (byte) (code_length >> 8);
    this.contents[codeAttributeOffset + 13] = (byte) code_length;
    // write the exception table
    if (localContentsOffset + 50 >= this.contents.length) {
      resizeContents(50);
    }

    // write the exception table
    this.contents[localContentsOffset++] = 0;
    this.contents[localContentsOffset++] = 0;
    // debug attributes
    int codeAttributeAttributeOffset = localContentsOffset;
    int attributesNumber = 0; // leave two bytes for the attribute_length
    localContentsOffset += 2; // first we handle the linenumber attribute
    if (localContentsOffset + 2 >= this.contents.length) {
      resizeContents(2);
    }

    this.contentsOffset = localContentsOffset;
    if ((this.produceAttributes & ClassFileConstants.ATTR_LINES) != 0) {
      if (problemLine == 0) {
        problemLine = Util.getLineNumber(binding.sourceStart(), startLineIndexes, 0, startLineIndexes.length-1);
      }
      attributesNumber += generateLineNumberAttribute(problemLine);
    }

    // then we do the local variable attribute
    if ((this.produceAttributes & ClassFileConstants.ATTR_VARS) != 0) {
      final boolean methodDeclarationIsStatic = this.codeStream.methodDeclaration.isStatic();
      attributesNumber += generateLocalVariableTableAttribute(code_length, methodDeclarationIsStatic, false);
    }

    if ((this.produceAttributes & ClassFileConstants.ATTR_STACK_MAP_TABLE) != 0) {
      attributesNumber += generateStackMapTableAttribute(
          binding,
          code_length,
          codeAttributeOffset,
          max_locals,
          false);
    }

    if ((this.produceAttributes & ClassFileConstants.ATTR_STACK_MAP) != 0) {
      attributesNumber += generateStackMapAttribute(
          binding,
          code_length,
          codeAttributeOffset,
          max_locals,
          false);
    }

    // update the number of attributes// ensure first that there is enough space available inside the localContents array
    if (codeAttributeAttributeOffset + 2 >= this.contents.length) {
      resizeContents(2);
    }
    this.contents[codeAttributeAttributeOffset++] = (byte) (attributesNumber >> 8);
    this.contents[codeAttributeAttributeOffset] = (byte) attributesNumber;
    // update the attribute length
    int codeAttributeLength = this.contentsOffset - (codeAttributeOffset + 6);
    this.contents[codeAttributeOffset + 2] = (byte) (codeAttributeLength >> 24);
    this.contents[codeAttributeOffset + 3] = (byte) (codeAttributeLength >> 16);
    this.contents[codeAttributeOffset + 4] = (byte) (codeAttributeLength >> 8);
    this.contents[codeAttributeOffset + 5] = (byte) codeAttributeLength;
  }

  /**
   * INTERNAL USE-ONLY
   * That method completes the creation of the code attribute by setting
   * - the attribute_length
   * - max_stack
   * - max_locals
   * - code_length
   * - exception table
   * - and debug attributes if necessary.
   *
   * @param binding org.eclipse.jdt.internal.compiler.lookup.SyntheticAccessMethodBinding
   * @param codeAttributeOffset <CODE>int</CODE>
   */
  public void completeCodeAttributeForSyntheticMethod(
      boolean hasExceptionHandlers,
      SyntheticMethodBinding binding,
      int codeAttributeOffset,
      int[] startLineIndexes) {
    // reinitialize the contents with the byte modified by the code stream
    this.contents = this.codeStream.bCodeStream;
    int localContentsOffset = this.codeStream.classFileOffset;
    // codeAttributeOffset is the position inside contents byte array before we started to write
    // any information about the codeAttribute
    // That means that to write the attribute_length you need to offset by 2 the value of codeAttributeOffset
    // to get the right position, 6 for the max_stack etc...
    int max_stack = this.codeStream.stackMax;
    this.contents[codeAttributeOffset + 6] = (byte) (max_stack >> 8);
    this.contents[codeAttributeOffset + 7] = (byte) max_stack;
    int max_locals = this.codeStream.maxLocals;
    this.contents[codeAttributeOffset + 8] = (byte) (max_locals >> 8);
    this.contents[codeAttributeOffset + 9] = (byte) max_locals;
    int code_length = this.codeStream.position;
    this.contents[codeAttributeOffset + 10] = (byte) (code_length >> 24);
    this.contents[codeAttributeOffset + 11] = (byte) (code_length >> 16);
    this.contents[codeAttributeOffset + 12] = (byte) (code_length >> 8);
    this.contents[codeAttributeOffset + 13] = (byte) code_length;
    if ((localContentsOffset + 40) >= this.contents.length) {
      resizeContents(40);
    }

    boolean addStackMaps = (this.produceAttributes & ClassFileConstants.ATTR_STACK_MAP_TABLE) != 0;
    if (hasExceptionHandlers) {
      // write the exception table
      ExceptionLabel[] exceptionLabels = this.codeStream.exceptionLabels;
      int exceptionHandlersCount = 0; // each label holds one handler per range (start/end contiguous)
      for (int i = 0, length = this.codeStream.exceptionLabelsCounter; i < length; i++) {
        exceptionHandlersCount += this.codeStream.exceptionLabels[i].getCount() / 2;
      }
      int exSize = exceptionHandlersCount * 8 + 2;
      if (exSize + localContentsOffset >= this.contents.length) {
        resizeContents(exSize);
      }
      // there is no exception table, so we need to offset by 2 the current offset and move
      // on the attribute generation
      this.contents[localContentsOffset++] = (byte) (exceptionHandlersCount >> 8);
      this.contents[localContentsOffset++] = (byte) exceptionHandlersCount;
      for (int i = 0, max = this.codeStream.exceptionLabelsCounter; i < max; i++) {
        ExceptionLabel exceptionLabel = exceptionLabels[i];
        if (exceptionLabel != null) {
          int iRange = 0, maxRange = exceptionLabel.getCount();
          if ((maxRange & 1) != 0) {
            this.referenceBinding.scope.problemReporter().abortDueToInternalError(
                Messages.bind(Messages.abort_invalidExceptionAttribute, new String(binding.selector),
                    this.referenceBinding.scope.problemReporter().referenceContext));
          }
          while  (iRange < maxRange) {
            int start = exceptionLabel.ranges[iRange++]; // even ranges are start positions
            this.contents[localContentsOffset++] = (byte) (start >> 8);
            this.contents[localContentsOffset++] = (byte) start;
            int end = exceptionLabel.ranges[iRange++]; // odd ranges are end positions
            this.contents[localContentsOffset++] = (byte) (end >> 8);
            this.contents[localContentsOffset++] = (byte) end;
            int handlerPC = exceptionLabel.position;
            if (addStackMaps) {
              StackMapFrameCodeStream stackMapFrameCodeStream = (StackMapFrameCodeStream) this.codeStream;
              stackMapFrameCodeStream.addFramePosition(handlerPC);
            }
            this.contents[localContentsOffset++] = (byte) (handlerPC >> 8);
            this.contents[localContentsOffset++] = (byte) handlerPC;
            if (exceptionLabel.exceptionType == null) {
              // any exception handler
              this.contents[localContentsOffset++] = 0;
              this.contents[localContentsOffset++] = 0;
            } else {
              int nameIndex;
              switch(exceptionLabel.exceptionType.id) {
                case T_null :
                  /* represents ClassNotFoundException, see class literal access*/
                  nameIndex = this.constantPool.literalIndexForType(ConstantPool.JavaLangClassNotFoundExceptionConstantPoolName);
                  break;
                case T_long :
                  /* represents NoSuchFieldError, see switch table generation*/
                  nameIndex = this.constantPool.literalIndexForType(ConstantPool.JavaLangNoSuchFieldErrorConstantPoolName);
                  break;
                default:
                  nameIndex = this.constantPool.literalIndexForType(exceptionLabel.exceptionType);
              }
              this.contents[localContentsOffset++] = (byte) (nameIndex >> 8);
              this.contents[localContentsOffset++] = (byte) nameIndex;
            }
          }
        }
      }
    } else {
      // there is no exception table, so we need to offset by 2 the current offset and move
      // on the attribute generation
      this.contents[localContentsOffset++] = 0;
      this.contents[localContentsOffset++] = 0;
    }
    // debug attributes
    int codeAttributeAttributeOffset = localContentsOffset;
    int attributesNumber = 0;
    // leave two bytes for the attribute_length
    localContentsOffset += 2;
    if (localContentsOffset + 2 >= this.contents.length) {
      resizeContents(2);
    }

    this.contentsOffset = localContentsOffset;
    // first we handle the linenumber attribute
    if ((this.produceAttributes & ClassFileConstants.ATTR_LINES) != 0) {
      int lineNumber = Util.getLineNumber(binding.sourceStart, startLineIndexes, 0, startLineIndexes.length-1);
      attributesNumber += generateLineNumberAttribute(lineNumber);
    }
    // then we do the local variable attribute
    if ((this.produceAttributes & ClassFileConstants.ATTR_VARS) != 0) {
      final boolean methodDeclarationIsStatic = binding.isStatic();
      attributesNumber += generateLocalVariableTableAttribute(code_length, methodDeclarationIsStatic, true);
    }
    if (addStackMaps) {
      attributesNumber += generateStackMapTableAttribute(binding, code_length, codeAttributeOffset, max_locals, false);
    }

    if ((this.produceAttributes & ClassFileConstants.ATTR_STACK_MAP) != 0) {
      attributesNumber += generateStackMapAttribute(
          binding,
          code_length,
          codeAttributeOffset,
          max_locals,
          false);
    }

    // update the number of attributes
    // ensure first that there is enough space available inside the contents array
    if (codeAttributeAttributeOffset + 2 >= this.contents.length) {
      resizeContents(2);
    }
    this.contents[codeAttributeAttributeOffset++] = (byte) (attributesNumber >> 8);
    this.contents[codeAttributeAttributeOffset] = (byte) attributesNumber;

    // update the attribute length
    int codeAttributeLength = this.contentsOffset - (codeAttributeOffset + 6);
    this.contents[codeAttributeOffset + 2] = (byte) (codeAttributeLength >> 24);
    this.contents[codeAttributeOffset + 3] = (byte) (codeAttributeLength >> 16);
    this.contents[codeAttributeOffset + 4] = (byte) (codeAttributeLength >> 8);
    this.contents[codeAttributeOffset + 5] = (byte) codeAttributeLength;
  }

  /**
   * INTERNAL USE-ONLY
   * That method completes the creation of the code attribute by setting
   * - the attribute_length
   * - max_stack
   * - max_locals
   * - code_length
   * - exception table
   * - and debug attributes if necessary.
   *
   * @param binding org.eclipse.jdt.internal.compiler.lookup.SyntheticAccessMethodBinding
   * @param codeAttributeOffset <CODE>int</CODE>
   */
  public void completeCodeAttributeForSyntheticMethod(
      SyntheticMethodBinding binding,
      int codeAttributeOffset,
      int[] startLineIndexes) {

    this.completeCodeAttributeForSyntheticMethod(
        false,
        binding,
        codeAttributeOffset,
        startLineIndexes);
  }

  /**
   * INTERNAL USE-ONLY
   * Complete the creation of a method info by setting up the number of attributes at the right offset.
   *
   * @param methodAttributeOffset <CODE>int</CODE>
   * @param attributesNumber <CODE>int</CODE>
   */
  public void completeMethodInfo(
      MethodBinding binding,
      int methodAttributeOffset,
      int attributesNumber) {
    // update the number of attributes
    this.contents[methodAttributeOffset++] = (byte) (attributesNumber >> 8);
    this.contents[methodAttributeOffset] = (byte) attributesNumber;
  }
  /**
   * INTERNAL USE-ONLY
   * This methods returns a char[] representing the file name of the receiver
   *
   * @return char[]
   */
  public char[] fileName() {
    return this.constantPool.UTF8Cache.returnKeyFor(2);
  }

  private void generateAnnotation(Annotation annotation, int currentOffset) {
    int startingContentsOffset = currentOffset;
    if (this.contentsOffset + 4 >= this.contents.length) {
      resizeContents(4);
    }
    TypeBinding annotationTypeBinding = annotation.resolvedType;
    if (annotationTypeBinding == null) {
      this.contentsOffset = startingContentsOffset;
      return;
    }
    final int typeIndex = this.constantPool.literalIndex(annotationTypeBinding.signature());
    this.contents[this.contentsOffset++] = (byte) (typeIndex >> 8);
    this.contents[this.contentsOffset++] = (byte) typeIndex;
    if (annotation instanceof NormalAnnotation) {
      NormalAnnotation normalAnnotation = (NormalAnnotation) annotation;
      MemberValuePair[] memberValuePairs = normalAnnotation.memberValuePairs;
      if (memberValuePairs != null) {
        final int memberValuePairsLength = memberValuePairs.length;
        this.contents[this.contentsOffset++] = (byte) (memberValuePairsLength >> 8);
        this.contents[this.contentsOffset++] = (byte) memberValuePairsLength;
        for (int i = 0; i < memberValuePairsLength; i++) {
          MemberValuePair memberValuePair = memberValuePairs[i];
          if (this.contentsOffset + 2 >= this.contents.length) {
            resizeContents(2);
          }
          final int elementNameIndex = this.constantPool.literalIndex(memberValuePair.name);
          this.contents[this.contentsOffset++] = (byte) (elementNameIndex >> 8);
          this.contents[this.contentsOffset++] = (byte) elementNameIndex;
          MethodBinding methodBinding = memberValuePair.binding;
          if (methodBinding == null) {
            this.contentsOffset = startingContentsOffset;
          } else {
            try {
              generateElementValue(memberValuePair.value, methodBinding.returnType, startingContentsOffset);
            } catch(ClassCastException e) {
              this.contentsOffset = startingContentsOffset;
            } catch(ShouldNotImplement e) {
              this.contentsOffset = startingContentsOffset;
            }
          }
        }
      } else {
        this.contents[this.contentsOffset++] = 0;
        this.contents[this.contentsOffset++] = 0;
      }
    } else if (annotation instanceof SingleMemberAnnotation) {
      SingleMemberAnnotation singleMemberAnnotation = (SingleMemberAnnotation) annotation;
      // this is a single member annotation (one member value)
      this.contents[this.contentsOffset++] = 0;
      this.contents[this.contentsOffset++] = 1;
      if (this.contentsOffset + 2 >= this.contents.length) {
        resizeContents(2);
      }
      final int elementNameIndex = this.constantPool.literalIndex(VALUE);
      this.contents[this.contentsOffset++] = (byte) (elementNameIndex >> 8);
      this.contents[this.contentsOffset++] = (byte) elementNameIndex;
      MethodBinding methodBinding = singleMemberAnnotation.memberValuePairs()[0].binding;
      if (methodBinding == null) {
        this.contentsOffset = startingContentsOffset;
      } else {
        try {
          generateElementValue(singleMemberAnnotation.memberValue, methodBinding.returnType, startingContentsOffset);
        } catch(ClassCastException e) {
          this.contentsOffset = startingContentsOffset;
        } catch(ShouldNotImplement e) {
          this.contentsOffset = startingContentsOffset;
        }
      }
    } else {
      // this is a marker annotation (no member value pairs)
      this.contents[this.contentsOffset++] = 0;
      this.contents[this.contentsOffset++] = 0;
    }
  }

  private int generateAnnotationDefaultAttribute(AnnotationMethodDeclaration declaration, int attributeOffset) {
    int attributesNumber = 0;
    // add an annotation default attribute
    int annotationDefaultNameIndex =
      this.constantPool.literalIndex(AttributeNamesConstants.AnnotationDefaultName);
    if (this.contentsOffset + 6 >= this.contents.length) {
      resizeContents(6);
    }
    this.contents[this.contentsOffset++] = (byte) (annotationDefaultNameIndex >> 8);
    this.contents[this.contentsOffset++] = (byte) annotationDefaultNameIndex;
    int attributeLengthOffset = this.contentsOffset;
    this.contentsOffset += 4;
    generateElementValue(declaration.defaultValue, declaration.binding.returnType, attributeOffset);
    if (this.contentsOffset != attributeOffset) {
      int attributeLength = this.contentsOffset - attributeLengthOffset - 4;
      this.contents[attributeLengthOffset++] = (byte) (attributeLength >> 24);
      this.contents[attributeLengthOffset++] = (byte) (attributeLength >> 16);
      this.contents[attributeLengthOffset++] = (byte) (attributeLength >> 8);
      this.contents[attributeLengthOffset++] = (byte) attributeLength;
      attributesNumber++;
    }
    return attributesNumber;
  }
  /**
   * INTERNAL USE-ONLY
   * That method generates the header of a code attribute.
   * - the index inside the constant pool for the attribute name ("Code")
   * - leave some space for attribute_length(4), max_stack(2), max_locals(2), code_length(4).
   */
  public void generateCodeAttributeHeader() {
    if (this.contentsOffset + 20 >= this.contents.length) {
      resizeContents(20);
    }
    int constantValueNameIndex =
      this.constantPool.literalIndex(AttributeNamesConstants.CodeName);
    this.contents[this.contentsOffset++] = (byte) (constantValueNameIndex >> 8);
    this.contents[this.contentsOffset++] = (byte) constantValueNameIndex;
    // leave space for attribute_length(4), max_stack(2), max_locals(2), code_length(4)
    this.contentsOffset += 12;
  }
 
  private int generateConstantValueAttribute(Constant fieldConstant, FieldBinding fieldBinding, int fieldAttributeOffset) {
    int localContentsOffset = this.contentsOffset;
    int attributesNumber = 1;
    if (localContentsOffset + 8 >= this.contents.length) {
      resizeContents(8);
    }
    // Now we generate the constant attribute corresponding to the fieldBinding
    int constantValueNameIndex =
      this.constantPool.literalIndex(AttributeNamesConstants.ConstantValueName);
    this.contents[localContentsOffset++] = (byte) (constantValueNameIndex >> 8);
    this.contents[localContentsOffset++] = (byte) constantValueNameIndex;
    // The attribute length = 2 in case of a constantValue attribute
    this.contents[localContentsOffset++] = 0;
    this.contents[localContentsOffset++] = 0;
    this.contents[localContentsOffset++] = 0;
    this.contents[localContentsOffset++] = 2;
    // Need to add the constant_value_index
    switch (fieldConstant.typeID()) {
      case T_boolean :
        int booleanValueIndex =
          this.constantPool.literalIndex(fieldConstant.booleanValue() ? 1 : 0);
        this.contents[localContentsOffset++] = (byte) (booleanValueIndex >> 8);
        this.contents[localContentsOffset++] = (byte) booleanValueIndex;
        break;
      case T_byte :
      case T_char :
      case T_int :
      case T_short :
        int integerValueIndex =
          this.constantPool.literalIndex(fieldConstant.intValue());
        this.contents[localContentsOffset++] = (byte) (integerValueIndex >> 8);
        this.contents[localContentsOffset++] = (byte) integerValueIndex;
        break;
      case T_float :
        int floatValueIndex =
          this.constantPool.literalIndex(fieldConstant.floatValue());
        this.contents[localContentsOffset++] = (byte) (floatValueIndex >> 8);
        this.contents[localContentsOffset++] = (byte) floatValueIndex;
        break;
      case T_double :
        int doubleValueIndex =
          this.constantPool.literalIndex(fieldConstant.doubleValue());
        this.contents[localContentsOffset++] = (byte) (doubleValueIndex >> 8);
        this.contents[localContentsOffset++] = (byte) doubleValueIndex;
        break;
      case T_long :
        int longValueIndex =
          this.constantPool.literalIndex(fieldConstant.longValue());
        this.contents[localContentsOffset++] = (byte) (longValueIndex >> 8);
        this.contents[localContentsOffset++] = (byte) longValueIndex;
        break;
      case T_JavaLangString :
        int stringValueIndex =
          this.constantPool.literalIndex(
            ((StringConstant) fieldConstant).stringValue());
        if (stringValueIndex == -1) {
          if (!this.creatingProblemType) {
            // report an error and abort: will lead to a problem type classfile creation
            TypeDeclaration typeDeclaration = this.referenceBinding.scope.referenceContext;
            FieldDeclaration[] fieldDecls = typeDeclaration.fields;
            int max = fieldDecls == null ? 0 : fieldDecls.length;
            for (int i = 0; i < max; i++) {
              if (fieldDecls[i].binding == fieldBinding) {
                // problem should abort
                typeDeclaration.scope.problemReporter().stringConstantIsExceedingUtf8Limit(
                  fieldDecls[i]);
              }
            }
          } else {
            // already inside a problem type creation : no constant for this field
            this.contentsOffset = fieldAttributeOffset;
            attributesNumber = 0;
          }
        } else {
          this.contents[localContentsOffset++] = (byte) (stringValueIndex >> 8);
          this.contents[localContentsOffset++] = (byte) stringValueIndex;
        }
    }
    this.contentsOffset = localContentsOffset;
    return attributesNumber;
  }
  private int generateDeprecatedAttribute() {
    int localContentsOffset = this.contentsOffset;
    if (localContentsOffset + 6 >= this.contents.length) {
      resizeContents(6);
    }
    int deprecatedAttributeNameIndex =
      this.constantPool.literalIndex(AttributeNamesConstants.DeprecatedName);
    this.contents[localContentsOffset++] = (byte) (deprecatedAttributeNameIndex >> 8);
    this.contents[localContentsOffset++] = (byte) deprecatedAttributeNameIndex;
    // the length of a deprecated attribute is equals to 0
    this.contents[localContentsOffset++] = 0;
    this.contents[localContentsOffset++] = 0;
    this.contents[localContentsOffset++] = 0;
    this.contents[localContentsOffset++] = 0;
    this.contentsOffset = localContentsOffset;
    return 1;
  }
  private void generateElementValue(
      Expression defaultValue,
      TypeBinding memberValuePairReturnType,
      int attributeOffset) {
    Constant constant = defaultValue.constant;
    TypeBinding defaultValueBinding = defaultValue.resolvedType;
    if (defaultValueBinding == null) {
      this.contentsOffset = attributeOffset;
    } else {
      if (memberValuePairReturnType.isArrayType() && !defaultValueBinding.isArrayType()) {
        // automatic wrapping
        if (this.contentsOffset + 3 >= this.contents.length) {
          resizeContents(3);
        }
        this.contents[this.contentsOffset++] = (byte) '[';
        this.contents[this.contentsOffset++] = (byte) 0;
        this.contents[this.contentsOffset++] = (byte) 1;
      }
      if (constant != null && constant != Constant.NotAConstant) {
        generateElementValue(attributeOffset, defaultValue, constant, memberValuePairReturnType.leafComponentType());
      } else {
        generateElementValueForNonConstantExpression(defaultValue, attributeOffset, defaultValueBinding);
      }
    }
  }
  /**
   * @param attributeOffset
   */
  private void generateElementValue(int attributeOffset, Expression defaultValue, Constant constant, TypeBinding binding) {
    if (this.contentsOffset + 3 >= this.contents.length) {
      resizeContents(3);
    }
    switch (binding.id) {
      case T_boolean :
        this.contents[this.contentsOffset++] = (byte) 'Z';
        int booleanValueIndex =
          this.constantPool.literalIndex(constant.booleanValue() ? 1 : 0);
        this.contents[this.contentsOffset++] = (byte) (booleanValueIndex >> 8);
        this.contents[this.contentsOffset++] = (byte) booleanValueIndex;
        break;
      case T_byte :
        this.contents[this.contentsOffset++] = (byte) 'B';
        int integerValueIndex =
          this.constantPool.literalIndex(constant.intValue());
        this.contents[this.contentsOffset++] = (byte) (integerValueIndex >> 8);
        this.contents[this.contentsOffset++] = (byte) integerValueIndex;
        break;
      case T_char :
        this.contents[this.contentsOffset++] = (byte) 'C';
        integerValueIndex =
          this.constantPool.literalIndex(constant.intValue());
        this.contents[this.contentsOffset++] = (byte) (integerValueIndex >> 8);
        this.contents[this.contentsOffset++] = (byte) integerValueIndex;
        break;
      case T_int :
        this.contents[this.contentsOffset++] = (byte) 'I';
        integerValueIndex =
          this.constantPool.literalIndex(constant.intValue());
        this.contents[this.contentsOffset++] = (byte) (integerValueIndex >> 8);
        this.contents[this.contentsOffset++] = (byte) integerValueIndex;
        break;
      case T_short :
        this.contents[this.contentsOffset++] = (byte) 'S';
        integerValueIndex =
          this.constantPool.literalIndex(constant.intValue());
        this.contents[this.contentsOffset++] = (byte) (integerValueIndex >> 8);
        this.contents[this.contentsOffset++] = (byte) integerValueIndex;
        break;
      case T_float :
        this.contents[this.contentsOffset++] = (byte) 'F';
        int floatValueIndex =
          this.constantPool.literalIndex(constant.floatValue());
        this.contents[this.contentsOffset++] = (byte) (floatValueIndex >> 8);
        this.contents[this.contentsOffset++] = (byte) floatValueIndex;
        break;
      case T_double :
        this.contents[this.contentsOffset++] = (byte) 'D';
        int doubleValueIndex =
          this.constantPool.literalIndex(constant.doubleValue());
        this.contents[this.contentsOffset++] = (byte) (doubleValueIndex >> 8);
        this.contents[this.contentsOffset++] = (byte) doubleValueIndex;
        break;
      case T_long :
        this.contents[this.contentsOffset++] = (byte) 'J';
        int longValueIndex =
          this.constantPool.literalIndex(constant.longValue());
        this.contents[this.contentsOffset++] = (byte) (longValueIndex >> 8);
        this.contents[this.contentsOffset++] = (byte) longValueIndex;
        break;
      case T_JavaLangString :
        this.contents[this.contentsOffset++] = (byte) 's';
        int stringValueIndex =
          this.constantPool.literalIndex(((StringConstant) constant).stringValue().toCharArray());
        if (stringValueIndex == -1) {
          if (!this.creatingProblemType) {
            // report an error and abort: will lead to a problem type classfile creation
            TypeDeclaration typeDeclaration = this.referenceBinding.scope.referenceContext;
            typeDeclaration.scope.problemReporter().stringConstantIsExceedingUtf8Limit(defaultValue);
          } else {
            // already inside a problem type creation : no attribute
            this.contentsOffset = attributeOffset;
          }
        } else {
          this.contents[this.contentsOffset++] = (byte) (stringValueIndex >> 8);
          this.contents[this.contentsOffset++] = (byte) stringValueIndex;
        }
    }
  }
 
  private void generateElementValueForNonConstantExpression(Expression defaultValue, int attributeOffset, TypeBinding defaultValueBinding) {
    if (defaultValueBinding != null) {
      if (defaultValueBinding.isEnum()) {
        if (this.contentsOffset + 5 >= this.contents.length) {
          resizeContents(5);
        }
        this.contents[this.contentsOffset++] = (byte) 'e';
        FieldBinding fieldBinding = null;
        if (defaultValue instanceof QualifiedNameReference) {
          QualifiedNameReference nameReference = (QualifiedNameReference) defaultValue;
          fieldBinding = (FieldBinding) nameReference.binding;
        } else if (defaultValue instanceof SingleNameReference) {
          SingleNameReference nameReference = (SingleNameReference) defaultValue;
          fieldBinding = (FieldBinding) nameReference.binding;
        } else {
          this.contentsOffset = attributeOffset;
        }
        if (fieldBinding != null) {
          final int enumConstantTypeNameIndex = this.constantPool.literalIndex(fieldBinding.type.signature());
          final int enumConstantNameIndex = this.constantPool.literalIndex(fieldBinding.name);
          this.contents[this.contentsOffset++] = (byte) (enumConstantTypeNameIndex >> 8);
          this.contents[this.contentsOffset++] = (byte) enumConstantTypeNameIndex;
          this.contents[this.contentsOffset++] = (byte) (enumConstantNameIndex >> 8);
          this.contents[this.contentsOffset++] = (byte) enumConstantNameIndex;
        }
      } else if (defaultValueBinding.isAnnotationType()) {
        if (this.contentsOffset + 1 >= this.contents.length) {
          resizeContents(1);
        }
        this.contents[this.contentsOffset++] = (byte) '@';
        generateAnnotation((Annotation) defaultValue, attributeOffset);
      } else if (defaultValueBinding.isArrayType()) {
        // array type
        if (this.contentsOffset + 3 >= this.contents.length) {
          resizeContents(3);
        }
        this.contents[this.contentsOffset++] = (byte) '[';
        if (defaultValue instanceof ArrayInitializer) {
          ArrayInitializer arrayInitializer = (ArrayInitializer) defaultValue;
          int arrayLength = arrayInitializer.expressions != null ? arrayInitializer.expressions.length : 0;
          this.contents[this.contentsOffset++] = (byte) (arrayLength >> 8);
          this.contents[this.contentsOffset++] = (byte) arrayLength;
          for (int i = 0; i < arrayLength; i++) {
            generateElementValue(arrayInitializer.expressions[i], defaultValueBinding.leafComponentType(), attributeOffset);
          }
        } else {
          this.contentsOffset = attributeOffset;
        }
      } else {
        // class type
        if (this.contentsOffset + 3 >= this.contents.length) {
          resizeContents(3);
        }
        this.contents[this.contentsOffset++] = (byte) 'c';
        if (defaultValue instanceof ClassLiteralAccess) {
          ClassLiteralAccess classLiteralAccess = (ClassLiteralAccess) defaultValue;
          final int classInfoIndex = this.constantPool.literalIndex(classLiteralAccess.targetType.signature());
          this.contents[this.contentsOffset++] = (byte) (classInfoIndex >> 8);
          this.contents[this.contentsOffset++] = (byte) classInfoIndex;
        } else {
          this.contentsOffset = attributeOffset;
        }
      }
    } else {
      this.contentsOffset = attributeOffset;
    }
  }

  private int generateEnclosingMethodAttribute() {
    int localContentsOffset = this.contentsOffset;
    // add enclosing method attribute (1.5 mode only)
    if (localContentsOffset + 10 >= this.contents.length) {
      resizeContents(10);
    }
    int enclosingMethodAttributeNameIndex =
      this.constantPool.literalIndex(AttributeNamesConstants.EnclosingMethodName);
    this.contents[localContentsOffset++] = (byte) (enclosingMethodAttributeNameIndex >> 8);
    this.contents[localContentsOffset++] = (byte) enclosingMethodAttributeNameIndex;
    // the length of a signature attribute is equals to 2
    this.contents[localContentsOffset++] = 0;
    this.contents[localContentsOffset++] = 0;
    this.contents[localContentsOffset++] = 0;
    this.contents[localContentsOffset++] = 4;

    int enclosingTypeIndex = this.constantPool.literalIndexForType(this.referenceBinding.enclosingType().constantPoolName());
    this.contents[localContentsOffset++] = (byte) (enclosingTypeIndex >> 8);
    this.contents[localContentsOffset++] = (byte) enclosingTypeIndex;
    byte methodIndexByte1 = 0;
    byte methodIndexByte2 = 0;
    if (this.referenceBinding instanceof LocalTypeBinding) {
      MethodBinding methodBinding = ((LocalTypeBinding) this.referenceBinding).enclosingMethod;
      if (methodBinding != null) {
        int enclosingMethodIndex = this.constantPool.literalIndexForNameAndType(methodBinding.selector, methodBinding.signature(this));
        methodIndexByte1 = (byte) (enclosingMethodIndex >> 8);
        methodIndexByte2 = (byte) enclosingMethodIndex;
      }
    }
    this.contents[localContentsOffset++] = methodIndexByte1;
    this.contents[localContentsOffset++] = methodIndexByte2;
    this.contentsOffset = localContentsOffset;
    return 1;
  }
  private int generateExceptionsAttribute(ReferenceBinding[] thrownsExceptions) {
    int localContentsOffset = this.contentsOffset;
    int length = thrownsExceptions.length;
    int exSize = 8 + length * 2;
    if (exSize + this.contentsOffset >= this.contents.length) {
      resizeContents(exSize);
    }
    int exceptionNameIndex =
      this.constantPool.literalIndex(AttributeNamesConstants.ExceptionsName);
    this.contents[localContentsOffset++] = (byte) (exceptionNameIndex >> 8);
    this.contents[localContentsOffset++] = (byte) exceptionNameIndex;
    // The attribute length = length * 2 + 2 in case of a exception attribute
    int attributeLength = length * 2 + 2;
    this.contents[localContentsOffset++] = (byte) (attributeLength >> 24);
    this.contents[localContentsOffset++] = (byte) (attributeLength >> 16);
    this.contents[localContentsOffset++] = (byte) (attributeLength >> 8);
    this.contents[localContentsOffset++] = (byte) attributeLength;
    this.contents[localContentsOffset++] = (byte) (length >> 8);
    this.contents[localContentsOffset++] = (byte) length;
    for (int i = 0; i < length; i++) {
      int exceptionIndex = this.constantPool.literalIndexForType(thrownsExceptions[i]);
      this.contents[localContentsOffset++] = (byte) (exceptionIndex >> 8);
      this.contents[localContentsOffset++] = (byte) exceptionIndex;
    }
    this.contentsOffset = localContentsOffset;
    return 1;
  }
  private int generateHierarchyInconsistentAttribute() {
    int localContentsOffset = this.contentsOffset;
    // add an attribute for inconsistent hierarchy
    if (localContentsOffset + 6 >= this.contents.length) {
      resizeContents(6);
    }
    int inconsistentHierarchyNameIndex =
      this.constantPool.literalIndex(AttributeNamesConstants.InconsistentHierarchy);
    this.contents[localContentsOffset++] = (byte) (inconsistentHierarchyNameIndex >> 8);
    this.contents[localContentsOffset++] = (byte) inconsistentHierarchyNameIndex;
    // the length of an inconsistent hierarchy attribute is equals to 0
    this.contents[localContentsOffset++] = 0;
    this.contents[localContentsOffset++] = 0;
    this.contents[localContentsOffset++] = 0;
    this.contents[localContentsOffset++] = 0;
    this.contentsOffset = localContentsOffset;
    return 1;
  }
  private int generateInnerClassAttribute(int numberOfInnerClasses, ReferenceBinding[] innerClasses) {
    int localContentsOffset = this.contentsOffset;
    // Generate the inner class attribute
    int exSize = 8 * numberOfInnerClasses + 8;
    if (exSize + localContentsOffset >= this.contents.length) {
      resizeContents(exSize);
    }
    // Now we now the size of the attribute and the number of entries
    // attribute name
    int attributeNameIndex =
      this.constantPool.literalIndex(AttributeNamesConstants.InnerClassName);
    this.contents[localContentsOffset++] = (byte) (attributeNameIndex >> 8);
    this.contents[localContentsOffset++] = (byte) attributeNameIndex;
    int value = (numberOfInnerClasses << 3) + 2;
    this.contents[localContentsOffset++] = (byte) (value >> 24);
    this.contents[localContentsOffset++] = (byte) (value >> 16);
    this.contents[localContentsOffset++] = (byte) (value >> 8);
    this.contents[localContentsOffset++] = (byte) value;
    this.contents[localContentsOffset++] = (byte) (numberOfInnerClasses >> 8);
    this.contents[localContentsOffset++] = (byte) numberOfInnerClasses;
    for (int i = 0; i < numberOfInnerClasses; i++) {
      ReferenceBinding innerClass = innerClasses[i];
      int accessFlags = innerClass.getAccessFlags();
      int innerClassIndex = this.constantPool.literalIndexForType(innerClass.constantPoolName());
      // inner class index
      this.contents[localContentsOffset++] = (byte) (innerClassIndex >> 8);
      this.contents[localContentsOffset++] = (byte) innerClassIndex;
      // outer class index: anonymous and local have no outer class index
      if (innerClass.isMemberType()) {
        // member or member of local
        int outerClassIndex = this.constantPool.literalIndexForType(innerClass.enclosingType().constantPoolName());
        this.contents[localContentsOffset++] = (byte) (outerClassIndex >> 8);
        this.contents[localContentsOffset++] = (byte) outerClassIndex;
      } else {
        // equals to 0 if the innerClass is not a member type
        this.contents[localContentsOffset++] = 0;
        this.contents[localContentsOffset++] = 0;
      }
      // name index
      if (!innerClass.isAnonymousType()) {
        int nameIndex = this.constantPool.literalIndex(innerClass.sourceName());
        this.contents[localContentsOffset++] = (byte) (nameIndex >> 8);
        this.contents[localContentsOffset++] = (byte) nameIndex;
      } else {
        // equals to 0 if the innerClass is an anonymous type
        this.contents[localContentsOffset++] = 0;
        this.contents[localContentsOffset++] = 0;
      }
      // access flag
      if (innerClass.isAnonymousType()) {
        accessFlags &= ~ClassFileConstants.AccFinal;
      } else if (innerClass.isMemberType() && innerClass.isInterface()) {
        accessFlags |= ClassFileConstants.AccStatic; // implicitely static
      }
      this.contents[localContentsOffset++] = (byte) (accessFlags >> 8);
      this.contents[localContentsOffset++] = (byte) accessFlags;
    }
    this.contentsOffset = localContentsOffset;
    return 1;
  }
  private int generateLineNumberAttribute() {
    int localContentsOffset = this.contentsOffset;
    int attributesNumber = 0;
    /* Create and add the line number attribute (used for debugging)
     * Build the pairs of:
     *   (bytecodePC lineNumber)
     * according to the table of start line indexes and the pcToSourceMap table
     * contained into the codestream
     */
    int[] pcToSourceMapTable;
    if (((pcToSourceMapTable = this.codeStream.pcToSourceMap) != null)
      && (this.codeStream.pcToSourceMapSize != 0)) {
      int lineNumberNameIndex =
        this.constantPool.literalIndex(AttributeNamesConstants.LineNumberTableName);
      if (localContentsOffset + 8 >= this.contents.length) {
        resizeContents(8);
      }
      this.contents[localContentsOffset++] = (byte) (lineNumberNameIndex >> 8);
      this.contents[localContentsOffset++] = (byte) lineNumberNameIndex;
      int lineNumberTableOffset = localContentsOffset;
      localContentsOffset += 6;
      // leave space for attribute_length and line_number_table_length
      int numberOfEntries = 0;
      int length = this.codeStream.pcToSourceMapSize;
      for (int i = 0; i < length;) {
        // write the entry
        if (localContentsOffset + 4 >= this.contents.length) {
          resizeContents(4);
        }
        int pc = pcToSourceMapTable[i++];
        this.contents[localContentsOffset++] = (byte) (pc >> 8);
        this.contents[localContentsOffset++] = (byte) pc;
        int lineNumber = pcToSourceMapTable[i++];
        this.contents[localContentsOffset++] = (byte) (lineNumber >> 8);
        this.contents[localContentsOffset++] = (byte) lineNumber;
        numberOfEntries++;
      }
      // now we change the size of the line number attribute
      int lineNumberAttr_length = numberOfEntries * 4 + 2;
      this.contents[lineNumberTableOffset++] = (byte) (lineNumberAttr_length >> 24);
      this.contents[lineNumberTableOffset++] = (byte) (lineNumberAttr_length >> 16);
      this.contents[lineNumberTableOffset++] = (byte) (lineNumberAttr_length >> 8);
      this.contents[lineNumberTableOffset++] = (byte) lineNumberAttr_length;
      this.contents[lineNumberTableOffset++] = (byte) (numberOfEntries >> 8);
      this.contents[lineNumberTableOffset++] = (byte) numberOfEntries;
      attributesNumber = 1;
    }
    this.contentsOffset = localContentsOffset;
    return attributesNumber;
  }
  // this is used for problem and synthetic methods
  private int generateLineNumberAttribute(int problemLine) {
    int localContentsOffset = this.contentsOffset;
    if (localContentsOffset + 12 >= this.contents.length) {
      resizeContents(12);
    }
    /* Create and add the line number attribute (used for debugging)
     * Build the pairs of:
     * (bytecodePC lineNumber)
     * according to the table of start line indexes and the pcToSourceMap table
     * contained into the codestream
     */
    int lineNumberNameIndex =
      this.constantPool.literalIndex(AttributeNamesConstants.LineNumberTableName);
    this.contents[localContentsOffset++] = (byte) (lineNumberNameIndex >> 8);
    this.contents[localContentsOffset++] = (byte) lineNumberNameIndex;
    this.contents[localContentsOffset++] = 0;
    this.contents[localContentsOffset++] = 0;
    this.contents[localContentsOffset++] = 0;
    this.contents[localContentsOffset++] = 6;
    this.contents[localContentsOffset++] = 0;
    this.contents[localContentsOffset++] = 1;
    // first entry at pc = 0
    this.contents[localContentsOffset++] = 0;
    this.contents[localContentsOffset++] = 0;
    this.contents[localContentsOffset++] = (byte) (problemLine >> 8);
    this.contents[localContentsOffset++] = (byte) problemLine;
    // now we change the size of the line number attribute
    this.contentsOffset = localContentsOffset;
    return 1;
  }
 
  private int generateLocalVariableTableAttribute(int code_length, boolean methodDeclarationIsStatic, boolean isSynthetic) {
    int attributesNumber = 0;
    int localContentsOffset = this.contentsOffset;
    int numberOfEntries = 0;
    int localVariableNameIndex =
      this.constantPool.literalIndex(AttributeNamesConstants.LocalVariableTableName);
    int maxOfEntries = 8 + 10 * (methodDeclarationIsStatic ? 0 : 1);
    for (int i = 0; i < this.codeStream.allLocalsCounter; i++) {
      LocalVariableBinding localVariableBinding = this.codeStream.locals[i];
      maxOfEntries += 10 * localVariableBinding.initializationCount;
    }
    // reserve enough space
    if (localContentsOffset + maxOfEntries >= this.contents.length) {
      resizeContents(maxOfEntries);
    }
    this.contents[localContentsOffset++] = (byte) (localVariableNameIndex >> 8);
    this.contents[localContentsOffset++] = (byte) localVariableNameIndex;
    int localVariableTableOffset = localContentsOffset;
    // leave space for attribute_length and local_variable_table_length
    localContentsOffset += 6;
    int nameIndex;
    int descriptorIndex;
    SourceTypeBinding declaringClassBinding = null;
    if (!methodDeclarationIsStatic && !isSynthetic) {
      numberOfEntries++;
      this.contents[localContentsOffset++] = 0; // the startPC for this is always 0
      this.contents[localContentsOffset++] = 0;
      this.contents[localContentsOffset++] = (byte) (code_length >> 8);
      this.contents[localContentsOffset++] = (byte) code_length;
      nameIndex = this.constantPool.literalIndex(ConstantPool.This);
      this.contents[localContentsOffset++] = (byte) (nameIndex >> 8);
      this.contents[localContentsOffset++] = (byte) nameIndex;
      declaringClassBinding = (SourceTypeBinding) this.codeStream.methodDeclaration.binding.declaringClass;
      descriptorIndex =
        this.constantPool.literalIndex(
          declaringClassBinding.signature());
      this.contents[localContentsOffset++] = (byte) (descriptorIndex >> 8);
      this.contents[localContentsOffset++] = (byte) descriptorIndex;
      this.contents[localContentsOffset++] = 0;// the resolved position for this is always 0
      this.contents[localContentsOffset++] = 0;
    }
    // used to remember the local variable with a generic type
    int genericLocalVariablesCounter = 0;
    LocalVariableBinding[] genericLocalVariables = null;
    int numberOfGenericEntries = 0;

    for (int i = 0, max = this.codeStream.allLocalsCounter; i < max; i++) {
      LocalVariableBinding localVariable = this.codeStream.locals[i];
      int initializationCount = localVariable.initializationCount;
      if (initializationCount == 0) continue;
      if (localVariable.declaration == null) continue;
      final TypeBinding localVariableTypeBinding = localVariable.type;
      boolean isParameterizedType = localVariableTypeBinding.isParameterizedType() || localVariableTypeBinding.isTypeVariable();
      if (isParameterizedType) {
        if (genericLocalVariables == null) {
          // we cannot have more than max locals
          genericLocalVariables = new LocalVariableBinding[max];
        }
        genericLocalVariables[genericLocalVariablesCounter++] = localVariable;
      }
      for (int j = 0; j < initializationCount; j++) {
        int startPC = localVariable.initializationPCs[j << 1];
        int endPC = localVariable.initializationPCs[(j << 1) + 1];
        if (startPC != endPC) { // only entries for non zero length
          if (endPC == -1) {
            localVariable.declaringScope.problemReporter().abortDueToInternalError(
                Messages.bind(Messages.abort_invalidAttribute, new String(localVariable.name)),
                (ASTNode) localVariable.declaringScope.methodScope().referenceContext);
          }
          if (isParameterizedType) {
            numberOfGenericEntries++;
          }
          // now we can safely add the local entry
          numberOfEntries++;
          this.contents[localContentsOffset++] = (byte) (startPC >> 8);
          this.contents[localContentsOffset++] = (byte) startPC;
          int length = endPC - startPC;
          this.contents[localContentsOffset++] = (byte) (length >> 8);
          this.contents[localContentsOffset++] = (byte) length;
          nameIndex = this.constantPool.literalIndex(localVariable.name);
          this.contents[localContentsOffset++] = (byte) (nameIndex >> 8);
          this.contents[localContentsOffset++] = (byte) nameIndex;
          descriptorIndex = this.constantPool.literalIndex(localVariableTypeBinding.signature());
          this.contents[localContentsOffset++] = (byte) (descriptorIndex >> 8);
          this.contents[localContentsOffset++] = (byte) descriptorIndex;
          int resolvedPosition = localVariable.resolvedPosition;
          this.contents[localContentsOffset++] = (byte) (resolvedPosition >> 8);
          this.contents[localContentsOffset++] = (byte) resolvedPosition;
        }
      }
    }
    int value = numberOfEntries * 10 + 2;
    this.contents[localVariableTableOffset++] = (byte) (value >> 24);
    this.contents[localVariableTableOffset++] = (byte) (value >> 16);
    this.contents[localVariableTableOffset++] = (byte) (value >> 8);
    this.contents[localVariableTableOffset++] = (byte) value;
    this.contents[localVariableTableOffset++] = (byte) (numberOfEntries >> 8);
    this.contents[localVariableTableOffset] = (byte) numberOfEntries;
    attributesNumber++;

    final boolean currentInstanceIsGeneric =
      !methodDeclarationIsStatic
      && declaringClassBinding != null
      && declaringClassBinding.typeVariables != Binding.NO_TYPE_VARIABLES;
    if (genericLocalVariablesCounter != 0 || currentInstanceIsGeneric) {
      // add the local variable type table attribute
      numberOfGenericEntries += (currentInstanceIsGeneric ? 1 : 0);
      maxOfEntries = 8 + numberOfGenericEntries * 10;
      // reserve enough space
      if (localContentsOffset + maxOfEntries >= this.contents.length) {
        resizeContents(maxOfEntries);
      }
      int localVariableTypeNameIndex =
        this.constantPool.literalIndex(AttributeNamesConstants.LocalVariableTypeTableName);
      this.contents[localContentsOffset++] = (byte) (localVariableTypeNameIndex >> 8);
      this.contents[localContentsOffset++] = (byte) localVariableTypeNameIndex;
      value = numberOfGenericEntries * 10 + 2;
      this.contents[localContentsOffset++] = (byte) (value >> 24);
      this.contents[localContentsOffset++] = (byte) (value >> 16);
      this.contents[localContentsOffset++] = (byte) (value >> 8);
      this.contents[localContentsOffset++] = (byte) value;
      this.contents[localContentsOffset++] = (byte) (numberOfGenericEntries >> 8);
      this.contents[localContentsOffset++] = (byte) numberOfGenericEntries;
      if (currentInstanceIsGeneric) {
        this.contents[localContentsOffset++] = 0; // the startPC for this is always 0
        this.contents[localContentsOffset++] = 0;
        this.contents[localContentsOffset++] = (byte) (code_length >> 8);
        this.contents[localContentsOffset++] = (byte) code_length;
        nameIndex = this.constantPool.literalIndex(ConstantPool.This);
        this.contents[localContentsOffset++] = (byte) (nameIndex >> 8);
        this.contents[localContentsOffset++] = (byte) nameIndex;
        descriptorIndex = this.constantPool.literalIndex(declaringClassBinding.genericTypeSignature());
        this.contents[localContentsOffset++] = (byte) (descriptorIndex >> 8);
        this.contents[localContentsOffset++] = (byte) descriptorIndex;
        this.contents[localContentsOffset++] = 0;// the resolved position for this is always 0
        this.contents[localContentsOffset++] = 0;
      }

      for (int i = 0; i < genericLocalVariablesCounter; i++) {
        LocalVariableBinding localVariable = genericLocalVariables[i];
        for (int j = 0; j < localVariable.initializationCount; j++) {
          int startPC = localVariable.initializationPCs[j << 1];
          int endPC = localVariable.initializationPCs[(j << 1) + 1];
          if (startPC != endPC) {
            // only entries for non zero length
            // now we can safely add the local entry
            this.contents[localContentsOffset++] = (byte) (startPC >> 8);
            this.contents[localContentsOffset++] = (byte) startPC;
            int length = endPC - startPC;
            this.contents[localContentsOffset++] = (byte) (length >> 8);
            this.contents[localContentsOffset++] = (byte) length;
            nameIndex = this.constantPool.literalIndex(localVariable.name);
            this.contents[localContentsOffset++] = (byte) (nameIndex >> 8);
            this.contents[localContentsOffset++] = (byte) nameIndex;
            descriptorIndex = this.constantPool.literalIndex(localVariable.type.genericTypeSignature());
            this.contents[localContentsOffset++] = (byte) (descriptorIndex >> 8);
            this.contents[localContentsOffset++] = (byte) descriptorIndex;
            int resolvedPosition = localVariable.resolvedPosition;
            this.contents[localContentsOffset++] = (byte) (resolvedPosition >> 8);
            this.contents[localContentsOffset++] = (byte) resolvedPosition;
          }
        }
      }
      attributesNumber++;
    }
    this.contentsOffset = localContentsOffset;
    return attributesNumber;
  }
  /**
   * INTERNAL USE-ONLY
   * That method generates the attributes of a code attribute.
   * They could be:
   * - an exception attribute for each try/catch found inside the method
   * - a deprecated attribute
   * - a synthetic attribute for synthetic access methods
   *
   * It returns the number of attributes created for the code attribute.
   *
   * @param methodBinding org.eclipse.jdt.internal.compiler.lookup.MethodBinding
   * @return <CODE>int</CODE>
   */
  public int generateMethodInfoAttributes(MethodBinding methodBinding) {
    // leave two bytes for the attribute_number
    this.contentsOffset += 2;
    if (this.contentsOffset + 2 >= this.contents.length) {
      resizeContents(2);
    }
    // now we can handle all the attribute for that method info:
    // it could be:
    // - a CodeAttribute
    // - a ExceptionAttribute
    // - a DeprecatedAttribute
    // - a SyntheticAttribute

    // Exception attribute
    ReferenceBinding[] thrownsExceptions;
    int attributesNumber = 0;
    if ((thrownsExceptions = methodBinding.thrownExceptions) != Binding.NO_EXCEPTIONS) {
      // The method has a throw clause. So we need to add an exception attribute
      // check that there is enough space to write all the bytes for the exception attribute
      attributesNumber += generateExceptionsAttribute(thrownsExceptions);
    }
    if (methodBinding.isDeprecated()) {
      // Deprecated attribute
      attributesNumber += generateDeprecatedAttribute();
    }
    if (this.targetJDK < ClassFileConstants.JDK1_5) {
      if (methodBinding.isSynthetic()) {
        attributesNumber += generateSyntheticAttribute();
      }
      if (methodBinding.isVarargs()) {
        attributesNumber += generateVarargsAttribute();
      }
    }
    // add signature attribute
    char[] genericSignature = methodBinding.genericSignature();
    if (genericSignature != null) {
      attributesNumber += generateSignatureAttribute(genericSignature);
    }
    if (this.targetJDK >= ClassFileConstants.JDK1_4) {
      AbstractMethodDeclaration methodDeclaration = methodBinding.sourceMethod();
      if (methodDeclaration != null) {
        Annotation[] annotations = methodDeclaration.annotations;
        if (annotations != null) {
          attributesNumber += generateRuntimeAnnotations(annotations);
        }
        if ((methodBinding.tagBits & TagBits.HasParameterAnnotations) != 0) {
          Argument[] arguments = methodDeclaration.arguments;
          if (arguments != null) {
            attributesNumber += generateRuntimeAnnotationsForParameters(arguments);
          }
        }
      }
    }
    if ((methodBinding.tagBits & TagBits.HasMissingType) != 0) {
      this.missingTypes = methodBinding.collectMissingTypes(this.missingTypes);
    }
    return attributesNumber;
  }
  public int generateMethodInfoAttributes(MethodBinding methodBinding, AnnotationMethodDeclaration declaration) {
    int attributesNumber = generateMethodInfoAttributes(methodBinding);
    int attributeOffset = this.contentsOffset;
    if ((declaration.modifiers & ClassFileConstants.AccAnnotationDefault) != 0) {
      // add an annotation default attribute
      attributesNumber += generateAnnotationDefaultAttribute(declaration, attributeOffset);
    }
    return attributesNumber;
  }
  /**
   * INTERNAL USE-ONLY
   * That method generates the header of a method info:
   * The header consists in:
   * - the access flags
   * - the name index of the method name inside the constant pool
   * - the descriptor index of the signature of the method inside the constant pool.
   *
   * @param methodBinding org.eclipse.jdt.internal.compiler.lookup.MethodBinding
   */
  public void generateMethodInfoHeader(MethodBinding methodBinding) {
    generateMethodInfoHeader(methodBinding, methodBinding.modifiers);
  }

  /**
   * INTERNAL USE-ONLY
   * That method generates the header of a method info:
   * The header consists in:
   * - the access flags
   * - the name index of the method name inside the constant pool
   * - the descriptor index of the signature of the method inside the constant pool.
   *
   * @param methodBinding org.eclipse.jdt.internal.compiler.lookup.MethodBinding
   * @param accessFlags the access flags
   */
  public void generateMethodInfoHeader(MethodBinding methodBinding, int accessFlags) {
    // check that there is enough space to write all the bytes for the method info corresponding
    // to the @methodBinding
    this.methodCount++; // add one more method
    if (this.contentsOffset + 10 >= this.contents.length) {
      resizeContents(10);
    }
    if (this.targetJDK < ClassFileConstants.JDK1_5) {
      // pre 1.5, synthetic is an attribute, not a modifier
      // pre 1.5, varargs is an attribute, not a modifier (-target jsr14 mode)
      accessFlags &= ~(ClassFileConstants.AccSynthetic | ClassFileConstants.AccVarargs);
    }
    if ((methodBinding.tagBits & TagBits.ClearPrivateModifier) != 0) {
      accessFlags &= ~ClassFileConstants.AccPrivate;
    }
    this.contents[this.contentsOffset++] = (byte) (accessFlags >> 8);
    this.contents[this.contentsOffset++] = (byte) accessFlags;
    int nameIndex = this.constantPool.literalIndex(methodBinding.selector);
    this.contents[this.contentsOffset++] = (byte) (nameIndex >> 8);
    this.contents[this.contentsOffset++] = (byte) nameIndex;
    int descriptorIndex = this.constantPool.literalIndex(methodBinding.signature(this));
    this.contents[this.contentsOffset++] = (byte) (descriptorIndex >> 8);
    this.contents[this.contentsOffset++] = (byte) descriptorIndex;
  }

  /**
   * INTERNAL USE-ONLY
   * That method generates the method info header of a clinit:
   * The header consists in:
   * - the access flags (always default access + static)
   * - the name index of the method name (always <clinit>) inside the constant pool
   * - the descriptor index of the signature (always ()V) of the method inside the constant pool.
   */
  public void generateMethodInfoHeaderForClinit() {
    // check that there is enough space to write all the bytes for the method info corresponding
    // to the @methodBinding
    this.methodCount++; // add one more method
    if (this.contentsOffset + 10 >= this.contents.length) {
      resizeContents(10);
    }
    this.contents[this.contentsOffset++] = (byte) ((ClassFileConstants.AccDefault | ClassFileConstants.AccStatic) >> 8);
    this.contents[this.contentsOffset++] = (byte) (ClassFileConstants.AccDefault | ClassFileConstants.AccStatic);
    int nameIndex = this.constantPool.literalIndex(ConstantPool.Clinit);
    this.contents[this.contentsOffset++] = (byte) (nameIndex >> 8);
    this.contents[this.contentsOffset++] = (byte) nameIndex;
    int descriptorIndex =
      this.constantPool.literalIndex(ConstantPool.ClinitSignature);
    this.contents[this.contentsOffset++] = (byte) (descriptorIndex >> 8);
    this.contents[this.contentsOffset++] = (byte) descriptorIndex;
    // We know that we won't get more than 1 attribute: the code attribute
    this.contents[this.contentsOffset++] = 0;
    this.contents[this.contentsOffset++] = 1;
  }

  /**
   * INTERNAL USE-ONLY
   * Generate the byte for problem method infos that correspond to missing abstract methods.
   * http://dev.eclipse.org/bugs/show_bug.cgi?id=3179
   *
   * @param methodDeclarations Array of all missing abstract methods
   */
  public void generateMissingAbstractMethods(MethodDeclaration[] methodDeclarations, CompilationResult compilationResult) {
    if (methodDeclarations != null) {
      TypeDeclaration currentDeclaration = this.referenceBinding.scope.referenceContext;
      int typeDeclarationSourceStart = currentDeclaration.sourceStart();
      int typeDeclarationSourceEnd = currentDeclaration.sourceEnd();
      for (int i = 0, max = methodDeclarations.length; i < max; i++) {
        MethodDeclaration methodDeclaration = methodDeclarations[i];
        MethodBinding methodBinding = methodDeclaration.binding;
         String readableName = new String(methodBinding.readableName());
         CategorizedProblem[] problems = compilationResult.problems;
         int problemsCount = compilationResult.problemCount;
        for (int j = 0; j < problemsCount; j++) {
          CategorizedProblem problem = problems[j];
          if (problem != null
              && problem.getID() == IProblem.AbstractMethodMustBeImplemented
              && problem.getMessage().indexOf(readableName) != -1
              && problem.getSourceStart() >= typeDeclarationSourceStart
              && problem.getSourceEnd() <= typeDeclarationSourceEnd) {
            // we found a match
            addMissingAbstractProblemMethod(methodDeclaration, methodBinding, problem, compilationResult);
          }
        }
      }
    }
  }

  private void generateMissingTypesAttribute() {
    int initialSize = this.missingTypes.size();
    int[] missingTypesIndexes = new int[initialSize];
    int numberOfMissingTypes = 0;
    if (initialSize > 1) {
      Collections.sort(this.missingTypes, new Comparator() {
        public int compare(Object o1, Object o2) {
          TypeBinding typeBinding1 = (TypeBinding) o1;
          TypeBinding typeBinding2 = (TypeBinding) o2;
          return CharOperation.compareTo(typeBinding1.constantPoolName(), typeBinding2.constantPoolName());
        }
      });
    }
    int previousIndex = 0;
    next: for (int i = 0; i < initialSize; i++) {
      int missingTypeIndex = this.constantPool.literalIndexForType((TypeBinding) this.missingTypes.get(i));
      if (previousIndex == missingTypeIndex) {
        continue next;
      }
      previousIndex = missingTypeIndex;
      missingTypesIndexes[numberOfMissingTypes++] = missingTypeIndex;
    }
    // we don't need to resize as we interate from 0 to numberOfMissingTypes when recording the indexes in the .class file
    int attributeLength = numberOfMissingTypes * 2 + 2;
    if (this.contentsOffset + attributeLength + 6 >= this.contents.length) {
      resizeContents(attributeLength + 6);
    }
    int missingTypesNameIndex = this.constantPool.literalIndex(AttributeNamesConstants.MissingTypesName);
    this.contents[this.contentsOffset++] = (byte) (missingTypesNameIndex >> 8);
    this.contents[this.contentsOffset++] = (byte) missingTypesNameIndex;

    // generate attribute length
    this.contents[this.contentsOffset++] = (byte) (attributeLength >> 24);
    this.contents[this.contentsOffset++] = (byte) (attributeLength >> 16);
    this.contents[this.contentsOffset++] = (byte) (attributeLength >> 8);
    this.contents[this.contentsOffset++] = (byte) attributeLength;

    // generate number of missing types
    this.contents[this.contentsOffset++] = (byte) (numberOfMissingTypes >> 8);
    this.contents[this.contentsOffset++] = (byte) numberOfMissingTypes;
    // generate entry for each missing type
    for (int i = 0; i < numberOfMissingTypes; i++) {
      int missingTypeIndex = missingTypesIndexes[i];
      this.contents[this.contentsOffset++] = (byte) (missingTypeIndex >> 8);
      this.contents[this.contentsOffset++] = (byte) missingTypeIndex;
    }
  }

  /**
   * @param annotations
   * @return the number of attributes created while dumping the annotations in the .class file
   */
  private int generateRuntimeAnnotations(final Annotation[] annotations) {
    int attributesNumber = 0;
    final int length = annotations.length;
    int visibleAnnotationsCounter = 0;
    int invisibleAnnotationsCounter = 0;

    for (int i = 0; i < length; i++) {
      Annotation annotation = annotations[i];
      if (annotation.isRuntimeInvisible()) {
        invisibleAnnotationsCounter++;
      } else if (annotation.isRuntimeVisible()) {
        visibleAnnotationsCounter++;
      }
    }

    int annotationAttributeOffset = this.contentsOffset;
    int constantPOffset = this.constantPool.currentOffset;
    int constantPoolIndex = this.constantPool.currentIndex;
    if (invisibleAnnotationsCounter != 0) {
      if (this.contentsOffset + 10 >= this.contents.length) {
        resizeContents(10);
      }
      int runtimeInvisibleAnnotationsAttributeNameIndex =
        this.constantPool.literalIndex(AttributeNamesConstants.RuntimeInvisibleAnnotationsName);
      this.contents[this.contentsOffset++] = (byte) (runtimeInvisibleAnnotationsAttributeNameIndex >> 8);
      this.contents[this.contentsOffset++] = (byte) runtimeInvisibleAnnotationsAttributeNameIndex;
      int attributeLengthOffset = this.contentsOffset;
      this.contentsOffset += 4; // leave space for the attribute length

      int annotationsLengthOffset = this.contentsOffset;
      this.contentsOffset += 2; // leave space for the annotations length

      int counter = 0;
      loop: for (int i = 0; i < length; i++) {
        if (invisibleAnnotationsCounter == 0) break loop;
        Annotation annotation = annotations[i];
        if (annotation.isRuntimeInvisible()) {
          int currentAnnotationOffset = this.contentsOffset;
          generateAnnotation(annotation, currentAnnotationOffset);
          invisibleAnnotationsCounter--;
          if (this.contentsOffset != currentAnnotationOffset) {
            counter++;
          }
        }
      }
      if (counter != 0) {
        this.contents[annotationsLengthOffset++] = (byte) (counter >> 8);
        this.contents[annotationsLengthOffset++] = (byte) counter;

        int attributeLength = this.contentsOffset - attributeLengthOffset - 4;
        this.contents[attributeLengthOffset++] = (byte) (attributeLength >> 24);
        this.contents[attributeLengthOffset++] = (byte) (attributeLength >> 16);
        this.contents[attributeLengthOffset++] = (byte) (attributeLength >> 8);
        this.contents[attributeLengthOffset++] = (byte) attributeLength;
        attributesNumber++;
      } else {
        this.contentsOffset = annotationAttributeOffset;
        // reset the constant pool to its state before the clinit
        this.constantPool.resetForAttributeName(AttributeNamesConstants.RuntimeInvisibleAnnotationsName, constantPoolIndex, constantPOffset);
      }
    }

    annotationAttributeOffset = this.contentsOffset;
    constantPOffset = this.constantPool.currentOffset;
    constantPoolIndex = this.constantPool.currentIndex;
    if (visibleAnnotationsCounter != 0) {
      if (this.contentsOffset + 10 >= this.contents.length) {
        resizeContents(10);
      }
      int runtimeVisibleAnnotationsAttributeNameIndex =
        this.constantPool.literalIndex(AttributeNamesConstants.RuntimeVisibleAnnotationsName);
      this.contents[this.contentsOffset++] = (byte) (runtimeVisibleAnnotationsAttributeNameIndex >> 8);
      this.contents[this.contentsOffset++] = (byte) runtimeVisibleAnnotationsAttributeNameIndex;
      int attributeLengthOffset = this.contentsOffset;
      this.contentsOffset += 4; // leave space for the attribute length

      int annotationsLengthOffset = this.contentsOffset;
      this.contentsOffset += 2; // leave space for the annotations length

      int counter = 0;
      loop: for (int i = 0; i < length; i++) {
        if (visibleAnnotationsCounter == 0) break loop;
        Annotation annotation = annotations[i];
        if (annotation.isRuntimeVisible()) {
          visibleAnnotationsCounter--;
          int currentAnnotationOffset = this.contentsOffset;
          generateAnnotation(annotation, currentAnnotationOffset);
          if (this.contentsOffset != currentAnnotationOffset) {
            counter++;
          }
        }
      }
      if (counter != 0) {
        this.contents[annotationsLengthOffset++] = (byte) (counter >> 8);
        this.contents[annotationsLengthOffset++] = (byte) counter;

        int attributeLength = this.contentsOffset - attributeLengthOffset - 4;
        this.contents[attributeLengthOffset++] = (byte) (attributeLength >> 24);
        this.contents[attributeLengthOffset++] = (byte) (attributeLength >> 16);
        this.contents[attributeLengthOffset++] = (byte) (attributeLength >> 8);
        this.contents[attributeLengthOffset++] = (byte) attributeLength;
        attributesNumber++;
      } else {
        this.contentsOffset = annotationAttributeOffset;
        this.constantPool.resetForAttributeName(AttributeNamesConstants.RuntimeVisibleAnnotationsName, constantPoolIndex, constantPOffset);
      }
    }
    return attributesNumber;
  }
  private int generateRuntimeAnnotationsForParameters(Argument[] arguments) {
    final int argumentsLength = arguments.length;
    final int VISIBLE_INDEX = 0;
    final int INVISIBLE_INDEX = 1;
    int invisibleParametersAnnotationsCounter = 0;
    int visibleParametersAnnotationsCounter = 0;
    int[][] annotationsCounters = new int[argumentsLength][2];
    for (int i = 0; i < argumentsLength; i++) {
      Argument argument = arguments[i];
      Annotation[] annotations = argument.annotations;
      if (annotations != null) {
        for (int j = 0, max2 = annotations.length; j < max2; j++) {
          Annotation annotation = annotations[j];
          if (annotation.isRuntimeInvisible()) {
            annotationsCounters[i][INVISIBLE_INDEX]++;
            invisibleParametersAnnotationsCounter++;
          } else if (annotation.isRuntimeVisible()) {
            annotationsCounters[i][VISIBLE_INDEX]++;
            visibleParametersAnnotationsCounter++;
          }
        }
      }
    }
    int attributesNumber = 0;
    int annotationAttributeOffset = this.contentsOffset;
    if (invisibleParametersAnnotationsCounter != 0) {
      int globalCounter = 0;
      if (this.contentsOffset + 7 >= this.contents.length) {
        resizeContents(7);
      }
      int attributeNameIndex =
        this.constantPool.literalIndex(AttributeNamesConstants.RuntimeInvisibleParameterAnnotationsName);
      this.contents[this.contentsOffset++] = (byte) (attributeNameIndex >> 8);
      this.contents[this.contentsOffset++] = (byte) attributeNameIndex;
      int attributeLengthOffset = this.contentsOffset;
      this.contentsOffset += 4; // leave space for the attribute length

      this.contents[this.contentsOffset++] = (byte) argumentsLength;
      for (int i = 0; i < argumentsLength; i++) {
        if (this.contentsOffset + 2 >= this.contents.length) {
          resizeContents(2);
        }
        if (invisibleParametersAnnotationsCounter == 0) {
          this.contents[this.contentsOffset++] = (byte) 0;
          this.contents[this.contentsOffset++] = (byte) 0;
        } else {
          final int numberOfInvisibleAnnotations = annotationsCounters[i][INVISIBLE_INDEX];
          int invisibleAnnotationsOffset = this.contentsOffset;
          // leave space for number of annotations
          this.contentsOffset += 2;
          int counter = 0;
          if (numberOfInvisibleAnnotations != 0) {
            Argument argument = arguments[i];
            Annotation[] annotations = argument.annotations;
            for (int j = 0, max = annotations.length; j < max; j++) {
              Annotation annotation = annotations[j];
              if (annotation.isRuntimeInvisible()) {
                int currentAnnotationOffset = this.contentsOffset;
                generateAnnotation(annotation, currentAnnotationOffset);
                if (this.contentsOffset != currentAnnotationOffset) {
                  counter++;
                  globalCounter++;
                }
                invisibleParametersAnnotationsCounter--;
              }
            }
          }
          this.contents[invisibleAnnotationsOffset++] = (byte) (counter >> 8);
          this.contents[invisibleAnnotationsOffset] = (byte) counter;
        }
      }
      if (globalCounter != 0) {
        int attributeLength = this.contentsOffset - attributeLengthOffset - 4;
        this.contents[attributeLengthOffset++] = (byte) (attributeLength >> 24);
        this.contents[attributeLengthOffset++] = (byte) (attributeLength >> 16);
        this.contents[attributeLengthOffset++] = (byte) (attributeLength >> 8);
        this.contents[attributeLengthOffset++] = (byte) attributeLength;
        attributesNumber++;
      } else {
        // if globalCounter is 0, this means that the code generation for all visible annotations failed
        this.contentsOffset = annotationAttributeOffset;
      }
    }
    if (visibleParametersAnnotationsCounter != 0) {
      int globalCounter = 0;
      if (this.contentsOffset + 7 >= this.contents.length) {
        resizeContents(7);
      }
      int attributeNameIndex =
        this.constantPool.literalIndex(AttributeNamesConstants.RuntimeVisibleParameterAnnotationsName);
      this.contents[this.contentsOffset++] = (byte) (attributeNameIndex >> 8);
      this.contents[this.contentsOffset++] = (byte) attributeNameIndex;
      int attributeLengthOffset = this.contentsOffset;
      this.contentsOffset += 4; // leave space for the attribute length

      this.contents[this.contentsOffset++] = (byte) argumentsLength;
      for (int i = 0; i < argumentsLength; i++) {
        if (this.contentsOffset + 2 >= this.contents.length) {
          resizeContents(2);
        }
        if (visibleParametersAnnotationsCounter == 0) {
          this.contents[this.contentsOffset++] = (byte) 0;
          this.contents[this.contentsOffset++] = (byte) 0;
        } else {
          final int numberOfVisibleAnnotations = annotationsCounters[i][VISIBLE_INDEX];
          int visibleAnnotationsOffset = this.contentsOffset;
          // leave space for number of annotations
          this.contentsOffset += 2;
          int counter = 0;
          if (numberOfVisibleAnnotations != 0) {
            Argument argument = arguments[i];
            Annotation[] annotations = argument.annotations;
            for (int j = 0, max = annotations.length; j < max; j++) {
              Annotation annotation = annotations[j];
              if (annotation.isRuntimeVisible()) {
                int currentAnnotationOffset = this.contentsOffset;
                generateAnnotation(annotation, currentAnnotationOffset);
                if (this.contentsOffset != currentAnnotationOffset) {
                  counter++;
                  globalCounter++;
                }
                visibleParametersAnnotationsCounter--;
              }
            }
          }
          this.contents[visibleAnnotationsOffset++] = (byte) (counter >> 8);
          this.contents[visibleAnnotationsOffset] = (byte) counter;
        }
      }
      if (globalCounter != 0) {
        int attributeLength = this.contentsOffset - attributeLengthOffset - 4;
        this.contents[attributeLengthOffset++] = (byte) (attributeLength >> 24);
        this.contents[attributeLengthOffset++] = (byte) (attributeLength >> 16);
        this.contents[attributeLengthOffset++] = (byte) (attributeLength >> 8);
        this.contents[attributeLengthOffset++] = (byte) attributeLength;
        attributesNumber++;
      } else {
        // if globalCounter is 0, this means that the code generation for all visible annotations failed
        this.contentsOffset = annotationAttributeOffset;
      }
    }
    return attributesNumber;
  }

  private int generateSignatureAttribute(char[] genericSignature) {
    int localContentsOffset = this.contentsOffset;
    if (localContentsOffset + 8 >= this.contents.length) {
      resizeContents(8);
    }
    int signatureAttributeNameIndex =
      this.constantPool.literalIndex(AttributeNamesConstants.SignatureName);
    this.contents[localContentsOffset++] = (byte) (signatureAttributeNameIndex >> 8);
    this.contents[localContentsOffset++] = (byte) signatureAttributeNameIndex;
    // the length of a signature attribute is equals to 2
    this.contents[localContentsOffset++] = 0;
    this.contents[localContentsOffset++] = 0;
    this.contents[localContentsOffset++] = 0;
    this.contents[localContentsOffset++] = 2;
    int signatureIndex =
      this.constantPool.literalIndex(genericSignature);
    this.contents[localContentsOffset++] = (byte) (signatureIndex >> 8);
    this.contents[localContentsOffset++] = (byte) signatureIndex;
    this.contentsOffset = localContentsOffset;
    return 1;
  }

  private int generateSourceAttribute(String fullFileName) {
    int localContentsOffset = this.contentsOffset;
    // check that there is enough space to write all the bytes for the field info corresponding
    // to the @fieldBinding
    if (localContentsOffset + 8 >= this.contents.length) {
      resizeContents(8);
    }
    int sourceAttributeNameIndex =
      this.constantPool.literalIndex(AttributeNamesConstants.SourceName);
    this.contents[localContentsOffset++] = (byte) (sourceAttributeNameIndex >> 8);
    this.contents[localContentsOffset++] = (byte) sourceAttributeNameIndex;
    // The length of a source file attribute is 2. This is a fixed-length
    // attribute
    this.contents[localContentsOffset++] = 0;
    this.contents[localContentsOffset++] = 0;
    this.contents[localContentsOffset++] = 0;
    this.contents[localContentsOffset++] = 2;
    // write the source file name
    int fileNameIndex = this.constantPool.literalIndex(fullFileName.toCharArray());
    this.contents[localContentsOffset++] = (byte) (fileNameIndex >> 8);
    this.contents[localContentsOffset++] = (byte) fileNameIndex;
    this.contentsOffset = localContentsOffset;
    return 1;
  }
  private int generateStackMapAttribute(
      MethodBinding methodBinding,
      int code_length,
      int codeAttributeOffset,
      int max_locals,
      boolean isClinit) {
    int attributesNumber = 0;
    int localContentsOffset = this.contentsOffset;
    StackMapFrameCodeStream stackMapFrameCodeStream = (StackMapFrameCodeStream) this.codeStream;
    stackMapFrameCodeStream.removeFramePosition(code_length);
    if (stackMapFrameCodeStream.hasFramePositions()) {
      ArrayList frames = new ArrayList();
      traverse(isClinit ? null : methodBinding, max_locals, this.contents, codeAttributeOffset + 14, code_length, frames, isClinit);
      int numberOfFrames = frames.size();
      if (numberOfFrames > 1) {
        int stackMapTableAttributeOffset = localContentsOffset;
        // add the stack map table attribute
        if (localContentsOffset + 8 >= this.contents.length) {
          resizeContents(8);
        }
        int stackMapAttributeNameIndex =
          this.constantPool.literalIndex(AttributeNamesConstants.StackMapName);
        this.contents[localContentsOffset++] = (byte) (stackMapAttributeNameIndex >> 8);
        this.contents[localContentsOffset++] = (byte) stackMapAttributeNameIndex;

        int stackMapAttributeLengthOffset = localContentsOffset;
        // generate the attribute
        localContentsOffset += 4;
        if (localContentsOffset + 4 >= this.contents.length) {
          resizeContents(4);
        }
        int numberOfFramesOffset = localContentsOffset;
        localContentsOffset += 2;
        if (localContentsOffset + 2 >= this.contents.length) {
          resizeContents(2);
        }
        StackMapFrame currentFrame = (StackMapFrame) frames.get(0);
        for (int j = 1; j < numberOfFrames; j++) {
          // select next frame
          currentFrame = (StackMapFrame) frames.get(j);
          // generate current frame
          // need to find differences between the current frame and the previous frame
          int frameOffset = currentFrame.pc;
          // FULL_FRAME
          if (localContentsOffset + 5 >= this.contents.length) {
            resizeContents(5);
          }
          this.contents[localContentsOffset++] = (byte) (frameOffset >> 8);
          this.contents[localContentsOffset++] = (byte) frameOffset;
          int numberOfLocalOffset = localContentsOffset;
          localContentsOffset += 2; // leave two spots for number of locals
          int numberOfLocalEntries = 0;
          int numberOfLocals = currentFrame.getNumberOfLocals();
          int numberOfEntries = 0;
          int localsLength = currentFrame.locals == null ? 0 : currentFrame.locals.length;
          for (int i = 0; i < localsLength && numberOfLocalEntries < numberOfLocals; i++) {
            if (localContentsOffset + 3 >= this.contents.length) {
              resizeContents(3);
            }
            VerificationTypeInfo info = currentFrame.locals[i];
            if (info == null) {
              this.contents[localContentsOffset++] = (byte) VerificationTypeInfo.ITEM_TOP;
            } else {
              switch(info.id()) {
                case T_boolean :
                case T_byte :
                case T_char :
                case T_int :
                case T_short :
                  this.contents[localContentsOffset++] = (byte) VerificationTypeInfo.ITEM_INTEGER;
                  break;
                case T_float :
                  this.contents[localContentsOffset++] = (byte) VerificationTypeInfo.ITEM_FLOAT;
                  break;
                case T_long :
                  this.contents[localContentsOffset++] = (byte) VerificationTypeInfo.ITEM_LONG;
                  i++;
                  break;
                case T_double :
                  this.contents[localContentsOffset++] = (byte) VerificationTypeInfo.ITEM_DOUBLE;
                  i++;
                  break;
                case T_null :
                  this.contents[localContentsOffset++] = (byte) VerificationTypeInfo.ITEM_NULL;
                  break;
                default:
                  this.contents[localContentsOffset++] = (byte) info.tag;
                switch (info.tag) {
                  case VerificationTypeInfo.ITEM_UNINITIALIZED :
                    int offset = info.offset;
                    this.contents[localContentsOffset++] = (byte) (offset >> 8);
                    this.contents[localContentsOffset++] = (byte) offset;
                    break;
                  case VerificationTypeInfo.ITEM_OBJECT :
                    int indexForType = this.constantPool.literalIndexForType(info.constantPoolName());
                    this.contents[localContentsOffset++] = (byte) (indexForType >> 8);
                    this.contents[localContentsOffset++] = (byte) indexForType;
                }
              }
              numberOfLocalEntries++;
            }
            numberOfEntries++;
          }
          if (localContentsOffset + 4 >= this.contents.length) {
            resizeContents(4);
          }
          this.contents[numberOfLocalOffset++] = (byte) (numberOfEntries >> 8);
          this.contents[numberOfLocalOffset] = (byte) numberOfEntries;
          int numberOfStackItems = currentFrame.numberOfStackItems;
          this.contents[localContentsOffset++] = (byte) (numberOfStackItems >> 8);
          this.contents[localContentsOffset++] = (byte) numberOfStackItems;
          for (int i = 0; i < numberOfStackItems; i++) {
            if (localContentsOffset + 3 >= this.contents.length) {
              resizeContents(3);
            }
            VerificationTypeInfo info = currentFrame.stackItems[i];
            if (info == null) {
              this.contents[localContentsOffset++] = (byte) VerificationTypeInfo.ITEM_TOP;
            } else {
              switch(info.id()) {
                case T_boolean :
                case T_byte :
                case T_char :
                case T_int :
                case T_short :
                  this.contents[localContentsOffset++] = (byte) VerificationTypeInfo.ITEM_INTEGER;
                  break;
                case T_float :
                  this.contents[localContentsOffset++] = (byte) VerificationTypeInfo.ITEM_FLOAT;
                  break;
                case T_long :
                  this.contents[localContentsOffset++] = (byte) VerificationTypeInfo.ITEM_LONG;
                  break;
                case T_double :
                  this.contents[localContentsOffset++] = (byte) VerificationTypeInfo.ITEM_DOUBLE;
                  break;
                case T_null :
                  this.contents[localContentsOffset++] = (byte) VerificationTypeInfo.ITEM_NULL;
                  break;
                default:
                  this.contents[localContentsOffset++] = (byte) info.tag;
                switch (info.tag) {
                  case VerificationTypeInfo.ITEM_UNINITIALIZED :
                    int offset = info.offset;
                    this.contents[localContentsOffset++] = (byte) (offset >> 8);
                    this.contents[localContentsOffset++] = (byte) offset;
                    break;
                  case VerificationTypeInfo.ITEM_OBJECT :
                    int indexForType = this.constantPool.literalIndexForType(info.constantPoolName());
                    this.contents[localContentsOffset++] = (byte) (indexForType >> 8);
                    this.contents[localContentsOffset++] = (byte) indexForType;
                }
              }
            }
          }
        }

        numberOfFrames--;
        if (numberOfFrames != 0) {
          this.contents[numberOfFramesOffset++] = (byte) (numberOfFrames >> 8);
          this.contents[numberOfFramesOffset] = (byte) numberOfFrames;

          int attributeLength = localContentsOffset - stackMapAttributeLengthOffset - 4;
          this.contents[stackMapAttributeLengthOffset++] = (byte) (attributeLength >> 24);
          this.contents[stackMapAttributeLengthOffset++] = (byte) (attributeLength >> 16);
          this.contents[stackMapAttributeLengthOffset++] = (byte) (attributeLength >> 8);
          this.contents[stackMapAttributeLengthOffset] = (byte) attributeLength;
          attributesNumber++;
        } else {
          localContentsOffset = stackMapTableAttributeOffset;
        }
      }
    }
    this.contentsOffset = localContentsOffset;
    return attributesNumber;
  }

  private int generateStackMapTableAttribute(
      MethodBinding methodBinding,
      int code_length,
      int codeAttributeOffset,
      int max_locals,
      boolean isClinit) {
    int attributesNumber = 0;
    int localContentsOffset = this.contentsOffset;
    StackMapFrameCodeStream stackMapFrameCodeStream = (StackMapFrameCodeStream) this.codeStream;
    stackMapFrameCodeStream.removeFramePosition(code_length);
    if (stackMapFrameCodeStream.hasFramePositions()) {
      ArrayList frames = new ArrayList();
      traverse(isClinit ? null: methodBinding, max_locals, this.contents, codeAttributeOffset + 14, code_length, frames, isClinit);
      int numberOfFrames = frames.size();
      if (numberOfFrames > 1) {
        int stackMapTableAttributeOffset = localContentsOffset;
        // add the stack map table attribute
        if (localContentsOffset + 8 >= this.contents.length) {
          resizeContents(8);
        }
        int stackMapTableAttributeNameIndex =
          this.constantPool.literalIndex(AttributeNamesConstants.StackMapTableName);
        this.contents[localContentsOffset++] = (byte) (stackMapTableAttributeNameIndex >> 8);
        this.contents[localContentsOffset++] = (byte) stackMapTableAttributeNameIndex;

        int stackMapTableAttributeLengthOffset = localContentsOffset;
        // generate the attribute
        localContentsOffset += 4;
        if (localContentsOffset + 4 >= this.contents.length) {
          resizeContents(4);
        }
        int numberOfFramesOffset = localContentsOffset;
        localContentsOffset += 2;
        if (localContentsOffset + 2 >= this.contents.length) {
          resizeContents(2);
        }
        StackMapFrame currentFrame = (StackMapFrame) frames.get(0);
        StackMapFrame prevFrame = null;
        for (int j = 1; j < numberOfFrames; j++) {
          // select next frame
          prevFrame = currentFrame;
          currentFrame = (StackMapFrame) frames.get(j);
          // generate current frame
          // need to find differences between the current frame and the previous frame
          int offsetDelta = currentFrame.getOffsetDelta(prevFrame);
          switch (currentFrame.getFrameType(prevFrame)) {
            case StackMapFrame.APPEND_FRAME :
              if (localContentsOffset + 3 >= this.contents.length) {
                resizeContents(3);
              }
              int numberOfDifferentLocals = currentFrame.numberOfDifferentLocals(prevFrame);
              this.contents[localContentsOffset++] = (byte) (251 + numberOfDifferentLocals);
              this.contents[localContentsOffset++] = (byte) (offsetDelta >> 8);
              this.contents[localContentsOffset++] = (byte) offsetDelta;
              int index = currentFrame.getIndexOfDifferentLocals(numberOfDifferentLocals);
              int numberOfLocals = currentFrame.getNumberOfLocals();
              for (int i = index; i < currentFrame.locals.length && numberOfDifferentLocals > 0; i++) {
                if (localContentsOffset + 6 >= this.contents.length) {
                  resizeContents(6);
                }
                VerificationTypeInfo info = currentFrame.locals[i];
                if (info == null) {
                  this.contents[localContentsOffset++] = (byte) VerificationTypeInfo.ITEM_TOP;
                } else {
                  switch(info.id()) {
                    case T_boolean :
                    case T_byte :
                    case T_char :
                    case T_int :
                    case T_short :
                      this.contents[localContentsOffset++] = (byte) VerificationTypeInfo.ITEM_INTEGER;
                      break;
                    case T_float :
                      this.contents[localContentsOffset++] = (byte) VerificationTypeInfo.ITEM_FLOAT;
                      break;
                    case T_long :
                      this.contents[localContentsOffset++] = (byte) VerificationTypeInfo.ITEM_LONG;
                      i++;
                      break;
                    case T_double :
                      this.contents[localContentsOffset++] = (byte) VerificationTypeInfo.ITEM_DOUBLE;
                      i++;
                      break;
                    case T_null :
                      this.contents[localContentsOffset++] = (byte) VerificationTypeInfo.ITEM_NULL;
                      break;
                    default:
                      this.contents[localContentsOffset++] = (byte) info.tag;
                      switch (info.tag) {
                        case VerificationTypeInfo.ITEM_UNINITIALIZED :
                          int offset = info.offset;
                          this.contents[localContentsOffset++] = (byte) (offset >> 8);
                          this.contents[localContentsOffset++] = (byte) offset;
                          break;
                        case VerificationTypeInfo.ITEM_OBJECT :
                          int indexForType = this.constantPool.literalIndexForType(info.constantPoolName());
                          this.contents[localContentsOffset++] = (byte) (indexForType >> 8);
                          this.contents[localContentsOffset++] = (byte) indexForType;
                      }
                  }
                  numberOfDifferentLocals--;
                }
              }
              break;
            case StackMapFrame.SAME_FRAME :
              if (localContentsOffset + 1 >= this.contents.length) {
                resizeContents(1);
              }
              this.contents[localContentsOffset++] = (byte) offsetDelta;
              break;
            case StackMapFrame.SAME_FRAME_EXTENDED :
              if (localContentsOffset + 3 >= this.contents.length) {
                resizeContents(3);
              }
              this.contents[localContentsOffset++] = (byte) 251;
              this.contents[localContentsOffset++] = (byte) (offsetDelta >> 8);
              this.contents[localContentsOffset++] = (byte) offsetDelta;
              break;
            case StackMapFrame.CHOP_FRAME :
              if (localContentsOffset + 3 >= this.contents.length) {
                resizeContents(3);
              }
              numberOfDifferentLocals = -currentFrame.numberOfDifferentLocals(prevFrame);
              this.contents[localContentsOffset++] = (byte) (251 - numberOfDifferentLocals);
              this.contents[localContentsOffset++] = (byte) (offsetDelta >> 8);
              this.contents[localContentsOffset++] = (byte) offsetDelta;
              break;
            case StackMapFrame.SAME_LOCALS_1_STACK_ITEMS :
              if (localContentsOffset + 4 >= this.contents.length) {
                resizeContents(4);
              }
              this.contents[localContentsOffset++] = (byte) (offsetDelta + 64);
              if (currentFrame.stackItems[0] == null) {
                this.contents[localContentsOffset++] = (byte) VerificationTypeInfo.ITEM_TOP;
              } else {
                switch(currentFrame.stackItems[0].id()) {
                  case T_boolean :
                  case T_byte :
                  case T_char :
                  case T_int :
                  case T_short :
                    this.contents[localContentsOffset++] = (byte) VerificationTypeInfo.ITEM_INTEGER;
                    break;
                  case T_float :
                    this.contents[localContentsOffset++] = (byte) VerificationTypeInfo.ITEM_FLOAT;
                    break;
                  case T_long :
                    this.contents[localContentsOffset++] = (byte) VerificationTypeInfo.ITEM_LONG;
                    break;
                  case T_double :
                    this.contents[localContentsOffset++] = (byte) VerificationTypeInfo.ITEM_DOUBLE;
                    break;
                  case T_null :
                    this.contents[localContentsOffset++] = (byte) VerificationTypeInfo.ITEM_NULL;
                    break;
                  default:
                    VerificationTypeInfo info = currentFrame.stackItems[0];
                    byte tag = (byte) info.tag;
                    this.contents[localContentsOffset++] = tag;
                    switch (tag) {
                      case VerificationTypeInfo.ITEM_UNINITIALIZED :
                        int offset = info.offset;
                        this.contents[localContentsOffset++] = (byte) (offset >> 8);
                        this.contents[localContentsOffset++] = (byte) offset;
                        break;
                      case VerificationTypeInfo.ITEM_OBJECT :
                        int indexForType = this.constantPool.literalIndexForType(info.constantPoolName());
                        this.contents[localContentsOffset++] = (byte) (indexForType >> 8);
                        this.contents[localContentsOffset++] = (byte) indexForType;
                    }
                }
              }
              break;
            case StackMapFrame.SAME_LOCALS_1_STACK_ITEMS_EXTENDED :
              if (localContentsOffset + 6 >= this.contents.length) {
                resizeContents(6);
              }
              this.contents[localContentsOffset++] = (byte) 247;
              this.contents[localContentsOffset++] = (byte) (offsetDelta >> 8);
              this.contents[localContentsOffset++] = (byte) offsetDelta;
              if (currentFrame.stackItems[0] == null) {
                this.contents[localContentsOffset++] = (byte) VerificationTypeInfo.ITEM_TOP;
              } else {
                switch(currentFrame.stackItems[0].id()) {
                  case T_boolean :
                  case T_byte :
                  case T_char :
                  case T_int :
                  case T_short :
                    this.contents[localContentsOffset++] = (byte) VerificationTypeInfo.ITEM_INTEGER;
                    break;
                  case T_float :
                    this.contents[localContentsOffset++] = (byte) VerificationTypeInfo.ITEM_FLOAT;
                    break;
                  case T_long :
                    this.contents[localContentsOffset++] = (byte) VerificationTypeInfo.ITEM_LONG;
                    break;
                  case T_double :
                    this.contents[localContentsOffset++] = (byte) VerificationTypeInfo.ITEM_DOUBLE;
                    break;
                  case T_null :
                    this.contents[localContentsOffset++] = (byte) VerificationTypeInfo.ITEM_NULL;
                    break;
                  default:
                    VerificationTypeInfo info = currentFrame.stackItems[0];
                    byte tag = (byte) info.tag;
                    this.contents[localContentsOffset++] = tag;
                    switch (tag) {
                      case VerificationTypeInfo.ITEM_UNINITIALIZED :
                        int offset = info.offset;
                        this.contents[localContentsOffset++] = (byte) (offset >> 8);
                        this.contents[localContentsOffset++] = (byte) offset;
                        break;
                      case VerificationTypeInfo.ITEM_OBJECT :
                        int indexForType = this.constantPool.literalIndexForType(info.constantPoolName());
                        this.contents[localContentsOffset++] = (byte) (indexForType >> 8);
                        this.contents[localContentsOffset++] = (byte) indexForType;
                    }
                }
              }
              break;
            default :
              // FULL_FRAME
              if (localContentsOffset + 5 >= this.contents.length) {
                resizeContents(5);
              }
              this.contents[localContentsOffset++] = (byte) 255;
              this.contents[localContentsOffset++] = (byte) (offsetDelta >> 8);
              this.contents[localContentsOffset++] = (byte) offsetDelta;
              int numberOfLocalOffset = localContentsOffset;
              localContentsOffset += 2; // leave two spots for number of locals
              int numberOfLocalEntries = 0;
              numberOfLocals = currentFrame.getNumberOfLocals();
              int numberOfEntries = 0;
              int localsLength = currentFrame.locals == null ? 0 : currentFrame.locals.length;
              for (int i = 0; i < localsLength && numberOfLocalEntries < numberOfLocals; i++) {
                if (localContentsOffset + 3 >= this.contents.length) {
                  resizeContents(3);
                }
                VerificationTypeInfo info = currentFrame.locals[i];
                if (info == null) {
                  this.contents[localContentsOffset++] = (byte) VerificationTypeInfo.ITEM_TOP;
                } else {
                  switch(info.id()) {
                    case T_boolean :
                    case T_byte :
                    case T_char :
                    case T_int :
                    case T_short :
                      this.contents[localContentsOffset++] = (byte) VerificationTypeInfo.ITEM_INTEGER;
                      break;
                    case T_float :
                      this.contents[localContentsOffset++] = (byte) VerificationTypeInfo.ITEM_FLOAT;
                      break;
                    case T_long :
                      this.contents[localContentsOffset++] = (byte) VerificationTypeInfo.ITEM_LONG;
                      i++;
                      break;
                    case T_double :
                      this.contents[localContentsOffset++] = (byte) VerificationTypeInfo.ITEM_DOUBLE;
                      i++;
                      break;
                    case T_null :
                      this.contents[localContentsOffset++] = (byte) VerificationTypeInfo.ITEM_NULL;
                      break;
                    default:
                      this.contents[localContentsOffset++] = (byte) info.tag;
                      switch (info.tag) {
                        case VerificationTypeInfo.ITEM_UNINITIALIZED :
                          int offset = info.offset;
                          this.contents[localContentsOffset++] = (byte) (offset >> 8);
                          this.contents[localContentsOffset++] = (byte) offset;
                          break;
                        case VerificationTypeInfo.ITEM_OBJECT :
                          int indexForType = this.constantPool.literalIndexForType(info.constantPoolName());
                          this.contents[localContentsOffset++] = (byte) (indexForType >> 8);
                          this.contents[localContentsOffset++] = (byte) indexForType;
                      }
                  }
                  numberOfLocalEntries++;
                }
                numberOfEntries++;
              }
              if (localContentsOffset + 4 >= this.contents.length) {
                resizeContents(4);
              }
              this.contents[numberOfLocalOffset++] = (byte) (numberOfEntries >> 8);
              this.contents[numberOfLocalOffset] = (byte) numberOfEntries;
              int numberOfStackItems = currentFrame.numberOfStackItems;
              this.contents[localContentsOffset++] = (byte) (numberOfStackItems >> 8);
              this.contents[localContentsOffset++] = (byte) numberOfStackItems;
              for (int i = 0; i < numberOfStackItems; i++) {
                if (localContentsOffset + 3 >= this.contents.length) {
                  resizeContents(3);
                }
                VerificationTypeInfo info = currentFrame.stackItems[i];
                if (info == null) {
                  this.contents[localContentsOffset++] = (byte) VerificationTypeInfo.ITEM_TOP;
                } else {
                  switch(info.id()) {
                    case T_boolean :
                    case T_byte :
                    case T_char :
                    case T_int :
                    case T_short :
                      this.contents[localContentsOffset++] = (byte) VerificationTypeInfo.ITEM_INTEGER;
                      break;
                    case T_float :
                      this.contents[localContentsOffset++] = (byte) VerificationTypeInfo.ITEM_FLOAT;
                      break;
                    case T_long :
                      this.contents[localContentsOffset++] = (byte) VerificationTypeInfo.ITEM_LONG;
                      break;
                    case T_double :
                      this.contents[localContentsOffset++] = (byte) VerificationTypeInfo.ITEM_DOUBLE;
                      break;
                    case T_null :
                      this.contents[localContentsOffset++] = (byte) VerificationTypeInfo.ITEM_NULL;
                      break;
                    default:
                      this.contents[localContentsOffset++] = (byte) info.tag;
                      switch (info.tag) {
                        case VerificationTypeInfo.ITEM_UNINITIALIZED :
                          int offset = info.offset;
                          this.contents[localContentsOffset++] = (byte) (offset >> 8);
                          this.contents[localContentsOffset++] = (byte) offset;
                          break;
                        case VerificationTypeInfo.ITEM_OBJECT :
                          int indexForType = this.constantPool.literalIndexForType(info.constantPoolName());
                          this.contents[localContentsOffset++] = (byte) (indexForType >> 8);
                          this.contents[localContentsOffset++] = (byte) indexForType;
                      }
                  }
                }
              }
          }
        }

        numberOfFrames--;
        if (numberOfFrames != 0) {
          this.contents[numberOfFramesOffset++] = (byte) (numberOfFrames >> 8);
          this.contents[numberOfFramesOffset] = (byte) numberOfFrames;

          int attributeLength = localContentsOffset - stackMapTableAttributeLengthOffset - 4;
          this.contents[stackMapTableAttributeLengthOffset++] = (byte) (attributeLength >> 24);
          this.contents[stackMapTableAttributeLengthOffset++] = (byte) (attributeLength >> 16);
          this.contents[stackMapTableAttributeLengthOffset++] = (byte) (attributeLength >> 8);
          this.contents[stackMapTableAttributeLengthOffset] = (byte) attributeLength;
          attributesNumber++;
        } else {
          localContentsOffset = stackMapTableAttributeOffset;
        }
      }
    }
    this.contentsOffset = localContentsOffset;
    return attributesNumber;
  }

  private int generateSyntheticAttribute() {
    int localContentsOffset = this.contentsOffset;
    if (localContentsOffset + 6 >= this.contents.length) {
      resizeContents(6);
    }
    int syntheticAttributeNameIndex =
      this.constantPool.literalIndex(AttributeNamesConstants.SyntheticName);
    this.contents[localContentsOffset++] = (byte) (syntheticAttributeNameIndex >> 8);
    this.contents[localContentsOffset++] = (byte) syntheticAttributeNameIndex;
    // the length of a synthetic attribute is equals to 0
    this.contents[localContentsOffset++] = 0;
    this.contents[localContentsOffset++] = 0;
    this.contents[localContentsOffset++] = 0;
    this.contents[localContentsOffset++] = 0;
    this.contentsOffset = localContentsOffset;
    return 1;
  }

  private int generateVarargsAttribute() {
    int localContentsOffset = this.contentsOffset;
    /*
     * handle of the target jsr14 for varargs in the source
     * Varargs attribute
     * Check that there is enough space to write the attribute
     */
    if (localContentsOffset + 6 >= this.contents.length) {
      resizeContents(6);
    }
    int varargsAttributeNameIndex =
      this.constantPool.literalIndex(AttributeNamesConstants.VarargsName);
    this.contents[localContentsOffset++] = (byte) (varargsAttributeNameIndex >> 8);
    this.contents[localContentsOffset++] = (byte) varargsAttributeNameIndex;
    // the length of a varargs attribute is equals to 0
    this.contents[localContentsOffset++] = 0;
    this.contents[localContentsOffset++] = 0;
    this.contents[localContentsOffset++] = 0;
    this.contents[localContentsOffset++] = 0;

    this.contentsOffset = localContentsOffset;
    return 1;
  }

  /**
   * EXTERNAL API
   * Answer the actual bytes of the class file
   *
   * This method encodes the receiver structure into a byte array which is the content of the classfile.
   * Returns the byte array that represents the encoded structure of the receiver.
   *
   * @return byte[]
   */
  public byte[] getBytes() {
    if (this.bytes == null) {
      this.bytes = new byte[this.headerOffset + this.contentsOffset];
      System.arraycopy(this.header, 0, this.bytes, 0, this.headerOffset);
      System.arraycopy(this.contents, 0, this.bytes, this.headerOffset, this.contentsOffset);
    }
    return this.bytes;
  }
  /**
   * EXTERNAL API
   * Answer the compound name of the class file.
   * @return char[][]
   * e.g. {{java}, {util}, {Hashtable}}.
   */
  public char[][] getCompoundName() {
    return CharOperation.splitOn('/', fileName());
  }

  private int getParametersCount(char[] methodSignature) {
    int i = CharOperation.indexOf('(', methodSignature);
    i++;
    char currentCharacter = methodSignature[i];
    if (currentCharacter == ')') {
      return 0;
    }
    int result = 0;
    while (true) {
      currentCharacter = methodSignature[i];
      if (currentCharacter == ')') {
        return result;
      }
      switch (currentCharacter) {
        case '[':
          // array type
          int scanType = scanType(methodSignature, i + 1);
          result++;
          i = scanType + 1;
          break;
        case 'L':
          scanType = CharOperation.indexOf(';', methodSignature,
              i + 1);
          result++;
          i = scanType + 1;
          break;
        case 'Z':
        case 'B':
        case 'C':
        case 'D':
        case 'F':
        case 'I':
        case 'J':
        case 'S':
          result++;
          i++;
          break;
        default:
          throw new IllegalArgumentException("Invalid starting type character : " + currentCharacter); //$NON-NLS-1$
      }
    }
  }

  private char[] getReturnType(char[] methodSignature) {
    // skip type parameters
    int paren = CharOperation.lastIndexOf(')', methodSignature);
    // there could be thrown exceptions behind, thus scan one type exactly
    return CharOperation.subarray(methodSignature, paren + 1,
        methodSignature.length);
  }


  private final int i4At(byte[] reference, int relativeOffset,
      int structOffset) {
    int position = relativeOffset + structOffset;
    return ((reference[position++] & 0xFF) << 24)
        + ((reference[position++] & 0xFF) << 16)
        + ((reference[position++] & 0xFF) << 8)
        + (reference[position] & 0xFF);
  }

  protected void initByteArrays() {
    int members = this.referenceBinding.methods().length + this.referenceBinding.fields().length;
    this.header = new byte[INITIAL_HEADER_SIZE];
    this.contents = new byte[members < 15 ? INITIAL_CONTENTS_SIZE : INITIAL_HEADER_SIZE];
  }

  public void initialize(SourceTypeBinding aType, ClassFile parentClassFile, boolean createProblemType) {
    // generate the magic numbers inside the header
    this.header[this.headerOffset++] = (byte) (0xCAFEBABEL >> 24);
    this.header[this.headerOffset++] = (byte) (0xCAFEBABEL >> 16);
    this.header[this.headerOffset++] = (byte) (0xCAFEBABEL >> 8);
    this.header[this.headerOffset++] = (byte) (0xCAFEBABEL >> 0);

    long targetVersion = this.targetJDK;
    this.header[this.headerOffset++] = (byte) (targetVersion >> 8); // minor high
    this.header[this.headerOffset++] = (byte) (targetVersion>> 0); // minor low
    this.header[this.headerOffset++] = (byte) (targetVersion >> 24); // major high
    this.header[this.headerOffset++] = (byte) (targetVersion >> 16); // major low

    this.constantPoolOffset = this.headerOffset;
    this.headerOffset += 2;
    this.constantPool.initialize(this);

    // Modifier manipulations for classfile
    int accessFlags = aType.getAccessFlags();
    if (aType.isPrivate()) { // rewrite private to non-public
      accessFlags &= ~ClassFileConstants.AccPublic;
    }
    if (aType.isProtected()) { // rewrite protected into public
      accessFlags |= ClassFileConstants.AccPublic;
    }
    // clear all bits that are illegal for a class or an interface
    accessFlags
      &= ~(
        ClassFileConstants.AccStrictfp
          | ClassFileConstants.AccProtected
          | ClassFileConstants.AccPrivate
          | ClassFileConstants.AccStatic
          | ClassFileConstants.AccSynchronized
          | ClassFileConstants.AccNative);

    // set the AccSuper flag (has to be done after clearing AccSynchronized - since same value)
    if (!aType.isInterface()) { // class or enum
      accessFlags |= ClassFileConstants.AccSuper;
    }
    if (aType.isAnonymousType()) {
      accessFlags &= ~ClassFileConstants.AccFinal;
    }
    int finalAbstract = ClassFileConstants.AccFinal | ClassFileConstants.AccAbstract;
    if ((accessFlags & finalAbstract) == finalAbstract) {
      accessFlags &= ~finalAbstract;
    }
    this.enclosingClassFile = parentClassFile;
    // innerclasses get their names computed at code gen time

    // now we continue to generate the bytes inside the contents array
    this.contents[this.contentsOffset++] = (byte) (accessFlags >> 8);
    this.contents[this.contentsOffset++] = (byte) accessFlags;
    int classNameIndex = this.constantPool.literalIndexForType(aType);
    this.contents[this.contentsOffset++] = (byte) (classNameIndex >> 8);
    this.contents[this.contentsOffset++] = (byte) classNameIndex;
    int superclassNameIndex;
    if (aType.isInterface()) {
      superclassNameIndex = this.constantPool.literalIndexForType(ConstantPool.JavaLangObjectConstantPoolName);
    } else {
      if (aType.superclass != null) {
         if ((aType.superclass.tagBits & TagBits.HasMissingType) != 0) {
            superclassNameIndex = this.constantPool.literalIndexForType(ConstantPool.JavaLangObjectConstantPoolName);
         } else {
            superclassNameIndex = this.constantPool.literalIndexForType(aType.superclass);
         }
      } else {
        superclassNameIndex = 0;
      }
    }
    this.contents[this.contentsOffset++] = (byte) (superclassNameIndex >> 8);
    this.contents[this.contentsOffset++] = (byte) superclassNameIndex;
    ReferenceBinding[] superInterfacesBinding = aType.superInterfaces();
    int interfacesCount = superInterfacesBinding.length;
    int interfacesCountPosition = this.contentsOffset;
    this.contentsOffset += 2;
    int interfaceCounter = 0;
    for (int i = 0; i < interfacesCount; i++) {
      ReferenceBinding binding = superInterfacesBinding[i];
      if ((binding.tagBits & TagBits.HasMissingType) != 0) {
        continue;
      }
      interfaceCounter++;
      int interfaceIndex = this.constantPool.literalIndexForType(binding);
      this.contents[this.contentsOffset++] = (byte) (interfaceIndex >> 8);
      this.contents[this.contentsOffset++] = (byte) interfaceIndex;
    }
    this.contents[interfacesCountPosition++] = (byte) (interfaceCounter >> 8);
    this.contents[interfacesCountPosition] = (byte) interfaceCounter;
    this.creatingProblemType = createProblemType;

    // retrieve the enclosing one guaranteed to be the one matching the propagated flow info
    // 1FF9ZBU: LFCOM:ALL - Local variable attributes busted (Sanity check)
    this.codeStream.maxFieldCount = aType.scope.outerMostClassScope().referenceType().maxFieldCount;
  }

  private void initializeDefaultLocals(StackMapFrame frame,
      MethodBinding methodBinding,
      int maxLocals,
      int codeLength) {
    if (maxLocals != 0) {
      int resolvedPosition = 0;
      // take into account enum constructor synthetic name+ordinal
      final boolean isConstructor = methodBinding.isConstructor();
      if (isConstructor || !methodBinding.isStatic()) {
        LocalVariableBinding localVariableBinding = new LocalVariableBinding(ConstantPool.This, methodBinding.declaringClass, 0, false);
        localVariableBinding.resolvedPosition = 0;
        this.codeStream.record(localVariableBinding);
        localVariableBinding.recordInitializationStartPC(0);
        localVariableBinding.recordInitializationEndPC(codeLength);
        frame.putLocal(resolvedPosition, new VerificationTypeInfo(
            isConstructor ? VerificationTypeInfo.ITEM_UNINITIALIZED_THIS : VerificationTypeInfo.ITEM_OBJECT,
            methodBinding.declaringClass));
        resolvedPosition++;
      }

      if (isConstructor) {
        if (methodBinding.declaringClass.isEnum()) {
          LocalVariableBinding localVariableBinding = new LocalVariableBinding(" name".toCharArray(), this.referenceBinding.scope.getJavaLangString(), 0, false); //$NON-NLS-1$
          localVariableBinding.resolvedPosition = resolvedPosition;
          this.codeStream.record(localVariableBinding);
          localVariableBinding.recordInitializationStartPC(0);
          localVariableBinding.recordInitializationEndPC(codeLength);

          frame.putLocal(resolvedPosition, new VerificationTypeInfo(
              TypeIds.T_JavaLangString,
              ConstantPool.JavaLangStringConstantPoolName));
          resolvedPosition++;

          localVariableBinding = new LocalVariableBinding(" ordinal".toCharArray(), TypeBinding.INT, 0, false); //$NON-NLS-1$
          localVariableBinding.resolvedPosition = resolvedPosition;
          this.codeStream.record(localVariableBinding);
          localVariableBinding.recordInitializationStartPC(0);
          localVariableBinding.recordInitializationEndPC(codeLength);
          frame.putLocal(resolvedPosition, new VerificationTypeInfo(
              TypeBinding.INT));
          resolvedPosition++;
        }

        // take into account the synthetic parameters
        if (methodBinding.declaringClass.isNestedType()) {
          ReferenceBinding enclosingInstanceTypes[];
          if ((enclosingInstanceTypes = methodBinding.declaringClass.syntheticEnclosingInstanceTypes()) != null) {
            for (int i = 0, max = enclosingInstanceTypes.length; i < max; i++) {
              // an enclosingInstanceType can only be a reference
              // binding. It cannot be
              // LongBinding or DoubleBinding
              LocalVariableBinding localVariableBinding = new LocalVariableBinding((" enclosingType" + i).toCharArray(), enclosingInstanceTypes[i], 0, false); //$NON-NLS-1$
              localVariableBinding.resolvedPosition = resolvedPosition;
              this.codeStream.record(localVariableBinding);
              localVariableBinding.recordInitializationStartPC(0);
              localVariableBinding.recordInitializationEndPC(codeLength);

              frame.putLocal(resolvedPosition,
                  new VerificationTypeInfo(enclosingInstanceTypes[i]));
              resolvedPosition++;
            }
          }

          TypeBinding[] arguments;
          if ((arguments = methodBinding.parameters) != null) {
            for (int i = 0, max = arguments.length; i < max; i++) {
              final TypeBinding typeBinding = arguments[i];
              frame.putLocal(resolvedPosition,
                  new VerificationTypeInfo(typeBinding));
              switch (typeBinding.id) {
                case TypeIds.T_double:
                case TypeIds.T_long:
                  resolvedPosition += 2;
                  break;
                default:
                  resolvedPosition++;
              }
            }
          }

          SyntheticArgumentBinding syntheticArguments[];
          if ((syntheticArguments = methodBinding.declaringClass.syntheticOuterLocalVariables()) != null) {
            for (int i = 0, max = syntheticArguments.length; i < max; i++) {
              final TypeBinding typeBinding = syntheticArguments[i].type;
              LocalVariableBinding localVariableBinding = new LocalVariableBinding((" synthetic" + i).toCharArray(), typeBinding, 0, false); //$NON-NLS-1$
              localVariableBinding.resolvedPosition = resolvedPosition;
              this.codeStream.record(localVariableBinding);
              localVariableBinding.recordInitializationStartPC(0);
              localVariableBinding.recordInitializationEndPC(codeLength);

              frame.putLocal(resolvedPosition,
                  new VerificationTypeInfo(typeBinding));
              switch (typeBinding.id) {
                case TypeIds.T_double:
                case TypeIds.T_long:
                  resolvedPosition += 2;
                  break;
                default:
                  resolvedPosition++;
              }
            }
          }
        } else {
          TypeBinding[] arguments;
          if ((arguments = methodBinding.parameters) != null) {
            for (int i = 0, max = arguments.length; i < max; i++) {
              final TypeBinding typeBinding = arguments[i];
              frame.putLocal(resolvedPosition,
                  new VerificationTypeInfo(typeBinding));
              switch (typeBinding.id) {
                case TypeIds.T_double:
                case TypeIds.T_long:
                  resolvedPosition += 2;
                  break;
                default:
                  resolvedPosition++;
              }
            }
          }
        }
      } else {
        TypeBinding[] arguments;
        if ((arguments = methodBinding.parameters) != null) {
          for (int i = 0, max = arguments.length; i < max; i++) {
            final TypeBinding typeBinding = arguments[i];
            frame.putLocal(resolvedPosition,
                new VerificationTypeInfo(typeBinding));
            switch (typeBinding.id) {
              case TypeIds.T_double:
              case TypeIds.T_long:
                resolvedPosition += 2;
                break;
              default:
                resolvedPosition++;
            }
          }
        }
      }
    }
  }

  private void initializeLocals(boolean isStatic, int currentPC, StackMapFrame currentFrame) {
    VerificationTypeInfo[] locals = currentFrame.locals;
    int localsLength = locals.length;
    int i = 0;
    if (!isStatic) {
      // we don't want to reset the first local if the method is not static
      i = 1;
    }
    for (; i < localsLength; i++) {
      locals[i] = null;
    }
    i = 0;
    locals: for (int max = this.codeStream.allLocalsCounter; i < max; i++) {
      LocalVariableBinding localVariable = this.codeStream.locals[i];
      if (localVariable == null) continue;
      int resolvedPosition = localVariable.resolvedPosition;
      final TypeBinding localVariableTypeBinding = localVariable.type;
      inits: for (int j = 0; j < localVariable.initializationCount; j++) {
        int startPC = localVariable.initializationPCs[j << 1];
        int endPC = localVariable.initializationPCs[(j << 1) + 1];
        if (currentPC < startPC) {
          continue inits;
        } else if (currentPC < endPC) {
          // the current local is an active local
          if (currentFrame.locals[resolvedPosition] == null) {
            currentFrame.locals[resolvedPosition] =
                new VerificationTypeInfo(
                    localVariableTypeBinding);
          }
          continue locals;
        }
      }
    }
  }
  /**
   * INTERNAL USE-ONLY
   * Returns the most enclosing classfile of the receiver. This is used know to store the constant pool name
   * for all inner types of the receiver.
   * @return org.eclipse.jdt.internal.compiler.codegen.ClassFile
   */
  public ClassFile outerMostEnclosingClassFile() {
    ClassFile current = this;
    while (current.enclosingClassFile != null)
      current = current.enclosingClassFile;
    return current;
  }

  public void recordInnerClasses(TypeBinding binding) {
    if (this.innerClassesBindings == null) {
      this.innerClassesBindings = new HashSet(INNER_CLASSES_SIZE);
    }
    ReferenceBinding innerClass = (ReferenceBinding) binding;
    this.innerClassesBindings.add(innerClass.erasure());
    ReferenceBinding enclosingType = innerClass.enclosingType();
    while (enclosingType != null
        && enclosingType.isNestedType()) {
      this.innerClassesBindings.add(enclosingType.erasure());
      enclosingType = enclosingType.enclosingType();
    }
  }

  public void reset(SourceTypeBinding typeBinding) {
    // the code stream is reinitialized for each method
    final CompilerOptions options = typeBinding.scope.compilerOptions();
    this.referenceBinding = typeBinding;
    this.isNestedType = typeBinding.isNestedType();
    this.targetJDK = options.targetJDK;
    this.produceAttributes = options.produceDebugAttributes;
    if (this.targetJDK >= ClassFileConstants.JDK1_6) {
      this.produceAttributes |= ClassFileConstants.ATTR_STACK_MAP_TABLE;
    } else if (this.targetJDK == ClassFileConstants.CLDC_1_1) {
      this.targetJDK = ClassFileConstants.JDK1_1; // put back 45.3
      this.produceAttributes |= ClassFileConstants.ATTR_STACK_MAP;
    }
    this.bytes = null;
    this.constantPool.reset();
    this.codeStream.reset(this);
    this.constantPoolOffset = 0;
    this.contentsOffset = 0;
    this.creatingProblemType = false;
    this.enclosingClassFile = null;
    this.headerOffset = 0;
    this.methodCount = 0;
    this.methodCountOffset = 0;
    if (this.innerClassesBindings != null) {
      this.innerClassesBindings.clear();
    }
    this.missingTypes = null;
    this.visitedTypes = null;
  }

  /**
   * Resize the pool contents
   */
  private final void resizeContents(int minimalSize) {
    int length = this.contents.length;
    int toAdd = length;
    if (toAdd < minimalSize)
      toAdd = minimalSize;
    System.arraycopy(this.contents, 0, this.contents = new byte[length + toAdd], 0, length);
  }

  private VerificationTypeInfo retrieveLocal(int currentPC, int resolvedPosition) {
    for (int i = 0, max = this.codeStream.allLocalsCounter; i < max; i++) {
      LocalVariableBinding localVariable = this.codeStream.locals[i];
      if (localVariable == null) continue;
      if (resolvedPosition == localVariable.resolvedPosition) {
        inits: for (int j = 0; j < localVariable.initializationCount; j++) {
          int startPC = localVariable.initializationPCs[j << 1];
          int endPC = localVariable.initializationPCs[(j << 1) + 1];
          if (currentPC < startPC) {
            continue inits;
          } else if (currentPC < endPC) {
            // the current local is an active local
            return new VerificationTypeInfo(localVariable.type);
          }
        }
      }
    }
    return null;
  }

  private int scanType(char[] methodSignature, int index) {
    switch (methodSignature[index]) {
      case '[':
        // array type
        return scanType(methodSignature, index + 1);
      case 'L':
        return CharOperation.indexOf(';', methodSignature, index + 1);
      case 'Z':
      case 'B':
      case 'C':
      case 'D':
      case 'F':
      case 'I':
      case 'J':
      case 'S':
        return index;
      default:
        throw new IllegalArgumentException();
    }
  }

  /**
   * INTERNAL USE-ONLY
   * This methods leaves the space for method counts recording.
   */
  public void setForMethodInfos() {
    // leave some space for the methodCount
    this.methodCountOffset = this.contentsOffset;
    this.contentsOffset += 2;
  }

  public void traverse(MethodBinding methodBinding, int maxLocals, byte[] bytecodes, int codeOffset, int codeLength, ArrayList frames, boolean isClinit) {
    StackMapFrameCodeStream stackMapFrameCodeStream = (StackMapFrameCodeStream) this.codeStream;
    int[] framePositions = stackMapFrameCodeStream.getFramePositions();
    int pc = codeOffset;
    int index;
    int[] constantPoolOffsets = this.constantPool.offsets;
    byte[] poolContents = this.constantPool.poolContent;

    // set initial values for frame positions
    int indexInFramePositions = 0;
    int framePositionsLength = framePositions.length;
    int currentFramePosition = framePositions[0];

    // set initial values for stack depth markers
    int indexInStackDepthMarkers = 0;
    StackDepthMarker[] stackDepthMarkers = stackMapFrameCodeStream.getStackDepthMarkers();
    int stackDepthMarkersLength = stackDepthMarkers == null ? 0 : stackDepthMarkers.length;
    boolean hasStackDepthMarkers = stackDepthMarkersLength != 0;
    StackDepthMarker stackDepthMarker = null;
    if (hasStackDepthMarkers) {
      stackDepthMarker = stackDepthMarkers[0];
    }

    // set initial values for stack markers (used only in cldc mode)
    int indexInStackMarkers = 0;
    StackMarker[] stackMarkers = stackMapFrameCodeStream.getStackMarkers();
    int stackMarkersLength = stackMarkers == null ? 0 : stackMarkers.length;
    boolean hasStackMarkers = stackMarkersLength != 0;
    StackMarker stackMarker = null;
    if (hasStackMarkers) {
      stackMarker = stackMarkers[0];
    }

    // set initial values for exception markers
    int indexInExceptionMarkers = 0;
    ExceptionMarker[] exceptionMarkers= stackMapFrameCodeStream.getExceptionMarkers();
    int exceptionsMarkersLength = exceptionMarkers == null ? 0 : exceptionMarkers.length;
    boolean hasExceptionMarkers = exceptionsMarkersLength != 0;
    ExceptionMarker exceptionMarker = null;
    if (hasExceptionMarkers) {
      exceptionMarker = exceptionMarkers[0];
    }

    StackMapFrame frame = new StackMapFrame(maxLocals);
    if (!isClinit) {
      initializeDefaultLocals(frame, methodBinding, maxLocals, codeLength);
    }
    frame.pc = -1;
    frames.add(frame.duplicate());
    while (true) {
      int currentPC = pc - codeOffset;
      if (hasStackMarkers && stackMarker.pc == currentPC) {
        VerificationTypeInfo[] infos = frame.stackItems;
        VerificationTypeInfo[] tempInfos = new VerificationTypeInfo[frame.numberOfStackItems];
        System.arraycopy(infos, 0, tempInfos, 0, frame.numberOfStackItems);
        stackMarker.setInfos(tempInfos);
      } else if (hasStackMarkers && stackMarker.destinationPC == currentPC) {
        VerificationTypeInfo[] infos = stackMarker.infos;
        frame.stackItems = infos;
        frame.numberOfStackItems = infos.length;
        indexInStackMarkers++;
        if (indexInStackMarkers < stackMarkersLength) {
          stackMarker = stackMarkers[indexInStackMarkers];
        } else {
          hasStackMarkers = false;
        }
      }
      if (hasStackDepthMarkers && stackDepthMarker.pc == currentPC) {
        TypeBinding typeBinding = stackDepthMarker.typeBinding;
        if (typeBinding != null) {
          if (stackDepthMarker.delta > 0) {
            frame.addStackItem(new VerificationTypeInfo(typeBinding));
          } else {
            frame.stackItems[frame.numberOfStackItems - 1] = new VerificationTypeInfo(typeBinding);
          }
        } else {
          frame.numberOfStackItems--;
        }
        indexInStackDepthMarkers++;
        if (indexInStackDepthMarkers < stackDepthMarkersLength) {
          stackDepthMarker = stackDepthMarkers[indexInStackDepthMarkers];
        } else {
          hasStackDepthMarkers = false;
        }
      }
      if (hasExceptionMarkers && exceptionMarker.pc == currentPC) {
        frame.numberOfStackItems = 0;
        frame.addStackItem(new VerificationTypeInfo(0, VerificationTypeInfo.ITEM_OBJECT, exceptionMarker.constantPoolName));
        indexInExceptionMarkers++;
        if (indexInExceptionMarkers < exceptionsMarkersLength) {
          exceptionMarker = exceptionMarkers[indexInExceptionMarkers];
        } else {
          hasExceptionMarkers = false;
        }
      }
      if (currentFramePosition < currentPC) {
        do {
          indexInFramePositions++;
          if (indexInFramePositions < framePositionsLength) {
            currentFramePosition = framePositions[indexInFramePositions];
          } else {
            // no more frame to generate
            return;
          }
        } while (currentFramePosition < currentPC);
      }
      if (currentFramePosition == currentPC) {
        // need to build a new frame and create a stack map attribute entry
        StackMapFrame currentFrame = frame.duplicate();
        currentFrame.pc = currentPC;
        // initialize locals
        initializeLocals(isClinit ? true : methodBinding.isStatic(), currentPC, currentFrame);
        // insert a new frame
        frames.add(currentFrame);
        indexInFramePositions++;
        if (indexInFramePositions < framePositionsLength) {
          currentFramePosition = framePositions[indexInFramePositions];
        } else {
          // no more frame to generate
          return;
        }
      }
      byte opcode = (byte) u1At(bytecodes, 0, pc);
      switch (opcode) {
        case Opcodes.OPC_nop:
          pc++;
          break;
        case Opcodes.OPC_aconst_null:
          frame.addStackItem(TypeBinding.NULL);
          pc++;
          break;
        case Opcodes.OPC_iconst_m1:
        case Opcodes.OPC_iconst_0:
        case Opcodes.OPC_iconst_1:
        case Opcodes.OPC_iconst_2:
        case Opcodes.OPC_iconst_3:
        case Opcodes.OPC_iconst_4:
        case Opcodes.OPC_iconst_5:
          frame.addStackItem(TypeBinding.INT);
          pc++;
          break;
        case Opcodes.OPC_lconst_0:
        case Opcodes.OPC_lconst_1:
          frame.addStackItem(TypeBinding.LONG);
          pc++;
          break;
        case Opcodes.OPC_fconst_0:
        case Opcodes.OPC_fconst_1:
        case Opcodes.OPC_fconst_2:
          frame.addStackItem(TypeBinding.FLOAT);
          pc++;
          break;
        case Opcodes.OPC_dconst_0:
        case Opcodes.OPC_dconst_1:
          frame.addStackItem(TypeBinding.DOUBLE);
          pc++;
          break;
        case Opcodes.OPC_bipush:
          frame.addStackItem(TypeBinding.BYTE);
          pc += 2;
          break;
        case Opcodes.OPC_sipush:
          frame.addStackItem(TypeBinding.SHORT);
          pc += 3;
          break;
        case Opcodes.OPC_ldc:
          index = u1At(bytecodes, 1, pc);
          switch (u1At(poolContents, 0, constantPoolOffsets[index])) {
            case ClassFileConstants.StringTag:
              frame
                  .addStackItem(new VerificationTypeInfo(
                      TypeIds.T_JavaLangString,
                      ConstantPool.JavaLangStringConstantPoolName));
              break;
            case ClassFileConstants.IntegerTag:
              frame.addStackItem(TypeBinding.INT);
              break;
            case ClassFileConstants.FloatTag:
              frame.addStackItem(TypeBinding.FLOAT);
              break;
            case ClassFileConstants.ClassTag:
              frame.addStackItem(new VerificationTypeInfo(
                  TypeIds.T_JavaLangClass,
                  ConstantPool.JavaLangClassConstantPoolName));
          }
          pc += 2;
          break;
        case Opcodes.OPC_ldc_w:
          index = u2At(bytecodes, 1, pc);
          switch (u1At(poolContents, 0, constantPoolOffsets[index])) {
            case ClassFileConstants.StringTag:
              frame
                  .addStackItem(new VerificationTypeInfo(
                      TypeIds.T_JavaLangString,
                      ConstantPool.JavaLangStringConstantPoolName));
              break;
            case ClassFileConstants.IntegerTag:
              frame.addStackItem(TypeBinding.INT);
              break;
            case ClassFileConstants.FloatTag:
              frame.addStackItem(TypeBinding.FLOAT);
              break;
            case ClassFileConstants.ClassTag:
              frame.addStackItem(new VerificationTypeInfo(
                  TypeIds.T_JavaLangClass,
                  ConstantPool.JavaLangClassConstantPoolName));
          }
          pc += 3;
          break;
        case Opcodes.OPC_ldc2_w:
          index = u2At(bytecodes, 1, pc);
          switch (u1At(poolContents, 0, constantPoolOffsets[index])) {
            case ClassFileConstants.DoubleTag:
              frame.addStackItem(TypeBinding.DOUBLE);
              break;
            case ClassFileConstants.LongTag:
              frame.addStackItem(TypeBinding.LONG);
              break;
          }
          pc += 3;
          break;
        case Opcodes.OPC_iload:
          frame.addStackItem(TypeBinding.INT);
          pc += 2;
          break;
        case Opcodes.OPC_lload:
          frame.addStackItem(TypeBinding.LONG);
          pc += 2;
          break;
        case Opcodes.OPC_fload:
          frame.addStackItem(TypeBinding.FLOAT);
          pc += 2;
          break;
        case Opcodes.OPC_dload:
          frame.addStackItem(TypeBinding.DOUBLE);
          pc += 2;
          break;
        case Opcodes.OPC_aload:
          index = u1At(bytecodes, 1, pc);
          VerificationTypeInfo localsN = retrieveLocal(currentPC, index);
          frame.addStackItem(localsN);
          pc += 2;
          break;
        case Opcodes.OPC_iload_0:
        case Opcodes.OPC_iload_1:
        case Opcodes.OPC_iload_2:
        case Opcodes.OPC_iload_3:
          frame.addStackItem(TypeBinding.INT);
          pc++;
          break;
        case Opcodes.OPC_lload_0:
        case Opcodes.OPC_lload_1:
        case Opcodes.OPC_lload_2:
        case Opcodes.OPC_lload_3:
          frame.addStackItem(TypeBinding.LONG);
          pc++;
          break;
        case Opcodes.OPC_fload_0:
        case Opcodes.OPC_fload_1:
        case Opcodes.OPC_fload_2:
        case Opcodes.OPC_fload_3:
          frame.addStackItem(TypeBinding.FLOAT);
          pc++;
          break;
        case Opcodes.OPC_dload_0:
        case Opcodes.OPC_dload_1:
        case Opcodes.OPC_dload_2:
        case Opcodes.OPC_dload_3:
          frame.addStackItem(TypeBinding.DOUBLE);
          pc++;
          break;
        case Opcodes.OPC_aload_0:
          VerificationTypeInfo locals0 = frame.locals[0];
          if (locals0.tag != VerificationTypeInfo.ITEM_UNINITIALIZED_THIS) {
            // special case to handle uninitialized object
            locals0 = retrieveLocal(currentPC, 0);
          }
          frame.addStackItem(locals0);
          pc++;
          break;
        case Opcodes.OPC_aload_1:
          VerificationTypeInfo locals1 = retrieveLocal(currentPC, 1);
          frame.addStackItem(locals1);
          pc++;
          break;
        case Opcodes.OPC_aload_2:
          VerificationTypeInfo locals2 = retrieveLocal(currentPC, 2);
          frame.addStackItem(locals2);
          pc++;
          break;
        case Opcodes.OPC_aload_3:
          VerificationTypeInfo locals3 = retrieveLocal(currentPC, 3);
          frame.addStackItem(locals3);
          pc++;
          break;
        case Opcodes.OPC_iaload:
          frame.numberOfStackItems -=2;
          frame.addStackItem(TypeBinding.INT);
          pc++;
          break;
        case Opcodes.OPC_laload:
          frame.numberOfStackItems -=2;
          frame.addStackItem(TypeBinding.LONG);
          pc++;
          break;
        case Opcodes.OPC_faload:
          frame.numberOfStackItems -=2;
          frame.addStackItem(TypeBinding.FLOAT);
          pc++;
          break;
        case Opcodes.OPC_daload:
          frame.numberOfStackItems -=2;
          frame.addStackItem(TypeBinding.DOUBLE);
          pc++;
          break;
        case Opcodes.OPC_aaload:
          frame.numberOfStackItems--;
          frame.replaceWithElementType();
          pc++;
          break;
        case Opcodes.OPC_baload:
          frame.numberOfStackItems -=2;
          frame.addStackItem(TypeBinding.BYTE);
          pc++;
          break;
        case Opcodes.OPC_caload:
          frame.numberOfStackItems -=2;
          frame.addStackItem(TypeBinding.CHAR);
          pc++;
          break;
        case Opcodes.OPC_saload:
          frame.numberOfStackItems -=2;
          frame.addStackItem(TypeBinding.SHORT);
          pc++;
          break;
        case Opcodes.OPC_istore:
        case Opcodes.OPC_lstore:
        case Opcodes.OPC_fstore:
        case Opcodes.OPC_dstore:
          frame.numberOfStackItems--;
          pc += 2;
          break;
        case Opcodes.OPC_astore:
          index = u1At(bytecodes, 1, pc);
          frame.numberOfStackItems--;
          pc += 2;
          break;
        case Opcodes.OPC_astore_0:
          frame.locals[0] = frame.stackItems[frame.numberOfStackItems - 1];
          frame.numberOfStackItems--;
          pc++;
          break;
        case Opcodes.OPC_astore_1:
        case Opcodes.OPC_astore_2:
        case Opcodes.OPC_astore_3:
        case Opcodes.OPC_istore_0:
        case Opcodes.OPC_istore_1:
        case Opcodes.OPC_istore_2:
        case Opcodes.OPC_istore_3:
        case Opcodes.OPC_lstore_0:
        case Opcodes.OPC_lstore_1:
        case Opcodes.OPC_lstore_2:
        case Opcodes.OPC_lstore_3:
        case Opcodes.OPC_fstore_0:
        case Opcodes.OPC_fstore_1:
        case Opcodes.OPC_fstore_2:
        case Opcodes.OPC_fstore_3:
        case Opcodes.OPC_dstore_0:
        case Opcodes.OPC_dstore_1:
        case Opcodes.OPC_dstore_2:
        case Opcodes.OPC_dstore_3:
          frame.numberOfStackItems--;
          pc++;
          break;
        case Opcodes.OPC_iastore:
        case Opcodes.OPC_lastore:
        case Opcodes.OPC_fastore:
        case Opcodes.OPC_dastore:
        case Opcodes.OPC_aastore:
        case Opcodes.OPC_bastore:
        case Opcodes.OPC_castore:
        case Opcodes.OPC_sastore:
          frame.numberOfStackItems-=3;
          pc++;
          break;
        case Opcodes.OPC_pop:
          frame.numberOfStackItems--;
          pc++;
          break;
        case Opcodes.OPC_pop2:
          int numberOfStackItems = frame.numberOfStackItems;
          switch(frame.stackItems[numberOfStackItems - 1].id()) {
            case TypeIds.T_long :
            case TypeIds.T_double :
              frame.numberOfStackItems--;
              break;
            default:
              frame.numberOfStackItems -= 2;
          }
          pc++;
          break;
        case Opcodes.OPC_dup:
          frame.addStackItem(frame.stackItems[frame.numberOfStackItems - 1]);
          pc++;
          break;
        case Opcodes.OPC_dup_x1:
          VerificationTypeInfo info = frame.stackItems[frame.numberOfStackItems - 1];
          frame.numberOfStackItems--;
          VerificationTypeInfo info2 = frame.stackItems[frame.numberOfStackItems - 1];
          frame.numberOfStackItems--;
          frame.addStackItem(info);
          frame.addStackItem(info2);
          frame.addStackItem(info);
          pc++;
          break;
        case Opcodes.OPC_dup_x2:
          info = frame.stackItems[frame.numberOfStackItems - 1];
          frame.numberOfStackItems--;
          info2 = frame.stackItems[frame.numberOfStackItems - 1];
          frame.numberOfStackItems--;
          switch(info2.id()) {
            case TypeIds.T_long :
            case TypeIds.T_double :
              frame.addStackItem(info);
              frame.addStackItem(info2);
              frame.addStackItem(info);
              break;
            default:
              numberOfStackItems = frame.numberOfStackItems;
              VerificationTypeInfo info3 = frame.stackItems[numberOfStackItems - 1];
              frame.numberOfStackItems--;
              frame.addStackItem(info);
              frame.addStackItem(info3);
              frame.addStackItem(info2);
              frame.addStackItem(info);
          }
          pc++;
          break;
        case Opcodes.OPC_dup2:
          info = frame.stackItems[frame.numberOfStackItems - 1];
          frame.numberOfStackItems--;
          switch(info.id()) {
            case TypeIds.T_double :
            case TypeIds.T_long :
              frame.addStackItem(info);
              frame.addStackItem(info);
              break;
            default:
              info2 = frame.stackItems[frame.numberOfStackItems - 1];
              frame.numberOfStackItems--;
              frame.addStackItem(info2);
              frame.addStackItem(info);
              frame.addStackItem(info2);
              frame.addStackItem(info);
          }
          pc++;
          break;
        case Opcodes.OPC_dup2_x1:
          info = frame.stackItems[frame.numberOfStackItems - 1];
          frame.numberOfStackItems--;
          info2 = frame.stackItems[frame.numberOfStackItems - 1];
          frame.numberOfStackItems--;
          switch(info.id()) {
            case TypeIds.T_double :
            case TypeIds.T_long :
              frame.addStackItem(info);
              frame.addStackItem(info2);
              frame.addStackItem(info);
              break;
            default:
              VerificationTypeInfo info3 = frame.stackItems[frame.numberOfStackItems - 1];
              frame.numberOfStackItems--;
              frame.addStackItem(info2);
              frame.addStackItem(info);
              frame.addStackItem(info3);
              frame.addStackItem(info2);
              frame.addStackItem(info);
          }
          pc++;
          break;
        case Opcodes.OPC_dup2_x2:
          numberOfStackItems = frame.numberOfStackItems;
          info = frame.stackItems[numberOfStackItems - 1];
          frame.numberOfStackItems--;
          info2 = frame.stackItems[frame.numberOfStackItems - 1];
          frame.numberOfStackItems--;
          switch(info.id()) {
            case TypeIds.T_long :
            case TypeIds.T_double :
              switch(info2.id()) {
                case TypeIds.T_long :
                case TypeIds.T_double :
                  // form 4
                  frame.addStackItem(info);
                  frame.addStackItem(info2);
                  frame.addStackItem(info);
                  break;
                default:
                  // form 2
                  numberOfStackItems = frame.numberOfStackItems;
                  VerificationTypeInfo info3 = frame.stackItems[numberOfStackItems - 1];
                  frame.numberOfStackItems--;
                  frame.addStackItem(info);
                  frame.addStackItem(info3);
                  frame.addStackItem(info2);
                  frame.addStackItem(info);
              }
              break;
            default:
              numberOfStackItems = frame.numberOfStackItems;
              VerificationTypeInfo info3 = frame.stackItems[numberOfStackItems - 1];
              frame.numberOfStackItems--;
              switch(info3.id()) {
                case TypeIds.T_long :
                case TypeIds.T_double :
                  // form 3
                  frame.addStackItem(info2);
                  frame.addStackItem(info);
                  frame.addStackItem(info3);
                  frame.addStackItem(info2);
                  frame.addStackItem(info);
                  break;
                default:
                  // form 1
                  numberOfStackItems = frame.numberOfStackItems;
                  VerificationTypeInfo info4 = frame.stackItems[numberOfStackItems - 1];
                  frame.numberOfStackItems--;
                  frame.addStackItem(info2);
                  frame.addStackItem(info);
                  frame.addStackItem(info4);
                  frame.addStackItem(info3);
                  frame.addStackItem(info2);
                  frame.addStackItem(info);
              }
          }
          pc++;
          break;
        case Opcodes.OPC_swap:
          numberOfStackItems = frame.numberOfStackItems;
          info = frame.stackItems[numberOfStackItems - 1];
          info2 = frame.stackItems[numberOfStackItems - 2];
          frame.stackItems[numberOfStackItems - 1] = info2;
          frame.stackItems[numberOfStackItems - 2] = info;
          pc++;
          break;
        case Opcodes.OPC_iadd:
        case Opcodes.OPC_ladd:
        case Opcodes.OPC_fadd:
        case Opcodes.OPC_dadd:
        case Opcodes.OPC_isub:
        case Opcodes.OPC_lsub:
        case Opcodes.OPC_fsub:
        case Opcodes.OPC_dsub:
        case Opcodes.OPC_imul:
        case Opcodes.OPC_lmul:
        case Opcodes.OPC_fmul:
        case Opcodes.OPC_dmul:
        case Opcodes.OPC_idiv:
        case Opcodes.OPC_ldiv:
        case Opcodes.OPC_fdiv:
        case Opcodes.OPC_ddiv:
        case Opcodes.OPC_irem:
        case Opcodes.OPC_lrem:
        case Opcodes.OPC_frem:
        case Opcodes.OPC_drem:
        case Opcodes.OPC_ishl:
        case Opcodes.OPC_lshl:
        case Opcodes.OPC_ishr:
        case Opcodes.OPC_lshr:
        case Opcodes.OPC_iushr:
        case Opcodes.OPC_lushr:
        case Opcodes.OPC_iand:
        case Opcodes.OPC_land:
        case Opcodes.OPC_ior:
        case Opcodes.OPC_lor:
        case Opcodes.OPC_ixor:
        case Opcodes.OPC_lxor:
          frame.numberOfStackItems--;
          pc++;
          break;
        case Opcodes.OPC_ineg:
        case Opcodes.OPC_lneg:
        case Opcodes.OPC_fneg:
        case Opcodes.OPC_dneg:
          pc++;
          break;
        case Opcodes.OPC_iinc:
          pc += 3;
          break;
        case Opcodes.OPC_i2l:
          frame.stackItems[frame.numberOfStackItems - 1] = new VerificationTypeInfo(TypeBinding.LONG);
          pc++;
          break;
        case Opcodes.OPC_i2f:
          frame.stackItems[frame.numberOfStackItems - 1] = new VerificationTypeInfo(TypeBinding.FLOAT);
          pc++;
          break;
        case Opcodes.OPC_i2d:
          frame.stackItems[frame.numberOfStackItems - 1] = new VerificationTypeInfo(TypeBinding.DOUBLE);
          pc++;
          break;
        case Opcodes.OPC_l2i:
          frame.stackItems[frame.numberOfStackItems - 1] = new VerificationTypeInfo(TypeBinding.INT);
          pc++;
          break;
        case Opcodes.OPC_l2f:
          frame.stackItems[frame.numberOfStackItems - 1] = new VerificationTypeInfo(TypeBinding.FLOAT);
          pc++;
          break;
        case Opcodes.OPC_l2d:
          frame.stackItems[frame.numberOfStackItems - 1] = new VerificationTypeInfo(TypeBinding.DOUBLE);
          pc++;
          break;
        case Opcodes.OPC_f2i:
          frame.stackItems[frame.numberOfStackItems - 1] = new VerificationTypeInfo(TypeBinding.INT);
          pc++;
          break;
        case Opcodes.OPC_f2l:
          frame.stackItems[frame.numberOfStackItems - 1] = new VerificationTypeInfo(TypeBinding.LONG);
          pc++;
          break;
        case Opcodes.OPC_f2d:
          frame.stackItems[frame.numberOfStackItems - 1] = new VerificationTypeInfo(TypeBinding.DOUBLE);
          pc++;
          break;
        case Opcodes.OPC_d2i:
          frame.stackItems[frame.numberOfStackItems - 1] = new VerificationTypeInfo(TypeBinding.INT);
          pc++;
          break;
        case Opcodes.OPC_d2l:
          frame.stackItems[frame.numberOfStackItems - 1] = new VerificationTypeInfo(TypeBinding.LONG);
          pc++;
          break;
        case Opcodes.OPC_d2f:
          frame.stackItems[frame.numberOfStackItems - 1] = new VerificationTypeInfo(TypeBinding.FLOAT);
          pc++;
          break;
        case Opcodes.OPC_i2b:
          frame.stackItems[frame.numberOfStackItems - 1] = new VerificationTypeInfo(TypeBinding.BYTE);
          pc++;
          break;
        case Opcodes.OPC_i2c:
          frame.stackItems[frame.numberOfStackItems - 1] = new VerificationTypeInfo(TypeBinding.CHAR);
          pc++;
          break;
        case Opcodes.OPC_i2s:
          frame.stackItems[frame.numberOfStackItems - 1] = new VerificationTypeInfo(TypeBinding.SHORT);
          pc++;
          break;
        case Opcodes.OPC_lcmp:
        case Opcodes.OPC_fcmpl:
        case Opcodes.OPC_fcmpg:
        case Opcodes.OPC_dcmpl:
        case Opcodes.OPC_dcmpg:
          frame.numberOfStackItems-=2;
          frame.addStackItem(TypeBinding.INT);
          pc++;
          break;
        case Opcodes.OPC_ifeq:
        case Opcodes.OPC_ifne:
        case Opcodes.OPC_iflt:
        case Opcodes.OPC_ifge:
        case Opcodes.OPC_ifgt:
        case Opcodes.OPC_ifle:
          frame.numberOfStackItems--;
          pc += 3;
          break;
        case Opcodes.OPC_if_icmpeq:
        case Opcodes.OPC_if_icmpne:
        case Opcodes.OPC_if_icmplt:
        case Opcodes.OPC_if_icmpge:
        case Opcodes.OPC_if_icmpgt:
        case Opcodes.OPC_if_icmple:
        case Opcodes.OPC_if_acmpeq:
        case Opcodes.OPC_if_acmpne:
          frame.numberOfStackItems -= 2;
          pc += 3;
          break;
        case Opcodes.OPC_goto:
          pc += 3;
          break;
        case Opcodes.OPC_tableswitch:
          pc++;
          while (((pc - codeOffset) & 0x03) != 0) {
            pc++;
          }
          pc += 4; // default
          int low = i4At(bytecodes, 0, pc);
          pc += 4;
          int high = i4At(bytecodes, 0, pc);
          pc += 4;
          int length = high - low + 1;
          pc += (length * 4);
          frame.numberOfStackItems--;
          break;
        case Opcodes.OPC_lookupswitch:
          pc++;
          while (((pc - codeOffset) & 0x03) != 0) {
            pc++;
          }
          pc += 4; // default
          int npairs = (int) u4At(bytecodes, 0, pc);
          pc += (4 + npairs * 8);
          frame.numberOfStackItems--;
          break;
        case Opcodes.OPC_ireturn:
        case Opcodes.OPC_lreturn:
        case Opcodes.OPC_freturn:
        case Opcodes.OPC_dreturn:
        case Opcodes.OPC_areturn:
          frame.numberOfStackItems--;
          pc++;
          break;
        case Opcodes.OPC_return:
          pc++;
          break;
        case Opcodes.OPC_getstatic:
          index = u2At(bytecodes, 1, pc);
          int nameAndTypeIndex = u2At(poolContents, 3,
              constantPoolOffsets[index]);
          int utf8index = u2At(poolContents, 3,
              constantPoolOffsets[nameAndTypeIndex]);
          char[] descriptor = utf8At(poolContents,
              constantPoolOffsets[utf8index] + 3, u2At(
                  poolContents, 1,
                  constantPoolOffsets[utf8index]));
          if (descriptor.length == 1) {
            // base type
            switch(descriptor[0]) {
              case 'Z':
                frame.addStackItem(TypeBinding.BOOLEAN);
                break;
              case 'B':
                frame.addStackItem(TypeBinding.BYTE);
                break;
              case 'C':
                frame.addStackItem(TypeBinding.CHAR);
                break;
              case 'D':
                frame.addStackItem(TypeBinding.DOUBLE);
                break;
              case 'F':
                frame.addStackItem(TypeBinding.FLOAT);
                break;
              case 'I':
                frame.addStackItem(TypeBinding.INT);
                break;
              case 'J':
                frame.addStackItem(TypeBinding.LONG);
                break;
              case 'S':
                frame.addStackItem(TypeBinding.SHORT);
                break;
            }
          } else if (descriptor[0] == '[') {
            frame.addStackItem(new VerificationTypeInfo(0, descriptor));
          } else {
            frame.addStackItem(new VerificationTypeInfo(0, CharOperation.subarray(descriptor, 1, descriptor.length - 1)));
          }
          pc += 3;
          break;
        case Opcodes.OPC_putstatic:
          frame.numberOfStackItems--;
          pc += 3;
          break;
        case Opcodes.OPC_getfield:
          index = u2At(bytecodes, 1, pc);
          nameAndTypeIndex = u2At(poolContents, 3,
              constantPoolOffsets[index]);
          utf8index = u2At(poolContents, 3,
              constantPoolOffsets[nameAndTypeIndex]);
          descriptor = utf8At(poolContents,
              constantPoolOffsets[utf8index] + 3, u2At(
                  poolContents, 1,
                  constantPoolOffsets[utf8index]));
          frame.numberOfStackItems--;
          if (descriptor.length == 1) {
            // base type
            switch(descriptor[0]) {
              case 'Z':
                frame.addStackItem(TypeBinding.BOOLEAN);
                break;
              case 'B':
                frame.addStackItem(TypeBinding.BYTE);
                break;
              case 'C':
                frame.addStackItem(TypeBinding.CHAR);
                break;
              case 'D':
                frame.addStackItem(TypeBinding.DOUBLE);
                break;
              case 'F':
                frame.addStackItem(TypeBinding.FLOAT);
                break;
              case 'I':
                frame.addStackItem(TypeBinding.INT);
                break;
              case 'J':
                frame.addStackItem(TypeBinding.LONG);
                break;
              case 'S':
                frame.addStackItem(TypeBinding.SHORT);
                break;
            }
          } else if (descriptor[0] == '[') {
            frame.addStackItem(new VerificationTypeInfo(0, descriptor));
          } else {
            frame.addStackItem(new VerificationTypeInfo(0, CharOperation.subarray(descriptor, 1, descriptor.length - 1)));
          }
          pc += 3;
          break;
        case Opcodes.OPC_putfield:
          frame.numberOfStackItems -= 2;
          pc += 3;
          break;
        case Opcodes.OPC_invokevirtual:
          index = u2At(bytecodes, 1, pc);
          nameAndTypeIndex = u2At(poolContents, 3,
              constantPoolOffsets[index]);
          utf8index = u2At(poolContents, 3,
              constantPoolOffsets[nameAndTypeIndex]);
          descriptor = utf8At(poolContents,
              constantPoolOffsets[utf8index] + 3, u2At(
                  poolContents, 1,
                  constantPoolOffsets[utf8index]));
          utf8index = u2At(poolContents, 1,
              constantPoolOffsets[nameAndTypeIndex]);
          char[] name = utf8At(poolContents,
              constantPoolOffsets[utf8index] + 3, u2At(
                  poolContents, 1,
                  constantPoolOffsets[utf8index]));
          frame.numberOfStackItems -= (getParametersCount(descriptor) + 1);
          char[] returnType = getReturnType(descriptor);
          if (returnType.length == 1) {
            // base type
            switch(returnType[0]) {
              case 'Z':
                frame.addStackItem(TypeBinding.BOOLEAN);
                break;
              case 'B':
                frame.addStackItem(TypeBinding.BYTE);
                break;
              case 'C':
                frame.addStackItem(TypeBinding.CHAR);
                break;
              case 'D':
                frame.addStackItem(TypeBinding.DOUBLE);
                break;
              case 'F':
                frame.addStackItem(TypeBinding.FLOAT);
                break;
              case 'I':
                frame.addStackItem(TypeBinding.INT);
                break;
              case 'J':
                frame.addStackItem(TypeBinding.LONG);
                break;
              case 'S':
                frame.addStackItem(TypeBinding.SHORT);
                break;
            }
          } else {
            if (returnType[0] == '[') {
              frame.addStackItem(new VerificationTypeInfo(0, returnType));
            } else {
              frame.addStackItem(new VerificationTypeInfo(0, CharOperation.subarray(returnType, 1, returnType.length - 1)));
            }
          }
          pc += 3;
          break;
        case Opcodes.OPC_invokespecial:
          index = u2At(bytecodes, 1, pc);
          nameAndTypeIndex = u2At(poolContents, 3,
              constantPoolOffsets[index]);
          utf8index = u2At(poolContents, 3,
              constantPoolOffsets[nameAndTypeIndex]);
          descriptor = utf8At(poolContents,
              constantPoolOffsets[utf8index] + 3, u2At(
                  poolContents, 1,
                  constantPoolOffsets[utf8index]));
          utf8index = u2At(poolContents, 1,
              constantPoolOffsets[nameAndTypeIndex]);
          name = utf8At(poolContents,
              constantPoolOffsets[utf8index] + 3, u2At(
                  poolContents, 1,
                  constantPoolOffsets[utf8index]));
          frame.numberOfStackItems -= getParametersCount(descriptor);
          if (CharOperation.equals(ConstantPool.Init, name)) {
            // constructor
            frame.stackItems[frame.numberOfStackItems - 1].tag = VerificationTypeInfo.ITEM_OBJECT;
          }
          frame.numberOfStackItems--;
          returnType = getReturnType(descriptor);
          if (returnType.length == 1) {
            // base type
            switch(returnType[0]) {
              case 'Z':
                frame.addStackItem(TypeBinding.BOOLEAN);
                break;
              case 'B':
                frame.addStackItem(TypeBinding.BYTE);
                break;
              case 'C':
                frame.addStackItem(TypeBinding.CHAR);
                break;
              case 'D':
                frame.addStackItem(TypeBinding.DOUBLE);
                break;
              case 'F':
                frame.addStackItem(TypeBinding.FLOAT);
                break;
              case 'I':
                frame.addStackItem(TypeBinding.INT);
                break;
              case 'J':
                frame.addStackItem(TypeBinding.LONG);
                break;
              case 'S':
                frame.addStackItem(TypeBinding.SHORT);
                break;
            }
          } else {
            if (returnType[0] == '[') {
              frame.addStackItem(new VerificationTypeInfo(0, returnType));
            } else {
              frame.addStackItem(new VerificationTypeInfo(0, CharOperation.subarray(returnType, 1, returnType.length - 1)));
            }
          }
          pc += 3;
          break;
        case Opcodes.OPC_invokestatic:
          index = u2At(bytecodes, 1, pc);
          nameAndTypeIndex = u2At(poolContents, 3,
              constantPoolOffsets[index]);
          utf8index = u2At(poolContents, 3,
              constantPoolOffsets[nameAndTypeIndex]);
          descriptor = utf8At(poolContents,
              constantPoolOffsets[utf8index] + 3, u2At(
                  poolContents, 1,
                  constantPoolOffsets[utf8index]));
          utf8index = u2At(poolContents, 1,
              constantPoolOffsets[nameAndTypeIndex]);
          name = utf8At(poolContents,
              constantPoolOffsets[utf8index] + 3, u2At(
                  poolContents, 1,
                  constantPoolOffsets[utf8index]));
          frame.numberOfStackItems -= getParametersCount(descriptor);
          returnType = getReturnType(descriptor);
          if (returnType.length == 1) {
            // base type
            switch(returnType[0]) {
              case 'Z':
                frame.addStackItem(TypeBinding.BOOLEAN);
                break;
              case 'B':
                frame.addStackItem(TypeBinding.BYTE);
                break;
              case 'C':
                frame.addStackItem(TypeBinding.CHAR);
                break;
              case 'D':
                frame.addStackItem(TypeBinding.DOUBLE);
                break;
              case 'F':
                frame.addStackItem(TypeBinding.FLOAT);
                break;
              case 'I':
                frame.addStackItem(TypeBinding.INT);
                break;
              case 'J':
                frame.addStackItem(TypeBinding.LONG);
                break;
              case 'S':
                frame.addStackItem(TypeBinding.SHORT);
                break;
            }
          } else {
            if (returnType[0] == '[') {
              frame.addStackItem(new VerificationTypeInfo(0, returnType));
            } else {
              frame.addStackItem(new VerificationTypeInfo(0, CharOperation.subarray(returnType, 1, returnType.length - 1)));
            }
          }
          pc += 3;
          break;
        case Opcodes.OPC_invokeinterface:
          index = u2At(bytecodes, 1, pc);
          nameAndTypeIndex = u2At(poolContents, 3,
              constantPoolOffsets[index]);
          utf8index = u2At(poolContents, 3,
              constantPoolOffsets[nameAndTypeIndex]);
          descriptor = utf8At(poolContents,
              constantPoolOffsets[utf8index] + 3, u2At(
                  poolContents, 1,
                  constantPoolOffsets[utf8index]));
          utf8index = u2At(poolContents, 1,
              constantPoolOffsets[nameAndTypeIndex]);
          name = utf8At(poolContents,
              constantPoolOffsets[utf8index] + 3, u2At(
                  poolContents, 1,
                  constantPoolOffsets[utf8index]));
          // we don't need count and args
          // u1At(bytecodes, 3, pc); // count
          // u1At(bytecodes, 4, pc); // extra args
          frame.numberOfStackItems -= (getParametersCount(descriptor) + 1);
          returnType = getReturnType(descriptor);
          if (returnType.length == 1) {
            // base type
            switch(returnType[0]) {
              case 'Z':
                frame.addStackItem(TypeBinding.BOOLEAN);
                break;
              case 'B':
                frame.addStackItem(TypeBinding.BYTE);
                break;
              case 'C':
                frame.addStackItem(TypeBinding.CHAR);
                break;
              case 'D':
                frame.addStackItem(TypeBinding.DOUBLE);
                break;
              case 'F':
                frame.addStackItem(TypeBinding.FLOAT);
                break;
              case 'I':
                frame.addStackItem(TypeBinding.INT);
                break;
              case 'J':
                frame.addStackItem(TypeBinding.LONG);
                break;
              case 'S':
                frame.addStackItem(TypeBinding.SHORT);
                break;
            }
          } else {
            if (returnType[0] == '[') {
              frame.addStackItem(new VerificationTypeInfo(0, returnType));
            } else {
              frame.addStackItem(new VerificationTypeInfo(0, CharOperation.subarray(returnType, 1, returnType.length - 1)));
            }
          }
          pc += 5;
          break;
        case Opcodes.OPC_new:
          index = u2At(bytecodes, 1, pc);
          utf8index = u2At(poolContents, 1,
              constantPoolOffsets[index]);
          char[] className = utf8At(poolContents,
              constantPoolOffsets[utf8index] + 3, u2At(
                  poolContents, 1,
                  constantPoolOffsets[utf8index]));
          VerificationTypeInfo verificationTypeInfo = new VerificationTypeInfo(0, VerificationTypeInfo.ITEM_UNINITIALIZED, className);
          verificationTypeInfo.offset = currentPC;
          frame.addStackItem(verificationTypeInfo);
          pc += 3;
          break;
        case Opcodes.OPC_newarray:
          char[] constantPoolName = null;
          switch (u1At(bytecodes, 1, pc)) {
            case ClassFileConstants.INT_ARRAY :
              constantPoolName = new char[] { '[', 'I' };
              break;
            case ClassFileConstants.BYTE_ARRAY :
              constantPoolName = new char[] { '[', 'B' };
              break;
            case ClassFileConstants.BOOLEAN_ARRAY :
              constantPoolName = new char[] { '[', 'Z' };
              break;
            case ClassFileConstants.SHORT_ARRAY :
              constantPoolName = new char[] { '[', 'S' };
              break;
            case ClassFileConstants.CHAR_ARRAY :
              constantPoolName = new char[] { '[', 'C' };
              break;
            case ClassFileConstants.LONG_ARRAY :
              constantPoolName = new char[] { '[', 'J' };
              break;
            case ClassFileConstants.FLOAT_ARRAY :
              constantPoolName = new char[] { '[', 'F' };
              break;
            case ClassFileConstants.DOUBLE_ARRAY :
              constantPoolName = new char[] { '[', 'D' };
              break;
          }
          frame.stackItems[frame.numberOfStackItems - 1] = new VerificationTypeInfo(TypeIds.T_JavaLangObject, constantPoolName);
          pc += 2;
          break;
        case Opcodes.OPC_anewarray:
          index = u2At(bytecodes, 1, pc);
          utf8index = u2At(poolContents, 1,
              constantPoolOffsets[index]);
          className = utf8At(poolContents,
              constantPoolOffsets[utf8index] + 3, u2At(
                  poolContents, 1,
                  constantPoolOffsets[utf8index]));
          int classNameLength = className.length;
          if (className[0] != '[') {
            // this is a type name (class or interface). So we add appropriate '[', 'L' and ';'.
            System.arraycopy(className, 0, (constantPoolName = new char[classNameLength + 3]), 2, classNameLength);
            constantPoolName[0] = '[';
            constantPoolName[1] = 'L';
            constantPoolName[classNameLength + 2] = ';';
          } else {
            // if class name is already an array, we just need to add one dimension
            System.arraycopy(className, 0, (constantPoolName = new char[classNameLength + 1]), 1, classNameLength);
            constantPoolName[0] = '[';
          }
          frame.stackItems[frame.numberOfStackItems - 1] = new VerificationTypeInfo(0, constantPoolName);
          pc += 3;
          break;
        case Opcodes.OPC_arraylength:
          frame.stackItems[frame.numberOfStackItems - 1] = new VerificationTypeInfo(TypeBinding.INT);
          pc++;
          break;
        case Opcodes.OPC_athrow:
          frame.numberOfStackItems--;
          pc++;
          break;
        case Opcodes.OPC_checkcast:
          index = u2At(bytecodes, 1, pc);
          utf8index = u2At(poolContents, 1,
              constantPoolOffsets[index]);
          className = utf8At(poolContents,
              constantPoolOffsets[utf8index] + 3, u2At(
                  poolContents, 1,
                  constantPoolOffsets[utf8index]));
          frame.stackItems[frame.numberOfStackItems - 1] = new VerificationTypeInfo(0, className);
          pc += 3;
          break;
        case Opcodes.OPC_instanceof:
          // no need to know the class index = u2At(bytecodes, 1, pc);
          frame.stackItems[frame.numberOfStackItems - 1] = new VerificationTypeInfo(TypeBinding.INT);
          pc += 3;
          break;
        case Opcodes.OPC_monitorenter:
        case Opcodes.OPC_monitorexit:
          frame.numberOfStackItems--;
          pc++;
          break;
        case Opcodes.OPC_wide:
          opcode = (byte) u1At(bytecodes, 1, pc);
          if (opcode == Opcodes.OPC_iinc) {
            // index = u2At(bytecodes, 2, pc);
            // i2At(bytecodes, 4, pc); // const
            // we don't need the index and the const value
            pc += 6;
          } else {
            index = u2At(bytecodes, 2, pc);
            // need to handle iload, fload, aload, lload, dload, istore, fstore, astore, lstore or dstore
            switch(opcode) {
              case Opcodes.OPC_iload :
                frame.addStackItem(TypeBinding.INT);
                break;
              case Opcodes.OPC_fload :
                frame.addStackItem(TypeBinding.FLOAT);
                break;
              case Opcodes.OPC_aload :
                localsN = frame.locals[index];
                if (localsN == null) {
                  localsN = retrieveLocal(currentPC, index);
                }
                frame.addStackItem(localsN);
                break;
              case Opcodes.OPC_lload :
                frame.addStackItem(TypeBinding.LONG);
                break;
              case Opcodes.OPC_dload :
                frame.addStackItem(TypeBinding.DOUBLE);
                break;
              case Opcodes.OPC_istore :
                frame.numberOfStackItems--;
                break;
              case Opcodes.OPC_fstore :
                frame.numberOfStackItems--;
                break;
              case Opcodes.OPC_astore :
                frame.locals[index] = frame.stackItems[frame.numberOfStackItems - 1];
                frame.numberOfStackItems--;
                break;
              case Opcodes.OPC_lstore :
                frame.numberOfStackItems--;
                break;
              case Opcodes.OPC_dstore :
                frame.numberOfStackItems--;
                break;
            }
            pc += 4;
          }
          break;
        case Opcodes.OPC_multianewarray:
          index = u2At(bytecodes, 1, pc);
          utf8index = u2At(poolContents, 1,
              constantPoolOffsets[index]);
          className = utf8At(poolContents,
              constantPoolOffsets[utf8index] + 3, u2At(
                  poolContents, 1,
                  constantPoolOffsets[utf8index]));
          int dimensions = u1At(bytecodes, 3, pc); // dimensions
          frame.numberOfStackItems -= dimensions;
          classNameLength = className.length;
          constantPoolName = new char[classNameLength + dimensions];
          for (int i = 0; i < dimensions; i++) {
            constantPoolName[i] = '[';
          }
          System.arraycopy(className, 0, constantPoolName, dimensions, classNameLength);
          frame.addStackItem(new VerificationTypeInfo(0, constantPoolName));
          pc += 4;
          break;
        case Opcodes.OPC_ifnull:
        case Opcodes.OPC_ifnonnull:
          frame.numberOfStackItems--;
          pc += 3;
          break;
        case Opcodes.OPC_goto_w:
          pc += 5;
          break;
        default: // should not occur
          this.codeStream.methodDeclaration.scope.problemReporter().abortDueToInternalError(
              Messages.bind(
                  Messages.abort_invalidOpcode,
                  new Object[] {
                    new Byte(opcode),
                    new Integer(pc),
                    new String(methodBinding.shortReadableName()),
                  }),
              this.codeStream.methodDeclaration);
        break;
      }
      if (pc >= (codeLength + codeOffset)) {
        break;
      }
    }
  }

  private final int u1At(byte[] reference, int relativeOffset,
      int structOffset) {
    return (reference[relativeOffset + structOffset] & 0xFF);
  }

  private final int u2At(byte[] reference, int relativeOffset,
      int structOffset) {
    int position = relativeOffset + structOffset;
    return ((reference[position++] & 0xFF) << 8)
        + (reference[position] & 0xFF);
  }

  private final long u4At(byte[] reference, int relativeOffset,
      int structOffset) {
    int position = relativeOffset + structOffset;
    return (((reference[position++] & 0xFFL) << 24)
        + ((reference[position++] & 0xFF) << 16)
        + ((reference[position++] & 0xFF) << 8) + (reference[position] & 0xFF));
  }

  public char[] utf8At(byte[] reference, int absoluteOffset,
      int bytesAvailable) {
    int length = bytesAvailable;
    char outputBuf[] = new char[bytesAvailable];
    int outputPos = 0;
    int readOffset = absoluteOffset;

    while (length != 0) {
      int x = reference[readOffset++] & 0xFF;
      length--;
      if ((0x80 & x) != 0) {
        if ((x & 0x20) != 0) {
          length -= 2;
          x = ((x & 0xF) << 12)
              | ((reference[readOffset++] & 0x3F) << 6)
              | (reference[readOffset++] & 0x3F);
        } else {
          length--;
          x = ((x & 0x1F) << 6) | (reference[readOffset++] & 0x3F);
        }
      }
      outputBuf[outputPos++] = (char) x;
    }

    if (outputPos != bytesAvailable) {
      System.arraycopy(outputBuf, 0, (outputBuf = new char[outputPos]),
          0, outputPos);
    }
    return outputBuf;
  }
}
TOP

Related Classes of org.eclipse.jdt.internal.compiler.ClassFile

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