org.eclipse.jdt.internal.compiler.ClassFile.java Source code

Java tutorial

Introduction

Here is the source code for org.eclipse.jdt.internal.compiler.ClassFile.java

Source

/*******************************************************************************
 * Copyright (c) 2000, 2019 IBM Corporation and others.
 *
 * This program and the accompanying materials
 * are made available under the terms of the Eclipse Public License 2.0
 * which accompanies this distribution, and is available at
 * https://www.eclipse.org/legal/epl-2.0/
 *
 * SPDX-License-Identifier: EPL-2.0
 *
 * Contributors:
 *     IBM Corporation - initial API and implementation
 *     Jesper S Moller - Contributions for
 *                     Bug 405066 - [1.8][compiler][codegen] Implement code generation infrastructure for JSR335             
 *                     Bug 406982 - [1.8][compiler] Generation of MethodParameters Attribute in classfile
 *                     Bug 416885 - [1.8][compiler]IncompatibleClassChange error (edit)
 *                     Bug 412149 - [1.8][compiler] Emit repeated annotations into the designated container
 *     Andy Clement (GoPivotal, Inc) aclement@gopivotal.com - Contributions for
 *                          Bug 383624 - [1.8][compiler] Revive code generation support for type annotations (from Olivier's work)
 *                          Bug 409236 - [1.8][compiler] Type annotations on intersection cast types dropped by code generator
 *                          Bug 409246 - [1.8][compiler] Type annotations on catch parameters not handled properly
 *                          Bug 415541 - [1.8][compiler] Type annotations in the body of static initializer get dropped
 *                          Bug 415399 - [1.8][compiler] Type annotations on constructor results dropped by the code generator
 *                          Bug 415470 - [1.8][compiler] Type annotations on class declaration go vanishing
 *                          Bug 405104 - [1.8][compiler][codegen] Implement support for serializeable lambdas
 *                          Bug 434556 - Broken class file generated for incorrect annotation usage
 *                          Bug 442416 - $deserializeLambda$ missing cases for nested lambdas
 *     Stephan Herrmann - Contribution for
 *                     Bug 438458 - [1.8][null] clean up handling of null type annotations wrt type variables
 *     Olivier Tardieu tardieu@us.ibm.com - Contributions for
 *                     Bug 442416 - $deserializeLambda$ missing cases for nested lambdas
 *******************************************************************************/
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.HashMap;
import java.util.HashSet;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
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.CompilationUnitDeclaration;
import org.eclipse.jdt.internal.compiler.ast.ExportsStatement;
import org.eclipse.jdt.internal.compiler.ast.Expression;
import org.eclipse.jdt.internal.compiler.ast.FieldDeclaration;
import org.eclipse.jdt.internal.compiler.ast.FunctionalExpression;
import org.eclipse.jdt.internal.compiler.ast.LambdaExpression;
import org.eclipse.jdt.internal.compiler.ast.LocalDeclaration;
import org.eclipse.jdt.internal.compiler.ast.MemberValuePair;
import org.eclipse.jdt.internal.compiler.ast.MethodDeclaration;
import org.eclipse.jdt.internal.compiler.ast.ModuleDeclaration;
import org.eclipse.jdt.internal.compiler.ast.NormalAnnotation;
import org.eclipse.jdt.internal.compiler.ast.OpensStatement;
import org.eclipse.jdt.internal.compiler.ast.QualifiedNameReference;
import org.eclipse.jdt.internal.compiler.ast.Receiver;
import org.eclipse.jdt.internal.compiler.ast.ReferenceExpression;
import org.eclipse.jdt.internal.compiler.ast.RequiresStatement;
import org.eclipse.jdt.internal.compiler.ast.SingleMemberAnnotation;
import org.eclipse.jdt.internal.compiler.ast.SingleNameReference;
import org.eclipse.jdt.internal.compiler.ast.SwitchStatement;
import org.eclipse.jdt.internal.compiler.ast.TypeDeclaration;
import org.eclipse.jdt.internal.compiler.ast.TypeParameter;
import org.eclipse.jdt.internal.compiler.ast.TypeReference;
import org.eclipse.jdt.internal.compiler.classfmt.ClassFileConstants;
import org.eclipse.jdt.internal.compiler.codegen.AnnotationContext;
import org.eclipse.jdt.internal.compiler.codegen.AnnotationTargetTypeConstants;
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.TypeAnnotationCodeStream;
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.ArrayBinding;
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.MethodScope;
import org.eclipse.jdt.internal.compiler.lookup.ModuleBinding;
import org.eclipse.jdt.internal.compiler.lookup.PolymorphicMethodBinding;
import org.eclipse.jdt.internal.compiler.lookup.ProblemReasons;
import org.eclipse.jdt.internal.compiler.lookup.ProblemReferenceBinding;
import org.eclipse.jdt.internal.compiler.lookup.ReferenceBinding;
import org.eclipse.jdt.internal.compiler.lookup.Scope;
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.AbortMethod;
import org.eclipse.jdt.internal.compiler.problem.AbortType;
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
 */
