    001/*
    002 *  Licensed to the Apache Software Foundation (ASF) under one or more
    003 *  contributor license agreements.  See the NOTICE file distributed with
    004 *  this work for additional information regarding copyright ownership.
    005 *  The ASF licenses this file to You under the Apache License, Version 2.0
    006 *  (the "License"); you may not use this file except in compliance with
    007 *  the License.  You may obtain a copy of the License at
    008 *
    009 *     http://www.apache.org/licenses/LICENSE-2.0
    010 *
    011 *  Unless required by applicable law or agreed to in writing, software
    012 *  distributed under the License is distributed on an "AS IS" BASIS,
    013 *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    014 *  See the License for the specific language governing permissions and
    015 *  limitations under the License.
    016 */
    017package org.apache.commons.compress.harmony.pack200;
    018
    019import java.io.IOException;
    020import java.io.OutputStream;
    021import java.util.ArrayList;
    022import java.util.Collections;
    023import java.util.Comparator;
    024import java.util.HashMap;
    025import java.util.HashSet;
    026import java.util.Iterator;
    027import java.util.List;
    028import java.util.Map;
    029import java.util.Set;
    030
    031import org.apache.commons.compress.harmony.pack200.AttributeDefinitionBands.AttributeDefinition;
    032import org.apache.commons.compress.harmony.pack200.IcBands.IcTuple;
    033import org.objectweb.asm.Label;
    034import org.objectweb.asm.Opcodes;
    035
    036/**
    037 * Class bands (corresponds to the <code>class_bands</code> set of bands in the pack200 specification)
    038 */
    039public class ClassBands extends BandSet {
    040
    041    private final CpBands cpBands;
    042    private final AttributeDefinitionBands attrBands;
    043
    044    private final CPClass[] class_this;
    045    private final CPClass[] class_super;
    046    private final CPClass[][] class_interface;
    047    private final int[] class_interface_count;
    048
    049    private final int[] major_versions;
    050
    051    private final long[] class_flags;
    052    private int[] class_attr_calls;
    053    private final List classSourceFile = new ArrayList();
    054    private final List classEnclosingMethodClass = new ArrayList();
    055    private final List classEnclosingMethodDesc = new ArrayList();
    056    private final List classSignature = new ArrayList();
    057
    058    private final IntList classFileVersionMinor = new IntList();
    059    private final IntList classFileVersionMajor = new IntList();
    060
    061    private final int[] class_field_count;
    062    private final CPNameAndType[][] field_descr;
    063    private final long[][] field_flags;
    064    private int[] field_attr_calls;
    065    private final List fieldConstantValueKQ = new ArrayList();
    066    private final List fieldSignature = new ArrayList();
    067
    068    private final int[] class_method_count;
    069    private final CPNameAndType[][] method_descr;
    070    private final long[][] method_flags;
    071    private int[] method_attr_calls;
    072    private final List methodSignature = new ArrayList();
    073    private final IntList methodExceptionNumber = new IntList();
    074    private final List methodExceptionClasses = new ArrayList();
    075
    076    private int[] codeHeaders;
    077    private final IntList codeMaxStack = new IntList();
    078    private final IntList codeMaxLocals = new IntList();
    079    private final IntList codeHandlerCount = new IntList();
    080    private final List codeHandlerStartP = new ArrayList();
    081    private final List codeHandlerEndPO = new ArrayList();
    082    private final List codeHandlerCatchPO = new ArrayList();
    083    private final List codeHandlerClass = new ArrayList();
    084    private final List codeFlags = new ArrayList();
    085    private int[] code_attr_calls;
    086    private final IntList codeLineNumberTableN = new IntList();
    087    private final List codeLineNumberTableBciP = new ArrayList();
    088    private final IntList codeLineNumberTableLine = new IntList();
    089    private final IntList codeLocalVariableTableN = new IntList();
    090    private final List codeLocalVariableTableBciP = new ArrayList();
    091    private final List codeLocalVariableTableSpanO = new ArrayList();
    092    private final List codeLocalVariableTableNameRU = new ArrayList();
    093    private final List codeLocalVariableTableTypeRS = new ArrayList();
    094    private final IntList codeLocalVariableTableSlot = new IntList();
    095    private final IntList codeLocalVariableTypeTableN = new IntList();
    096    private final List codeLocalVariableTypeTableBciP = new ArrayList();
    097    private final List codeLocalVariableTypeTableSpanO = new ArrayList();
    098    private final List codeLocalVariableTypeTableNameRU = new ArrayList();
    099    private final List codeLocalVariableTypeTableTypeRS = new ArrayList();
    100    private final IntList codeLocalVariableTypeTableSlot = new IntList();
    101
    102    private final MetadataBandGroup class_RVA_bands;
    103    private final MetadataBandGroup class_RIA_bands;
    104    private final MetadataBandGroup field_RVA_bands;
    105    private final MetadataBandGroup field_RIA_bands;
    106    private final MetadataBandGroup method_RVA_bands;
    107    private final MetadataBandGroup method_RIA_bands;
    108    private final MetadataBandGroup method_RVPA_bands;
    109    private final MetadataBandGroup method_RIPA_bands;
    110    private final MetadataBandGroup method_AD_bands;
    111
    112    private final List classAttributeBands = new ArrayList();
    113    private final List methodAttributeBands = new ArrayList();
    114    private final List fieldAttributeBands = new ArrayList();
    115    private final List codeAttributeBands = new ArrayList();
    116
    117    private final List tempFieldFlags = new ArrayList();
    118    private final List tempFieldDesc = new ArrayList();
    119    private final List tempMethodFlags = new ArrayList();
    120    private final List tempMethodDesc = new ArrayList();
    121    private TempParamAnnotation tempMethodRVPA;
    122    private TempParamAnnotation tempMethodRIPA;
    123
    124    private boolean anySyntheticClasses = false;
    125    private boolean anySyntheticFields = false;
    126    private boolean anySyntheticMethods = false;
    127    private final Segment segment;
    128
    129    private final Map classReferencesInnerClass = new HashMap();
    130    private final boolean stripDebug;
    131
    132    private int index = 0;
    133
    134    private int numMethodArgs = 0;
    135    private int[] class_InnerClasses_N;
    136    private CPClass[] class_InnerClasses_RC;
    137    private int[] class_InnerClasses_F;
    138    private List classInnerClassesOuterRCN;
    139    private List classInnerClassesNameRUN;
    140
    141    public ClassBands(final Segment segment, final int numClasses, final int effort, final boolean stripDebug)
    142        throws IOException {
    143        super(effort, segment.getSegmentHeader());
    144        this.stripDebug = stripDebug;
    145        this.segment = segment;
    146        this.cpBands = segment.getCpBands();
    147        this.attrBands = segment.getAttrBands();
    148        class_this = new CPClass[numClasses];
    149        class_super = new CPClass[numClasses];
    150        class_interface_count = new int[numClasses];
    151        class_interface = new CPClass[numClasses][];
    152        class_field_count = new int[numClasses];
    153        class_method_count = new int[numClasses];
    154        field_descr = new CPNameAndType[numClasses][];
    155        field_flags = new long[numClasses][];
    156        method_descr = new CPNameAndType[numClasses][];
    157        method_flags = new long[numClasses][];
    158        for (int i = 0; i < numClasses; i++) {
    159            field_flags[i] = new long[0];
    160            method_flags[i] = new long[0];
    161        }
    162        // minor_versions = new int[numClasses];
    163        major_versions = new int[numClasses];
    164        class_flags = new long[numClasses];
    165
    166        class_RVA_bands = new MetadataBandGroup("RVA", MetadataBandGroup.CONTEXT_CLASS, cpBands, segmentHeader, effort);
    167        class_RIA_bands = new MetadataBandGroup("RIA", MetadataBandGroup.CONTEXT_CLASS, cpBands, segmentHeader, effort);
    168        field_RVA_bands = new MetadataBandGroup("RVA", MetadataBandGroup.CONTEXT_FIELD, cpBands, segmentHeader, effort);
    169        field_RIA_bands = new MetadataBandGroup("RIA", MetadataBandGroup.CONTEXT_FIELD, cpBands, segmentHeader, effort);
    170        method_RVA_bands = new MetadataBandGroup("RVA", MetadataBandGroup.CONTEXT_METHOD, cpBands, segmentHeader,
    171            effort);
    172        method_RIA_bands = new MetadataBandGroup("RIA", MetadataBandGroup.CONTEXT_METHOD, cpBands, segmentHeader,
    173            effort);
    174        method_RVPA_bands = new MetadataBandGroup("RVPA", MetadataBandGroup.CONTEXT_METHOD, cpBands, segmentHeader,
    175            effort);
    176        method_RIPA_bands = new MetadataBandGroup("RIPA", MetadataBandGroup.CONTEXT_METHOD, cpBands, segmentHeader,
    177            effort);
    178        method_AD_bands = new MetadataBandGroup("AD", MetadataBandGroup.CONTEXT_METHOD, cpBands, segmentHeader, effort);
    179
    180        createNewAttributeBands();
    181    }
    182
    183    private void createNewAttributeBands() throws IOException {
    184        final List classAttributeLayouts = attrBands.getClassAttributeLayouts();
    185        for (final Iterator iterator = classAttributeLayouts.iterator(); iterator.hasNext();) {
    186            final AttributeDefinition def = (AttributeDefinition) iterator.next();
    187            classAttributeBands.add(new NewAttributeBands(effort, cpBands, segment.getSegmentHeader(), def));
    188        }
    189        final List methodAttributeLayouts = attrBands.getMethodAttributeLayouts();
    190        for (final Iterator iterator = methodAttributeLayouts.iterator(); iterator.hasNext();) {
    191            final AttributeDefinition def = (AttributeDefinition) iterator.next();
    192            methodAttributeBands.add(new NewAttributeBands(effort, cpBands, segment.getSegmentHeader(), def));
    193        }
    194        final List fieldAttributeLayouts = attrBands.getFieldAttributeLayouts();
    195        for (final Iterator iterator = fieldAttributeLayouts.iterator(); iterator.hasNext();) {
    196            final AttributeDefinition def = (AttributeDefinition) iterator.next();
    197            fieldAttributeBands.add(new NewAttributeBands(effort, cpBands, segment.getSegmentHeader(), def));
    198        }
    199        final List codeAttributeLayouts = attrBands.getCodeAttributeLayouts();
    200        for (final Iterator iterator = codeAttributeLayouts.iterator(); iterator.hasNext();) {
    201            final AttributeDefinition def = (AttributeDefinition) iterator.next();
    202            codeAttributeBands.add(new NewAttributeBands(effort, cpBands, segment.getSegmentHeader(), def));
    203        }
    204    }
    205
    206    public void addClass(final int major, int flags, final String className, final String signature,
    207        final String superName, final String[] interfaces) {
    208        class_this[index] = cpBands.getCPClass(className);
    209        class_super[index] = cpBands.getCPClass(superName);
    210        class_interface_count[index] = interfaces.length;
    211        class_interface[index] = new CPClass[interfaces.length];
    212        for (int i = 0; i < interfaces.length; i++) {
    213            class_interface[index][i] = cpBands.getCPClass(interfaces[i]);
    214        }
    215        major_versions[index] = major;
    216        class_flags[index] = flags;
    217        if (!anySyntheticClasses && ((flags & (1 << 12)) != 0)
    218            && segment.getCurrentClassReader().hasSyntheticAttributes()) {
    219            cpBands.addCPUtf8("Synthetic");
    220            anySyntheticClasses = true;
    221        }
    222        if ((flags & Opcodes.ACC_DEPRECATED) != 0) { // ASM uses (1<<17) flag for deprecated
    223            flags = flags & ~Opcodes.ACC_DEPRECATED;
    224            flags = flags | (1 << 20);
    225        }
    226        if (signature != null) {
    227            class_flags[index] |= (1 << 19);
    228            classSignature.add(cpBands.getCPSignature(signature));
    229        }
    230    }
    231
    232    public void currentClassReferencesInnerClass(final CPClass inner) {
    233        if (!(index >= class_this.length)) {
    234            final CPClass currentClass = class_this[index];
    235            if (currentClass != null && !currentClass.equals(inner)
    236                && !isInnerClassOf(currentClass.toString(), inner)) {
    237                Set referencedInnerClasses = (Set) classReferencesInnerClass.get(currentClass);
    238                if (referencedInnerClasses == null) {
    239                    referencedInnerClasses = new HashSet();
    240                    classReferencesInnerClass.put(currentClass, referencedInnerClasses);
    241                }
    242                referencedInnerClasses.add(inner);
    243            }
    244        }
    245    }
    246
    247    private boolean isInnerClassOf(final String possibleInner, final CPClass possibleOuter) {
    248        if (isInnerClass(possibleInner)) {
    249            final String superClassName = possibleInner.substring(0, possibleInner.lastIndexOf('$'));
    250            if (superClassName.equals(possibleOuter.toString())) {
    251                return true;
    252            }
    253            return isInnerClassOf(superClassName, possibleOuter);
    254        }
    255        return false;
    256    }
    257
    258    private boolean isInnerClass(final String possibleInner) {
    259        return possibleInner.indexOf('$') != -1;
    260    }
    261
    262    public void addField(int flags, final String name, final String desc, final String signature, final Object value) {
    263        flags = flags & 0xFFFF;
    264        tempFieldDesc.add(cpBands.getCPNameAndType(name, desc));
    265        if (signature != null) {
    266            fieldSignature.add(cpBands.getCPSignature(signature));
    267            flags |= (1 << 19);
    268        }
    269        if ((flags & Opcodes.ACC_DEPRECATED) != 0) { // ASM uses (1<<17) flag for deprecated
    270            flags = flags & ~Opcodes.ACC_DEPRECATED;
    271            flags = flags | (1 << 20);
    272        }
    273        if (value != null) {
    274            fieldConstantValueKQ.add(cpBands.getConstant(value));
    275            flags |= (1 << 17);
    276        }
    277        if (!anySyntheticFields && ((flags & (1 << 12)) != 0)
    278            && segment.getCurrentClassReader().hasSyntheticAttributes()) {
    279            cpBands.addCPUtf8("Synthetic");
    280            anySyntheticFields = true;
    281        }
    282        tempFieldFlags.add(Long.valueOf(flags));
    283    }
    284
    285    /**
    286     * All input classes for the segment have now been read in, so this method is called so that this class can
    287     * calculate/complete anything it could not do while classes were being read.
    288     */
    289    public void finaliseBands() {
    290        final int defaultMajorVersion = segmentHeader.getDefaultMajorVersion();
    291        for (int i = 0; i < class_flags.length; i++) {
    292            final int major = major_versions[i];
    293            if (major != defaultMajorVersion) {
    294                class_flags[i] |= 1 << 24;
    295                classFileVersionMajor.add(major);
    296                classFileVersionMinor.add(0);
    297            }
    298        }
    299        // Calculate code headers
    300        codeHeaders = new int[codeHandlerCount.size()];
    301        int removed = 0;
    302        for (int i = 0; i < codeHeaders.length; i++) {
    303            final int numHandlers = codeHandlerCount.get(i - removed);
    304            final int maxLocals = codeMaxLocals.get(i - removed);
    305            final int maxStack = codeMaxStack.get(i - removed);
    306            if (numHandlers == 0) {
    307                final int header = maxLocals * 12 + maxStack + 1;
    308                if (header < 145 && maxStack < 12) {
    309                    codeHeaders[i] = header;
    310                }
    311            } else if (numHandlers == 1) {
    312                final int header = maxLocals * 8 + maxStack + 145;
    313                if (header < 209 && maxStack < 8) {
    314                    codeHeaders[i] = header;
    315                }
    316            } else if (numHandlers == 2) {
    317                final int header = maxLocals * 7 + maxStack + 209;
    318                if (header < 256 && maxStack < 7) {
    319                    codeHeaders[i] = header;
    320                }
    321            }
    322            if (codeHeaders[i] != 0) { // Remove the redundant values from
    323                                       // codeHandlerCount, codeMaxLocals and
    324                                       // codeMaxStack
    325                codeHandlerCount.remove(i - removed);
    326                codeMaxLocals.remove(i - removed);
    327                codeMaxStack.remove(i - removed);
    328                removed++;
    329            } else if (!segment.getSegmentHeader().have_all_code_flags()) {
    330                codeFlags.add(Long.valueOf(0));
    331            }
    332        }
    333
    334        // Compute any required IcLocals
    335        final IntList innerClassesN = new IntList();
    336        final List icLocal = new ArrayList();
    337        for (int i = 0; i < class_this.length; i++) {
    338            final CPClass cpClass = class_this[i];
    339            final Set referencedInnerClasses = (Set) classReferencesInnerClass.get(cpClass);
    340            if (referencedInnerClasses != null) {
    341                int innerN = 0;
    342                final List innerClasses = segment.getIcBands().getInnerClassesForOuter(cpClass.toString());
    343                if (innerClasses != null) {
    344                    for (final Iterator iterator2 = innerClasses.iterator(); iterator2.hasNext();) {
    345                        referencedInnerClasses.remove(((IcTuple) iterator2.next()).C);
    346                    }
    347                }
    348                for (final Iterator iterator2 = referencedInnerClasses.iterator(); iterator2.hasNext();) {
    349                    final CPClass inner = (CPClass) iterator2.next();
    350                    final IcTuple icTuple = segment.getIcBands().getIcTuple(inner);
    351                    if (icTuple != null && !icTuple.isAnonymous()) {
    352                        // should transmit an icLocal entry
    353                        icLocal.add(icTuple);
    354                        innerN++;
    355                    }
    356                }
    357                if (innerN != 0) {
    358                    innerClassesN.add(innerN);
    359                    class_flags[i] |= (1 << 23);
    360                }
    361            }
    362        }
    363        class_InnerClasses_N = innerClassesN.toArray();
    364        class_InnerClasses_RC = new CPClass[icLocal.size()];
    365        class_InnerClasses_F = new int[icLocal.size()];
    366        classInnerClassesOuterRCN = new ArrayList();
    367        classInnerClassesNameRUN = new ArrayList();
    368        for (int i = 0; i < class_InnerClasses_RC.length; i++) {
    369            final IcTuple icTuple = (IcTuple) icLocal.get(i);
    370            class_InnerClasses_RC[i] = (icTuple.C);
    371            if (icTuple.C2 == null && icTuple.N == null) {
    372                class_InnerClasses_F[i] = 0;
    373            } else {
    374                if (icTuple.F == 0) {
    375                    class_InnerClasses_F[i] = 0x00010000;
    376                } else {
    377                    class_InnerClasses_F[i] = icTuple.F;
    378                }
    379                classInnerClassesOuterRCN.add(icTuple.C2);
    380                classInnerClassesNameRUN.add(icTuple.N);
    381            }
    382        }
    383        // Calculate any backwards calls from metadata bands
    384        final IntList classAttrCalls = new IntList();
    385        final IntList fieldAttrCalls = new IntList();
    386        final IntList methodAttrCalls = new IntList();
    387        final IntList codeAttrCalls = new IntList();
    388
    389        if (class_RVA_bands.hasContent()) {
    390            classAttrCalls.add(class_RVA_bands.numBackwardsCalls());
    391        }
    392        if (class_RIA_bands.hasContent()) {
    393            classAttrCalls.add(class_RIA_bands.numBackwardsCalls());
    394        }
    395        if (field_RVA_bands.hasContent()) {
    396            fieldAttrCalls.add(field_RVA_bands.numBackwardsCalls());
    397        }
    398        if (field_RIA_bands.hasContent()) {
    399            fieldAttrCalls.add(field_RIA_bands.numBackwardsCalls());
    400        }
    401        if (method_RVA_bands.hasContent()) {
    402            methodAttrCalls.add(method_RVA_bands.numBackwardsCalls());
    403        }
    404        if (method_RIA_bands.hasContent()) {
    405            methodAttrCalls.add(method_RIA_bands.numBackwardsCalls());
    406        }
    407        if (method_RVPA_bands.hasContent()) {
    408            methodAttrCalls.add(method_RVPA_bands.numBackwardsCalls());
    409        }
    410        if (method_RIPA_bands.hasContent()) {
    411            methodAttrCalls.add(method_RIPA_bands.numBackwardsCalls());
    412        }
    413        if (method_AD_bands.hasContent()) {
    414            methodAttrCalls.add(method_AD_bands.numBackwardsCalls());
    415        }
    416
    417        // Sort non-predefined attribute bands
    418        final Comparator comparator = (arg0, arg1) -> {
    419            final NewAttributeBands bands0 = (NewAttributeBands) arg0;
    420            final NewAttributeBands bands1 = (NewAttributeBands) arg1;
    421            return bands0.getFlagIndex() - bands1.getFlagIndex();
    422        };
    423        Collections.sort(classAttributeBands, comparator);
    424        Collections.sort(methodAttributeBands, comparator);
    425        Collections.sort(fieldAttributeBands, comparator);
    426        Collections.sort(codeAttributeBands, comparator);
    427
    428        for (final Iterator iterator = classAttributeBands.iterator(); iterator.hasNext();) {
    429            final NewAttributeBands bands = (NewAttributeBands) iterator.next();
    430            if (bands.isUsedAtLeastOnce()) {
    431                final int[] backwardsCallCounts = bands.numBackwardsCalls();
    432                for (int i = 0; i < backwardsCallCounts.length; i++) {
    433                    classAttrCalls.add(backwardsCallCounts[i]);
    434                }
    435            }
    436        }
    437        for (final Iterator iterator = methodAttributeBands.iterator(); iterator.hasNext();) {
    438            final NewAttributeBands bands = (NewAttributeBands) iterator.next();
    439            if (bands.isUsedAtLeastOnce()) {
    440                final int[] backwardsCallCounts = bands.numBackwardsCalls();
    441                for (int i = 0; i < backwardsCallCounts.length; i++) {
    442                    methodAttrCalls.add(backwardsCallCounts[i]);
    443                }
    444            }
    445        }
    446        for (final Iterator iterator = fieldAttributeBands.iterator(); iterator.hasNext();) {
    447            final NewAttributeBands bands = (NewAttributeBands) iterator.next();
    448            if (bands.isUsedAtLeastOnce()) {
    449                final int[] backwardsCallCounts = bands.numBackwardsCalls();
    450                for (int i = 0; i < backwardsCallCounts.length; i++) {
    451                    fieldAttrCalls.add(backwardsCallCounts[i]);
    452                }
    453            }
    454        }
    455        for (final Iterator iterator = codeAttributeBands.iterator(); iterator.hasNext();) {
    456            final NewAttributeBands bands = (NewAttributeBands) iterator.next();
    457            if (bands.isUsedAtLeastOnce()) {
    458                final int[] backwardsCallCounts = bands.numBackwardsCalls();
    459                for (int i = 0; i < backwardsCallCounts.length; i++) {
    460                    codeAttrCalls.add(backwardsCallCounts[i]);
    461                }
    462            }
    463        }
    464
    465        class_attr_calls = classAttrCalls.toArray();
    466        field_attr_calls = fieldAttrCalls.toArray();
    467        method_attr_calls = methodAttrCalls.toArray();
    468        code_attr_calls = codeAttrCalls.toArray();
    469    }
    470
    471    @Override
    472    public void pack(final OutputStream out) throws IOException, Pack200Exception {
    473        PackingUtils.log("Writing class bands...");
    474
    475        byte[] encodedBand = encodeBandInt("class_this", getInts(class_this), Codec.DELTA5);
    476        out.write(encodedBand);
    477        PackingUtils.log("Wrote " + encodedBand.length + " bytes from class_this[" + class_this.length + "]");
    478
    479        encodedBand = encodeBandInt("class_super", getInts(class_super), Codec.DELTA5);
    480        out.write(encodedBand);
    481        PackingUtils.log("Wrote " + encodedBand.length + " bytes from class_super[" + class_super.length + "]");
    482
    483        encodedBand = encodeBandInt("class_interface_count", class_interface_count, Codec.DELTA5);
    484        out.write(encodedBand);
    485        PackingUtils.log(
    486            "Wrote " + encodedBand.length + " bytes from class_interface_count[" + class_interface_count.length + "]");
    487
    488        final int totalInterfaces = sum(class_interface_count);
    489        final int[] classInterface = new int[totalInterfaces];
    490        int k = 0;
    491        for (int i = 0; i < class_interface.length; i++) {
    492            if (class_interface[i] != null) {
    493                for (int j = 0; j < class_interface[i].length; j++) {
    494                    final CPClass cpClass = class_interface[i][j];
    495                    classInterface[k] = cpClass.getIndex();
    496                    k++;
    497                }
    498            }
    499        }
    500
    501        encodedBand = encodeBandInt("class_interface", classInterface, Codec.DELTA5);
    502        out.write(encodedBand);
    503        PackingUtils.log("Wrote " + encodedBand.length + " bytes from class_interface[" + classInterface.length + "]");
    504
    505        encodedBand = encodeBandInt("class_field_count", class_field_count, Codec.DELTA5);
    506        out.write(encodedBand);
    507        PackingUtils
    508            .log("Wrote " + encodedBand.length + " bytes from class_field_count[" + class_field_count.length + "]");
    509
    510        encodedBand = encodeBandInt("class_method_count", class_method_count, Codec.DELTA5);
    511        out.write(encodedBand);
    512        PackingUtils
    513            .log("Wrote " + encodedBand.length + " bytes from class_method_count[" + class_method_count.length + "]");
    514
    515        final int totalFields = sum(class_field_count);
    516        final int[] fieldDescr = new int[totalFields];
    517        k = 0;
    518        for (int i = 0; i < index; i++) {
    519            for (int j = 0; j < field_descr[i].length; j++) {
    520                final CPNameAndType descr = field_descr[i][j];
    521                fieldDescr[k] = descr.getIndex();
    522                k++;
    523            }
    524        }
    525
    526        encodedBand = encodeBandInt("field_descr", fieldDescr, Codec.DELTA5);
    527        out.write(encodedBand);
    528        PackingUtils.log("Wrote " + encodedBand.length + " bytes from field_descr[" + fieldDescr.length + "]");
    529
    530        writeFieldAttributeBands(out);
    531
    532        final int totalMethods = sum(class_method_count);
    533        final int[] methodDescr = new int[totalMethods];
    534        k = 0;
    535        for (int i = 0; i < index; i++) {
    536            for (int j = 0; j < method_descr[i].length; j++) {
    537                final CPNameAndType descr = method_descr[i][j];
    538                methodDescr[k] = descr.getIndex();
    539                k++;
    540            }
    541        }
    542
    543        encodedBand = encodeBandInt("method_descr", methodDescr, Codec.MDELTA5);
    544        out.write(encodedBand);
    545        PackingUtils.log("Wrote " + encodedBand.length + " bytes from method_descr[" + methodDescr.length + "]");
    546
    547        writeMethodAttributeBands(out);
    548        writeClassAttributeBands(out);
    549        writeCodeBands(out);
    550    }
    551
    552    private int sum(final int[] ints) {
    553        int sum = 0;
    554        for (int i = 0; i < ints.length; i++) {
    555            sum += ints[i];
    556        }
    557        return sum;
    558    }
    559
    560    private void writeFieldAttributeBands(final OutputStream out) throws IOException, Pack200Exception {
    561        byte[] encodedBand = encodeFlags("field_flags", field_flags, Codec.UNSIGNED5, Codec.UNSIGNED5,
    562            segmentHeader.have_field_flags_hi());
    563        out.write(encodedBand);
    564        PackingUtils.log("Wrote " + encodedBand.length + " bytes from field_flags[" + field_flags.length + "]");
    565
    566        // *field_attr_count :UNSIGNED5 [COUNT(1<<16,...)]
    567        // *field_attr_indexes :UNSIGNED5 [SUM(*field_attr_count)]
    568        encodedBand = encodeBandInt("field_attr_calls", field_attr_calls, Codec.UNSIGNED5);
    569        out.write(encodedBand);
    570        PackingUtils
    571            .log("Wrote " + encodedBand.length + " bytes from field_attr_calls[" + field_attr_calls.length + "]");
    572
    573        encodedBand = encodeBandInt("fieldConstantValueKQ", cpEntryListToArray(fieldConstantValueKQ), Codec.UNSIGNED5);
    574        out.write(encodedBand);
    575        PackingUtils.log(
    576            "Wrote " + encodedBand.length + " bytes from fieldConstantValueKQ[" + fieldConstantValueKQ.size() + "]");
    577
    578        encodedBand = encodeBandInt("fieldSignature", cpEntryListToArray(fieldSignature), Codec.UNSIGNED5);
    579        out.write(encodedBand);
    580        PackingUtils.log("Wrote " + encodedBand.length + " bytes from fieldSignature[" + fieldSignature.size() + "]");
    581
    582        field_RVA_bands.pack(out);
    583        field_RIA_bands.pack(out);
    584        for (final Iterator iterator = fieldAttributeBands.iterator(); iterator.hasNext();) {
    585            final NewAttributeBands bands = (NewAttributeBands) iterator.next();
    586            bands.pack(out);
    587        }
    588    }
    589
    590    private void writeMethodAttributeBands(final OutputStream out) throws IOException, Pack200Exception {
    591        byte[] encodedBand = encodeFlags("method_flags", method_flags, Codec.UNSIGNED5, Codec.UNSIGNED5,
    592            segmentHeader.have_method_flags_hi());
    593        out.write(encodedBand);
    594        PackingUtils.log("Wrote " + encodedBand.length + " bytes from method_flags[" + method_flags.length + "]");
    595
    596        // *method_attr_count :UNSIGNED5 [COUNT(1<<16,...)]
    597        // *method_attr_indexes :UNSIGNED5 [SUM(*method_attr_count)]
    598        encodedBand = encodeBandInt("method_attr_calls", method_attr_calls, Codec.UNSIGNED5);
    599        out.write(encodedBand);
    600        PackingUtils
    601            .log("Wrote " + encodedBand.length + " bytes from method_attr_calls[" + method_attr_calls.length + "]");
    602
    603        encodedBand = encodeBandInt("methodExceptionNumber", methodExceptionNumber.toArray(), Codec.UNSIGNED5);
    604        out.write(encodedBand);
    605        PackingUtils.log(
    606            "Wrote " + encodedBand.length + " bytes from methodExceptionNumber[" + methodExceptionNumber.size() + "]");
    607
    608        encodedBand = encodeBandInt("methodExceptionClasses", cpEntryListToArray(methodExceptionClasses),
    609            Codec.UNSIGNED5);
    610        out.write(encodedBand);
    611        PackingUtils.log("Wrote " + encodedBand.length + " bytes from methodExceptionClasses["
    612            + methodExceptionClasses.size() + "]");
    613
    614        encodedBand = encodeBandInt("methodSignature", cpEntryListToArray(methodSignature), Codec.UNSIGNED5);
    615        out.write(encodedBand);
    616        PackingUtils.log("Wrote " + encodedBand.length + " bytes from methodSignature[" + methodSignature.size() + "]");
    617
    618        method_RVA_bands.pack(out);
    619        method_RIA_bands.pack(out);
    620        method_RVPA_bands.pack(out);
    621        method_RIPA_bands.pack(out);
    622        method_AD_bands.pack(out);
    623        for (final Iterator iterator = methodAttributeBands.iterator(); iterator.hasNext();) {
    624            final NewAttributeBands bands = (NewAttributeBands) iterator.next();
    625            bands.pack(out);
    626        }
    627    }
    628
    629    private void writeClassAttributeBands(final OutputStream out) throws IOException, Pack200Exception {
    630        byte[] encodedBand = encodeFlags("class_flags", class_flags, Codec.UNSIGNED5, Codec.UNSIGNED5,
    631            segmentHeader.have_class_flags_hi());
    632        out.write(encodedBand);
    633        PackingUtils.log("Wrote " + encodedBand.length + " bytes from class_flags[" + class_flags.length + "]");
    634
    635        // These bands are not needed, but could be used to reduce the size of
    636        // the archive if there are enough different non-standard attributes
    637        // defined that segmentHeader.have_class_flags_hi() is true. The same
    638        // applies to method_attr_count, field_attr_count, code_attr_count etc.
    639
    640        // *class_attr_count :UNSIGNED5 [COUNT(1<<16,...)]
    641        // *class_attr_indexes :UNSIGNED5 [SUM(*class_attr_count)]
    642
    643        encodedBand = encodeBandInt("class_attr_calls", class_attr_calls, Codec.UNSIGNED5);
    644        out.write(encodedBand);
    645        PackingUtils
    646            .log("Wrote " + encodedBand.length + " bytes from class_attr_calls[" + class_attr_calls.length + "]");
    647
    648        encodedBand = encodeBandInt("classSourceFile", cpEntryOrNullListToArray(classSourceFile), Codec.UNSIGNED5);
    649        out.write(encodedBand);
    650        PackingUtils.log("Wrote " + encodedBand.length + " bytes from classSourceFile[" + classSourceFile.size() + "]");
    651
    652        encodedBand = encodeBandInt("class_enclosing_method_RC", cpEntryListToArray(classEnclosingMethodClass),
    653            Codec.UNSIGNED5);
    654        out.write(encodedBand);
    655        PackingUtils.log("Wrote " + encodedBand.length + " bytes from class_enclosing_method_RC["
    656            + classEnclosingMethodClass.size() + "]");
    657
    658        encodedBand = encodeBandInt("class_EnclosingMethod_RDN", cpEntryOrNullListToArray(classEnclosingMethodDesc),
    659            Codec.UNSIGNED5);
    660        out.write(encodedBand);
    661        PackingUtils.log("Wrote " + encodedBand.length + " bytes from class_EnclosingMethod_RDN["
    662            + classEnclosingMethodDesc.size() + "]");
    663
    664        encodedBand = encodeBandInt("class_Signature_RS", cpEntryListToArray(classSignature), Codec.UNSIGNED5);
    665        out.write(encodedBand);
    666        PackingUtils
    667            .log("Wrote " + encodedBand.length + " bytes from class_Signature_RS[" + classSignature.size() + "]");
    668
    669        class_RVA_bands.pack(out);
    670        class_RIA_bands.pack(out);
    671
    672        encodedBand = encodeBandInt("class_InnerClasses_N", class_InnerClasses_N, Codec.UNSIGNED5);
    673        out.write(encodedBand);
    674        PackingUtils.log(
    675            "Wrote " + encodedBand.length + " bytes from class_InnerClasses_N[" + class_InnerClasses_N.length + "]");
    676
    677        encodedBand = encodeBandInt("class_InnerClasses_RC", getInts(class_InnerClasses_RC), Codec.UNSIGNED5);
    678        out.write(encodedBand);
    679        PackingUtils.log(
    680            "Wrote " + encodedBand.length + " bytes from class_InnerClasses_RC[" + class_InnerClasses_RC.length + "]");
    681
    682        encodedBand = encodeBandInt("class_InnerClasses_F", class_InnerClasses_F, Codec.UNSIGNED5);
    683        out.write(encodedBand);
    684        PackingUtils.log(
    685            "Wrote " + encodedBand.length + " bytes from class_InnerClasses_F[" + class_InnerClasses_F.length + "]");
    686
    687        encodedBand = encodeBandInt("class_InnerClasses_outer_RCN", cpEntryOrNullListToArray(classInnerClassesOuterRCN),
    688            Codec.UNSIGNED5);
    689        out.write(encodedBand);
    690        PackingUtils.log("Wrote " + encodedBand.length + " bytes from class_InnerClasses_outer_RCN["
    691            + classInnerClassesOuterRCN.size() + "]");
    692
    693        encodedBand = encodeBandInt("class_InnerClasses_name_RUN", cpEntryOrNullListToArray(classInnerClassesNameRUN),
    694            Codec.UNSIGNED5);
    695        out.write(encodedBand);
    696        PackingUtils.log("Wrote " + encodedBand.length + " bytes from class_InnerClasses_name_RUN["
    697            + classInnerClassesNameRUN.size() + "]");
    698
    699        encodedBand = encodeBandInt("classFileVersionMinor", classFileVersionMinor.toArray(), Codec.UNSIGNED5);
    700        out.write(encodedBand);
    701        PackingUtils.log(
    702            "Wrote " + encodedBand.length + " bytes from classFileVersionMinor[" + classFileVersionMinor.size() + "]");
    703
    704        encodedBand = encodeBandInt("classFileVersionMajor", classFileVersionMajor.toArray(), Codec.UNSIGNED5);
    705        out.write(encodedBand);
    706        PackingUtils.log(
    707            "Wrote " + encodedBand.length + " bytes from classFileVersionMajor[" + classFileVersionMajor.size() + "]");
    708
    709        for (final Iterator iterator = classAttributeBands.iterator(); iterator.hasNext();) {
    710            final NewAttributeBands bands = (NewAttributeBands) iterator.next();
    711            bands.pack(out);
    712        }
    713    }
    714
    715    private int[] getInts(final CPClass[] cpClasses) {
    716        final int[] ints = new int[cpClasses.length];
    717        for (int i = 0; i < ints.length; i++) {
    718            if (cpClasses[i] != null) {
    719                ints[i] = cpClasses[i].getIndex();
    720            }
    721        }
    722        return ints;
    723    }
    724
    725    private void writeCodeBands(final OutputStream out) throws IOException, Pack200Exception {
    726        byte[] encodedBand = encodeBandInt("codeHeaders", codeHeaders, Codec.BYTE1);
    727        out.write(encodedBand);
    728        PackingUtils.log("Wrote " + encodedBand.length + " bytes from codeHeaders[" + codeHeaders.length + "]");
    729
    730        encodedBand = encodeBandInt("codeMaxStack", codeMaxStack.toArray(), Codec.UNSIGNED5);
    731        out.write(encodedBand);
    732        PackingUtils.log("Wrote " + encodedBand.length + " bytes from codeMaxStack[" + codeMaxStack.size() + "]");
    733
    734        encodedBand = encodeBandInt("codeMaxLocals", codeMaxLocals.toArray(), Codec.UNSIGNED5);
    735        out.write(encodedBand);
    736        PackingUtils.log("Wrote " + encodedBand.length + " bytes from codeMaxLocals[" + codeMaxLocals.size() + "]");
    737
    738        encodedBand = encodeBandInt("codeHandlerCount", codeHandlerCount.toArray(), Codec.UNSIGNED5);
    739        out.write(encodedBand);
    740        PackingUtils
    741            .log("Wrote " + encodedBand.length + " bytes from codeHandlerCount[" + codeHandlerCount.size() + "]");
    742
    743        encodedBand = encodeBandInt("codeHandlerStartP", integerListToArray(codeHandlerStartP), Codec.BCI5);
    744        out.write(encodedBand);
    745        PackingUtils
    746            .log("Wrote " + encodedBand.length + " bytes from codeHandlerStartP[" + codeHandlerStartP.size() + "]");
    747
    748        encodedBand = encodeBandInt("codeHandlerEndPO", integerListToArray(codeHandlerEndPO), Codec.BRANCH5);
    749        out.write(encodedBand);
    750        PackingUtils
    751            .log("Wrote " + encodedBand.length + " bytes from codeHandlerEndPO[" + codeHandlerEndPO.size() + "]");
    752
    753        encodedBand = encodeBandInt("codeHandlerCatchPO", integerListToArray(codeHandlerCatchPO), Codec.BRANCH5);
    754        out.write(encodedBand);
    755        PackingUtils
    756            .log("Wrote " + encodedBand.length + " bytes from codeHandlerCatchPO[" + codeHandlerCatchPO.size() + "]");
    757
    758        encodedBand = encodeBandInt("codeHandlerClass", cpEntryOrNullListToArray(codeHandlerClass), Codec.UNSIGNED5);
    759        out.write(encodedBand);
    760        PackingUtils
    761            .log("Wrote " + encodedBand.length + " bytes from codeHandlerClass[" + codeHandlerClass.size() + "]");
    762
    763        writeCodeAttributeBands(out);
    764    }
    765
    766    private void writeCodeAttributeBands(final OutputStream out) throws IOException, Pack200Exception {
    767        byte[] encodedBand = encodeFlags("codeFlags", longListToArray(codeFlags), Codec.UNSIGNED5, Codec.UNSIGNED5,
    768            segmentHeader.have_code_flags_hi());
    769        out.write(encodedBand);
    770        PackingUtils.log("Wrote " + encodedBand.length + " bytes from codeFlags[" + codeFlags.size() + "]");
    771
    772        // *code_attr_count :UNSIGNED5 [COUNT(1<<16,...)]
    773        // *code_attr_indexes :UNSIGNED5 [SUM(*code_attr_count)]
    774        encodedBand = encodeBandInt("code_attr_calls", code_attr_calls, Codec.UNSIGNED5);
    775        out.write(encodedBand);
    776        PackingUtils.log("Wrote " + encodedBand.length + " bytes from code_attr_calls[" + code_attr_calls.length + "]");
    777
    778        encodedBand = encodeBandInt("code_LineNumberTable_N", codeLineNumberTableN.toArray(), Codec.UNSIGNED5);
    779        out.write(encodedBand);
    780        PackingUtils.log(
    781            "Wrote " + encodedBand.length + " bytes from code_LineNumberTable_N[" + codeLineNumberTableN.size() + "]");
    782
    783        encodedBand = encodeBandInt("code_LineNumberTable_bci_P", integerListToArray(codeLineNumberTableBciP),
    784            Codec.BCI5);
    785        out.write(encodedBand);
    786        PackingUtils.log("Wrote " + encodedBand.length + " bytes from code_LineNumberTable_bci_P["
    787            + codeLineNumberTableBciP.size() + "]");
    788
    789        encodedBand = encodeBandInt("code_LineNumberTable_line", codeLineNumberTableLine.toArray(), Codec.UNSIGNED5);
    790        out.write(encodedBand);
    791        PackingUtils.log("Wrote " + encodedBand.length + " bytes from code_LineNumberTable_line["
    792            + codeLineNumberTableLine.size() + "]");
    793
    794        encodedBand = encodeBandInt("code_LocalVariableTable_N", codeLocalVariableTableN.toArray(), Codec.UNSIGNED5);
    795        out.write(encodedBand);
    796        PackingUtils.log("Wrote " + encodedBand.length + " bytes from code_LocalVariableTable_N["
    797            + codeLocalVariableTableN.size() + "]");
    798
    799        encodedBand = encodeBandInt("code_LocalVariableTable_bci_P", integerListToArray(codeLocalVariableTableBciP),
    800            Codec.BCI5);
    801        out.write(encodedBand);
    802        PackingUtils.log("Wrote " + encodedBand.length + " bytes from code_LocalVariableTable_bci_P["
    803            + codeLocalVariableTableBciP.size() + "]");
    804
    805        encodedBand = encodeBandInt("code_LocalVariableTable_span_O", integerListToArray(codeLocalVariableTableSpanO),
    806            Codec.BRANCH5);
    807        out.write(encodedBand);
    808        PackingUtils.log("Wrote " + encodedBand.length + " bytes from code_LocalVariableTable_span_O["
    809            + codeLocalVariableTableSpanO.size() + "]");
    810
    811        encodedBand = encodeBandInt("code_LocalVariableTable_name_RU", cpEntryListToArray(codeLocalVariableTableNameRU),
    812            Codec.UNSIGNED5);
    813        out.write(encodedBand);
    814        PackingUtils.log("Wrote " + encodedBand.length + " bytes from code_LocalVariableTable_name_RU["
    815            + codeLocalVariableTableNameRU.size() + "]");
    816
    817        encodedBand = encodeBandInt("code_LocalVariableTable_type_RS", cpEntryListToArray(codeLocalVariableTableTypeRS),
    818            Codec.UNSIGNED5);
    819        out.write(encodedBand);
    820        PackingUtils.log("Wrote " + encodedBand.length + " bytes from code_LocalVariableTable_type_RS["
    821            + codeLocalVariableTableTypeRS.size() + "]");
    822
    823        encodedBand = encodeBandInt("code_LocalVariableTable_slot", codeLocalVariableTableSlot.toArray(),
    824            Codec.UNSIGNED5);
    825        out.write(encodedBand);
    826        PackingUtils.log("Wrote " + encodedBand.length + " bytes from code_LocalVariableTable_slot["
    827            + codeLocalVariableTableSlot.size() + "]");
    828
    829        encodedBand = encodeBandInt("code_LocalVariableTypeTable_N", codeLocalVariableTypeTableN.toArray(),
    830            Codec.UNSIGNED5);
    831        out.write(encodedBand);
    832        PackingUtils.log("Wrote " + encodedBand.length + " bytes from code_LocalVariableTypeTable_N["
    833            + codeLocalVariableTypeTableN.size() + "]");
    834
    835        encodedBand = encodeBandInt("code_LocalVariableTypeTable_bci_P",
    836            integerListToArray(codeLocalVariableTypeTableBciP), Codec.BCI5);
    837        out.write(encodedBand);
    838        PackingUtils.log("Wrote " + encodedBand.length + " bytes from code_LocalVariableTypeTable_bci_P["
    839            + codeLocalVariableTypeTableBciP.size() + "]");
    840
    841        encodedBand = encodeBandInt("code_LocalVariableTypeTable_span_O",
    842            integerListToArray(codeLocalVariableTypeTableSpanO), Codec.BRANCH5);
    843        out.write(encodedBand);
    844        PackingUtils.log("Wrote " + encodedBand.length + " bytes from code_LocalVariableTypeTable_span_O["
    845            + codeLocalVariableTypeTableSpanO.size() + "]");
    846
    847        encodedBand = encodeBandInt("code_LocalVariableTypeTable_name_RU",
    848            cpEntryListToArray(codeLocalVariableTypeTableNameRU), Codec.UNSIGNED5);
    849        out.write(encodedBand);
    850        PackingUtils.log("Wrote " + encodedBand.length + " bytes from code_LocalVariableTypeTable_name_RU["
    851            + codeLocalVariableTypeTableNameRU.size() + "]");
    852
    853        encodedBand = encodeBandInt("code_LocalVariableTypeTable_type_RS",
    854            cpEntryListToArray(codeLocalVariableTypeTableTypeRS), Codec.UNSIGNED5);
    855        out.write(encodedBand);
    856        PackingUtils.log("Wrote " + encodedBand.length + " bytes from code_LocalVariableTypeTable_type_RS["
    857            + codeLocalVariableTypeTableTypeRS.size() + "]");
    858
    859        encodedBand = encodeBandInt("code_LocalVariableTypeTable_slot", codeLocalVariableTypeTableSlot.toArray(),
    860            Codec.UNSIGNED5);
    861        out.write(encodedBand);
    862        PackingUtils.log("Wrote " + encodedBand.length + " bytes from code_LocalVariableTypeTable_slot["
    863            + codeLocalVariableTypeTableSlot.size() + "]");
    864
    865        for (final Iterator iterator = codeAttributeBands.iterator(); iterator.hasNext();) {
    866            final NewAttributeBands bands = (NewAttributeBands) iterator.next();
    867            bands.pack(out);
    868        }
    869    }
    870
    871    public void addMethod(int flags, final String name, final String desc, final String signature,
    872        final String[] exceptions) {
    873        final CPNameAndType nt = cpBands.getCPNameAndType(name, desc);
    874        tempMethodDesc.add(nt);
    875        if (signature != null) {
    876            methodSignature.add(cpBands.getCPSignature(signature));
    877            flags |= (1 << 19);
    878        }
    879        if (exceptions != null) {
    880            methodExceptionNumber.add(exceptions.length);
    881            for (int i = 0; i < exceptions.length; i++) {
    882                methodExceptionClasses.add(cpBands.getCPClass(exceptions[i]));
    883            }
    884            flags |= (1 << 18);
    885        }
    886        if ((flags & Opcodes.ACC_DEPRECATED) != 0) { // ASM uses (1<<17) flag for deprecated
    887            flags = flags & ~Opcodes.ACC_DEPRECATED;
    888            flags = flags | (1 << 20);
    889        }
    890        tempMethodFlags.add(Long.valueOf(flags));
    891        numMethodArgs = countArgs(desc);
    892        if (!anySyntheticMethods && ((flags & (1 << 12)) != 0)
    893            && segment.getCurrentClassReader().hasSyntheticAttributes()) {
    894            cpBands.addCPUtf8("Synthetic");
    895            anySyntheticMethods = true;
    896        }
    897    }
    898
    899    public void endOfMethod() {
    900        if (tempMethodRVPA != null) {
    901            method_RVPA_bands.addParameterAnnotation(tempMethodRVPA.numParams, tempMethodRVPA.annoN,
    902                tempMethodRVPA.pairN, tempMethodRVPA.typeRS, tempMethodRVPA.nameRU, tempMethodRVPA.t,
    903                tempMethodRVPA.values, tempMethodRVPA.caseArrayN, tempMethodRVPA.nestTypeRS, tempMethodRVPA.nestNameRU,
    904                tempMethodRVPA.nestPairN);
    905            tempMethodRVPA = null;
    906        }
    907        if (tempMethodRIPA != null) {
    908            method_RIPA_bands.addParameterAnnotation(tempMethodRIPA.numParams, tempMethodRIPA.annoN,
    909                tempMethodRIPA.pairN, tempMethodRIPA.typeRS, tempMethodRIPA.nameRU, tempMethodRIPA.t,
    910                tempMethodRIPA.values, tempMethodRIPA.caseArrayN, tempMethodRIPA.nestTypeRS, tempMethodRIPA.nestNameRU,
    911                tempMethodRIPA.nestPairN);
    912            tempMethodRIPA = null;
    913        }
    914        if (codeFlags.size() > 0) {
    915            final long latestCodeFlag = ((Long) codeFlags.get(codeFlags.size() - 1)).longValue();
    916            final int latestLocalVariableTableN = codeLocalVariableTableN.get(codeLocalVariableTableN.size() - 1);
    917            if (latestCodeFlag == (1 << 2) && latestLocalVariableTableN == 0) {
    918                codeLocalVariableTableN.remove(codeLocalVariableTableN.size() - 1);
    919                codeFlags.remove(codeFlags.size() - 1);
    920                codeFlags.add(Integer.valueOf(0));
    921            }
    922        }
    923    }
    924
    925    protected static int countArgs(final String descriptor) {
    926        final int bra = descriptor.indexOf('(');
    927        final int ket = descriptor.indexOf(')');
    928        if (bra == -1 || ket == -1 || ket < bra) {
    929            throw new IllegalArgumentException("No arguments");
    930        }
    931
    932        boolean inType = false;
    933        boolean consumingNextType = false;
    934        int count = 0;
    935        for (int i = bra + 1; i < ket; i++) {
    936            final char charAt = descriptor.charAt(i);
    937            if (inType && charAt == ';') {
    938                inType = false;
    939                consumingNextType = false;
    940            } else if (!inType && charAt == 'L') {
    941                inType = true;
    942                count++;
    943            } else if (charAt == '[') {
    944                consumingNextType = true;
    945            } else if (inType) {
    946                // NOP
    947            } else if (consumingNextType) {
    948                count++;
    949                consumingNextType = false;
    950            } else if (charAt == 'D' || charAt == 'J') {
    951                count += 2;
    952            } else {
    953                count++;
    954            }
    955        }
    956        return count;
    957    }
    958
    959    public void endOfClass() { // All the data for the current class has been
    960                               // read
    961        final int numFields = tempFieldDesc.size();
    962        class_field_count[index] = numFields;
    963        field_descr[index] = new CPNameAndType[numFields];
    964        field_flags[index] = new long[numFields];
    965        for (int i = 0; i < numFields; i++) {
    966            field_descr[index][i] = (CPNameAndType) tempFieldDesc.get(i);
    967            field_flags[index][i] = ((Long) tempFieldFlags.get(i)).longValue();
    968        }
    969        final int numMethods = tempMethodDesc.size();
    970        class_method_count[index] = numMethods;
    971        method_descr[index] = new CPNameAndType[numMethods];
    972        method_flags[index] = new long[numMethods];
    973        for (int i = 0; i < numMethods; i++) {
    974            method_descr[index][i] = (CPNameAndType) tempMethodDesc.get(i);
    975            method_flags[index][i] = ((Long) tempMethodFlags.get(i)).longValue();
    976        }
    977        tempFieldDesc.clear();
    978        tempFieldFlags.clear();
    979        tempMethodDesc.clear();
    980        tempMethodFlags.clear();
    981        index++;
    982    }
    983
    984    public void addSourceFile(final String source) {
    985        String implicitSourceFileName = class_this[index].toString();
    986        if (implicitSourceFileName.indexOf('$') != -1) {
    987            implicitSourceFileName = implicitSourceFileName.substring(0, implicitSourceFileName.indexOf('$'));
    988        }
    989        implicitSourceFileName = implicitSourceFileName.substring(implicitSourceFileName.lastIndexOf('/') + 1)
    990            + ".java";
    991        if (source.equals(implicitSourceFileName)) {
    992            classSourceFile.add(null);
    993        } else {
    994            classSourceFile.add(cpBands.getCPUtf8(source));
    995        }
    996        class_flags[index] |= (1 << 17);
    997    }
    998
    999    public void addEnclosingMethod(final String owner, final String name, final String desc) {
    1000        class_flags[index] |= (1 << 18);
    1001        classEnclosingMethodClass.add(cpBands.getCPClass(owner));
    1002        classEnclosingMethodDesc.add(name == null ? null : cpBands.getCPNameAndType(name, desc));
    1003    }
    1004
    1005    public void addClassAttribute(final NewAttribute attribute) {
    1006        // TODO: backwards calls
    1007        final String attributeName = attribute.type;
    1008        for (final Iterator iterator = classAttributeBands.iterator(); iterator.hasNext();) {
    1009            final NewAttributeBands bands = (NewAttributeBands) iterator.next();
    1010            if (bands.getAttributeName().equals(attributeName)) {
    1011                bands.addAttribute(attribute);
    1012                final int flagIndex = bands.getFlagIndex();
    1013                class_flags[index] |= (1 << flagIndex);
    1014                return;
    1015            }
    1016        }
    1017        throw new RuntimeException("No suitable definition for " + attributeName);
    1018    }
    1019
    1020    public void addFieldAttribute(final NewAttribute attribute) {
    1021        final String attributeName = attribute.type;
    1022        for (final Iterator iterator = fieldAttributeBands.iterator(); iterator.hasNext();) {
    1023            final NewAttributeBands bands = (NewAttributeBands) iterator.next();
    1024            if (bands.getAttributeName().equals(attributeName)) {
    1025                bands.addAttribute(attribute);
    1026                final int flagIndex = bands.getFlagIndex();
    1027                final Long flags = (Long) tempFieldFlags.remove(tempFieldFlags.size() - 1);
    1028                tempFieldFlags.add(Long.valueOf(flags.longValue() | (1 << flagIndex)));
    1029                return;
    1030            }
    1031        }
    1032        throw new RuntimeException("No suitable definition for " + attributeName);
    1033    }
    1034
    1035    public void addMethodAttribute(final NewAttribute attribute) {
    1036        final String attributeName = attribute.type;
    1037        for (final Iterator iterator = methodAttributeBands.iterator(); iterator.hasNext();) {
    1038            final NewAttributeBands bands = (NewAttributeBands) iterator.next();
    1039            if (bands.getAttributeName().equals(attributeName)) {
    1040                bands.addAttribute(attribute);
    1041                final int flagIndex = bands.getFlagIndex();
    1042                final Long flags = (Long) tempMethodFlags.remove(tempMethodFlags.size() - 1);
    1043                tempMethodFlags.add(Long.valueOf(flags.longValue() | (1 << flagIndex)));
    1044                return;
    1045            }
    1046        }
    1047        throw new RuntimeException("No suitable definition for " + attributeName);
    1048    }
    1049
    1050    public void addCodeAttribute(final NewAttribute attribute) {
    1051        final String attributeName = attribute.type;
    1052        for (final Iterator iterator = codeAttributeBands.iterator(); iterator.hasNext();) {
    1053            final NewAttributeBands bands = (NewAttributeBands) iterator.next();
    1054            if (bands.getAttributeName().equals(attributeName)) {
    1055                bands.addAttribute(attribute);
    1056                final int flagIndex = bands.getFlagIndex();
    1057                final Long flags = (Long) codeFlags.remove(codeFlags.size() - 1);
    1058                codeFlags.add(Long.valueOf(flags.longValue() | (1 << flagIndex)));
    1059                return;
    1060            }
    1061        }
    1062        throw new RuntimeException("No suitable definition for " + attributeName);
    1063    }
    1064
    1065    public void addMaxStack(final int maxStack, int maxLocals) {
    1066        final Long latestFlag = (Long) tempMethodFlags.remove(tempMethodFlags.size() - 1);
    1067        final Long newFlag = Long.valueOf(latestFlag.intValue() | (1 << 17));
    1068        tempMethodFlags.add(newFlag);
    1069        codeMaxStack.add(maxStack);
    1070        if ((newFlag.longValue() & (1 << 3)) == 0) { // not static
    1071            maxLocals--; // minus 'this' local
    1072        }
    1073        maxLocals -= numMethodArgs;
    1074        codeMaxLocals.add(maxLocals);
    1075    }
    1076
    1077    public void addCode() {
    1078        codeHandlerCount.add(0);
    1079        if (!stripDebug) {
    1080            codeFlags.add(Long.valueOf(1 << 2));
    1081            codeLocalVariableTableN.add(0);
    1082        }
    1083    }
    1084
    1085    public void addHandler(final Label start, final Label end, final Label handler, final String type) {
    1086        final int handlers = codeHandlerCount.remove(codeHandlerCount.size() - 1);
    1087        codeHandlerCount.add(handlers + 1);
    1088        codeHandlerStartP.add(start);
    1089        codeHandlerEndPO.add(end);
    1090        codeHandlerCatchPO.add(handler);
    1091        codeHandlerClass.add(type == null ? null : cpBands.getCPClass(type));
    1092    }
    1093
    1094    public void addLineNumber(final int line, final Label start) {
    1095        final Long latestCodeFlag = (Long) codeFlags.get(codeFlags.size() - 1);
    1096        if ((latestCodeFlag.intValue() & (1 << 1)) == 0) {
    1097            codeFlags.remove(codeFlags.size() - 1);
    1098            codeFlags.add(Long.valueOf(latestCodeFlag.intValue() | (1 << 1)));
    1099            codeLineNumberTableN.add(1);
    1100        } else {
    1101            codeLineNumberTableN.increment(codeLineNumberTableN.size() - 1);
    1102        }
    1103        codeLineNumberTableLine.add(line);
    1104        codeLineNumberTableBciP.add(start);
    1105    }
    1106
    1107    public void addLocalVariable(final String name, final String desc, final String signature, final Label start,
    1108        final Label end, final int indx) {
    1109        if (signature != null) { // LocalVariableTypeTable attribute
    1110            final Long latestCodeFlag = (Long) codeFlags.get(codeFlags.size() - 1);
    1111            if ((latestCodeFlag.intValue() & (1 << 3)) == 0) {
    1112                codeFlags.remove(codeFlags.size() - 1);
    1113                codeFlags.add(Long.valueOf(latestCodeFlag.intValue() | (1 << 3)));
    1114                codeLocalVariableTypeTableN.add(1);
    1115            } else {
    1116                codeLocalVariableTypeTableN.increment(codeLocalVariableTypeTableN.size() - 1);
    1117            }
    1118            codeLocalVariableTypeTableBciP.add(start);
    1119            codeLocalVariableTypeTableSpanO.add(end);
    1120            codeLocalVariableTypeTableNameRU.add(cpBands.getCPUtf8(name));
    1121            codeLocalVariableTypeTableTypeRS.add(cpBands.getCPSignature(signature));
    1122            codeLocalVariableTypeTableSlot.add(indx);
    1123        }
    1124        // LocalVariableTable attribute
    1125        codeLocalVariableTableN.increment(codeLocalVariableTableN.size() - 1);
    1126        codeLocalVariableTableBciP.add(start);
    1127        codeLocalVariableTableSpanO.add(end);
    1128        codeLocalVariableTableNameRU.add(cpBands.getCPUtf8(name));
    1129        codeLocalVariableTableTypeRS.add(cpBands.getCPSignature(desc));
    1130        codeLocalVariableTableSlot.add(indx);
    1131    }
    1132
    1133    public void doBciRenumbering(final IntList bciRenumbering, final Map labelsToOffsets) {
    1134        renumberBci(codeLineNumberTableBciP, bciRenumbering, labelsToOffsets);
    1135        renumberBci(codeLocalVariableTableBciP, bciRenumbering, labelsToOffsets);
    1136        renumberOffsetBci(codeLocalVariableTableBciP, codeLocalVariableTableSpanO, bciRenumbering, labelsToOffsets);
    1137        renumberBci(codeLocalVariableTypeTableBciP, bciRenumbering, labelsToOffsets);
    1138        renumberOffsetBci(codeLocalVariableTypeTableBciP, codeLocalVariableTypeTableSpanO, bciRenumbering,
    1139            labelsToOffsets);
    1140        renumberBci(codeHandlerStartP, bciRenumbering, labelsToOffsets);
    1141        renumberOffsetBci(codeHandlerStartP, codeHandlerEndPO, bciRenumbering, labelsToOffsets);
    1142        renumberDoubleOffsetBci(codeHandlerStartP, codeHandlerEndPO, codeHandlerCatchPO, bciRenumbering,
    1143            labelsToOffsets);
    1144
    1145        for (final Iterator iterator = classAttributeBands.iterator(); iterator.hasNext();) {
    1146            final NewAttributeBands newAttributeBandSet = (NewAttributeBands) iterator.next();
    1147            newAttributeBandSet.renumberBci(bciRenumbering, labelsToOffsets);
    1148        }
    1149        for (final Iterator iterator = methodAttributeBands.iterator(); iterator.hasNext();) {
    1150            final NewAttributeBands newAttributeBandSet = (NewAttributeBands) iterator.next();
    1151            newAttributeBandSet.renumberBci(bciRenumbering, labelsToOffsets);
    1152        }
    1153        for (final Iterator iterator = fieldAttributeBands.iterator(); iterator.hasNext();) {
    1154            final NewAttributeBands newAttributeBandSet = (NewAttributeBands) iterator.next();
    1155            newAttributeBandSet.renumberBci(bciRenumbering, labelsToOffsets);
    1156        }
    1157        for (final Iterator iterator = codeAttributeBands.iterator(); iterator.hasNext();) {
    1158            final NewAttributeBands newAttributeBandSet = (NewAttributeBands) iterator.next();
    1159            newAttributeBandSet.renumberBci(bciRenumbering, labelsToOffsets);
    1160        }
    1161    }
    1162
    1163    private void renumberBci(final List list, final IntList bciRenumbering, final Map labelsToOffsets) {
    1164        for (int i = list.size() - 1; i >= 0; i--) {
    1165            final Object label = list.get(i);
    1166            if (label instanceof Integer) {
    1167                break;
    1168            }
    1169            if (label instanceof Label) {
    1170                list.remove(i);
    1171                final Integer bytecodeIndex = (Integer) labelsToOffsets.get(label);
    1172                list.add(i, Integer.valueOf(bciRenumbering.get(bytecodeIndex.intValue())));
    1173            }
    1174        }
    1175    }
    1176
    1177    private void renumberOffsetBci(final List relative, final List list, final IntList bciRenumbering,
    1178        final Map labelsToOffsets) {
    1179        for (int i = list.size() - 1; i >= 0; i--) {
    1180            final Object label = list.get(i);
    1181            if (label instanceof Integer) {
    1182                break;
    1183            }
    1184            if (label instanceof Label) {
    1185                list.remove(i);
    1186                final Integer bytecodeIndex = (Integer) labelsToOffsets.get(label);
    1187                final Integer renumberedOffset = Integer
    1188                    .valueOf(bciRenumbering.get(bytecodeIndex.intValue()) - ((Integer) relative.get(i)).intValue());
    1189                list.add(i, renumberedOffset);
    1190            }
    1191        }
    1192    }
    1193
    1194    private void renumberDoubleOffsetBci(final List relative, final List firstOffset, final List list,
    1195        final IntList bciRenumbering, final Map labelsToOffsets) {
    1196        // TODO: There's probably a nicer way of doing this...
    1197        for (int i = list.size() - 1; i >= 0; i--) {
    1198            final Object label = list.get(i);
    1199            if (label instanceof Integer) {
    1200                break;
    1201            }
    1202            if (label instanceof Label) {
    1203                list.remove(i);
    1204                final Integer bytecodeIndex = (Integer) labelsToOffsets.get(label);
    1205                final Integer renumberedOffset = Integer.valueOf(bciRenumbering.get(bytecodeIndex.intValue())
    1206                    - ((Integer) relative.get(i)).intValue() - ((Integer) firstOffset.get(i)).intValue());
    1207                list.add(i, renumberedOffset);
    1208            }
    1209        }
    1210    }
    1211
    1212    public boolean isAnySyntheticClasses() {
    1213        return anySyntheticClasses;
    1214    }
    1215
    1216    public boolean isAnySyntheticFields() {
    1217        return anySyntheticFields;
    1218    }
    1219
    1220    public boolean isAnySyntheticMethods() {
    1221        return anySyntheticMethods;
    1222    }
    1223
    1224    public void addParameterAnnotation(final int parameter, final String desc, final boolean visible, final List nameRU,
    1225        final List t, final List values, final List caseArrayN, final List nestTypeRS, final List nestNameRU,
    1226        final List nestPairN) {
    1227        if (visible) {
    1228            if (tempMethodRVPA == null) {
    1229                tempMethodRVPA = new TempParamAnnotation(numMethodArgs);
    1230                tempMethodRVPA.addParameterAnnotation(parameter, desc, nameRU, t, values, caseArrayN, nestTypeRS,
    1231                    nestNameRU, nestPairN);
    1232            }
    1233            final Long flag = (Long) tempMethodFlags.remove(tempMethodFlags.size() - 1);
    1234            tempMethodFlags.add(Long.valueOf(flag.longValue() | (1 << 23)));
    1235        } else {
    1236            if (tempMethodRIPA == null) {
    1237                tempMethodRIPA = new TempParamAnnotation(numMethodArgs);
    1238                tempMethodRIPA.addParameterAnnotation(parameter, desc, nameRU, t, values, caseArrayN, nestTypeRS,
    1239                    nestNameRU, nestPairN);
    1240            }
    1241            final Long flag = (Long) tempMethodFlags.remove(tempMethodFlags.size() - 1);
    1242            tempMethodFlags.add(Long.valueOf(flag.longValue() | (1 << 24)));
    1243        }
    1244    }
    1245
    1246    private static class TempParamAnnotation {
    1247
    1248        int numParams;
    1249        int[] annoN;
    1250        IntList pairN = new IntList();
    1251        List typeRS = new ArrayList();
    1252        List nameRU = new ArrayList();
    1253        List t = new ArrayList();
    1254        List values = new ArrayList();
    1255        List caseArrayN = new ArrayList();
    1256        List nestTypeRS = new ArrayList();
    1257        List nestNameRU = new ArrayList();
    1258        List nestPairN = new ArrayList();
    1259
    1260        public TempParamAnnotation(final int numParams) {
    1261            this.numParams = numParams;
    1262            annoN = new int[numParams];
    1263        }
    1264
    1265        public void addParameterAnnotation(final int parameter, final String desc, final List nameRU, final List t,
    1266            final List values, final List caseArrayN, final List nestTypeRS, final List nestNameRU,
    1267            final List nestPairN) {
    1268            annoN[parameter]++;
    1269            typeRS.add(desc);
    1270            pairN.add(nameRU.size());
    1271            this.nameRU.addAll(nameRU);
    1272            this.t.addAll(t);
    1273            this.values.addAll(values);
    1274            this.caseArrayN.addAll(caseArrayN);
    1275            this.nestTypeRS.addAll(nestTypeRS);
    1276            this.nestNameRU.addAll(nestNameRU);
    1277            this.nestPairN.addAll(nestPairN);
    1278        }
    1279    }
    1280
    1281    public void addAnnotation(final int context, final String desc, final boolean visible, final List nameRU,
    1282        final List t, final List values, final List caseArrayN, final List nestTypeRS, final List nestNameRU,
    1283        final List nestPairN) {
    1284        switch (context) {
    1285        case MetadataBandGroup.CONTEXT_CLASS:
    1286            if (visible) {
    1287                class_RVA_bands.addAnnotation(desc, nameRU, t, values, caseArrayN, nestTypeRS, nestNameRU, nestPairN);
    1288                if ((class_flags[index] & (1 << 21)) != 0) {
    1289                    class_RVA_bands.incrementAnnoN();
    1290                } else {
    1291                    class_RVA_bands.newEntryInAnnoN();
    1292                    class_flags[index] = class_flags[index] | (1 << 21);
    1293                }
    1294            } else {
    1295                class_RIA_bands.addAnnotation(desc, nameRU, t, values, caseArrayN, nestTypeRS, nestNameRU, nestPairN);
    1296                if ((class_flags[index] & (1 << 22)) != 0) {
    1297                    class_RIA_bands.incrementAnnoN();
    1298                } else {
    1299                    class_RIA_bands.newEntryInAnnoN();
    1300                    class_flags[index] = class_flags[index] | (1 << 22);
    1301                }
    1302            }
    1303            break;
    1304        case MetadataBandGroup.CONTEXT_FIELD:
    1305            if (visible) {
    1306                field_RVA_bands.addAnnotation(desc, nameRU, t, values, caseArrayN, nestTypeRS, nestNameRU, nestPairN);
    1307                final Long flag = (Long) tempFieldFlags.remove(tempFieldFlags.size() - 1);
    1308                if ((flag.intValue() & (1 << 21)) != 0) {
    1309                    field_RVA_bands.incrementAnnoN();
    1310                } else {
    1311                    field_RVA_bands.newEntryInAnnoN();
    1312                }
    1313                tempFieldFlags.add(Long.valueOf(flag.intValue() | (1 << 21)));
    1314            } else {
    1315                field_RIA_bands.addAnnotation(desc, nameRU, t, values, caseArrayN, nestTypeRS, nestNameRU, nestPairN);
    1316                final Long flag = (Long) tempFieldFlags.remove(tempFieldFlags.size() - 1);
    1317                if ((flag.intValue() & (1 << 22)) != 0) {
    1318                    field_RIA_bands.incrementAnnoN();
    1319                } else {
    1320                    field_RIA_bands.newEntryInAnnoN();
    1321                }
    1322                tempFieldFlags.add(Long.valueOf(flag.intValue() | (1 << 22)));
    1323            }
    1324            break;
    1325        case MetadataBandGroup.CONTEXT_METHOD:
    1326            if (visible) {
    1327                method_RVA_bands.addAnnotation(desc, nameRU, t, values, caseArrayN, nestTypeRS, nestNameRU, nestPairN);
    1328                final Long flag = (Long) tempMethodFlags.remove(tempMethodFlags.size() - 1);
    1329                if ((flag.intValue() & (1 << 21)) != 0) {
    1330                    method_RVA_bands.incrementAnnoN();
    1331                } else {
    1332                    method_RVA_bands.newEntryInAnnoN();
    1333                }
    1334                tempMethodFlags.add(Long.valueOf(flag.intValue() | (1 << 21)));
    1335            } else {
    1336                method_RIA_bands.addAnnotation(desc, nameRU, t, values, caseArrayN, nestTypeRS, nestNameRU, nestPairN);
    1337                final Long flag = (Long) tempMethodFlags.remove(tempMethodFlags.size() - 1);
    1338                if ((flag.intValue() & (1 << 22)) != 0) {
    1339                    method_RIA_bands.incrementAnnoN();
    1340                } else {
    1341                    method_RIA_bands.newEntryInAnnoN();
    1342                }
    1343                tempMethodFlags.add(Long.valueOf(flag.intValue() | (1 << 22)));
    1344            }
    1345            break;
    1346        }
    1347    }
    1348
    1349    public void addAnnotationDefault(final List nameRU, final List t, final List values, final List caseArrayN,
    1350        final List nestTypeRS, final List nestNameRU, final List nestPairN) {
    1351        method_AD_bands.addAnnotation(null, nameRU, t, values, caseArrayN, nestTypeRS, nestNameRU, nestPairN);
    1352        final Long flag = (Long) tempMethodFlags.remove(tempMethodFlags.size() - 1);
    1353        tempMethodFlags.add(Long.valueOf(flag.longValue() | (1 << 25)));
    1354    }
    1355
    1356    /**
    1357     * Remove all entries for the current class
    1358     */
    1359    public void removeCurrentClass() {
    1360        // Note - this doesn't remove any entries added to the constant pool but
    1361        // that shouldn't be a problem
    1362        if ((class_flags[index] & (1 << 17)) != 0) {
    1363            classSourceFile.remove(classSourceFile.size() - 1);
    1364        }
    1365        if ((class_flags[index] & (1 << 18)) != 0) {
    1366            classEnclosingMethodClass.remove(classEnclosingMethodClass.size() - 1);
    1367            classEnclosingMethodDesc.remove(classEnclosingMethodDesc.size() - 1);
    1368        }
    1369        if ((class_flags[index] & (1 << 19)) != 0) {
    1370            classSignature.remove(classSignature.size() - 1);
    1371        }
    1372        if ((class_flags[index] & (1 << 21)) != 0) {
    1373            class_RVA_bands.removeLatest();
    1374        }
    1375        if ((class_flags[index] & (1 << 22)) != 0) {
    1376            class_RIA_bands.removeLatest();
    1377        }
    1378        for (final Iterator iterator = tempFieldFlags.iterator(); iterator.hasNext();) {
    1379            final Long flagsL = (Long) iterator.next();
    1380            final long flags = flagsL.longValue();
    1381            if ((flags & (1 << 19)) != 0) {
    1382                fieldSignature.remove(fieldSignature.size() - 1);
    1383            }
    1384            if ((flags & (1 << 17)) != 0) {
    1385                fieldConstantValueKQ.remove(fieldConstantValueKQ.size() - 1);
    1386            }
    1387            if ((flags & (1 << 21)) != 0) {
    1388                field_RVA_bands.removeLatest();
    1389            }
    1390            if ((flags & (1 << 22)) != 0) {
    1391                field_RIA_bands.removeLatest();
    1392            }
    1393        }
    1394        for (final Iterator iterator = tempMethodFlags.iterator(); iterator.hasNext();) {
    1395            final Long flagsL = (Long) iterator.next();
    1396            final long flags = flagsL.longValue();
    1397            if ((flags & (1 << 19)) != 0) {
    1398                methodSignature.remove(methodSignature.size() - 1);
    1399            }
    1400            if ((flags & (1 << 18)) != 0) {
    1401                final int exceptions = methodExceptionNumber.remove(methodExceptionNumber.size() - 1);
    1402                for (int i = 0; i < exceptions; i++) {
    1403                    methodExceptionClasses.remove(methodExceptionClasses.size() - 1);
    1404                }
    1405            }
    1406            if ((flags & (1 << 17)) != 0) { // has code attribute
    1407                codeMaxLocals.remove(codeMaxLocals.size() - 1);
    1408                codeMaxStack.remove(codeMaxStack.size() - 1);
    1409                final int handlers = codeHandlerCount.remove(codeHandlerCount.size() - 1);
    1410                for (int i = 0; i < handlers; i++) {
    1411                    final int index = codeHandlerStartP.size() - 1;
    1412                    codeHandlerStartP.remove(index);
    1413                    codeHandlerEndPO.remove(index);
    1414                    codeHandlerCatchPO.remove(index);
    1415                    codeHandlerClass.remove(index);
    1416                }
    1417                if (!stripDebug) {
    1418                    final long cdeFlags = ((Long) codeFlags.remove(codeFlags.size() - 1)).longValue();
    1419                    final int numLocalVariables = codeLocalVariableTableN.remove(codeLocalVariableTableN.size() - 1);
    1420                    for (int i = 0; i < numLocalVariables; i++) {
    1421                        final int location = codeLocalVariableTableBciP.size() - 1;
    1422                        codeLocalVariableTableBciP.remove(location);
    1423                        codeLocalVariableTableSpanO.remove(location);
    1424                        codeLocalVariableTableNameRU.remove(location);
    1425                        codeLocalVariableTableTypeRS.remove(location);
    1426                        codeLocalVariableTableSlot.remove(location);
    1427                    }
    1428                    if ((cdeFlags & (1 << 3)) != 0) {
    1429                        final int numLocalVariablesInTypeTable = codeLocalVariableTypeTableN
    1430                            .remove(codeLocalVariableTypeTableN.size() - 1);
    1431                        for (int i = 0; i < numLocalVariablesInTypeTable; i++) {
    1432                            final int location = codeLocalVariableTypeTableBciP.size() - 1;
    1433                            codeLocalVariableTypeTableBciP.remove(location);
    1434                            codeLocalVariableTypeTableSpanO.remove(location);
    1435                            codeLocalVariableTypeTableNameRU.remove(location);
    1436                            codeLocalVariableTypeTableTypeRS.remove(location);
    1437                            codeLocalVariableTypeTableSlot.remove(location);
    1438                        }
    1439                    }
    1440                    if ((cdeFlags & (1 << 1)) != 0) {
    1441                        final int numLineNumbers = codeLineNumberTableN.remove(codeLineNumberTableN.size() - 1);
    1442                        for (int i = 0; i < numLineNumbers; i++) {
    1443                            final int location = codeLineNumberTableBciP.size() - 1;
    1444                            codeLineNumberTableBciP.remove(location);
    1445                            codeLineNumberTableLine.remove(location);
    1446                        }
    1447                    }
    1448                }
    1449            }
    1450            if ((flags & (1 << 21)) != 0) {
    1451                method_RVA_bands.removeLatest();
    1452            }
    1453            if ((flags & (1 << 22)) != 0) {
    1454                method_RIA_bands.removeLatest();
    1455            }
    1456            if ((flags & (1 << 23)) != 0) {
    1457                method_RVPA_bands.removeLatest();
    1458            }
    1459            if ((flags & (1 << 24)) != 0) {
    1460                method_RIPA_bands.removeLatest();
    1461            }
    1462            if ((flags & (1 << 25)) != 0) {
    1463                method_AD_bands.removeLatest();
    1464            }
    1465        }
    1466        class_this[index] = null;
    1467        class_super[index] = null;
    1468        class_interface_count[index] = 0;
    1469        class_interface[index] = null;
    1470        major_versions[index] = 0;
    1471        class_flags[index] = 0;
    1472        tempFieldDesc.clear();
    1473        tempFieldFlags.clear();
    1474        tempMethodDesc.clear();
    1475        tempMethodFlags.clear();
    1476        if (index > 0) {
    1477            index--;
    1478        }
    1479    }
    1480
    1481    public int numClassesProcessed() {
    1482        return index;
    1483    }
    1484}