@SuppressWarnings({ "rawtypes", "unchecked" })
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 Map<TypeBinding, Boolean> innerClassesBindings;
    public List bootstrapMethods = null;
    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<TypeBinding> 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;
    public static final int NESTED_MEMBER_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) {
        createProblemType(typeDeclaration, null, unitResult);
    }

    private static void createProblemType(TypeDeclaration typeDeclaration, ClassFile parentClassFile,
            CompilationResult unitResult) {
        SourceTypeBinding typeBinding = typeDeclaration.binding;
        ClassFile classFile = ClassFile.getNewInstance(typeBinding);
        classFile.initialize(typeBinding, parentClassFile, 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
            if (classFile.contentsOffset + 2 >= classFile.contents.length) {
                classFile.resizeContents(2);
            }
            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;
        boolean abstractMethodsOnly = false;
        if (methodDecls != null) {
            if (typeBinding.isInterface()) {
                if (typeBinding.scope.compilerOptions().sourceLevel < ClassFileConstants.JDK1_8)
                    abstractMethodsOnly = true;
                // We generate a clinit which contains all the problems, since we may not be able to generate problem methods (< 1.8) and problem constructors (all levels).
                classFile.addProblemClinit(problemsCopy);
            }
            for (int i = 0, length = methodDecls.length; i < length; i++) {
                AbstractMethodDeclaration methodDecl = methodDecls[i];
                MethodBinding method = methodDecl.binding;
                if (method == null)
                    continue;
                if (abstractMethodsOnly) {
                    method.modifiers = ClassFileConstants.AccPublic | ClassFileConstants.AccAbstract;
                }
                if (method.isConstructor()) {
                    if (typeBinding.isInterface())
                        continue;
                    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, classFile, 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;
            if (this.targetJDK >= ClassFileConstants.JDK1_8) {
                this.produceAttributes |= ClassFileConstants.ATTR_TYPE_ANNOTATION;
                this.codeStream = new TypeAnnotationCodeStream(this);
                if (options.produceMethodParameters) {
                    this.produceAttributes |= ClassFileConstants.ATTR_METHOD_PARAMETERS;
                }
            } else {
                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(this.referenceBinding.methods().length + this.referenceBinding.fields().length);
    }

    public ClassFile(ModuleBinding moduleBinding, CompilerOptions options) {
        this.constantPool = new ConstantPool(this);
        this.targetJDK = options.targetJDK;
        this.produceAttributes = ClassFileConstants.ATTR_SOURCE;
        this.isNestedType = false;
        this.codeStream = new StackMapFrameCodeStream(this);
        initByteArrays(0);
    }

    /**
     * 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) {
                    long targetMask;
                    if (typeDeclaration.isPackageInfo())
                        targetMask = TagBits.AnnotationForPackage;
                    else if (this.referenceBinding.isAnnotationType())
                        targetMask = TagBits.AnnotationForType | TagBits.AnnotationForAnnotationType;
                    else
                        targetMask = TagBits.AnnotationForType | TagBits.AnnotationForTypeUse;
                    attributesNumber += generateRuntimeAnnotations(annotations, targetMask);
                }
            }
        }

        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();
        }
        // Functional expression and lambda bootstrap methods
        if (this.bootstrapMethods != null && !this.bootstrapMethods.isEmpty()) {
            attributesNumber += generateBootstrapMethods(this.bootstrapMethods);
        }
        // Inner class attribute
        int numberOfInnerClasses = this.innerClassesBindings == null ? 0 : this.innerClassesBindings.size();
        if (numberOfInnerClasses != 0) {
            ReferenceBinding[] innerClasses = new ReferenceBinding[numberOfInnerClasses];
            this.innerClassesBindings.keySet().toArray(innerClasses);
            Arrays.sort(innerClasses, new Comparator() {
                @Override
                public int compare(Object o1, Object o2) {
                    TypeBinding binding1 = (TypeBinding) o1;
                    TypeBinding binding2 = (TypeBinding) o2;
                    Boolean onBottom1 = ClassFile.this.innerClassesBindings.get(o1);
                    Boolean onBottom2 = ClassFile.this.innerClassesBindings.get(o2);
                    if (onBottom1) {
                        if (!onBottom2) {
                            return 1;
                        }
                    } else {
                        if (onBottom2) {
                            return -1;
                        }
                    }
                    return CharOperation.compareTo(binding1.constantPoolName(), binding2.constantPoolName());
                }
            });
            attributesNumber += generateInnerClassAttribute(numberOfInnerClasses, innerClasses);
        }
        if (this.missingTypes != null) {
            generateMissingTypesAttribute();
            attributesNumber++;
        }

        attributesNumber += generateTypeAnnotationAttributeForTypeDeclaration();

        if (this.targetJDK >= ClassFileConstants.JDK11) {
            // add nestMember and nestHost attributes
            attributesNumber += generateNestAttributes();
        }
        // 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 module attributes for the receiver.
     */
    public void addModuleAttributes(ModuleBinding module, Annotation[] annotations,
            CompilationUnitDeclaration cud) {
        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(cud.getFileName());
            fullFileName = fullFileName.replace('\\', '/');
            int lastIndex = fullFileName.lastIndexOf('/');
            if (lastIndex != -1) {
                fullFileName = fullFileName.substring(lastIndex + 1, fullFileName.length());
            }
            attributesNumber += generateSourceAttribute(fullFileName);
        }
        attributesNumber += generateModuleAttribute(cud.moduleDeclaration);
        if (annotations != null) {
            long targetMask = TagBits.AnnotationForModule;
            attributesNumber += generateRuntimeAnnotations(annotations, targetMask);
        }
        char[] mainClass = cud.moduleDeclaration.binding.mainClassName;
        if (mainClass != null) {
            attributesNumber += generateModuleMainClassAttribute(CharOperation.replaceOnCopy(mainClass, '.', '/'));
        }
        char[][] packageNames = cud.moduleDeclaration.binding.getPackageNamesForClassFile();
        if (packageNames != null) {
            attributesNumber += generateModulePackagesAttribute(packageNames);
        }

        // 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, TagBits.AnnotationForField);
                }

                if ((this.produceAttributes & ClassFileConstants.ATTR_TYPE_ANNOTATION) != 0) {
                    List allTypeAnnotationContexts = new ArrayList();
                    if (annotations != null && (fieldDeclaration.bits & ASTNode.HasTypeAnnotations) != 0) {
                        fieldDeclaration.getAllAnnotationContexts(AnnotationTargetTypeConstants.FIELD,
                                allTypeAnnotationContexts);
                    }
                    int invisibleTypeAnnotationsCounter = 0;
                    int visibleTypeAnnotationsCounter = 0;
                    TypeReference fieldType = fieldDeclaration.type;
                    if (fieldType != null && ((fieldType.bits & ASTNode.HasTypeAnnotations) != 0)) {
                        fieldType.getAllAnnotationContexts(AnnotationTargetTypeConstants.FIELD,
                                allTypeAnnotationContexts);
                    }
                    int size = allTypeAnnotationContexts.size();
                    if (size != 0) {
                        AnnotationContext[] allTypeAnnotationContextsArray = new AnnotationContext[size];
                        allTypeAnnotationContexts.toArray(allTypeAnnotationContextsArray);
                        for (int i = 0, max = allTypeAnnotationContextsArray.length; i < max; i++) {
                            AnnotationContext annotationContext = allTypeAnnotationContextsArray[i];
                            if ((annotationContext.visibility & AnnotationContext.INVISIBLE) != 0) {
                                invisibleTypeAnnotationsCounter++;
                                allTypeAnnotationContexts.add(annotationContext);
                            } else {
                                visibleTypeAnnotationsCounter++;
                                allTypeAnnotationContexts.add(annotationContext);
                            }
                        }
                        attributesNumber += generateRuntimeTypeAnnotations(allTypeAnnotationContextsArray,
                                visibleTypeAnnotationsCounter, invisibleTypeAnnotationsCounter);
                    }
                }
            }
        }
        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());
        }
        if (this.contentsOffset + 2 >= this.contents.length) {
            resizeContents(2);
        }
        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, null);
        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) {

        if (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())) {
                    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
     * - lambda 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
        int emittedSyntheticsCount = 0;
        SyntheticMethodBinding deserializeLambdaMethod = null;
        boolean continueScanningSynthetics = true;
        while (continueScanningSynthetics) {
            continueScanningSynthetics = false;
            SyntheticMethodBinding[] syntheticMethods = this.referenceBinding.syntheticMethods();
            int currentSyntheticsCount = syntheticMethods == null ? 0 : syntheticMethods.length;
            if (emittedSyntheticsCount != currentSyntheticsCount) {
                for (int i = emittedSyntheticsCount, max = currentSyntheticsCount; 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);
                        break;
                    case SyntheticMethodBinding.LambdaMethod:
                        syntheticMethod.lambda.generateCode(this.referenceBinding.scope, this);
                        continueScanningSynthetics = true; // lambda code generation could schedule additional nested lambdas for code generation.
                        break;
                    case SyntheticMethodBinding.ArrayConstructor:
                        addSyntheticArrayConstructor(syntheticMethod);
                        break;
                    case SyntheticMethodBinding.ArrayClone:
                        addSyntheticArrayClone(syntheticMethod);
                        break;
                    case SyntheticMethodBinding.FactoryMethod:
                        addSyntheticFactoryMethod(syntheticMethod);
                        break;
                    case SyntheticMethodBinding.DeserializeLambda:
                        deserializeLambdaMethod = syntheticMethod; // delay processing
                        break;
                    case SyntheticMethodBinding.SerializableMethodReference:
                        // Nothing to be done
                        break;
                    }
                }
                emittedSyntheticsCount = currentSyntheticsCount;
            }
        }
        if (deserializeLambdaMethod != null) {
            int problemResetPC = 0;
            this.codeStream.wideMode = false;
            boolean restart = false;
            do {
                try {
                    problemResetPC = this.contentsOffset;
                    addSyntheticDeserializeLambda(deserializeLambdaMethod,
                            this.referenceBinding.syntheticMethods());
                    restart = false;
                } catch (AbortMethod e) {
                    // Restart code generation if possible ...
                    if (e.compilationResult == CodeStream.RESTART_IN_WIDE_MODE) {
                        // a branch target required a goto_w, restart code generation in wide mode.
                        this.contentsOffset = problemResetPC;
                        this.methodCount--;
                        this.codeStream.resetInWideMode(); // request wide mode
                        restart = true;
                    } else {
                        throw new AbortType(this.referenceBinding.scope.referenceContext.compilationResult,
                                e.problem);
                    }
                }
            } while (restart);
        }
    }

    public void addSyntheticArrayConstructor(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.generateSyntheticBodyForArrayConstructor(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 addSyntheticArrayClone(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.generateSyntheticBodyForArrayClone(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 addSyntheticFactoryMethod(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.generateSyntheticBodyForFactoryMethod(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 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
        if ((this.produceAttributes & ClassFileConstants.ATTR_METHOD_PARAMETERS) != 0) {
            attributeNumber += generateMethodParameters(methodBinding);
        }
        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);
        int code_length = this.codeStream.position;
        if (code_length > 65535) {
            SwitchStatement switchStatement = methodBinding.switchStatement;
            if (switchStatement != null) {
                switchStatement.scope.problemReporter().bytecodeExceeds64KLimit(switchStatement);
            }
        }
        completeCodeAttributeForSyntheticMethod(true, methodBinding, codeAttributeOffset,
                ((SourceTypeBinding) methodBinding.declaringClass).scope
                        .referenceCompilationUnit().compilationResult.getLineSeparatorPositions(),
                ((SourceTypeBinding) methodBinding.declaringClass).scope);
        // 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, MethodScope scope) {
        // 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) {
            if (this.codeStream.methodDeclaration != null) {
                this.codeStream.methodDeclaration.scope.problemReporter()
                        .bytecodeExceeds64KLimit(this.codeStream.methodDeclaration);
            } else {
                this.codeStream.lambdaExpression.scope.problemReporter()
                        .bytecodeExceeds64KLimit(this.codeStream.lambdaExpression);
            }
        }
        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) {
                    if (this.codeStream.methodDeclaration != null) {
                        this.codeStream.methodDeclaration.scope.problemReporter().abortDueToInternalError(
                                Messages.bind(Messages.abort_invalidExceptionAttribute,
                                        new String(this.codeStream.methodDeclaration.selector)),
                                this.codeStream.methodDeclaration);
                    } else {
                        this.codeStream.lambdaExpression.scope.problemReporter().abortDueToInternalError(
                                Messages.bind(Messages.abort_invalidExceptionAttribute,
                                        new String(this.codeStream.lambdaExpression.binding.selector)),
                                this.codeStream.lambdaExpression);
                    }
                }
                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 != null
                    ? this.codeStream.methodDeclaration.isStatic()
                    : this.codeStream.lambdaExpression.binding.isStatic();
            attributesNumber += generateLocalVariableTableAttribute(code_length, methodDeclarationIsStatic, false);
        }

        if (addStackMaps) {
            attributesNumber += generateStackMapTableAttribute(
                    this.codeStream.methodDeclaration != null ? this.codeStream.methodDeclaration.binding
                            : this.codeStream.lambdaExpression.binding,
                    code_length, codeAttributeOffset, max_locals, false, scope);
        }

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

        if ((this.produceAttributes & ClassFileConstants.ATTR_TYPE_ANNOTATION) != 0) {
            attributesNumber += generateTypeAnnotationsOnCodeAttribute();
        }

        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 int generateTypeAnnotationsOnCodeAttribute() {
        int attributesNumber = 0;

        List allTypeAnnotationContexts = ((TypeAnnotationCodeStream) this.codeStream).allTypeAnnotationContexts;
        int invisibleTypeAnnotationsCounter = 0;
        int visibleTypeAnnotationsCounter = 0;

        for (int i = 0, max = this.codeStream.allLocalsCounter; i < max; i++) {
            LocalVariableBinding localVariable = this.codeStream.locals[i];
            if (localVariable.isCatchParameter())
                continue;
            LocalDeclaration declaration = localVariable.declaration;
            if (declaration == null || (declaration.isArgument() && ((declaration.bits & ASTNode.IsUnionType) == 0))
                    || (localVariable.initializationCount == 0)
                    || ((declaration.bits & ASTNode.HasTypeAnnotations) == 0)) {
                continue;
            }
            int targetType = ((localVariable.tagBits & TagBits.IsResource) == 0)
                    ? AnnotationTargetTypeConstants.LOCAL_VARIABLE
                    : AnnotationTargetTypeConstants.RESOURCE_VARIABLE;
            declaration.getAllAnnotationContexts(targetType, localVariable, allTypeAnnotationContexts);
        }

        ExceptionLabel[] exceptionLabels = this.codeStream.exceptionLabels;
        for (int i = 0, max = this.codeStream.exceptionLabelsCounter; i < max; i++) {
            ExceptionLabel exceptionLabel = exceptionLabels[i];
            if (exceptionLabel.exceptionTypeReference != null
                    && (exceptionLabel.exceptionTypeReference.bits & ASTNode.HasTypeAnnotations) != 0) {
                exceptionLabel.exceptionTypeReference.getAllAnnotationContexts(
                        AnnotationTargetTypeConstants.EXCEPTION_PARAMETER, i, allTypeAnnotationContexts,
                        exceptionLabel.se7Annotations);
            }
        }

        int size = allTypeAnnotationContexts.size();
        if (size != 0) {
            AnnotationContext[] allTypeAnnotationContextsArray = new AnnotationContext[size];
            allTypeAnnotationContexts.toArray(allTypeAnnotationContextsArray);
            for (int j = 0, max2 = allTypeAnnotationContextsArray.length; j < max2; j++) {
                AnnotationContext annotationContext = allTypeAnnotationContextsArray[j];
                if ((annotationContext.visibility & AnnotationContext.INVISIBLE) != 0) {
                    invisibleTypeAnnotationsCounter++;
                } else {
                    visibleTypeAnnotationsCounter++;
                }
            }
            attributesNumber += generateRuntimeTypeAnnotations(allTypeAnnotationContextsArray,
                    visibleTypeAnnotationsCounter, invisibleTypeAnnotationsCounter);
        }
        return attributesNumber;
    }

    /**
     * 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, Scope scope) {
        // 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, scope);
        }

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

        if ((this.produceAttributes & ClassFileConstants.ATTR_TYPE_ANNOTATION) != 0) {
            attributesNumber += generateTypeAnnotationsOnCodeAttribute();
        }

        // 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, MethodScope scope) {
        // 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, scope);
        }

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

        if ((this.produceAttributes & ClassFileConstants.ATTR_TYPE_ANNOTATION) != 0) {
            attributesNumber += generateTypeAnnotationsOnCodeAttribute();
        }

        // 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, null);
        }

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

        // 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, null);
        }

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

        // 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, Scope scope) {
        // 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, scope);
        }

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

        // 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,
                ((SourceTypeBinding) binding.declaringClass).scope);
    }

    private void completeArgumentAnnotationInfo(Argument[] arguments, List allAnnotationContexts) {
        for (int i = 0, max = arguments.length; i < max; i++) {
            Argument argument = arguments[i];
            if ((argument.bits & ASTNode.HasTypeAnnotations) != 0) {
                argument.getAllAnnotationContexts(AnnotationTargetTypeConstants.METHOD_FORMAL_PARAMETER, i,
                        allAnnotationContexts);
            }
        }
    }

    /**
     * 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) {

        if ((this.produceAttributes & ClassFileConstants.ATTR_TYPE_ANNOTATION) != 0) {
            List allTypeAnnotationContexts = new ArrayList();
            int invisibleTypeAnnotationsCounter = 0;
            int visibleTypeAnnotationsCounter = 0;
            AbstractMethodDeclaration methodDeclaration = binding.sourceMethod();
            if (methodDeclaration != null) {
                if ((methodDeclaration.bits & ASTNode.HasTypeAnnotations) != 0) {
                    Argument[] arguments = methodDeclaration.arguments;
                    if (arguments != null) {
                        completeArgumentAnnotationInfo(arguments, allTypeAnnotationContexts);
                    }
                    Receiver receiver = methodDeclaration.receiver;
                    if (receiver != null && (receiver.type.bits & ASTNode.HasTypeAnnotations) != 0) {
                        receiver.type.getAllAnnotationContexts(AnnotationTargetTypeConstants.METHOD_RECEIVER,
                                allTypeAnnotationContexts);
                    }
                }
                Annotation[] annotations = methodDeclaration.annotations;
                if (annotations != null && !methodDeclaration.isClinit()
                        && (methodDeclaration.isConstructor() || binding.returnType.id != T_void)) {
                    methodDeclaration.getAllAnnotationContexts(AnnotationTargetTypeConstants.METHOD_RETURN,
                            allTypeAnnotationContexts);
                }
                if (!methodDeclaration.isConstructor() && !methodDeclaration.isClinit()
                        && binding.returnType.id != T_void) {
                    MethodDeclaration declaration = (MethodDeclaration) methodDeclaration;
                    TypeReference typeReference = declaration.returnType;
                    if ((typeReference.bits & ASTNode.HasTypeAnnotations) != 0) {
                        typeReference.getAllAnnotationContexts(AnnotationTargetTypeConstants.METHOD_RETURN,
                                allTypeAnnotationContexts);
                    }
                }
                TypeReference[] thrownExceptions = methodDeclaration.thrownExceptions;
                if (thrownExceptions != null) {
                    for (int i = 0, max = thrownExceptions.length; i < max; i++) {
                        TypeReference thrownException = thrownExceptions[i];
                        thrownException.getAllAnnotationContexts(AnnotationTargetTypeConstants.THROWS, i,
                                allTypeAnnotationContexts);
                    }
                }
                TypeParameter[] typeParameters = methodDeclaration.typeParameters();
                if (typeParameters != null) {
                    for (int i = 0, max = typeParameters.length; i < max; i++) {
                        TypeParameter typeParameter = typeParameters[i];
                        if ((typeParameter.bits & ASTNode.HasTypeAnnotations) != 0) {
                            typeParameter.getAllAnnotationContexts(
                                    AnnotationTargetTypeConstants.METHOD_TYPE_PARAMETER, i,
                                    allTypeAnnotationContexts);
                        }
                    }
                }
            } else if (binding.sourceLambda() != null) { // SyntheticMethodBinding, purpose : LambdaMethod.
                LambdaExpression lambda = binding.sourceLambda();
                if ((lambda.bits & ASTNode.HasTypeAnnotations) != 0) {
                    if (lambda.arguments != null)
                        completeArgumentAnnotationInfo(lambda.arguments, allTypeAnnotationContexts);
                }
            }
            int size = allTypeAnnotationContexts.size();
            if (size != 0) {
                AnnotationContext[] allTypeAnnotationContextsArray = new AnnotationContext[size];
                allTypeAnnotationContexts.toArray(allTypeAnnotationContextsArray);
                for (int j = 0, max2 = allTypeAnnotationContextsArray.length; j < max2; j++) {
                    AnnotationContext annotationContext = allTypeAnnotationContextsArray[j];
                    if ((annotationContext.visibility & AnnotationContext.INVISIBLE) != 0) {
                        invisibleTypeAnnotationsCounter++;
                    } else {
                        visibleTypeAnnotationsCounter++;
                    }
                }
                attributesNumber += generateRuntimeTypeAnnotations(allTypeAnnotationContextsArray,
                        visibleTypeAnnotationsCounter, invisibleTypeAnnotationsCounter);
            }
        }
        if ((this.produceAttributes & ClassFileConstants.ATTR_METHOD_PARAMETERS) != 0) {
            attributesNumber += generateMethodParameters(binding);
        }
        // update the number of attributes
        this.contents[methodAttributeOffset++] = (byte) (attributesNumber >> 8);
        this.contents[methodAttributeOffset] = (byte) attributesNumber;
    }

    private void dumpLocations(int[] locations) {
        if (locations == null) {
            // no type path
            if (this.contentsOffset + 1 >= this.contents.length) {
                resizeContents(1);
            }
            this.contents[this.contentsOffset++] = (byte) 0;
        } else {
            int length = locations.length;
            if (this.contentsOffset + length >= this.contents.length) {
                resizeContents(length + 1);
            }
            this.contents[this.contentsOffset++] = (byte) (locations.length / 2);
            for (int i = 0; i < length; i++) {
                this.contents[this.contentsOffset++] = (byte) locations[i];
            }
        }
    }

    private void dumpTargetTypeContents(int targetType, AnnotationContext annotationContext) {
        switch (targetType) {
        case AnnotationTargetTypeConstants.CLASS_TYPE_PARAMETER:
        case AnnotationTargetTypeConstants.METHOD_TYPE_PARAMETER:
            // parameter index
            this.contents[this.contentsOffset++] = (byte) annotationContext.info;
            break;

        case AnnotationTargetTypeConstants.CLASS_TYPE_PARAMETER_BOUND:
            // type_parameter_index
            this.contents[this.contentsOffset++] = (byte) annotationContext.info;
            // bound_index
            this.contents[this.contentsOffset++] = (byte) annotationContext.info2;
            break;
        case AnnotationTargetTypeConstants.FIELD:
        case AnnotationTargetTypeConstants.METHOD_RECEIVER:
        case AnnotationTargetTypeConstants.METHOD_RETURN:
            // target_info is empty_target
            break;
        case AnnotationTargetTypeConstants.METHOD_FORMAL_PARAMETER:
            // target_info is parameter index
            this.contents[this.contentsOffset++] = (byte) annotationContext.info;
            break;

        case AnnotationTargetTypeConstants.INSTANCEOF:
        case AnnotationTargetTypeConstants.NEW:
        case AnnotationTargetTypeConstants.EXCEPTION_PARAMETER:
        case AnnotationTargetTypeConstants.CONSTRUCTOR_REFERENCE:
        case AnnotationTargetTypeConstants.METHOD_REFERENCE:
            // bytecode offset for new/instanceof/method_reference
            // exception table entry index for exception_parameter
            this.contents[this.contentsOffset++] = (byte) (annotationContext.info >> 8);
            this.contents[this.contentsOffset++] = (byte) annotationContext.info;
            break;
        case AnnotationTargetTypeConstants.CAST:
            // bytecode offset
            this.contents[this.contentsOffset++] = (byte) (annotationContext.info >> 8);
            this.contents[this.contentsOffset++] = (byte) annotationContext.info;
            this.contents[this.contentsOffset++] = (byte) annotationContext.info2;
            break;

        case AnnotationTargetTypeConstants.CONSTRUCTOR_INVOCATION_TYPE_ARGUMENT:
        case AnnotationTargetTypeConstants.METHOD_INVOCATION_TYPE_ARGUMENT:
        case AnnotationTargetTypeConstants.CONSTRUCTOR_REFERENCE_TYPE_ARGUMENT:
        case AnnotationTargetTypeConstants.METHOD_REFERENCE_TYPE_ARGUMENT:
            // bytecode offset
            this.contents[this.contentsOffset++] = (byte) (annotationContext.info >> 8);
            this.contents[this.contentsOffset++] = (byte) annotationContext.info;
            // type_argument_index 
            this.contents[this.contentsOffset++] = (byte) annotationContext.info2;
            break;

        case AnnotationTargetTypeConstants.CLASS_EXTENDS:
        case AnnotationTargetTypeConstants.THROWS:
            // For CLASS_EXTENDS - info is supertype index (-1 = superclass)
            // For THROWS - info is exception table index
            this.contents[this.contentsOffset++] = (byte) (annotationContext.info >> 8);
            this.contents[this.contentsOffset++] = (byte) annotationContext.info;
            break;

        case AnnotationTargetTypeConstants.LOCAL_VARIABLE:
        case AnnotationTargetTypeConstants.RESOURCE_VARIABLE:
            int localVariableTableOffset = this.contentsOffset;
            LocalVariableBinding localVariable = annotationContext.variableBinding;
            int actualSize = 0;
            int initializationCount = localVariable.initializationCount;
            actualSize += 2 /* for number of entries */ + (6 * initializationCount);
            // reserve enough space
            if (this.contentsOffset + actualSize >= this.contents.length) {
                resizeContents(actualSize);
            }
            this.contentsOffset += 2;
            int numberOfEntries = 0;
            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
                    // now we can safely add the local entry
                    numberOfEntries++;
                    this.contents[this.contentsOffset++] = (byte) (startPC >> 8);
                    this.contents[this.contentsOffset++] = (byte) startPC;
                    int length = endPC - startPC;
                    this.contents[this.contentsOffset++] = (byte) (length >> 8);
                    this.contents[this.contentsOffset++] = (byte) length;
                    int resolvedPosition = localVariable.resolvedPosition;
                    this.contents[this.contentsOffset++] = (byte) (resolvedPosition >> 8);
                    this.contents[this.contentsOffset++] = (byte) resolvedPosition;
                }
            }
            this.contents[localVariableTableOffset++] = (byte) (numberOfEntries >> 8);
            this.contents[localVariableTableOffset] = (byte) numberOfEntries;
            break;
        case AnnotationTargetTypeConstants.METHOD_TYPE_PARAMETER_BOUND:
            this.contents[this.contentsOffset++] = (byte) annotationContext.info;
            this.contents[this.contentsOffset++] = (byte) annotationContext.info2;
            break;
        }
    }

    /**
     * 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;
        }
        if (annotationTypeBinding.isMemberType()) {
            this.recordInnerClasses(annotationTypeBinding);
        }
        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;
            int memberValuePairOffset = this.contentsOffset;
            if (memberValuePairs != null) {
                int memberValuePairsCount = 0;
                int memberValuePairsLengthPosition = this.contentsOffset;
                this.contentsOffset += 2; // leave space to fill in the pair count later
                int resetPosition = this.contentsOffset;
                final int memberValuePairsLength = memberValuePairs.length;
                loop: 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 = resetPosition;
                    } else {
                        try {
                            generateElementValue(memberValuePair.value, methodBinding.returnType,
                                    memberValuePairOffset);
                            if (this.contentsOffset == memberValuePairOffset) {
                                // ignore all annotation values
                                this.contents[this.contentsOffset++] = 0;
                                this.contents[this.contentsOffset++] = 0;
                                break loop;
                            }
                            memberValuePairsCount++;
                            resetPosition = this.contentsOffset;
                        } catch (ClassCastException | ShouldNotImplement e) {
                            this.contentsOffset = resetPosition;
                        }
                    }
                }
                this.contents[memberValuePairsLengthPosition++] = (byte) (memberValuePairsCount >> 8);
                this.contents[memberValuePairsLengthPosition++] = (byte) memberValuePairsCount;
            } 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 {
                int memberValuePairOffset = this.contentsOffset;
                try {
                    generateElementValue(singleMemberAnnotation.memberValue, methodBinding.returnType,
                            memberValuePairOffset);
                    if (this.contentsOffset == memberValuePairOffset) {
                        // completely remove the annotation as its value is invalid
                        this.contentsOffset = startingContentsOffset;
                    }
                } catch (ClassCastException | 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 int generateNestHostAttribute() {
        SourceTypeBinding nestHost = this.referenceBinding.getNestHost();
        if (nestHost == null)
            return 0;
        int localContentsOffset = this.contentsOffset;
        if (localContentsOffset + 10 >= this.contents.length) {
            resizeContents(10);
        }
        int nestHostAttributeNameIndex = this.constantPool.literalIndex(AttributeNamesConstants.NestHost);
        this.contents[localContentsOffset++] = (byte) (nestHostAttributeNameIndex >> 8);
        this.contents[localContentsOffset++] = (byte) nestHostAttributeNameIndex;

        // The value of the attribute_length item must be two.
        this.contents[localContentsOffset++] = 0;
        this.contents[localContentsOffset++] = 0;
        this.contents[localContentsOffset++] = 0;
        this.contents[localContentsOffset++] = 2;

        int nestHostIndex = this.constantPool.literalIndexForType(nestHost.constantPoolName());
        this.contents[localContentsOffset++] = (byte) (nestHostIndex >> 8);
        this.contents[localContentsOffset++] = (byte) nestHostIndex;
        this.contentsOffset = localContentsOffset;
        return 1;
    }

    private int generateNestMembersAttribute() {

        int localContentsOffset = this.contentsOffset;
        List<String> nestedMembers = this.referenceBinding.getNestMembers();
        int numberOfNestedMembers = nestedMembers != null ? nestedMembers.size() : 0;
        if (numberOfNestedMembers == 0) // JVMS 11 4.7.29 says "at most one" NestMembers attribute - return if none.
            return 0;

        int exSize = 8 + 2 * numberOfNestedMembers;
        if (exSize + localContentsOffset >= this.contents.length) {
            resizeContents(exSize);
        }
        int attributeNameIndex = this.constantPool.literalIndex(AttributeNamesConstants.NestMembers);
        this.contents[localContentsOffset++] = (byte) (attributeNameIndex >> 8);
        this.contents[localContentsOffset++] = (byte) attributeNameIndex;
        int value = (numberOfNestedMembers << 1) + 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) (numberOfNestedMembers >> 8);
        this.contents[localContentsOffset++] = (byte) numberOfNestedMembers;

        for (int i = 0; i < numberOfNestedMembers; i++) {
            char[] nestMemberName = nestedMembers.get(i).toCharArray();
            int nestedMemberIndex = this.constantPool.literalIndexForType(nestMemberName);
            this.contents[localContentsOffset++] = (byte) (nestedMemberIndex >> 8);
            this.contents[localContentsOffset++] = (byte) nestedMemberIndex;
        }
        this.contentsOffset = localContentsOffset;
        return 1;
    }

    private int generateNestAttributes() {
        int nAttrs = generateNestMembersAttribute(); //either member or host will exist 4.7.29
        nAttrs += generateNestHostAttribute();
        return nAttrs;
    }

    private int generateModuleAttribute(ModuleDeclaration module) {
        ModuleBinding binding = module.binding;
        int localContentsOffset = this.contentsOffset;
        if (localContentsOffset + 10 >= this.contents.length) {
            resizeContents(10);
        }
        int moduleAttributeNameIndex = this.constantPool.literalIndex(AttributeNamesConstants.ModuleName);
        this.contents[localContentsOffset++] = (byte) (moduleAttributeNameIndex >> 8);
        this.contents[localContentsOffset++] = (byte) moduleAttributeNameIndex;
        int attrLengthOffset = localContentsOffset;
        localContentsOffset += 4;
        int moduleNameIndex = this.constantPool.literalIndexForModule(binding.moduleName);
        this.contents[localContentsOffset++] = (byte) (moduleNameIndex >> 8);
        this.contents[localContentsOffset++] = (byte) moduleNameIndex;
        int flags = module.modifiers & ~(ClassFileConstants.AccModule);
        this.contents[localContentsOffset++] = (byte) (flags >> 8);
        this.contents[localContentsOffset++] = (byte) flags;
        String moduleVersion = module.getModuleVersion();
        int module_version_idx = moduleVersion == null ? 0
                : this.constantPool.literalIndex(moduleVersion.toCharArray());
        this.contents[localContentsOffset++] = (byte) (module_version_idx >> 8);
        this.contents[localContentsOffset++] = (byte) module_version_idx;
        int attrLength = 6;

        // ================= requires section =================
        /** u2 requires_count;
            {   u2 requires_index;
          u2 requires_flags;
            } requires[requires_count];
         **/
        int requiresCountOffset = localContentsOffset;
        int requiresCount = module.requiresCount;
        int requiresSize = 2 + requiresCount * 6;
        if (localContentsOffset + requiresSize >= this.contents.length) {
            resizeContents(requiresSize);
        }

        localContentsOffset += 2;
        ModuleBinding javaBaseBinding = null;
        for (int i = 0; i < module.requiresCount; i++) {
            RequiresStatement req = module.requires[i];
            ModuleBinding reqBinding = req.resolvedBinding;
            if (CharOperation.equals(reqBinding.moduleName, TypeConstants.JAVA_BASE)) {
                javaBaseBinding = reqBinding;
            }
            int nameIndex = this.constantPool.literalIndexForModule(reqBinding.moduleName);
            this.contents[localContentsOffset++] = (byte) (nameIndex >> 8);
            this.contents[localContentsOffset++] = (byte) (nameIndex);
            flags = req.modifiers;
            this.contents[localContentsOffset++] = (byte) (flags >> 8);
            this.contents[localContentsOffset++] = (byte) (flags);
            int required_version = 0;
            this.contents[localContentsOffset++] = (byte) (required_version >> 8);
            this.contents[localContentsOffset++] = (byte) (required_version);
        }
        if (!CharOperation.equals(binding.moduleName, TypeConstants.JAVA_BASE) && javaBaseBinding == null) {
            if (localContentsOffset + 6 >= this.contents.length) {
                resizeContents(6);
            }
            javaBaseBinding = binding.environment.javaBaseModule();
            int javabase_index = this.constantPool.literalIndexForModule(javaBaseBinding.moduleName);
            this.contents[localContentsOffset++] = (byte) (javabase_index >> 8);
            this.contents[localContentsOffset++] = (byte) (javabase_index);
            flags = ClassFileConstants.AccMandated;
            this.contents[localContentsOffset++] = (byte) (flags >> 8);
            this.contents[localContentsOffset++] = (byte) flags;
            int required_version = 0;
            this.contents[localContentsOffset++] = (byte) (required_version >> 8);
            this.contents[localContentsOffset++] = (byte) (required_version);
            requiresCount++;
        }
        this.contents[requiresCountOffset++] = (byte) (requiresCount >> 8);
        this.contents[requiresCountOffset++] = (byte) requiresCount;
        attrLength += 2 + 6 * requiresCount;
        // ================= end requires section =================

        // ================= exports section =================
        /**
         * u2 exports_count;
         * {   u2 exports_index;
         *     u2 exports_flags;
         *     u2 exports_to_count;
         *     u2 exports_to_index[exports_to_count];
         * } exports[exports_count];
         */
        int exportsSize = 2 + module.exportsCount * 6;
        if (localContentsOffset + exportsSize >= this.contents.length) {
            resizeContents(exportsSize);
        }
        this.contents[localContentsOffset++] = (byte) (module.exportsCount >> 8);
        this.contents[localContentsOffset++] = (byte) module.exportsCount;
        for (int i = 0; i < module.exportsCount; i++) {
            ExportsStatement ref = module.exports[i];
            if (localContentsOffset + 6 >= this.contents.length) {
                resizeContents((module.exportsCount - i) * 6);
            }
            int nameIndex = this.constantPool
                    .literalIndexForPackage(CharOperation.replaceOnCopy(ref.pkgName, '.', '/'));
            this.contents[localContentsOffset++] = (byte) (nameIndex >> 8);
            this.contents[localContentsOffset++] = (byte) (nameIndex);
            // TODO exports_flags - check when they are set
            this.contents[localContentsOffset++] = (byte) 0;
            this.contents[localContentsOffset++] = (byte) 0;

            int exportsToCount = ref.isQualified() ? ref.targets.length : 0;
            this.contents[localContentsOffset++] = (byte) (exportsToCount >> 8);
            this.contents[localContentsOffset++] = (byte) (exportsToCount);
            if (exportsToCount > 0) {
                int targetSize = 2 * exportsToCount;
                if (localContentsOffset + targetSize >= this.contents.length) {
                    resizeContents(targetSize);
                }
                for (int j = 0; j < exportsToCount; j++) {
                    nameIndex = this.constantPool.literalIndexForModule(ref.targets[j].moduleName);
                    this.contents[localContentsOffset++] = (byte) (nameIndex >> 8);
                    this.contents[localContentsOffset++] = (byte) (nameIndex);
                }
                attrLength += targetSize;
            }
        }
        attrLength += exportsSize;
        // ================= end exports section =================

        // ================= opens section =================
        /**
         * u2 opens_count;
         * {   u2 opens_index;
         *     u2 opens_flags;
         *     u2 opens_to_count;
         *     u2 opens_to_index[opens_to_count];
         * } exports[exports_count];
         */
        int opensSize = 2 + module.opensCount * 6;
        if (localContentsOffset + opensSize >= this.contents.length) {
            resizeContents(opensSize);
        }
        this.contents[localContentsOffset++] = (byte) (module.opensCount >> 8);
        this.contents[localContentsOffset++] = (byte) module.opensCount;
        for (int i = 0; i < module.opensCount; i++) {
            OpensStatement ref = module.opens[i];
            if (localContentsOffset + 6 >= this.contents.length) {
                resizeContents((module.opensCount - i) * 6);
            }
            int nameIndex = this.constantPool
                    .literalIndexForPackage(CharOperation.replaceOnCopy(ref.pkgName, '.', '/'));
            this.contents[localContentsOffset++] = (byte) (nameIndex >> 8);
            this.contents[localContentsOffset++] = (byte) (nameIndex);
            // TODO opens_flags - check when they are set
            this.contents[localContentsOffset++] = (byte) 0;
            this.contents[localContentsOffset++] = (byte) 0;

            int opensToCount = ref.isQualified() ? ref.targets.length : 0;
            this.contents[localContentsOffset++] = (byte) (opensToCount >> 8);
            this.contents[localContentsOffset++] = (byte) (opensToCount);
            if (opensToCount > 0) {
                int targetSize = 2 * opensToCount;
                if (localContentsOffset + targetSize >= this.contents.length) {
                    resizeContents(targetSize);
                }
                for (int j = 0; j < opensToCount; j++) {
                    nameIndex = this.constantPool.literalIndexForModule(ref.targets[j].moduleName);
                    this.contents[localContentsOffset++] = (byte) (nameIndex >> 8);
                    this.contents[localContentsOffset++] = (byte) (nameIndex);
                }
                attrLength += targetSize;
            }
        }
        attrLength += opensSize;
        // ================= end opens section =================

        // ================= uses section =================
        /**
         * u2 uses_count;
         * u2 uses_index[uses_count];
         */
        int usesSize = 2 + 2 * module.usesCount;
        if (localContentsOffset + usesSize >= this.contents.length) {
            resizeContents(usesSize);
        }
        this.contents[localContentsOffset++] = (byte) (module.usesCount >> 8);
        this.contents[localContentsOffset++] = (byte) module.usesCount;
        for (int i = 0; i < module.usesCount; i++) {
            int nameIndex = this.constantPool
                    .literalIndexForType(module.uses[i].serviceInterface.resolvedType.constantPoolName());
            this.contents[localContentsOffset++] = (byte) (nameIndex >> 8);
            this.contents[localContentsOffset++] = (byte) (nameIndex);
        }
        attrLength += usesSize;
        // ================= end uses section =================

        // ================= provides section =================
        /**
         * u2 provides_count;
         * {
         *       u2 provides_index;
         *       u2 provides_with_count;
         *       u2 provides_with_index[provides_with_count];
         * } provides[provides_count];
         */
        int servicesSize = 2 + 4 * module.servicesCount;
        if (localContentsOffset + servicesSize >= this.contents.length) {
            resizeContents(servicesSize);
        }
        this.contents[localContentsOffset++] = (byte) (module.servicesCount >> 8);
        this.contents[localContentsOffset++] = (byte) module.servicesCount;
        for (int i = 0; i < module.servicesCount; i++) {
            if (localContentsOffset + 4 >= this.contents.length) {
                resizeContents((module.servicesCount - i) * 4);
            }
            int nameIndex = this.constantPool
                    .literalIndexForType(module.services[i].serviceInterface.resolvedType.constantPoolName());
            this.contents[localContentsOffset++] = (byte) (nameIndex >> 8);
            this.contents[localContentsOffset++] = (byte) (nameIndex);
            TypeReference[] impls = module.services[i].implementations;
            int implLength = impls.length;
            this.contents[localContentsOffset++] = (byte) (implLength >> 8);
            this.contents[localContentsOffset++] = (byte) implLength;
            int targetSize = implLength * 2;
            if (localContentsOffset + targetSize >= this.contents.length) {
                resizeContents(targetSize);
            }
            for (int j = 0; j < implLength; j++) {
                nameIndex = this.constantPool.literalIndexForType(impls[j].resolvedType.constantPoolName());
                this.contents[localContentsOffset++] = (byte) (nameIndex >> 8);
                this.contents[localContentsOffset++] = (byte) (nameIndex);
            }
            attrLength += targetSize;
        }
        attrLength += servicesSize;
        // ================= end provides section =================

        this.contents[attrLengthOffset++] = (byte) (attrLength >> 24);
        this.contents[attrLengthOffset++] = (byte) (attrLength >> 16);
        this.contents[attrLengthOffset++] = (byte) (attrLength >> 8);
        this.contents[attrLengthOffset++] = (byte) attrLength;
        this.contentsOffset = localContentsOffset;
        return 1;
    }

    private int generateModuleMainClassAttribute(char[] moduleMainClass) {
        int localContentsOffset = this.contentsOffset;
        if (localContentsOffset + 8 >= this.contents.length) {
            resizeContents(8);
        }
        int moduleAttributeNameIndex = this.constantPool.literalIndex(AttributeNamesConstants.ModuleMainClass);
        this.contents[localContentsOffset++] = (byte) (moduleAttributeNameIndex >> 8);
        this.contents[localContentsOffset++] = (byte) moduleAttributeNameIndex;
        int attrLength = 2;
        this.contents[localContentsOffset++] = (byte) (attrLength >> 24);
        this.contents[localContentsOffset++] = (byte) (attrLength >> 16);
        this.contents[localContentsOffset++] = (byte) (attrLength >> 8);
        this.contents[localContentsOffset++] = (byte) attrLength;
        int moduleNameIndex = this.constantPool.literalIndexForType(moduleMainClass);
        this.contents[localContentsOffset++] = (byte) (moduleNameIndex >> 8);
        this.contents[localContentsOffset++] = (byte) moduleNameIndex;
        this.contentsOffset = localContentsOffset;
        return 1;
    }

    private int generateModulePackagesAttribute(char[][] packageNames) {
        int localContentsOffset = this.contentsOffset;
        int maxSize = 6 + 2 * packageNames.length;
        if (localContentsOffset + maxSize >= this.contents.length) {
            resizeContents(maxSize);
        }
        int moduleAttributeNameIndex = this.constantPool.literalIndex(AttributeNamesConstants.ModulePackages);
        this.contents[localContentsOffset++] = (byte) (moduleAttributeNameIndex >> 8);
        this.contents[localContentsOffset++] = (byte) moduleAttributeNameIndex;

        int attrLengthOffset = localContentsOffset;
        localContentsOffset += 4;
        int packageCountOffset = localContentsOffset;
        localContentsOffset += 2;

        int packagesCount = 0;
        for (char[] packageName : packageNames) {
            if (packageName == null || packageName.length == 0)
                continue;
            int packageNameIndex = this.constantPool.literalIndexForPackage(packageName);
            this.contents[localContentsOffset++] = (byte) (packageNameIndex >> 8);
            this.contents[localContentsOffset++] = (byte) packageNameIndex;
            packagesCount++;
        }

        this.contents[packageCountOffset++] = (byte) (packagesCount >> 8);
        this.contents[packageCountOffset++] = (byte) packagesCount;
        int attrLength = 2 + 2 * packagesCount;
        this.contents[attrLengthOffset++] = (byte) (attrLength >> 24);
        this.contents[attrLengthOffset++] = (byte) (attrLength >> 16);
        this.contents[attrLengthOffset++] = (byte) (attrLength >> 8);
        this.contents[attrLengthOffset++] = (byte) attrLength;
        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 (defaultValueBinding.isMemberType()) {
                this.recordInnerClasses(defaultValueBinding);
            }
            if (memberValuePairReturnType.isMemberType()) {
                this.recordInnerClasses(memberValuePairReturnType);
            }
            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 generateBootstrapMethods(List functionalExpressionList) {
        /* See JVM spec 4.7.21
           The BootstrapMethods attribute has the following format:
           BootstrapMethods_attribute {
        u2 attribute_name_index;
        u4 attribute_length;
        u2 num_bootstrap_methods;
        {   u2 bootstrap_method_ref;
            u2 num_bootstrap_arguments;
            u2 bootstrap_arguments[num_bootstrap_arguments];
        } bootstrap_methods[num_bootstrap_methods];
         }
        */
        // Record inner classes for MethodHandles$Lookup
        ReferenceBinding methodHandlesLookup = this.referenceBinding.scope.getJavaLangInvokeMethodHandlesLookup();
        if (methodHandlesLookup == null)
            return 0; // skip bootstrap section, class path problem already reported, just avoid NPE.
        recordInnerClasses(methodHandlesLookup); // Should be done, it's what javac does also
        ReferenceBinding javaLangInvokeLambdaMetafactory = this.referenceBinding.scope
                .getJavaLangInvokeLambdaMetafactory();

        // Depending on the complexity of the expression it may be necessary to use the altMetafactory() rather than the metafactory()
        int indexForMetaFactory = 0;
        int indexForAltMetaFactory = 0;

        int numberOfBootstraps = functionalExpressionList.size();
        int localContentsOffset = this.contentsOffset;
        // Generate the boot strap attribute - since we are only making lambdas and
        // functional expressions, we know the size ahead of time - this less general
        // than the full invokedynamic scope, but fine for Java 8

        final int contentsEntries = 10;
        int exSize = contentsEntries * numberOfBootstraps + 8;
        if (exSize + localContentsOffset >= this.contents.length) {
            resizeContents(exSize);
        }

        int attributeNameIndex = this.constantPool.literalIndex(AttributeNamesConstants.BootstrapMethodsName);
        this.contents[localContentsOffset++] = (byte) (attributeNameIndex >> 8);
        this.contents[localContentsOffset++] = (byte) attributeNameIndex;
        // leave space for attribute_length and remember where to insert it
        int attributeLengthPosition = localContentsOffset;
        localContentsOffset += 4;
        this.contents[localContentsOffset++] = (byte) (numberOfBootstraps >> 8);
        this.contents[localContentsOffset++] = (byte) numberOfBootstraps;
        for (int i = 0; i < numberOfBootstraps; i++) {
            FunctionalExpression functional = (FunctionalExpression) functionalExpressionList.get(i);
            MethodBinding[] bridges = functional.getRequiredBridges();
            TypeBinding[] markerInterfaces = null;
            if ((functional instanceof LambdaExpression
                    && (((markerInterfaces = ((LambdaExpression) functional).getMarkerInterfaces()) != null))
                    || bridges != null) || functional.isSerializable) {
                // may need even more space
                int extraSpace = 2; // at least 2 more than when the normal metafactory is used, for the bitflags entry
                if (markerInterfaces != null) {
                    // 2 for the marker interface list size then 2 per marker interface index
                    extraSpace += (2 + 2 * markerInterfaces.length);
                }
                if (bridges != null) {
                    // 2 for bridge count then 2 per bridge method type.
                    extraSpace += (2 + 2 * bridges.length);
                }
                if (extraSpace + contentsEntries + localContentsOffset >= this.contents.length) {
                    resizeContents(extraSpace + contentsEntries);
                }

                if (indexForAltMetaFactory == 0) {
                    indexForAltMetaFactory = this.constantPool.literalIndexForMethodHandle(
                            ClassFileConstants.MethodHandleRefKindInvokeStatic, javaLangInvokeLambdaMetafactory,
                            ConstantPool.ALTMETAFACTORY,
                            ConstantPool.JAVA_LANG_INVOKE_LAMBDAMETAFACTORY_ALTMETAFACTORY_SIGNATURE, false);
                }
                this.contents[localContentsOffset++] = (byte) (indexForAltMetaFactory >> 8);
                this.contents[localContentsOffset++] = (byte) indexForAltMetaFactory;

                // u2 num_bootstrap_arguments
                this.contents[localContentsOffset++] = 0;
                this.contents[localContentsOffset++] = (byte) (4
                        + (markerInterfaces == null ? 0 : 1 + markerInterfaces.length)
                        + (bridges == null ? 0 : 1 + bridges.length));

                int functionalDescriptorIndex = this.constantPool
                        .literalIndexForMethodType(functional.descriptor.original().signature());
                this.contents[localContentsOffset++] = (byte) (functionalDescriptorIndex >> 8);
                this.contents[localContentsOffset++] = (byte) functionalDescriptorIndex;

                int methodHandleIndex = this.constantPool
                        .literalIndexForMethodHandle(functional.binding.original()); // Speak of " implementation" (erased) version here, adaptations described below.
                this.contents[localContentsOffset++] = (byte) (methodHandleIndex >> 8);
                this.contents[localContentsOffset++] = (byte) methodHandleIndex;

                char[] instantiatedSignature = functional.descriptor.signature();
                int methodTypeIndex = this.constantPool.literalIndexForMethodType(instantiatedSignature);
                this.contents[localContentsOffset++] = (byte) (methodTypeIndex >> 8);
                this.contents[localContentsOffset++] = (byte) methodTypeIndex;

                int bitflags = 0;
                if (functional.isSerializable) {
                    bitflags |= ClassFileConstants.FLAG_SERIALIZABLE;
                }
                if (markerInterfaces != null) {
                    bitflags |= ClassFileConstants.FLAG_MARKERS;
                }
                if (bridges != null) {
                    bitflags |= ClassFileConstants.FLAG_BRIDGES;
                }
                int indexForBitflags = this.constantPool.literalIndex(bitflags);

                this.contents[localContentsOffset++] = (byte) (indexForBitflags >> 8);
                this.contents[localContentsOffset++] = (byte) (indexForBitflags);

                if (markerInterfaces != null) {
                    int markerInterfaceCountIndex = this.constantPool.literalIndex(markerInterfaces.length);
                    this.contents[localContentsOffset++] = (byte) (markerInterfaceCountIndex >> 8);
                    this.contents[localContentsOffset++] = (byte) (markerInterfaceCountIndex);
                    for (int m = 0, maxm = markerInterfaces.length; m < maxm; m++) {
                        int classTypeIndex = this.constantPool.literalIndexForType(markerInterfaces[m]);
                        this.contents[localContentsOffset++] = (byte) (classTypeIndex >> 8);
                        this.contents[localContentsOffset++] = (byte) (classTypeIndex);
                    }
                }
                if (bridges != null) {
                    int bridgeCountIndex = this.constantPool.literalIndex(bridges.length);
                    this.contents[localContentsOffset++] = (byte) (bridgeCountIndex >> 8);
                    this.contents[localContentsOffset++] = (byte) (bridgeCountIndex);
                    for (int m = 0, maxm = bridges.length; m < maxm; m++) {
                        char[] bridgeSignature = bridges[m].signature();
                        int bridgeMethodTypeIndex = this.constantPool.literalIndexForMethodType(bridgeSignature);
                        this.contents[localContentsOffset++] = (byte) (bridgeMethodTypeIndex >> 8);
                        this.contents[localContentsOffset++] = (byte) bridgeMethodTypeIndex;
                    }
                }
            } else {
                if (contentsEntries + localContentsOffset >= this.contents.length) {
                    resizeContents(contentsEntries);
                }
                if (indexForMetaFactory == 0) {
                    indexForMetaFactory = this.constantPool.literalIndexForMethodHandle(
                            ClassFileConstants.MethodHandleRefKindInvokeStatic, javaLangInvokeLambdaMetafactory,
                            ConstantPool.METAFACTORY,
                            ConstantPool.JAVA_LANG_INVOKE_LAMBDAMETAFACTORY_METAFACTORY_SIGNATURE, false);
                }
                this.contents[localContentsOffset++] = (byte) (indexForMetaFactory >> 8);
                this.contents[localContentsOffset++] = (byte) indexForMetaFactory;

                // u2 num_bootstrap_arguments
                this.contents[localContentsOffset++] = 0;
                this.contents[localContentsOffset++] = (byte) 3;

                int functionalDescriptorIndex = this.constantPool
                        .literalIndexForMethodType(functional.descriptor.original().signature());
                this.contents[localContentsOffset++] = (byte) (functionalDescriptorIndex >> 8);
                this.contents[localContentsOffset++] = (byte) functionalDescriptorIndex;

                int methodHandleIndex = this.constantPool.literalIndexForMethodHandle(
                        functional.binding instanceof PolymorphicMethodBinding ? functional.binding
                                : functional.binding.original()); // Speak of " implementation" (erased) version here, adaptations described below.
                this.contents[localContentsOffset++] = (byte) (methodHandleIndex >> 8);
                this.contents[localContentsOffset++] = (byte) methodHandleIndex;

                char[] instantiatedSignature = functional.descriptor.signature();
                int methodTypeIndex = this.constantPool.literalIndexForMethodType(instantiatedSignature);
                this.contents[localContentsOffset++] = (byte) (methodTypeIndex >> 8);
                this.contents[localContentsOffset++] = (byte) methodTypeIndex;
            }
        }

        int attributeLength = localContentsOffset - attributeLengthPosition - 4;
        this.contents[attributeLengthPosition++] = (byte) (attributeLength >> 24);
        this.contents[attributeLengthPosition++] = (byte) (attributeLength >> 16);
        this.contents[attributeLengthPosition++] = (byte) (attributeLength >> 8);
        this.contents[attributeLengthPosition++] = (byte) attributeLength;
        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 != null
                    ? this.codeStream.methodDeclaration.binding.declaringClass
                    : this.codeStream.lambdaExpression.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 (methodBinding instanceof SyntheticMethodBinding) {
                SyntheticMethodBinding syntheticMethod = (SyntheticMethodBinding) methodBinding;
                if (syntheticMethod.purpose == SyntheticMethodBinding.SuperMethodAccess
                        && CharOperation.equals(syntheticMethod.selector, syntheticMethod.targetMethod.selector))
                    methodDeclaration = ((SyntheticMethodBinding) methodBinding).targetMethod.sourceMethod();
            }
            if (methodDeclaration != null) {
                Annotation[] annotations = methodDeclaration.annotations;
                if (annotations != null) {
                    attributesNumber += generateRuntimeAnnotations(annotations,
                            methodBinding.isConstructor() ? TagBits.AnnotationForConstructor
                                    : TagBits.AnnotationForMethod);
                }
                if ((methodBinding.tagBits & TagBits.HasParameterAnnotations) != 0) {
                    Argument[] arguments = methodDeclaration.arguments;
                    if (arguments != null) {
                        attributesNumber += generateRuntimeAnnotationsForParameters(arguments);
                    }
                }
            } else {
                LambdaExpression lambda = methodBinding.sourceLambda();
                if (lambda != null) {
                    if ((methodBinding.tagBits & TagBits.HasParameterAnnotations) != 0) {
                        Argument[] arguments = lambda.arguments();
                        if (arguments != null) {
                            int parameterCount = methodBinding.parameters.length;
                            int argumentCount = arguments.length;
                            if (parameterCount > argumentCount) { // synthetics prefixed 
                                int redShift = parameterCount - argumentCount;
                                System.arraycopy(arguments, 0, arguments = new Argument[parameterCount], redShift,
                                        argumentCount);
                                for (int i = 0; i < redShift; i++)
                                    arguments[i] = new Argument(CharOperation.NO_CHAR, 0, null, 0);
                            }
                            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;
    }

    public void addSyntheticDeserializeLambda(SyntheticMethodBinding methodBinding,
            SyntheticMethodBinding[] syntheticMethodBindings) {
        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.generateSyntheticBodyForDeserializeLambda(methodBinding, syntheticMethodBindings);
        int code_length = this.codeStream.position;
        if (code_length > 65535) {
            this.referenceBinding.scope.problemReporter().bytecodeExceeds64KLimit(methodBinding,
                    this.referenceBinding.sourceStart(), this.referenceBinding.sourceEnd());
        }
        completeCodeAttributeForSyntheticMethod(methodBinding, codeAttributeOffset,
                ((SourceTypeBinding) methodBinding.declaringClass).scope
                        .referenceCompilationUnit().compilationResult.getLineSeparatorPositions());
        this.contents[methodAttributeOffset++] = (byte) (attributeNumber >> 8);
        this.contents[methodAttributeOffset] = (byte) attributeNumber;
    }

    /**
     * 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() {
                @Override
                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(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;
        }
    }

    private boolean jdk16packageInfoAnnotation(final long annotationMask, final long targetMask) {
        if (this.targetJDK <= ClassFileConstants.JDK1_6 && targetMask == TagBits.AnnotationForPackage
                && annotationMask != 0 && (annotationMask & TagBits.AnnotationForPackage) == 0) {
            return true;
        }
        return false;
    }

    /**
     * @param annotations
     * @param targetMask allowed targets
     * @return the number of attributes created while dumping the annotations in the .class file
     */
    private int generateRuntimeAnnotations(final Annotation[] annotations, final long targetMask) {
        int attributesNumber = 0;
        final int length = annotations.length;
        int visibleAnnotationsCounter = 0;
        int invisibleAnnotationsCounter = 0;
        for (int i = 0; i < length; i++) {
            Annotation annotation;
            if ((annotation = annotations[i].getPersistibleAnnotation()) == null)
                continue; // already packaged into container.
            long annotationMask = annotation.resolvedType != null
                    ? annotation.resolvedType.getAnnotationTagBits() & TagBits.AnnotationTargetMASK
                    : 0;
            if (annotationMask != 0 && (annotationMask & targetMask) == 0) {
                if (!jdk16packageInfoAnnotation(annotationMask, targetMask))
                    continue;
            }
            if (annotation.isRuntimeInvisible() || annotation.isRuntimeTypeInvisible()) {
                invisibleAnnotationsCounter++;
            } else if (annotation.isRuntimeVisible() || annotation.isRuntimeTypeVisible()) {
                visibleAnnotationsCounter++;
            }
        }

        int annotationAttributeOffset = this.contentsOffset;
        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;
                if ((annotation = annotations[i].getPersistibleAnnotation()) == null)
                    continue; // already packaged into container.
                long annotationMask = annotation.resolvedType != null
                        ? annotation.resolvedType.getAnnotationTagBits() & TagBits.AnnotationTargetMASK
                        : 0;
                if (annotationMask != 0 && (annotationMask & targetMask) == 0) {
                    if (!jdk16packageInfoAnnotation(annotationMask, targetMask))
                        continue;
                }
                if (annotation.isRuntimeInvisible() || annotation.isRuntimeTypeInvisible()) {
                    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;
            }
        }

        annotationAttributeOffset = this.contentsOffset;
        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;
                if ((annotation = annotations[i].getPersistibleAnnotation()) == null)
                    continue; // already packaged into container.
                long annotationMask = annotation.resolvedType != null
                        ? annotation.resolvedType.getAnnotationTagBits() & TagBits.AnnotationTargetMASK
                        : 0;
                if (annotationMask != 0 && (annotationMask & targetMask) == 0) {
                    if (!jdk16packageInfoAnnotation(annotationMask, targetMask))
                        continue;
                }
                if (annotation.isRuntimeVisible() || annotation.isRuntimeTypeVisible()) {
                    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;
            }
        }
        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;
                    if ((annotation = annotations[j].getPersistibleAnnotation()) == null)
                        continue; // already packaged into container.
                    long annotationMask = annotation.resolvedType != null
                            ? annotation.resolvedType.getAnnotationTagBits() & TagBits.AnnotationTargetMASK
                            : 0;
                    if (annotationMask != 0 && (annotationMask & TagBits.AnnotationForParameter) == 0)
                        continue;
                    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;
                            if ((annotation = annotations[j].getPersistibleAnnotation()) == null)
                                continue; // already packaged into container.
                            long annotationMask = annotation.resolvedType != null
                                    ? annotation.resolvedType.getAnnotationTagBits() & TagBits.AnnotationTargetMASK
                                    : 0;
                            if (annotationMask != 0 && (annotationMask & TagBits.AnnotationForParameter) == 0)
                                continue;
                            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;
                            if ((annotation = annotations[j].getPersistibleAnnotation()) == null)
                                continue; // already packaged into container.
                            long annotationMask = annotation.resolvedType != null
                                    ? annotation.resolvedType.getAnnotationTagBits() & TagBits.AnnotationTargetMASK
                                    : 0;
                            if (annotationMask != 0 && (annotationMask & TagBits.AnnotationForParameter) == 0)
                                continue;
                            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;
    }

    /**
     * @param annotationContexts the given annotation contexts
     * @param visibleTypeAnnotationsNumber the given number of visible type annotations
     * @param invisibleTypeAnnotationsNumber the given number of invisible type annotations
     * @return the number of attributes created while dumping the annotations in the .class file
     */
    private int generateRuntimeTypeAnnotations(final AnnotationContext[] annotationContexts,
            int visibleTypeAnnotationsNumber, int invisibleTypeAnnotationsNumber) {
        int attributesNumber = 0;
        final int length = annotationContexts.length;

        int visibleTypeAnnotationsCounter = visibleTypeAnnotationsNumber;
        int invisibleTypeAnnotationsCounter = invisibleTypeAnnotationsNumber;
        int annotationAttributeOffset = this.contentsOffset;
        if (invisibleTypeAnnotationsCounter != 0) {
            if (this.contentsOffset + 10 >= this.contents.length) {
                resizeContents(10);
            }
            int runtimeInvisibleAnnotationsAttributeNameIndex = this.constantPool
                    .literalIndex(AttributeNamesConstants.RuntimeInvisibleTypeAnnotationsName);
            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 (invisibleTypeAnnotationsCounter == 0)
                    break loop;
                AnnotationContext annotationContext = annotationContexts[i];
                if ((annotationContext.visibility & AnnotationContext.INVISIBLE) != 0) {
                    int currentAnnotationOffset = this.contentsOffset;
                    generateTypeAnnotation(annotationContext, currentAnnotationOffset);
                    invisibleTypeAnnotationsCounter--;
                    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;
            }
        }

        annotationAttributeOffset = this.contentsOffset;
        if (visibleTypeAnnotationsCounter != 0) {
            if (this.contentsOffset + 10 >= this.contents.length) {
                resizeContents(10);
            }
            int runtimeVisibleAnnotationsAttributeNameIndex = this.constantPool
                    .literalIndex(AttributeNamesConstants.RuntimeVisibleTypeAnnotationsName);
            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 (visibleTypeAnnotationsCounter == 0)
                    break loop;
                AnnotationContext annotationContext = annotationContexts[i];
                if ((annotationContext.visibility & AnnotationContext.VISIBLE) != 0) {
                    visibleTypeAnnotationsCounter--;
                    int currentAnnotationOffset = this.contentsOffset;
                    generateTypeAnnotation(annotationContext, 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;
            }
        }
        return attributesNumber;
    }

    /**
     * @param binding the given method binding
     * @return the number of attributes created while dumping he method's parameters in the .class file (0 or 1)
     */
    private int generateMethodParameters(final MethodBinding binding) {

        if (binding.sourceLambda() != null)
            return 0;
        int initialContentsOffset = this.contentsOffset;
        int length = 0; // count of actual parameters

        AbstractMethodDeclaration methodDeclaration = binding.sourceMethod();

        boolean isConstructor = binding.isConstructor();
        TypeBinding[] targetParameters = binding.parameters;
        ReferenceBinding declaringClass = binding.declaringClass;

        if (declaringClass.isEnum()) {
            if (isConstructor) { // insert String name,int ordinal
                length = writeArgumentName(ConstantPool.EnumName, ClassFileConstants.AccSynthetic, length);
                length = writeArgumentName(ConstantPool.EnumOrdinal, ClassFileConstants.AccSynthetic, length);
            } else if (binding instanceof SyntheticMethodBinding
                    && CharOperation.equals(ConstantPool.ValueOf, binding.selector)) { // insert String name
                length = writeArgumentName(ConstantPool.Name, ClassFileConstants.AccMandated, length);
                targetParameters = Binding.NO_PARAMETERS; // Override "unknown" synthetics below
            }
        }

        boolean needSynthetics = isConstructor && declaringClass.isNestedType();
        if (needSynthetics) {
            // Take into account the synthetic argument names
            // This tracks JLS8, paragraph 8.8.9
            boolean anonymousWithLocalSuper = declaringClass.isAnonymousType()
                    && declaringClass.superclass().isLocalType();
            boolean anonymousWithNestedSuper = declaringClass.isAnonymousType()
                    && declaringClass.superclass().isNestedType();
            boolean isImplicitlyDeclared = ((!declaringClass.isPrivate()) || declaringClass.isAnonymousType())
                    && !anonymousWithLocalSuper;
            ReferenceBinding[] syntheticArgumentTypes = declaringClass.syntheticEnclosingInstanceTypes();
            if (syntheticArgumentTypes != null) {
                for (int i = 0, count = syntheticArgumentTypes.length; i < count; i++) {
                    // This behaviour tracks JLS 15.9.5.1
                    // This covers that the parameter ending up in a nested class must be mandated "on the way in", even if it
                    // isn't the first. The practical relevance of this is questionable, since the constructor call will be
                    // generated by the same constructor.
                    boolean couldForwardToMandated = anonymousWithNestedSuper
                            ? declaringClass.superclass().enclosingType().equals(syntheticArgumentTypes[i])
                            : true;
                    int modifier = couldForwardToMandated && isImplicitlyDeclared ? ClassFileConstants.AccMandated
                            : ClassFileConstants.AccSynthetic;
                    char[] name = CharOperation.concat(TypeConstants.SYNTHETIC_ENCLOSING_INSTANCE_PREFIX,
                            String.valueOf(i).toCharArray()); // cannot use depth, can be identical
                    length = writeArgumentName(name, modifier | ClassFileConstants.AccFinal, length);
                }
            }
            if (binding instanceof SyntheticMethodBinding) {
                targetParameters = ((SyntheticMethodBinding) binding).targetMethod.parameters;
                methodDeclaration = ((SyntheticMethodBinding) binding).targetMethod.sourceMethod();
            }
        }
        if (targetParameters != Binding.NO_PARAMETERS) {
            Argument[] arguments = null;
            if (methodDeclaration != null && methodDeclaration.arguments != null) {
                arguments = methodDeclaration.arguments;
            }
            for (int i = 0, max = targetParameters.length, argumentsLength = arguments != null ? arguments.length
                    : 0; i < max; i++) {
                if (argumentsLength > i && arguments[i] != null) {
                    Argument argument = arguments[i];
                    length = writeArgumentName(argument.name, argument.binding.modifiers, length);
                } else {
                    length = writeArgumentName(null, ClassFileConstants.AccSynthetic, length);
                }
            }
        }
        if (needSynthetics) {
            SyntheticArgumentBinding[] syntheticOuterArguments = declaringClass.syntheticOuterLocalVariables();
            int count = syntheticOuterArguments == null ? 0 : syntheticOuterArguments.length;
            for (int i = 0; i < count; i++) {
                length = writeArgumentName(syntheticOuterArguments[i].name,
                        syntheticOuterArguments[i].modifiers | ClassFileConstants.AccSynthetic, length);
            }
            // move the extra padding arguments of the synthetic constructor invocation to the end
            for (int i = targetParameters.length, extraLength = binding.parameters.length; i < extraLength; i++) {
                TypeBinding parameter = binding.parameters[i];
                length = writeArgumentName(parameter.constantPoolName(), ClassFileConstants.AccSynthetic, length);
            }
        }

        if (length > 0) {
            // so we actually output the parameter
            int attributeLength = 1 + 4 * length; // u1 for count, u2+u2 per parameter
            if (this.contentsOffset + 6 + attributeLength >= this.contents.length) {
                resizeContents(6 + attributeLength);
            }
            int methodParametersNameIndex = this.constantPool
                    .literalIndex(AttributeNamesConstants.MethodParametersName);
            this.contents[initialContentsOffset++] = (byte) (methodParametersNameIndex >> 8);
            this.contents[initialContentsOffset++] = (byte) methodParametersNameIndex;
            this.contents[initialContentsOffset++] = (byte) (attributeLength >> 24);
            this.contents[initialContentsOffset++] = (byte) (attributeLength >> 16);
            this.contents[initialContentsOffset++] = (byte) (attributeLength >> 8);
            this.contents[initialContentsOffset++] = (byte) attributeLength;
            this.contents[initialContentsOffset++] = (byte) length;
            return 1;
        } else {
            return 0;
        }
    }

    private int writeArgumentName(char[] name, int modifiers, int oldLength) {
        int ensureRoomForBytes = 4;
        if (oldLength == 0) {
            // Make room for 
            ensureRoomForBytes += 7;
            this.contentsOffset += 7; // Make room for attribute header + count byte
        }
        if (this.contentsOffset + ensureRoomForBytes > this.contents.length) {
            resizeContents(ensureRoomForBytes);
        }
        int parameterNameIndex = name == null ? 0 : this.constantPool.literalIndex(name);
        this.contents[this.contentsOffset++] = (byte) (parameterNameIndex >> 8);
        this.contents[this.contentsOffset++] = (byte) parameterNameIndex;
        int flags = modifiers
                & (ClassFileConstants.AccFinal | ClassFileConstants.AccSynthetic | ClassFileConstants.AccMandated);
        this.contents[this.contentsOffset++] = (byte) (flags >> 8);
        this.contents[this.contentsOffset++] = (byte) flags;
        return oldLength + 1;
    }

    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, Scope scope) {
        int attributesNumber = 0;
        int localContentsOffset = this.contentsOffset;
        StackMapFrameCodeStream stackMapFrameCodeStream = (StackMapFrameCodeStream) this.codeStream;
        stackMapFrameCodeStream.removeFramePosition(code_length);
        if (stackMapFrameCodeStream.hasFramePositions()) {
            Map frames = new HashMap();
            List realFrames = traverse(isClinit ? null : methodBinding, max_locals, this.contents,
                    codeAttributeOffset + 14, code_length, frames, isClinit, scope);
            int numberOfFrames = realFrames.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) realFrames.get(0);
                for (int j = 1; j < numberOfFrames; j++) {
                    // select next frame
                    currentFrame = (StackMapFrame) realFrames.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, Scope scope) {
        int attributesNumber = 0;
        int localContentsOffset = this.contentsOffset;
        StackMapFrameCodeStream stackMapFrameCodeStream = (StackMapFrameCodeStream) this.codeStream;
        stackMapFrameCodeStream.removeFramePosition(code_length);
        if (stackMapFrameCodeStream.hasFramePositions()) {
            Map frames = new HashMap();
            List realFrames = traverse(isClinit ? null : methodBinding, max_locals, this.contents,
                    codeAttributeOffset + 14, code_length, frames, isClinit, scope);
            int numberOfFrames = realFrames.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) realFrames.get(0);
                StackMapFrame prevFrame = null;
                for (int j = 1; j < numberOfFrames; j++) {
                    // select next frame
                    prevFrame = currentFrame;
                    currentFrame = (StackMapFrame) realFrames.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 void generateTypeAnnotation(AnnotationContext annotationContext, int currentOffset) {
        Annotation annotation = annotationContext.annotation.getPersistibleAnnotation();
        if (annotation == null || annotation.resolvedType == null)
            return;

        int targetType = annotationContext.targetType;

        int[] locations = Annotation.getLocations(annotationContext.typeReference, annotationContext.annotation);

        if (this.contentsOffset + 5 >= this.contents.length) {
            resizeContents(5);
        }
        this.contents[this.contentsOffset++] = (byte) targetType;
        dumpTargetTypeContents(targetType, annotationContext);
        dumpLocations(locations);
        generateAnnotation(annotation, currentOffset);
    }

    private int generateTypeAnnotationAttributeForTypeDeclaration() {
        TypeDeclaration typeDeclaration = this.referenceBinding.scope.referenceContext;
        if ((typeDeclaration.bits & ASTNode.HasTypeAnnotations) == 0) {
            return 0;
        }
        int attributesNumber = 0;
        int visibleTypeAnnotationsCounter = 0;
        int invisibleTypeAnnotationsCounter = 0;
        TypeReference superclass = typeDeclaration.superclass;
        List allTypeAnnotationContexts = new ArrayList();
        if (superclass != null && (superclass.bits & ASTNode.HasTypeAnnotations) != 0) {
            superclass.getAllAnnotationContexts(AnnotationTargetTypeConstants.CLASS_EXTENDS, -1,
                    allTypeAnnotationContexts);
        }
        TypeReference[] superInterfaces = typeDeclaration.superInterfaces;
        if (superInterfaces != null) {
            for (int i = 0; i < superInterfaces.length; i++) {
                TypeReference superInterface = superInterfaces[i];
                if ((superInterface.bits & ASTNode.HasTypeAnnotations) == 0) {
                    continue;
                }
                superInterface.getAllAnnotationContexts(AnnotationTargetTypeConstants.CLASS_EXTENDS, i,
                        allTypeAnnotationContexts);
            }
        }
        TypeParameter[] typeParameters = typeDeclaration.typeParameters;
        if (typeParameters != null) {
            for (int i = 0, max = typeParameters.length; i < max; i++) {
                TypeParameter typeParameter = typeParameters[i];
                if ((typeParameter.bits & ASTNode.HasTypeAnnotations) != 0) {
                    typeParameter.getAllAnnotationContexts(AnnotationTargetTypeConstants.CLASS_TYPE_PARAMETER, i,
                            allTypeAnnotationContexts);
                }
            }
        }
        int size = allTypeAnnotationContexts.size();
        if (size != 0) {
            AnnotationContext[] allTypeAnnotationContextsArray = new AnnotationContext[size];
            allTypeAnnotationContexts.toArray(allTypeAnnotationContextsArray);
            for (int j = 0, max = allTypeAnnotationContextsArray.length; j < max; j++) {
                AnnotationContext annotationContext = allTypeAnnotationContextsArray[j];
                if ((annotationContext.visibility & AnnotationContext.INVISIBLE) != 0) {
                    invisibleTypeAnnotationsCounter++;
                    allTypeAnnotationContexts.add(annotationContext);
                } else {
                    visibleTypeAnnotationsCounter++;
                    allTypeAnnotationContexts.add(annotationContext);
                }
            }
            attributesNumber += generateRuntimeTypeAnnotations(allTypeAnnotationContextsArray,
                    visibleTypeAnnotationsCounter, invisibleTypeAnnotationsCounter);
        }
        return attributesNumber;
    }

    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.header = new byte[INITIAL_HEADER_SIZE];
        this.contents = new byte[members < 15 ? INITIAL_CONTENTS_SIZE : INITIAL_HEADER_SIZE];
    }

    private void initializeHeader(ClassFile parentClassFile, int accessFlags) {
        // 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);
        this.enclosingClassFile = parentClassFile;

        // now we continue to generate the bytes inside the contents array
        this.contents[this.contentsOffset++] = (byte) (accessFlags >> 8);
        this.contents[this.contentsOffset++] = (byte) accessFlags;
    }

    public void initialize(SourceTypeBinding aType, ClassFile parentClassFile, boolean createProblemType) {

        // 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;
        }
        initializeHeader(parentClassFile, accessFlags);
        // innerclasses get their names computed at code gen time

        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;
    }

    public void initializeForModule(ModuleBinding module) {
        initializeHeader(null, ClassFileConstants.AccModule);
        int classNameIndex = this.constantPool.literalIndexForType(TypeConstants.MODULE_INFO_NAME);
        this.contents[this.contentsOffset++] = (byte) (classNameIndex >> 8);
        this.contents[this.contentsOffset++] = (byte) classNameIndex;
        this.codeStream.maxFieldCount = 0;
        // superclass:
        this.contents[this.contentsOffset++] = 0;
        this.contents[this.contentsOffset++] = 0;
        // superInterfacesCount
        this.contents[this.contentsOffset++] = 0;
        this.contents[this.contentsOffset++] = 0;
        // fieldsCount
        this.contents[this.contentsOffset++] = 0;
        this.contents[this.contentsOffset++] = 0;
        // methodsCount
        this.contents[this.contentsOffset++] = 0;
        this.contents[this.contentsOffset++] = 0;
    }

    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(), //$NON-NLS-1$
                            this.referenceBinding.scope.getJavaLangString(), 0, false);
                    localVariableBinding.resolvedPosition = resolvedPosition;
                    this.codeStream.record(localVariableBinding);
                    localVariableBinding.recordInitializationStartPC(0);
                    localVariableBinding.recordInitializationEndPC(codeLength);

                    frame.putLocal(resolvedPosition,
                            new VerificationTypeInfo(this.referenceBinding.scope.getJavaLangString()));
                    resolvedPosition++;

                    localVariableBinding = new LocalVariableBinding(" ordinal".toCharArray(), TypeBinding.INT, 0, //$NON-NLS-1$
                            false);
                    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];
                        // For the branching complexities in the generated $deserializeLambda$ we need the local variable
                        LocalVariableBinding localVariableBinding = new LocalVariableBinding(
                                (" synthetic" + i).toCharArray(), typeBinding, 0, true); //$NON-NLS-1$
                        localVariableBinding.resolvedPosition = i;
                        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++;
                        }
                    }
                }
            }
        }
    }

    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) {
        recordInnerClasses(binding, false);
    }

    public void recordInnerClasses(TypeBinding binding, boolean onBottomForBug445231) {
        if (this.innerClassesBindings == null) {
            this.innerClassesBindings = new HashMap(INNER_CLASSES_SIZE);
        }
        ReferenceBinding innerClass = (ReferenceBinding) binding;
        this.innerClassesBindings.put(innerClass.erasure().unannotated(), onBottomForBug445231); // should not emit yet another inner class for Outer.@Inner Inner.
        ReferenceBinding enclosingType = innerClass.enclosingType();
        while (enclosingType != null && enclosingType.isNestedType()) {
            this.innerClassesBindings.put(enclosingType.erasure().unannotated(), onBottomForBug445231);
            enclosingType = enclosingType.enclosingType();
        }
    }

    public int recordBootstrapMethod(FunctionalExpression expression) {
        if (this.bootstrapMethods == null) {
            this.bootstrapMethods = new ArrayList();
        }
        if (expression instanceof ReferenceExpression) {
            for (int i = 0; i < this.bootstrapMethods.size(); i++) {
                FunctionalExpression fexp = (FunctionalExpression) this.bootstrapMethods.get(i);
                if (fexp.binding == expression.binding
                        && TypeBinding.equalsEquals(fexp.expectedType(), expression.expectedType()))
                    return expression.bootstrapMethodNumber = i;
            }
        }
        this.bootstrapMethods.add(expression);
        // Record which bootstrap method was assigned to the expression
        return expression.bootstrapMethodNumber = this.bootstrapMethods.size() - 1;
    }

    public void reset(/*@Nullable*/SourceTypeBinding typeBinding, CompilerOptions options) {
        // the code stream is reinitialized for each method
        if (typeBinding != null) {
            this.referenceBinding = typeBinding;
            this.isNestedType = typeBinding.isNestedType();
        } else {
            this.referenceBinding = null;
            this.isNestedType = false;
        }
        this.targetJDK = options.targetJDK;
        this.produceAttributes = options.produceDebugAttributes;
        if (this.targetJDK >= ClassFileConstants.JDK1_6) {
            this.produceAttributes |= ClassFileConstants.ATTR_STACK_MAP_TABLE;
            if (this.targetJDK >= ClassFileConstants.JDK1_8) {
                this.produceAttributes |= ClassFileConstants.ATTR_TYPE_ANNOTATION;
                if (!(this.codeStream instanceof TypeAnnotationCodeStream) && this.referenceBinding != null)
                    this.codeStream = new TypeAnnotationCodeStream(this);
                if (options.produceMethodParameters) {
                    this.produceAttributes |= ClassFileConstants.ATTR_METHOD_PARAMETERS;
                }
            }
        } 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();
        }
        if (this.bootstrapMethods != null) {
            this.bootstrapMethods.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 newIllegalArgumentException(methodSignature, index);
        }
    }

    private static IllegalArgumentException newIllegalArgumentException(char[] string, int index) {
        return new IllegalArgumentException("\"" + String.valueOf(string) + "\" at " + index); //$NON-NLS-1$ //$NON-NLS-2$
    }

    /**
     * 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;
    }

    private List filterFakeFrames(Set realJumpTargets, Map frames, int codeLength) {
        // no more frame to generate
        // filter out "fake" frames
        realJumpTargets.remove(Integer.valueOf(codeLength));
        List result = new ArrayList();
        for (Iterator iterator = realJumpTargets.iterator(); iterator.hasNext();) {
            Integer jumpTarget = (Integer) iterator.next();
            StackMapFrame frame = (StackMapFrame) frames.get(jumpTarget);
            if (frame != null) {
                result.add(frame);
            }
        }
        Collections.sort(result, new Comparator() {
            @Override
            public int compare(Object o1, Object o2) {
                StackMapFrame frame = (StackMapFrame) o1;
                StackMapFrame frame2 = (StackMapFrame) o2;
                return frame.pc - frame2.pc;
            }
        });
        return result;
    }

    private TypeBinding getTypeBinding(char[] typeConstantPoolName, Scope scope, boolean checkcast) {
        if (typeConstantPoolName.length == 1) {
            // base type
            switch (typeConstantPoolName[0]) {
            case 'Z':
                return TypeBinding.BOOLEAN;
            case 'B':
                return TypeBinding.BYTE;
            case 'C':
                return TypeBinding.CHAR;
            case 'D':
                return TypeBinding.DOUBLE;
            case 'F':
                return TypeBinding.FLOAT;
            case 'I':
                return TypeBinding.INT;
            case 'J':
                return TypeBinding.LONG;
            case 'S':
                return TypeBinding.SHORT;
            default:
                return null;
            }
        } else if (typeConstantPoolName[0] == '[') {
            int dimensions = getDimensions(typeConstantPoolName);
            if (typeConstantPoolName.length - dimensions == 1) {
                // array of base types
                TypeBinding baseType = null;
                switch (typeConstantPoolName[typeConstantPoolName.length - 1]) {
                case 'Z':
                    baseType = TypeBinding.BOOLEAN;
                    break;
                case 'B':
                    baseType = TypeBinding.BYTE;
                    break;
                case 'C':
                    baseType = TypeBinding.CHAR;
                    break;
                case 'D':
                    baseType = TypeBinding.DOUBLE;
                    break;
                case 'F':
                    baseType = TypeBinding.FLOAT;
                    break;
                case 'I':
                    baseType = TypeBinding.INT;
                    break;
                case 'J':
                    baseType = TypeBinding.LONG;
                    break;
                case 'S':
                    baseType = TypeBinding.SHORT;
                    break;
                case 'V':
                    baseType = TypeBinding.VOID;
                }
                return scope.createArrayType(baseType, dimensions);
            } else {
                // array of object types
                char[] typeName = CharOperation.subarray(typeConstantPoolName, dimensions + 1,
                        typeConstantPoolName.length - 1);
                TypeBinding type = (TypeBinding) scope.getTypeOrPackage(CharOperation.splitOn('/', typeName));
                if (!type.isValidBinding()) {
                    ProblemReferenceBinding problemReferenceBinding = (ProblemReferenceBinding) type;
                    if ((problemReferenceBinding.problemId() & ProblemReasons.InternalNameProvided) != 0) {
                        type = problemReferenceBinding.closestMatch();
                    } else if ((problemReferenceBinding.problemId() & ProblemReasons.NotFound) != 0
                            && this.innerClassesBindings != null) {
                        // check local inner types to see if this is a anonymous type
                        Set<TypeBinding> innerTypeBindings = this.innerClassesBindings.keySet();
                        for (TypeBinding binding : innerTypeBindings) {
                            if (CharOperation.equals(binding.constantPoolName(), typeName)) {
                                type = binding;
                                break;
                            }
                        }
                    }
                }
                return scope.createArrayType(type, dimensions);
            }
        } else {
            char[] typeName = checkcast ? typeConstantPoolName
                    : CharOperation.subarray(typeConstantPoolName, 1, typeConstantPoolName.length - 1);
            TypeBinding type = (TypeBinding) scope.getTypeOrPackage(CharOperation.splitOn('/', typeName));
            if (!type.isValidBinding()) {
                ProblemReferenceBinding problemReferenceBinding = (ProblemReferenceBinding) type;
                if ((problemReferenceBinding.problemId() & ProblemReasons.InternalNameProvided) != 0) {
                    type = problemReferenceBinding.closestMatch();
                } else if ((problemReferenceBinding.problemId() & ProblemReasons.NotFound) != 0
                        && this.innerClassesBindings != null) {
                    // check local inner types to see if this is a anonymous type
                    Set<TypeBinding> innerTypeBindings = this.innerClassesBindings.keySet();
                    for (TypeBinding binding : innerTypeBindings) {
                        if (CharOperation.equals(binding.constantPoolName(), typeName)) {
                            type = binding;
                            break;
                        }
                    }
                }
            }
            return type;
        }
    }

    private TypeBinding getNewTypeBinding(char[] typeConstantPoolName, Scope scope) {
        char[] typeName = typeConstantPoolName;
        TypeBinding type = (TypeBinding) scope.getTypeOrPackage(CharOperation.splitOn('/', typeName));
        if (!type.isValidBinding()) {
            ProblemReferenceBinding problemReferenceBinding = (ProblemReferenceBinding) type;
            if ((problemReferenceBinding.problemId() & ProblemReasons.InternalNameProvided) != 0) {
                type = problemReferenceBinding.closestMatch();
            } else if ((problemReferenceBinding.problemId() & ProblemReasons.NotFound) != 0
                    && this.innerClassesBindings != null) {
                // check local inner types to see if this is a anonymous type
                Set<TypeBinding> innerTypeBindings = this.innerClassesBindings.keySet();
                for (TypeBinding binding : innerTypeBindings) {
                    if (CharOperation.equals(binding.constantPoolName(), typeName)) {
                        type = binding;
                        break;
                    }
                }
            }
        }
        return type;
    }

    private TypeBinding getANewArrayTypeBinding(char[] typeConstantPoolName, Scope scope) {
        if (typeConstantPoolName[0] == '[') {
            int dimensions = getDimensions(typeConstantPoolName);
            if (typeConstantPoolName.length - dimensions == 1) {
                // array of base types
                TypeBinding baseType = null;
                switch (typeConstantPoolName[typeConstantPoolName.length - 1]) {
                case 'Z':
                    baseType = TypeBinding.BOOLEAN;
                    break;
                case 'B':
                    baseType = TypeBinding.BYTE;
                    break;
                case 'C':
                    baseType = TypeBinding.CHAR;
                    break;
                case 'D':
                    baseType = TypeBinding.DOUBLE;
                    break;
                case 'F':
                    baseType = TypeBinding.FLOAT;
                    break;
                case 'I':
                    baseType = TypeBinding.INT;
                    break;
                case 'J':
                    baseType = TypeBinding.LONG;
                    break;
                case 'S':
                    baseType = TypeBinding.SHORT;
                    break;
                case 'V':
                    baseType = TypeBinding.VOID;
                }
                return scope.createArrayType(baseType, dimensions);
            } else {
                // array of object types
                char[] elementTypeClassName = CharOperation.subarray(typeConstantPoolName, dimensions + 1,
                        typeConstantPoolName.length - 1);
                TypeBinding type = (TypeBinding) scope
                        .getTypeOrPackage(CharOperation.splitOn('/', elementTypeClassName));
                if (!type.isValidBinding()) {
                    ProblemReferenceBinding problemReferenceBinding = (ProblemReferenceBinding) type;
                    if ((problemReferenceBinding.problemId() & ProblemReasons.InternalNameProvided) != 0) {
                        type = problemReferenceBinding.closestMatch();
                    } else if ((problemReferenceBinding.problemId() & ProblemReasons.NotFound) != 0
                            && this.innerClassesBindings != null) {
                        // check local inner types to see if this is a anonymous type
                        Set<TypeBinding> innerTypeBindings = this.innerClassesBindings.keySet();
                        for (TypeBinding binding : innerTypeBindings) {
                            if (CharOperation.equals(binding.constantPoolName(), elementTypeClassName)) {
                                type = binding;
                                break;
                            }
                        }
                    }
                }
                return scope.createArrayType(type, dimensions);
            }
        } else {
            TypeBinding type = (TypeBinding) scope
                    .getTypeOrPackage(CharOperation.splitOn('/', typeConstantPoolName));
            if (!type.isValidBinding()) {
                ProblemReferenceBinding problemReferenceBinding = (ProblemReferenceBinding) type;
                if ((problemReferenceBinding.problemId() & ProblemReasons.InternalNameProvided) != 0) {
                    type = problemReferenceBinding.closestMatch();
                } else if ((problemReferenceBinding.problemId() & ProblemReasons.NotFound) != 0
                        && this.innerClassesBindings != null) {
                    // check local inner types to see if this is a anonymous type
                    Set<TypeBinding> innerTypeBindings = this.innerClassesBindings.keySet();
                    for (TypeBinding binding : innerTypeBindings) {
                        if (CharOperation.equals(binding.constantPoolName(), typeConstantPoolName)) {
                            type = binding;
                            break;
                        }
                    }
                }
            }
            return type;
        }
    }

    public List traverse(MethodBinding methodBinding, int maxLocals, byte[] bytecodes, int codeOffset,
            int codeLength, Map<Integer, StackMapFrame> frames, boolean isClinit, Scope scope) {
        Set realJumpTarget = new HashSet();

        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 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;
        add(frames, frame.duplicate(), scope);
        addRealJumpTarget(realJumpTarget, -1);
        for (int i = 0, max = this.codeStream.exceptionLabelsCounter; i < max; i++) {
            ExceptionLabel exceptionLabel = this.codeStream.exceptionLabels[i];
            if (exceptionLabel != null) {
                addRealJumpTarget(realJumpTarget, exceptionLabel.position);
            }
        }
        while (true) {
            int currentPC = pc - codeOffset;
            if (hasExceptionMarkers && exceptionMarker.pc == currentPC) {
                frame.numberOfStackItems = 0;
                frame.addStackItem(new VerificationTypeInfo(exceptionMarker.getBinding()));
                indexInExceptionMarkers++;
                if (indexInExceptionMarkers < exceptionsMarkersLength) {
                    exceptionMarker = exceptionMarkers[indexInExceptionMarkers];
                } else {
                    hasExceptionMarkers = false;
                }
            }
            if (currentFramePosition < currentPC) {
                do {
                    indexInFramePositions++;
                    if (indexInFramePositions < framePositionsLength) {
                        currentFramePosition = framePositions[indexInFramePositions];
                    } else {
                        currentFramePosition = Integer.MAX_VALUE;
                    }
                } while (currentFramePosition < currentPC);
            }
            if (currentFramePosition == currentPC) {
                // need to build a new frame and create a stack map attribute entry
                StackMapFrame currentFrame = frames.get(Integer.valueOf(currentPC));
                if (currentFrame == null) {
                    currentFrame = createNewFrame(currentPC, frame, isClinit, methodBinding);
                    add(frames, currentFrame, scope);
                } else {
                    frame = currentFrame.merge(frame.duplicate(), scope).duplicate();
                }
                indexInFramePositions++;
                if (indexInFramePositions < framePositionsLength) {
                    currentFramePosition = framePositions[indexInFramePositions];
                } else {
                    currentFramePosition = Integer.MAX_VALUE;
                }
            }
            byte opcode = (byte) u1At(bytecodes, 0, pc);
            switch (opcode) {
            case Opcodes.OPC_nop:
                pc++;
                break;
            case Opcodes.OPC_aconst_null:
                frame.addStackItem(new VerificationTypeInfo(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(new VerificationTypeInfo(TypeBinding.INT));
                pc++;
                break;
            case Opcodes.OPC_lconst_0:
            case Opcodes.OPC_lconst_1:
                frame.addStackItem(new VerificationTypeInfo(TypeBinding.LONG));
                pc++;
                break;
            case Opcodes.OPC_fconst_0:
            case Opcodes.OPC_fconst_1:
            case Opcodes.OPC_fconst_2:
                frame.addStackItem(new VerificationTypeInfo(TypeBinding.FLOAT));
                pc++;
                break;
            case Opcodes.OPC_dconst_0:
            case Opcodes.OPC_dconst_1:
                frame.addStackItem(new VerificationTypeInfo(TypeBinding.DOUBLE));
                pc++;
                break;
            case Opcodes.OPC_bipush:
                frame.addStackItem(new VerificationTypeInfo(TypeBinding.BYTE));
                pc += 2;
                break;
            case Opcodes.OPC_sipush:
                frame.addStackItem(new VerificationTypeInfo(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(scope.getJavaLangString()));
                    break;
                case ClassFileConstants.IntegerTag:
                    frame.addStackItem(new VerificationTypeInfo(TypeBinding.INT));
                    break;
                case ClassFileConstants.FloatTag:
                    frame.addStackItem(new VerificationTypeInfo(TypeBinding.FLOAT));
                    break;
                case ClassFileConstants.ClassTag:
                    frame.addStackItem(new VerificationTypeInfo(scope.getJavaLangClass()));
                }
                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(scope.getJavaLangString()));
                    break;
                case ClassFileConstants.IntegerTag:
                    frame.addStackItem(new VerificationTypeInfo(TypeBinding.INT));
                    break;
                case ClassFileConstants.FloatTag:
                    frame.addStackItem(new VerificationTypeInfo(TypeBinding.FLOAT));
                    break;
                case ClassFileConstants.ClassTag:
                    frame.addStackItem(new VerificationTypeInfo(scope.getJavaLangClass()));
                }
                pc += 3;
                break;
            case Opcodes.OPC_ldc2_w:
                index = u2At(bytecodes, 1, pc);
                switch (u1At(poolContents, 0, constantPoolOffsets[index])) {
                case ClassFileConstants.DoubleTag:
                    frame.addStackItem(new VerificationTypeInfo(TypeBinding.DOUBLE));
                    break;
                case ClassFileConstants.LongTag:
                    frame.addStackItem(new VerificationTypeInfo(TypeBinding.LONG));
                    break;
                }
                pc += 3;
                break;
            case Opcodes.OPC_iload:
                frame.addStackItem(new VerificationTypeInfo(TypeBinding.INT));
                pc += 2;
                break;
            case Opcodes.OPC_lload:
                frame.addStackItem(new VerificationTypeInfo(TypeBinding.LONG));
                pc += 2;
                break;
            case Opcodes.OPC_fload:
                frame.addStackItem(new VerificationTypeInfo(TypeBinding.FLOAT));
                pc += 2;
                break;
            case Opcodes.OPC_dload:
                frame.addStackItem(new VerificationTypeInfo(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(new VerificationTypeInfo(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(new VerificationTypeInfo(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(new VerificationTypeInfo(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(new VerificationTypeInfo(TypeBinding.DOUBLE));
                pc++;
                break;
            case Opcodes.OPC_aload_0:
                VerificationTypeInfo locals0 = frame.locals[0];
                if (locals0 == null || 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(new VerificationTypeInfo(TypeBinding.INT));
                pc++;
                break;
            case Opcodes.OPC_laload:
                frame.numberOfStackItems -= 2;
                frame.addStackItem(new VerificationTypeInfo(TypeBinding.LONG));
                pc++;
                break;
            case Opcodes.OPC_faload:
                frame.numberOfStackItems -= 2;
                frame.addStackItem(new VerificationTypeInfo(TypeBinding.FLOAT));
                pc++;
                break;
            case Opcodes.OPC_daload:
                frame.numberOfStackItems -= 2;
                frame.addStackItem(new VerificationTypeInfo(TypeBinding.DOUBLE));
                pc++;
                break;
            case Opcodes.OPC_aaload:
                frame.numberOfStackItems--;
                frame.replaceWithElementType();
                pc++;
                break;
            case Opcodes.OPC_baload:
                frame.numberOfStackItems -= 2;
                frame.addStackItem(new VerificationTypeInfo(TypeBinding.BYTE));
                pc++;
                break;
            case Opcodes.OPC_caload:
                frame.numberOfStackItems -= 2;
                frame.addStackItem(new VerificationTypeInfo(TypeBinding.CHAR));
                pc++;
                break;
            case Opcodes.OPC_saload:
                frame.numberOfStackItems -= 2;
                frame.addStackItem(new VerificationTypeInfo(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(new VerificationTypeInfo(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--;
                int jumpPC = currentPC + i2At(bytecodes, 1, pc);
                addRealJumpTarget(realJumpTarget, jumpPC, frames,
                        createNewFrame(jumpPC, frame, isClinit, methodBinding), scope);
                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;
                jumpPC = currentPC + i2At(bytecodes, 1, pc);
                addRealJumpTarget(realJumpTarget, jumpPC, frames,
                        createNewFrame(jumpPC, frame, isClinit, methodBinding), scope);
                pc += 3;
                break;
            case Opcodes.OPC_goto:
                jumpPC = currentPC + i2At(bytecodes, 1, pc);
                addRealJumpTarget(realJumpTarget, jumpPC, frames,
                        createNewFrame(jumpPC, frame, isClinit, methodBinding), scope);
                pc += 3;
                addRealJumpTarget(realJumpTarget, pc - codeOffset);
                break;
            case Opcodes.OPC_tableswitch:
                frame.numberOfStackItems--;
                pc++;
                while (((pc - codeOffset) & 0x03) != 0) {
                    pc++;
                }
                // default offset
                jumpPC = currentPC + i4At(bytecodes, 0, pc);
                addRealJumpTarget(realJumpTarget, jumpPC, frames,
                        createNewFrame(jumpPC, frame, isClinit, methodBinding), scope);
                pc += 4; // default
                int low = i4At(bytecodes, 0, pc);
                pc += 4;
                int high = i4At(bytecodes, 0, pc);
                pc += 4;
                int length = high - low + 1;
                for (int i = 0; i < length; i++) {
                    // pair offset
                    jumpPC = currentPC + i4At(bytecodes, 0, pc);
                    addRealJumpTarget(realJumpTarget, jumpPC, frames,
                            createNewFrame(jumpPC, frame, isClinit, methodBinding), scope);
                    pc += 4;
                }
                break;
            case Opcodes.OPC_lookupswitch:
                frame.numberOfStackItems--;
                pc++;
                while (((pc - codeOffset) & 0x03) != 0) {
                    pc++;
                }
                jumpPC = currentPC + i4At(bytecodes, 0, pc);
                addRealJumpTarget(realJumpTarget, jumpPC, frames,
                        createNewFrame(jumpPC, frame, isClinit, methodBinding), scope);
                pc += 4; // default offset
                int npairs = (int) u4At(bytecodes, 0, pc);
                pc += 4; // npair value
                for (int i = 0; i < npairs; i++) {
                    pc += 4; // case value
                    // pair offset
                    jumpPC = currentPC + i4At(bytecodes, 0, pc);
                    addRealJumpTarget(realJumpTarget, jumpPC, frames,
                            createNewFrame(jumpPC, frame, isClinit, methodBinding), scope);
                    pc += 4;
                }
                break;
            case Opcodes.OPC_ireturn:
            case Opcodes.OPC_lreturn:
            case Opcodes.OPC_freturn:
            case Opcodes.OPC_dreturn:
            case Opcodes.OPC_areturn:
                frame.numberOfStackItems--;
                pc++;
                addRealJumpTarget(realJumpTarget, pc - codeOffset);
                break;
            case Opcodes.OPC_return:
                pc++;
                addRealJumpTarget(realJumpTarget, pc - codeOffset);
                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]));
                TypeBinding typeBinding = getTypeBinding(descriptor, scope, false);
                if (typeBinding != null) {
                    frame.addStackItem(new VerificationTypeInfo(typeBinding));
                }
                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--;
                typeBinding = getTypeBinding(descriptor, scope, false);
                if (typeBinding != null) {
                    frame.addStackItem(new VerificationTypeInfo(typeBinding));
                }
                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);
                typeBinding = getTypeBinding(returnType, scope, false);
                if (typeBinding != null) {
                    frame.addStackItem(new VerificationTypeInfo(typeBinding));
                }
                pc += 3;
                break;
            case Opcodes.OPC_invokedynamic:
                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 -= getParametersCount(descriptor);
                returnType = getReturnType(descriptor);
                typeBinding = getTypeBinding(returnType, scope, false);
                if (typeBinding != null) {
                    frame.addStackItem(new VerificationTypeInfo(typeBinding));
                }
                pc += 5;
                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);
                typeBinding = getTypeBinding(returnType, scope, false);
                if (typeBinding != null) {
                    frame.addStackItem(new VerificationTypeInfo(typeBinding));
                }
                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);
                typeBinding = getTypeBinding(returnType, scope, false);
                if (typeBinding != null) {
                    frame.addStackItem(new VerificationTypeInfo(typeBinding));
                }
                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);
                typeBinding = getTypeBinding(returnType, scope, false);
                if (typeBinding != null) {
                    frame.addStackItem(new VerificationTypeInfo(typeBinding));
                }
                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]));
                typeBinding = getNewTypeBinding(className, scope);
                VerificationTypeInfo verificationTypeInfo = new VerificationTypeInfo(
                        VerificationTypeInfo.ITEM_UNINITIALIZED, typeBinding);
                verificationTypeInfo.offset = currentPC;
                frame.addStackItem(verificationTypeInfo);
                pc += 3;
                break;
            case Opcodes.OPC_newarray:
                TypeBinding arrayType = null;
                switch (u1At(bytecodes, 1, pc)) {
                case ClassFileConstants.INT_ARRAY:
                    arrayType = scope.createArrayType(TypeBinding.INT, 1);
                    break;
                case ClassFileConstants.BYTE_ARRAY:
                    arrayType = scope.createArrayType(TypeBinding.BYTE, 1);
                    break;
                case ClassFileConstants.BOOLEAN_ARRAY:
                    arrayType = scope.createArrayType(TypeBinding.BOOLEAN, 1);
                    break;
                case ClassFileConstants.SHORT_ARRAY:
                    arrayType = scope.createArrayType(TypeBinding.SHORT, 1);
                    break;
                case ClassFileConstants.CHAR_ARRAY:
                    arrayType = scope.createArrayType(TypeBinding.CHAR, 1);
                    break;
                case ClassFileConstants.LONG_ARRAY:
                    arrayType = scope.createArrayType(TypeBinding.LONG, 1);
                    break;
                case ClassFileConstants.FLOAT_ARRAY:
                    arrayType = scope.createArrayType(TypeBinding.FLOAT, 1);
                    break;
                case ClassFileConstants.DOUBLE_ARRAY:
                    arrayType = scope.createArrayType(TypeBinding.DOUBLE, 1);
                    break;
                }
                frame.stackItems[frame.numberOfStackItems - 1] = new VerificationTypeInfo(arrayType);
                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]));
                frame.numberOfStackItems--;
                typeBinding = getANewArrayTypeBinding(className, scope);
                if (typeBinding != null) {
                    if (typeBinding.isArrayType()) {
                        ArrayBinding arrayBinding = (ArrayBinding) typeBinding;
                        frame.addStackItem(new VerificationTypeInfo(scope
                                .createArrayType(arrayBinding.leafComponentType(), arrayBinding.dimensions + 1)));
                    } else {
                        frame.addStackItem(new VerificationTypeInfo(scope.createArrayType(typeBinding, 1)));
                    }
                }
                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++;
                addRealJumpTarget(realJumpTarget, pc - codeOffset);
                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]));
                typeBinding = getTypeBinding(className, scope, true);
                if (typeBinding != null) {
                    frame.stackItems[frame.numberOfStackItems - 1] = new VerificationTypeInfo(typeBinding);
                }
                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(new VerificationTypeInfo(TypeBinding.INT));
                        break;
                    case Opcodes.OPC_fload:
                        frame.addStackItem(new VerificationTypeInfo(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(new VerificationTypeInfo(TypeBinding.LONG));
                        break;
                    case Opcodes.OPC_dload:
                        frame.addStackItem(new VerificationTypeInfo(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;
                // class name is already the name of the right array type with all dimensions
                typeBinding = getTypeBinding(className, scope, false);
                if (typeBinding != null) {
                    frame.addStackItem(new VerificationTypeInfo(typeBinding));
                }
                pc += 4;
                break;
            case Opcodes.OPC_ifnull:
            case Opcodes.OPC_ifnonnull:
                frame.numberOfStackItems--;
                jumpPC = currentPC + i2At(bytecodes, 1, pc);
                addRealJumpTarget(realJumpTarget, jumpPC, frames,
                        createNewFrame(jumpPC, frame, isClinit, methodBinding), scope);
                pc += 3;
                break;
            case Opcodes.OPC_goto_w:
                jumpPC = currentPC + i4At(bytecodes, 1, pc);
                addRealJumpTarget(realJumpTarget, jumpPC, frames,
                        createNewFrame(jumpPC, frame, isClinit, methodBinding), scope);
                pc += 5;
                addRealJumpTarget(realJumpTarget, pc - codeOffset); // handle infinite loop
                break;
            default: // should not occur
                if (this.codeStream.methodDeclaration != null) {
                    this.codeStream.methodDeclaration.scope.problemReporter().abortDueToInternalError(
                            Messages.bind(Messages.abort_invalidOpcode,
                                    new Object[] { Byte.valueOf(opcode), Integer.valueOf(pc),
                                            new String(methodBinding.shortReadableName()), }),
                            this.codeStream.methodDeclaration);
                } else {
                    this.codeStream.lambdaExpression.scope.problemReporter().abortDueToInternalError(
                            Messages.bind(Messages.abort_invalidOpcode,
                                    new Object[] { Byte.valueOf(opcode), Integer.valueOf(pc),
                                            new String(methodBinding.shortReadableName()), }),
                            this.codeStream.lambdaExpression);
                }
                break;
            }
            if (pc >= (codeLength + codeOffset)) {
                break;
            }
        }
        return filterFakeFrames(realJumpTarget, frames, codeLength);
    }

    private StackMapFrame createNewFrame(int currentPC, StackMapFrame frame, boolean isClinit,
            MethodBinding methodBinding) {
        StackMapFrame newFrame = frame.duplicate();
        newFrame.pc = currentPC;
        // initialize locals
        initializeLocals(isClinit ? true : methodBinding.isStatic(), currentPC, newFrame);
        return newFrame;
    }

    private int getDimensions(char[] returnType) {
        int dimensions = 0;
        while (returnType[dimensions] == '[') {
            dimensions++;
        }
        return dimensions;
    }

    private void addRealJumpTarget(Set realJumpTarget, int pc) {
        realJumpTarget.add(Integer.valueOf(pc));
    }

    private void addRealJumpTarget(Set realJumpTarget, int pc, Map frames, StackMapFrame frame, Scope scope) {
        realJumpTarget.add(Integer.valueOf(pc));
        add(frames, frame, scope);
    }

    private void add(Map<Integer, StackMapFrame> frames, StackMapFrame frame, Scope scope) {
        Integer key = Integer.valueOf(frame.pc);
        StackMapFrame existingFrame = frames.get(key);
        if (existingFrame == null) {
            frames.put(key, frame);
        } else {
            // we need to merge
            frames.put(key, existingFrame.merge(frame, scope));
        }
    }

    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));
    }

    private final int i2At(byte[] reference, int relativeOffset, int structOffset) {
        int position = relativeOffset + structOffset;
        return (reference[position++] << 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;
    }
}