    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.unpack200;
    018
    019import java.io.IOException;
    020import java.io.InputStream;
    021import java.util.ArrayList;
    022import java.util.Arrays;
    023import java.util.List;
    024import java.util.stream.Collectors;
    025import java.util.stream.Stream;
    026
    027import org.apache.commons.compress.harmony.pack200.Codec;
    028import org.apache.commons.compress.harmony.pack200.Pack200Exception;
    029import org.apache.commons.compress.harmony.unpack200.bytecode.Attribute;
    030import org.apache.commons.compress.harmony.unpack200.bytecode.CPClass;
    031import org.apache.commons.compress.harmony.unpack200.bytecode.CPNameAndType;
    032import org.apache.commons.compress.harmony.unpack200.bytecode.CPUTF8;
    033import org.apache.commons.compress.harmony.unpack200.bytecode.ClassFileEntry;
    034import org.apache.commons.compress.harmony.unpack200.bytecode.ConstantValueAttribute;
    035import org.apache.commons.compress.harmony.unpack200.bytecode.DeprecatedAttribute;
    036import org.apache.commons.compress.harmony.unpack200.bytecode.EnclosingMethodAttribute;
    037import org.apache.commons.compress.harmony.unpack200.bytecode.ExceptionsAttribute;
    038import org.apache.commons.compress.harmony.unpack200.bytecode.LineNumberTableAttribute;
    039import org.apache.commons.compress.harmony.unpack200.bytecode.LocalVariableTableAttribute;
    040import org.apache.commons.compress.harmony.unpack200.bytecode.LocalVariableTypeTableAttribute;
    041import org.apache.commons.compress.harmony.unpack200.bytecode.SignatureAttribute;
    042import org.apache.commons.compress.harmony.unpack200.bytecode.SourceFileAttribute;
    043
    044/**
    045 * Class Bands
    046 */
    047public class ClassBands extends BandSet {
    048
    049    private int[] classFieldCount;
    050
    051    private long[] classFlags;
    052
    053    private long[] classAccessFlags; // Access flags for writing to the class
    054    // file
    055
    056    private int[][] classInterfacesInts;
    057
    058    private int[] classMethodCount;
    059
    060    private int[] classSuperInts;
    061
    062    private String[] classThis;
    063
    064    private int[] classThisInts;
    065
    066    private ArrayList<Attribute>[] classAttributes;
    067
    068    private int[] classVersionMajor;
    069
    070    private int[] classVersionMinor;
    071
    072    private IcTuple[][] icLocal;
    073
    074    private List<Attribute>[] codeAttributes;
    075
    076    private int[] codeHandlerCount;
    077
    078    private int[] codeMaxNALocals;
    079
    080    private int[] codeMaxStack;
    081
    082    private ArrayList<Attribute>[][] fieldAttributes;
    083
    084    private String[][] fieldDescr;
    085
    086    private int[][] fieldDescrInts;
    087
    088    private long[][] fieldFlags;
    089
    090    private long[][] fieldAccessFlags;
    091
    092    private ArrayList<Attribute>[][] methodAttributes;
    093
    094    private String[][] methodDescr;
    095
    096    private int[][] methodDescrInts;
    097
    098    private long[][] methodFlags;
    099
    100    private long[][] methodAccessFlags;
    101
    102    private final AttributeLayoutMap attrMap;
    103
    104    private final CpBands cpBands;
    105
    106    private final SegmentOptions options;
    107
    108    private final int classCount;
    109
    110    private int[] methodAttrCalls;
    111
    112    private int[][] codeHandlerStartP;
    113
    114    private int[][] codeHandlerEndPO;
    115
    116    private int[][] codeHandlerCatchPO;
    117
    118    private int[][] codeHandlerClassRCN;
    119
    120    private boolean[] codeHasAttributes;
    121
    122    /**
    123     * @param segment TODO
    124     */
    125    public ClassBands(final Segment segment) {
    126        super(segment);
    127        this.attrMap = segment.getAttrDefinitionBands().getAttributeDefinitionMap();
    128        this.cpBands = segment.getCpBands();
    129        this.classCount = header.getClassCount();
    130        this.options = header.getOptions();
    131
    132    }
    133
    134    private int getCallCount(final int[][] methodAttrIndexes, final long[][] flags, final int context) {
    135        int callCount = 0;
    136        for (final int[] element : methodAttrIndexes) {
    137            for (final int index : element) {
    138                final AttributeLayout layout = attrMap.getAttributeLayout(index, context);
    139                callCount += layout.numBackwardsCallables();
    140            }
    141        }
    142        int layoutsUsed = 0;
    143        for (final long[] flag : flags) {
    144            for (final long element : flag) {
    145                layoutsUsed |= element;
    146            }
    147        }
    148        for (int i = 0; i < 26; i++) {
    149            if ((layoutsUsed & 1 << i) != 0) {
    150                final AttributeLayout layout = attrMap.getAttributeLayout(i, context);
    151                callCount += layout.numBackwardsCallables();
    152            }
    153        }
    154        return callCount;
    155    }
    156
    157    public ArrayList<Attribute>[] getClassAttributes() {
    158        return classAttributes;
    159    }
    160
    161    public int[] getClassFieldCount() {
    162        return classFieldCount;
    163    }
    164
    165    public long[] getClassFlags() {
    166        if (classAccessFlags == null) {
    167            long mask = 0x7FFF;
    168            for (int i = 0; i < 16; i++) {
    169                final AttributeLayout layout = attrMap.getAttributeLayout(i, AttributeLayout.CONTEXT_CLASS);
    170                if (layout != null && !layout.isDefaultLayout()) {
    171                    mask &= ~(1 << i);
    172                }
    173            }
    174            classAccessFlags = new long[classFlags.length];
    175            for (int i = 0; i < classFlags.length; i++) {
    176                classAccessFlags[i] = classFlags[i] & mask;
    177            }
    178        }
    179        return classAccessFlags;
    180    }
    181
    182    public int[][] getClassInterfacesInts() {
    183        return classInterfacesInts;
    184    }
    185
    186    public int[] getClassMethodCount() {
    187        return classMethodCount;
    188    }
    189
    190    public int[] getClassSuperInts() {
    191        return classSuperInts;
    192    }
    193
    194    public int[] getClassThisInts() {
    195        return classThisInts;
    196    }
    197
    198    /**
    199     * Returns null if all classes should use the default major and minor version or an array of integers containing the
    200     * major version numberss to use for each class in the segment
    201     *
    202     * @return Class file major version numbers, or null if none specified
    203     */
    204    public int[] getClassVersionMajor() {
    205        return classVersionMajor;
    206    }
    207
    208    /**
    209     * Returns null if all classes should use the default major and minor version or an array of integers containing the
    210     * minor version numberss to use for each class in the segment
    211     *
    212     * @return Class file minor version numbers, or null if none specified
    213     */
    214    public int[] getClassVersionMinor() {
    215        return classVersionMinor;
    216    }
    217
    218    public int[][] getCodeHandlerCatchPO() {
    219        return codeHandlerCatchPO;
    220    }
    221
    222    public int[][] getCodeHandlerClassRCN() {
    223        return codeHandlerClassRCN;
    224    }
    225
    226    public int[] getCodeHandlerCount() {
    227        return codeHandlerCount;
    228    }
    229
    230    public int[][] getCodeHandlerEndPO() {
    231        return codeHandlerEndPO;
    232    }
    233
    234    public int[][] getCodeHandlerStartP() {
    235        return codeHandlerStartP;
    236    }
    237
    238    public boolean[] getCodeHasAttributes() {
    239        return codeHasAttributes;
    240    }
    241
    242    public int[] getCodeMaxNALocals() {
    243        return codeMaxNALocals;
    244    }
    245
    246    public int[] getCodeMaxStack() {
    247        return codeMaxStack;
    248    }
    249
    250    public ArrayList<Attribute>[][] getFieldAttributes() {
    251        return fieldAttributes;
    252    }
    253
    254    public int[][] getFieldDescrInts() {
    255        return fieldDescrInts;
    256    }
    257
    258    public long[][] getFieldFlags() {
    259        if (fieldAccessFlags == null) {
    260            long mask = 0x7FFF;
    261            for (int i = 0; i < 16; i++) {
    262                final AttributeLayout layout = attrMap.getAttributeLayout(i, AttributeLayout.CONTEXT_FIELD);
    263                if (layout != null && !layout.isDefaultLayout()) {
    264                    mask &= ~(1 << i);
    265                }
    266            }
    267            fieldAccessFlags = new long[fieldFlags.length][];
    268            for (int i = 0; i < fieldFlags.length; i++) {
    269                fieldAccessFlags[i] = new long[fieldFlags[i].length];
    270                for (int j = 0; j < fieldFlags[i].length; j++) {
    271                    fieldAccessFlags[i][j] = fieldFlags[i][j] & mask;
    272                }
    273            }
    274        }
    275        return fieldAccessFlags;
    276    }
    277
    278    public IcTuple[][] getIcLocal() {
    279        return icLocal;
    280    }
    281
    282    public ArrayList<Attribute>[][] getMethodAttributes() {
    283        return methodAttributes;
    284    }
    285
    286    public String[][] getMethodDescr() {
    287        return methodDescr;
    288    }
    289
    290    public int[][] getMethodDescrInts() {
    291        return methodDescrInts;
    292    }
    293
    294    public long[][] getMethodFlags() {
    295        if (methodAccessFlags == null) {
    296            long mask = 0x7FFF;
    297            for (int i = 0; i < 16; i++) {
    298                final AttributeLayout layout = attrMap.getAttributeLayout(i, AttributeLayout.CONTEXT_METHOD);
    299                if (layout != null && !layout.isDefaultLayout()) {
    300                    mask &= ~(1 << i);
    301                }
    302            }
    303            methodAccessFlags = new long[methodFlags.length][];
    304            for (int i = 0; i < methodFlags.length; i++) {
    305                methodAccessFlags[i] = new long[methodFlags[i].length];
    306                for (int j = 0; j < methodFlags[i].length; j++) {
    307                    methodAccessFlags[i][j] = methodFlags[i][j] & mask;
    308                }
    309            }
    310        }
    311        return methodAccessFlags;
    312    }
    313
    314    /**
    315     * Gets an ArrayList of ArrayLists which hold the code attributes corresponding to all classes in order.
    316     *
    317     * If a class doesn't have any attributes, the corresponding element in this list will be an empty ArrayList.
    318     *
    319     * @return ArrayList
    320     */
    321    public ArrayList<List<Attribute>> getOrderedCodeAttributes() {
    322        return Stream.of(codeAttributes).map(ArrayList::new).collect(Collectors.toCollection(ArrayList::new));
    323    }
    324
    325    public long[] getRawClassFlags() {
    326        return classFlags;
    327    }
    328
    329    private void parseClassAttrBands(final InputStream in) throws IOException, Pack200Exception {
    330        final String[] cpUTF8 = cpBands.getCpUTF8();
    331        final String[] cpClass = cpBands.getCpClass();
    332
    333        // Prepare empty attribute lists
    334        classAttributes = new ArrayList[classCount];
    335        Arrays.setAll(classAttributes, i -> new ArrayList<>());
    336
    337        classFlags = parseFlags("class_flags", in, classCount, Codec.UNSIGNED5, options.hasClassFlagsHi());
    338        final int classAttrCount = SegmentUtils.countBit16(classFlags);
    339        final int[] classAttrCounts = decodeBandInt("class_attr_count", in, Codec.UNSIGNED5, classAttrCount);
    340        final int[][] classAttrIndexes = decodeBandInt("class_attr_indexes", in, Codec.UNSIGNED5, classAttrCounts);
    341        final int callCount = getCallCount(classAttrIndexes, new long[][] {classFlags}, AttributeLayout.CONTEXT_CLASS);
    342        final int[] classAttrCalls = decodeBandInt("class_attr_calls", in, Codec.UNSIGNED5, callCount);
    343
    344        final AttributeLayout deprecatedLayout = attrMap.getAttributeLayout(AttributeLayout.ATTRIBUTE_DEPRECATED,
    345            AttributeLayout.CONTEXT_CLASS);
    346
    347        final AttributeLayout sourceFileLayout = attrMap.getAttributeLayout(AttributeLayout.ATTRIBUTE_SOURCE_FILE,
    348            AttributeLayout.CONTEXT_CLASS);
    349        final int sourceFileCount = SegmentUtils.countMatches(classFlags, sourceFileLayout);
    350        final int[] classSourceFile = decodeBandInt("class_SourceFile_RUN", in, Codec.UNSIGNED5, sourceFileCount);
    351
    352        final AttributeLayout enclosingMethodLayout = attrMap
    353            .getAttributeLayout(AttributeLayout.ATTRIBUTE_ENCLOSING_METHOD, AttributeLayout.CONTEXT_CLASS);
    354        final int enclosingMethodCount = SegmentUtils.countMatches(classFlags, enclosingMethodLayout);
    355        final int[] enclosingMethodRC = decodeBandInt("class_EnclosingMethod_RC", in, Codec.UNSIGNED5,
    356            enclosingMethodCount);
    357        final int[] enclosingMethodRDN = decodeBandInt("class_EnclosingMethod_RDN", in, Codec.UNSIGNED5,
    358            enclosingMethodCount);
    359
    360        final AttributeLayout signatureLayout = attrMap.getAttributeLayout(AttributeLayout.ATTRIBUTE_SIGNATURE,
    361            AttributeLayout.CONTEXT_CLASS);
    362        final int signatureCount = SegmentUtils.countMatches(classFlags, signatureLayout);
    363        final int[] classSignature = decodeBandInt("class_Signature_RS", in, Codec.UNSIGNED5, signatureCount);
    364
    365        final int backwardsCallsUsed = parseClassMetadataBands(in, classAttrCalls);
    366
    367        final AttributeLayout innerClassLayout = attrMap.getAttributeLayout(AttributeLayout.ATTRIBUTE_INNER_CLASSES,
    368            AttributeLayout.CONTEXT_CLASS);
    369        final int innerClassCount = SegmentUtils.countMatches(classFlags, innerClassLayout);
    370        final int[] classInnerClassesN = decodeBandInt("class_InnerClasses_N", in, Codec.UNSIGNED5, innerClassCount);
    371        final int[][] classInnerClassesRC = decodeBandInt("class_InnerClasses_RC", in, Codec.UNSIGNED5,
    372            classInnerClassesN);
    373        final int[][] classInnerClassesF = decodeBandInt("class_InnerClasses_F", in, Codec.UNSIGNED5,
    374            classInnerClassesN);
    375        int flagsCount = 0;
    376        for (final int[] element : classInnerClassesF) {
    377            for (final int element2 : element) {
    378                if (element2 != 0) {
    379                    flagsCount++;
    380                }
    381            }
    382        }
    383        final int[] classInnerClassesOuterRCN = decodeBandInt("class_InnerClasses_outer_RCN", in, Codec.UNSIGNED5,
    384            flagsCount);
    385        final int[] classInnerClassesNameRUN = decodeBandInt("class_InnerClasses_name_RUN", in, Codec.UNSIGNED5,
    386            flagsCount);
    387
    388        final AttributeLayout versionLayout = attrMap.getAttributeLayout(AttributeLayout.ATTRIBUTE_CLASS_FILE_VERSION,
    389            AttributeLayout.CONTEXT_CLASS);
    390        final int versionCount = SegmentUtils.countMatches(classFlags, versionLayout);
    391        final int[] classFileVersionMinorH = decodeBandInt("class_file_version_minor_H", in, Codec.UNSIGNED5,
    392            versionCount);
    393        final int[] classFileVersionMajorH = decodeBandInt("class_file_version_major_H", in, Codec.UNSIGNED5,
    394            versionCount);
    395        if (versionCount > 0) {
    396            classVersionMajor = new int[classCount];
    397            classVersionMinor = new int[classCount];
    398        }
    399        final int defaultVersionMajor = header.getDefaultClassMajorVersion();
    400        final int defaultVersionMinor = header.getDefaultClassMinorVersion();
    401
    402        // Parse non-predefined attribute bands
    403        int backwardsCallIndex = backwardsCallsUsed;
    404        final int limit = options.hasClassFlagsHi() ? 62 : 31;
    405        final AttributeLayout[] otherLayouts = new AttributeLayout[limit + 1];
    406        final int[] counts = new int[limit + 1];
    407        final List<Attribute>[] otherAttributes = new List[limit + 1];
    408        for (int i = 0; i < limit; i++) {
    409            final AttributeLayout layout = attrMap.getAttributeLayout(i, AttributeLayout.CONTEXT_CLASS);
    410            if (layout != null && !(layout.isDefaultLayout())) {
    411                otherLayouts[i] = layout;
    412                counts[i] = SegmentUtils.countMatches(classFlags, layout);
    413            }
    414        }
    415        for (int i = 0; i < counts.length; i++) {
    416            if (counts[i] > 0) {
    417                final NewAttributeBands bands = attrMap.getAttributeBands(otherLayouts[i]);
    418                otherAttributes[i] = bands.parseAttributes(in, counts[i]);
    419                final int numBackwardsCallables = otherLayouts[i].numBackwardsCallables();
    420                if (numBackwardsCallables > 0) {
    421                    final int[] backwardsCalls = new int[numBackwardsCallables];
    422                    System.arraycopy(classAttrCalls, backwardsCallIndex, backwardsCalls, 0, numBackwardsCallables);
    423                    bands.setBackwardsCalls(backwardsCalls);
    424                    backwardsCallIndex += numBackwardsCallables;
    425                }
    426            }
    427        }
    428
    429        // Now process the attribute bands we have parsed
    430        int sourceFileIndex = 0;
    431        int enclosingMethodIndex = 0;
    432        int signatureIndex = 0;
    433        int innerClassIndex = 0;
    434        int innerClassC2NIndex = 0;
    435        int versionIndex = 0;
    436        icLocal = new IcTuple[classCount][];
    437        for (int i = 0; i < classCount; i++) {
    438            final long flag = classFlags[i];
    439            if (deprecatedLayout.matches(classFlags[i])) {
    440                classAttributes[i].add(new DeprecatedAttribute());
    441            }
    442            if (sourceFileLayout.matches(flag)) {
    443                final long result = classSourceFile[sourceFileIndex];
    444                ClassFileEntry value = sourceFileLayout.getValue(result, cpBands.getConstantPool());
    445                if (value == null) {
    446                    // Remove package prefix
    447                    String className = classThis[i].substring(classThis[i].lastIndexOf('/') + 1);
    448                    className = className.substring(className.lastIndexOf('.') + 1);
    449
    450                    // Remove mangled nested class names
    451                    final char[] chars = className.toCharArray();
    452                    int index = -1;
    453                    for (int j = 0; j < chars.length; j++) {
    454                        if (chars[j] <= 0x2D) {
    455                            index = j;
    456                            break;
    457                        }
    458                    }
    459                    if (index > -1) {
    460                        className = className.substring(0, index);
    461                    }
    462                    // Add .java to the end
    463                    value = cpBands.cpUTF8Value(className + ".java", true);
    464                }
    465                classAttributes[i].add(new SourceFileAttribute((CPUTF8) value));
    466                sourceFileIndex++;
    467            }
    468            if (enclosingMethodLayout.matches(flag)) {
    469                final CPClass theClass = cpBands.cpClassValue(enclosingMethodRC[enclosingMethodIndex]);
    470                CPNameAndType theMethod = null;
    471                if (enclosingMethodRDN[enclosingMethodIndex] != 0) {
    472                    theMethod = cpBands.cpNameAndTypeValue(enclosingMethodRDN[enclosingMethodIndex] - 1);
    473                }
    474                classAttributes[i].add(new EnclosingMethodAttribute(theClass, theMethod));
    475                enclosingMethodIndex++;
    476            }
    477            if (signatureLayout.matches(flag)) {
    478                final long result = classSignature[signatureIndex];
    479                final CPUTF8 value = (CPUTF8) signatureLayout.getValue(result, cpBands.getConstantPool());
    480                classAttributes[i].add(new SignatureAttribute(value));
    481                signatureIndex++;
    482            }
    483            if (innerClassLayout.matches(flag)) {
    484                // Just create the tuples for now because the attributes are
    485                // decided at the end when creating class constant pools
    486                icLocal[i] = new IcTuple[classInnerClassesN[innerClassIndex]];
    487                for (int j = 0; j < icLocal[i].length; j++) {
    488                    final int icTupleCIndex = classInnerClassesRC[innerClassIndex][j];
    489                    int icTupleC2Index = -1;
    490                    int icTupleNIndex = -1;
    491
    492                    final String icTupleC = cpClass[icTupleCIndex];
    493                    int icTupleF = classInnerClassesF[innerClassIndex][j];
    494                    String icTupleC2 = null;
    495                    String icTupleN = null;
    496
    497                    if (icTupleF != 0) {
    498                        icTupleC2Index = classInnerClassesOuterRCN[innerClassC2NIndex];
    499                        icTupleNIndex = classInnerClassesNameRUN[innerClassC2NIndex];
    500                        icTupleC2 = cpClass[icTupleC2Index];
    501                        icTupleN = cpUTF8[icTupleNIndex];
    502                        innerClassC2NIndex++;
    503                    } else {
    504                        // Get from icBands
    505                        final IcBands icBands = segment.getIcBands();
    506                        final IcTuple[] icAll = icBands.getIcTuples();
    507                        for (final IcTuple element : icAll) {
    508                            if (element.getC().equals(icTupleC)) {
    509                                icTupleF = element.getF();
    510                                icTupleC2 = element.getC2();
    511                                icTupleN = element.getN();
    512                                break;
    513                            }
    514                        }
    515                    }
    516
    517                    final IcTuple icTuple = new IcTuple(icTupleC, icTupleF, icTupleC2, icTupleN, icTupleCIndex,
    518                        icTupleC2Index, icTupleNIndex, j);
    519                    icLocal[i][j] = icTuple;
    520                }
    521                innerClassIndex++;
    522            }
    523            if (versionLayout.matches(flag)) {
    524                classVersionMajor[i] = classFileVersionMajorH[versionIndex];
    525                classVersionMinor[i] = classFileVersionMinorH[versionIndex];
    526                versionIndex++;
    527            } else if (classVersionMajor != null) {
    528                // Fill in with defaults
    529                classVersionMajor[i] = defaultVersionMajor;
    530                classVersionMinor[i] = defaultVersionMinor;
    531            }
    532            // Non-predefined attributes
    533            for (int j = 0; j < otherLayouts.length; j++) {
    534                if (otherLayouts[j] != null && otherLayouts[j].matches(flag)) {
    535                    // Add the next attribute
    536                    classAttributes[i].add(otherAttributes[j].get(0));
    537                    otherAttributes[j].remove(0);
    538                }
    539            }
    540        }
    541    }
    542
    543    /**
    544     * Parse the class metadata bands and return the number of backwards callables.
    545     *
    546     * @param in TODO
    547     * @param classAttrCalls TODO
    548     * @return the number of backwards callables.
    549     * @throws Pack200Exception TODO
    550     * @throws IOException If an I/O error occurs.
    551     */
    552    private int parseClassMetadataBands(final InputStream in, final int[] classAttrCalls)
    553        throws Pack200Exception, IOException {
    554        int numBackwardsCalls = 0;
    555        final String[] RxA = {"RVA", "RIA"};
    556
    557        final AttributeLayout rvaLayout = attrMap
    558            .getAttributeLayout(AttributeLayout.ATTRIBUTE_RUNTIME_VISIBLE_ANNOTATIONS, AttributeLayout.CONTEXT_CLASS);
    559        final AttributeLayout riaLayout = attrMap
    560            .getAttributeLayout(AttributeLayout.ATTRIBUTE_RUNTIME_INVISIBLE_ANNOTATIONS, AttributeLayout.CONTEXT_CLASS);
    561        final int rvaCount = SegmentUtils.countMatches(classFlags, rvaLayout);
    562        final int riaCount = SegmentUtils.countMatches(classFlags, riaLayout);
    563        final int[] RxACount = {rvaCount, riaCount};
    564        final int[] backwardsCalls = {0, 0};
    565        if (rvaCount > 0) {
    566            numBackwardsCalls++;
    567            backwardsCalls[0] = classAttrCalls[0];
    568            if (riaCount > 0) {
    569                numBackwardsCalls++;
    570                backwardsCalls[1] = classAttrCalls[1];
    571            }
    572        } else if (riaCount > 0) {
    573            numBackwardsCalls++;
    574            backwardsCalls[1] = classAttrCalls[0];
    575        }
    576        final MetadataBandGroup[] mbgs = parseMetadata(in, RxA, RxACount, backwardsCalls, "class");
    577        final List<Attribute> rvaAttributes = mbgs[0].getAttributes();
    578        final List<Attribute> riaAttributes = mbgs[1].getAttributes();
    579        int rvaAttributesIndex = 0;
    580        int riaAttributesIndex = 0;
    581        for (int i = 0; i < classFlags.length; i++) {
    582            if (rvaLayout.matches(classFlags[i])) {
    583                classAttributes[i].add(rvaAttributes.get(rvaAttributesIndex++));
    584            }
    585            if (riaLayout.matches(classFlags[i])) {
    586                classAttributes[i].add(riaAttributes.get(riaAttributesIndex++));
    587            }
    588        }
    589        return numBackwardsCalls;
    590    }
    591
    592    private void parseCodeAttrBands(final InputStream in, final int codeFlagsCount)
    593        throws IOException, Pack200Exception {
    594        final long[] codeFlags = parseFlags("code_flags", in, codeFlagsCount, Codec.UNSIGNED5,
    595            segment.getSegmentHeader().getOptions().hasCodeFlagsHi());
    596        final int codeAttrCount = SegmentUtils.countBit16(codeFlags);
    597        final int[] codeAttrCounts = decodeBandInt("code_attr_count", in, Codec.UNSIGNED5, codeAttrCount);
    598        final int[][] codeAttrIndexes = decodeBandInt("code_attr_indexes", in, Codec.UNSIGNED5, codeAttrCounts);
    599        int callCount = 0;
    600        for (final int[] element : codeAttrIndexes) {
    601            for (final int index : element) {
    602                final AttributeLayout layout = attrMap.getAttributeLayout(index, AttributeLayout.CONTEXT_CODE);
    603                callCount += layout.numBackwardsCallables();
    604            }
    605        }
    606        final int[] codeAttrCalls = decodeBandInt("code_attr_calls", in, Codec.UNSIGNED5, callCount);
    607
    608        final AttributeLayout lineNumberTableLayout = attrMap
    609            .getAttributeLayout(AttributeLayout.ATTRIBUTE_LINE_NUMBER_TABLE, AttributeLayout.CONTEXT_CODE);
    610        final int lineNumberTableCount = SegmentUtils.countMatches(codeFlags, lineNumberTableLayout);
    611        final int[] lineNumberTableN = decodeBandInt("code_LineNumberTable_N", in, Codec.UNSIGNED5,
    612            lineNumberTableCount);
    613        final int[][] lineNumberTableBciP = decodeBandInt("code_LineNumberTable_bci_P", in, Codec.BCI5,
    614            lineNumberTableN);
    615        final int[][] lineNumberTableLine = decodeBandInt("code_LineNumberTable_line", in, Codec.UNSIGNED5,
    616            lineNumberTableN);
    617
    618        final AttributeLayout localVariableTableLayout = attrMap
    619            .getAttributeLayout(AttributeLayout.ATTRIBUTE_LOCAL_VARIABLE_TABLE, AttributeLayout.CONTEXT_CODE);
    620        final AttributeLayout localVariableTypeTableLayout = attrMap
    621            .getAttributeLayout(AttributeLayout.ATTRIBUTE_LOCAL_VARIABLE_TYPE_TABLE, AttributeLayout.CONTEXT_CODE);
    622
    623        final int lengthLocalVariableNBand = SegmentUtils.countMatches(codeFlags, localVariableTableLayout);
    624        final int[] localVariableTableN = decodeBandInt("code_LocalVariableTable_N", in, Codec.UNSIGNED5,
    625            lengthLocalVariableNBand);
    626        final int[][] localVariableTableBciP = decodeBandInt("code_LocalVariableTable_bci_P", in, Codec.BCI5,
    627            localVariableTableN);
    628        final int[][] localVariableTableSpanO = decodeBandInt("code_LocalVariableTable_span_O", in, Codec.BRANCH5,
    629            localVariableTableN);
    630        final CPUTF8[][] localVariableTableNameRU = parseCPUTF8References("code_LocalVariableTable_name_RU", in,
    631            Codec.UNSIGNED5, localVariableTableN);
    632        final CPUTF8[][] localVariableTableTypeRS = parseCPSignatureReferences("code_LocalVariableTable_type_RS", in,
    633            Codec.UNSIGNED5, localVariableTableN);
    634        final int[][] localVariableTableSlot = decodeBandInt("code_LocalVariableTable_slot", in, Codec.UNSIGNED5,
    635            localVariableTableN);
    636
    637        final int lengthLocalVariableTypeTableNBand = SegmentUtils.countMatches(codeFlags,
    638            localVariableTypeTableLayout);
    639        final int[] localVariableTypeTableN = decodeBandInt("code_LocalVariableTypeTable_N", in, Codec.UNSIGNED5,
    640            lengthLocalVariableTypeTableNBand);
    641        final int[][] localVariableTypeTableBciP = decodeBandInt("code_LocalVariableTypeTable_bci_P", in, Codec.BCI5,
    642            localVariableTypeTableN);
    643        final int[][] localVariableTypeTableSpanO = decodeBandInt("code_LocalVariableTypeTable_span_O", in,
    644            Codec.BRANCH5, localVariableTypeTableN);
    645        final CPUTF8[][] localVariableTypeTableNameRU = parseCPUTF8References("code_LocalVariableTypeTable_name_RU", in,
    646            Codec.UNSIGNED5, localVariableTypeTableN);
    647        final CPUTF8[][] localVariableTypeTableTypeRS = parseCPSignatureReferences(
    648            "code_LocalVariableTypeTable_type_RS", in, Codec.UNSIGNED5, localVariableTypeTableN);
    649        final int[][] localVariableTypeTableSlot = decodeBandInt("code_LocalVariableTypeTable_slot", in,
    650            Codec.UNSIGNED5, localVariableTypeTableN);
    651
    652        // Parse non-predefined attribute bands
    653        int backwardsCallIndex = 0;
    654        final int limit = options.hasCodeFlagsHi() ? 62 : 31;
    655        final AttributeLayout[] otherLayouts = new AttributeLayout[limit + 1];
    656        final int[] counts = new int[limit + 1];
    657        final List<Attribute>[] otherAttributes = new List[limit + 1];
    658        for (int i = 0; i < limit; i++) {
    659            final AttributeLayout layout = attrMap.getAttributeLayout(i, AttributeLayout.CONTEXT_CODE);
    660            if (layout != null && !(layout.isDefaultLayout())) {
    661                otherLayouts[i] = layout;
    662                counts[i] = SegmentUtils.countMatches(codeFlags, layout);
    663            }
    664        }
    665        for (int i = 0; i < counts.length; i++) {
    666            if (counts[i] > 0) {
    667                final NewAttributeBands bands = attrMap.getAttributeBands(otherLayouts[i]);
    668                otherAttributes[i] = bands.parseAttributes(in, counts[i]);
    669                final int numBackwardsCallables = otherLayouts[i].numBackwardsCallables();
    670                if (numBackwardsCallables > 0) {
    671                    final int[] backwardsCalls = new int[numBackwardsCallables];
    672                    System.arraycopy(codeAttrCalls, backwardsCallIndex, backwardsCalls, 0, numBackwardsCallables);
    673                    bands.setBackwardsCalls(backwardsCalls);
    674                    backwardsCallIndex += numBackwardsCallables;
    675                }
    676            }
    677        }
    678
    679        int lineNumberIndex = 0;
    680        int lvtIndex = 0;
    681        int lvttIndex = 0;
    682        for (int i = 0; i < codeFlagsCount; i++) {
    683            if (lineNumberTableLayout.matches(codeFlags[i])) {
    684                final LineNumberTableAttribute lnta = new LineNumberTableAttribute(lineNumberTableN[lineNumberIndex],
    685                    lineNumberTableBciP[lineNumberIndex], lineNumberTableLine[lineNumberIndex]);
    686                lineNumberIndex++;
    687                codeAttributes[i].add(lnta);
    688            }
    689            if (localVariableTableLayout.matches(codeFlags[i])) {
    690                final LocalVariableTableAttribute lvta = new LocalVariableTableAttribute(localVariableTableN[lvtIndex],
    691                    localVariableTableBciP[lvtIndex], localVariableTableSpanO[lvtIndex],
    692                    localVariableTableNameRU[lvtIndex], localVariableTableTypeRS[lvtIndex],
    693                    localVariableTableSlot[lvtIndex]);
    694                lvtIndex++;
    695                codeAttributes[i].add(lvta);
    696            }
    697            if (localVariableTypeTableLayout.matches(codeFlags[i])) {
    698                final LocalVariableTypeTableAttribute lvtta = new LocalVariableTypeTableAttribute(
    699                    localVariableTypeTableN[lvttIndex], localVariableTypeTableBciP[lvttIndex],
    700                    localVariableTypeTableSpanO[lvttIndex], localVariableTypeTableNameRU[lvttIndex],
    701                    localVariableTypeTableTypeRS[lvttIndex], localVariableTypeTableSlot[lvttIndex]);
    702                lvttIndex++;
    703                codeAttributes[i].add(lvtta);
    704            }
    705            // Non-predefined attributes
    706            for (int j = 0; j < otherLayouts.length; j++) {
    707                if (otherLayouts[j] != null && otherLayouts[j].matches(codeFlags[i])) {
    708                    // Add the next attribute
    709                    codeAttributes[i].add(otherAttributes[j].get(0));
    710                    otherAttributes[j].remove(0);
    711                }
    712            }
    713        }
    714
    715    }
    716
    717    private void parseCodeBands(final InputStream in) throws Pack200Exception, IOException {
    718        final AttributeLayout layout = attrMap.getAttributeLayout(AttributeLayout.ATTRIBUTE_CODE,
    719            AttributeLayout.CONTEXT_METHOD);
    720
    721        final int codeCount = SegmentUtils.countMatches(methodFlags, layout);
    722        final int[] codeHeaders = decodeBandInt("code_headers", in, Codec.BYTE1, codeCount);
    723
    724        final boolean allCodeHasFlags = segment.getSegmentHeader().getOptions().hasAllCodeFlags();
    725        if (!allCodeHasFlags) {
    726            codeHasAttributes = new boolean[codeCount];
    727        }
    728        int codeSpecialHeader = 0;
    729        for (int i = 0; i < codeCount; i++) {
    730            if (codeHeaders[i] == 0) {
    731                codeSpecialHeader++;
    732                if (!allCodeHasFlags) {
    733                    codeHasAttributes[i] = true;
    734                }
    735            }
    736        }
    737        final int[] codeMaxStackSpecials = decodeBandInt("code_max_stack", in, Codec.UNSIGNED5, codeSpecialHeader);
    738        final int[] codeMaxNALocalsSpecials = decodeBandInt("code_max_na_locals", in, Codec.UNSIGNED5,
    739            codeSpecialHeader);
    740        final int[] codeHandlerCountSpecials = decodeBandInt("code_handler_count", in, Codec.UNSIGNED5,
    741            codeSpecialHeader);
    742
    743        codeMaxStack = new int[codeCount];
    744        codeMaxNALocals = new int[codeCount];
    745        codeHandlerCount = new int[codeCount];
    746        int special = 0;
    747        for (int i = 0; i < codeCount; i++) {
    748            final int header = 0xff & codeHeaders[i];
    749            if (header < 0) {
    750                throw new IllegalStateException("Shouldn't get here");
    751            }
    752            if (header == 0) {
    753                codeMaxStack[i] = codeMaxStackSpecials[special];
    754                codeMaxNALocals[i] = codeMaxNALocalsSpecials[special];
    755                codeHandlerCount[i] = codeHandlerCountSpecials[special];
    756                special++;
    757            } else if (header <= 144) {
    758                codeMaxStack[i] = (header - 1) % 12;
    759                codeMaxNALocals[i] = (header - 1) / 12;
    760                codeHandlerCount[i] = 0;
    761            } else if (header <= 208) {
    762                codeMaxStack[i] = (header - 145) % 8;
    763                codeMaxNALocals[i] = (header - 145) / 8;
    764                codeHandlerCount[i] = 1;
    765            } else if (header <= 255) {
    766                codeMaxStack[i] = (header - 209) % 7;
    767                codeMaxNALocals[i] = (header - 209) / 7;
    768                codeHandlerCount[i] = 2;
    769            } else {
    770                throw new IllegalStateException("Shouldn't get here either");
    771            }
    772        }
    773        codeHandlerStartP = decodeBandInt("code_handler_start_P", in, Codec.BCI5, codeHandlerCount);
    774        codeHandlerEndPO = decodeBandInt("code_handler_end_PO", in, Codec.BRANCH5, codeHandlerCount);
    775        codeHandlerCatchPO = decodeBandInt("code_handler_catch_PO", in, Codec.BRANCH5, codeHandlerCount);
    776        codeHandlerClassRCN = decodeBandInt("code_handler_class_RCN", in, Codec.UNSIGNED5, codeHandlerCount);
    777
    778        final int codeFlagsCount = allCodeHasFlags ? codeCount : codeSpecialHeader;
    779
    780        codeAttributes = new List[codeFlagsCount];
    781        Arrays.setAll(codeAttributes, i -> new ArrayList<>());
    782        parseCodeAttrBands(in, codeFlagsCount);
    783    }
    784
    785    private void parseFieldAttrBands(final InputStream in) throws IOException, Pack200Exception {
    786        fieldFlags = parseFlags("field_flags", in, classFieldCount, Codec.UNSIGNED5, options.hasFieldFlagsHi());
    787        final int fieldAttrCount = SegmentUtils.countBit16(fieldFlags);
    788        final int[] fieldAttrCounts = decodeBandInt("field_attr_count", in, Codec.UNSIGNED5, fieldAttrCount);
    789        final int[][] fieldAttrIndexes = decodeBandInt("field_attr_indexes", in, Codec.UNSIGNED5, fieldAttrCounts);
    790        final int callCount = getCallCount(fieldAttrIndexes, fieldFlags, AttributeLayout.CONTEXT_FIELD);
    791        final int[] fieldAttrCalls = decodeBandInt("field_attr_calls", in, Codec.UNSIGNED5, callCount);
    792
    793        // Assign empty field attributes
    794        fieldAttributes = new ArrayList[classCount][];
    795        for (int i = 0; i < classCount; i++) {
    796            fieldAttributes[i] = new ArrayList[fieldFlags[i].length];
    797            for (int j = 0; j < fieldFlags[i].length; j++) {
    798                fieldAttributes[i][j] = new ArrayList<>();
    799            }
    800        }
    801
    802        final AttributeLayout constantValueLayout = attrMap.getAttributeLayout("ConstantValue",
    803            AttributeLayout.CONTEXT_FIELD);
    804        final int constantCount = SegmentUtils.countMatches(fieldFlags, constantValueLayout);
    805        final int[] field_constantValue_KQ = decodeBandInt("field_ConstantValue_KQ", in, Codec.UNSIGNED5,
    806            constantCount);
    807        int constantValueIndex = 0;
    808
    809        final AttributeLayout signatureLayout = attrMap.getAttributeLayout(AttributeLayout.ATTRIBUTE_SIGNATURE,
    810            AttributeLayout.CONTEXT_FIELD);
    811        final int signatureCount = SegmentUtils.countMatches(fieldFlags, signatureLayout);
    812        final int[] fieldSignatureRS = decodeBandInt("field_Signature_RS", in, Codec.UNSIGNED5, signatureCount);
    813        int signatureIndex = 0;
    814
    815        final AttributeLayout deprecatedLayout = attrMap.getAttributeLayout(AttributeLayout.ATTRIBUTE_DEPRECATED,
    816            AttributeLayout.CONTEXT_FIELD);
    817
    818        for (int i = 0; i < classCount; i++) {
    819            for (int j = 0; j < fieldFlags[i].length; j++) {
    820                final long flag = fieldFlags[i][j];
    821                if (deprecatedLayout.matches(flag)) {
    822                    fieldAttributes[i][j].add(new DeprecatedAttribute());
    823                }
    824                if (constantValueLayout.matches(flag)) {
    825                    // we've got a value to read
    826                    final long result = field_constantValue_KQ[constantValueIndex];
    827                    final String desc = fieldDescr[i][j];
    828                    final int colon = desc.indexOf(':');
    829                    String type = desc.substring(colon + 1);
    830                    if (type.equals("B") || type.equals("S") || type.equals("C") || type.equals("Z")) {
    831                        type = "I";
    832                    }
    833                    final ClassFileEntry value = constantValueLayout.getValue(result, type, cpBands.getConstantPool());
    834                    fieldAttributes[i][j].add(new ConstantValueAttribute(value));
    835                    constantValueIndex++;
    836                }
    837                if (signatureLayout.matches(flag)) {
    838                    // we've got a signature attribute
    839                    final long result = fieldSignatureRS[signatureIndex];
    840                    final String desc = fieldDescr[i][j];
    841                    final int colon = desc.indexOf(':');
    842                    final String type = desc.substring(colon + 1);
    843                    final CPUTF8 value = (CPUTF8) signatureLayout.getValue(result, type, cpBands.getConstantPool());
    844                    fieldAttributes[i][j].add(new SignatureAttribute(value));
    845                    signatureIndex++;
    846                }
    847            }
    848        }
    849
    850        // Parse non-predefined attribute bands
    851        int backwardsCallIndex = parseFieldMetadataBands(in, fieldAttrCalls);
    852        final int limit = options.hasFieldFlagsHi() ? 62 : 31;
    853        final AttributeLayout[] otherLayouts = new AttributeLayout[limit + 1];
    854        final int[] counts = new int[limit + 1];
    855        final List<Attribute>[] otherAttributes = new List[limit + 1];
    856        for (int i = 0; i < limit; i++) {
    857            final AttributeLayout layout = attrMap.getAttributeLayout(i, AttributeLayout.CONTEXT_FIELD);
    858            if (layout != null && !(layout.isDefaultLayout())) {
    859                otherLayouts[i] = layout;
    860                counts[i] = SegmentUtils.countMatches(fieldFlags, layout);
    861            }
    862        }
    863        for (int i = 0; i < counts.length; i++) {
    864            if (counts[i] > 0) {
    865                final NewAttributeBands bands = attrMap.getAttributeBands(otherLayouts[i]);
    866                otherAttributes[i] = bands.parseAttributes(in, counts[i]);
    867                final int numBackwardsCallables = otherLayouts[i].numBackwardsCallables();
    868                if (numBackwardsCallables > 0) {
    869                    final int[] backwardsCalls = new int[numBackwardsCallables];
    870                    System.arraycopy(fieldAttrCalls, backwardsCallIndex, backwardsCalls, 0, numBackwardsCallables);
    871                    bands.setBackwardsCalls(backwardsCalls);
    872                    backwardsCallIndex += numBackwardsCallables;
    873                }
    874            }
    875        }
    876
    877        // Non-predefined attributes
    878        for (int i = 0; i < classCount; i++) {
    879            for (int j = 0; j < fieldFlags[i].length; j++) {
    880                final long flag = fieldFlags[i][j];
    881                int othersAddedAtStart = 0;
    882                for (int k = 0; k < otherLayouts.length; k++) {
    883                    if (otherLayouts[k] != null && otherLayouts[k].matches(flag)) {
    884                        // Add the next attribute
    885                        if (otherLayouts[k].getIndex() < 15) {
    886                            fieldAttributes[i][j].add(othersAddedAtStart++, otherAttributes[k].get(0));
    887                        } else {
    888                            fieldAttributes[i][j].add(otherAttributes[k].get(0));
    889                        }
    890                        otherAttributes[k].remove(0);
    891                    }
    892                }
    893            }
    894        }
    895    }
    896
    897    private void parseFieldBands(final InputStream in) throws IOException, Pack200Exception {
    898        fieldDescrInts = decodeBandInt("field_descr", in, Codec.DELTA5, classFieldCount);
    899        fieldDescr = getReferences(fieldDescrInts, cpBands.getCpDescriptor());
    900        parseFieldAttrBands(in);
    901    }
    902
    903    private int parseFieldMetadataBands(final InputStream in, final int[] fieldAttrCalls)
    904        throws Pack200Exception, IOException {
    905        int backwardsCallsUsed = 0;
    906        final String[] RxA = {"RVA", "RIA"};
    907
    908        final AttributeLayout rvaLayout = attrMap
    909            .getAttributeLayout(AttributeLayout.ATTRIBUTE_RUNTIME_VISIBLE_ANNOTATIONS, AttributeLayout.CONTEXT_FIELD);
    910        final AttributeLayout riaLayout = attrMap
    911            .getAttributeLayout(AttributeLayout.ATTRIBUTE_RUNTIME_INVISIBLE_ANNOTATIONS, AttributeLayout.CONTEXT_FIELD);
    912
    913        final int rvaCount = SegmentUtils.countMatches(fieldFlags, rvaLayout);
    914        final int riaCount = SegmentUtils.countMatches(fieldFlags, riaLayout);
    915        final int[] RxACount = {rvaCount, riaCount};
    916        final int[] backwardsCalls = {0, 0};
    917        if (rvaCount > 0) {
    918            backwardsCalls[0] = fieldAttrCalls[0];
    919            backwardsCallsUsed++;
    920            if (riaCount > 0) {
    921                backwardsCalls[1] = fieldAttrCalls[1];
    922                backwardsCallsUsed++;
    923            }
    924        } else if (riaCount > 0) {
    925            backwardsCalls[1] = fieldAttrCalls[0];
    926            backwardsCallsUsed++;
    927        }
    928        final MetadataBandGroup[] mb = parseMetadata(in, RxA, RxACount, backwardsCalls, "field");
    929        final List<Attribute> rvaAttributes = mb[0].getAttributes();
    930        final List<Attribute> riaAttributes = mb[1].getAttributes();
    931        int rvaAttributesIndex = 0;
    932        int riaAttributesIndex = 0;
    933        for (int i = 0; i < fieldFlags.length; i++) {
    934            for (int j = 0; j < fieldFlags[i].length; j++) {
    935                if (rvaLayout.matches(fieldFlags[i][j])) {
    936                    fieldAttributes[i][j].add(rvaAttributes.get(rvaAttributesIndex++));
    937                }
    938                if (riaLayout.matches(fieldFlags[i][j])) {
    939                    fieldAttributes[i][j].add(riaAttributes.get(riaAttributesIndex++));
    940                }
    941            }
    942        }
    943        return backwardsCallsUsed;
    944    }
    945
    946    private MetadataBandGroup[] parseMetadata(final InputStream in, final String[] RxA, final int[] RxACount,
    947        final int[] backwardsCallCounts, final String contextName) throws IOException, Pack200Exception {
    948        final MetadataBandGroup[] mbg = new MetadataBandGroup[RxA.length];
    949        for (int i = 0; i < RxA.length; i++) {
    950            mbg[i] = new MetadataBandGroup(RxA[i], cpBands);
    951            final String rxa = RxA[i];
    952            if (rxa.indexOf('P') >= 0) {
    953                mbg[i].param_NB = decodeBandInt(contextName + "_" + rxa + "_param_NB", in, Codec.BYTE1, RxACount[i]);
    954            }
    955            int pairCount = 0;
    956            if (!rxa.equals("AD")) {
    957                mbg[i].anno_N = decodeBandInt(contextName + "_" + rxa + "_anno_N", in, Codec.UNSIGNED5, RxACount[i]);
    958                mbg[i].type_RS = parseCPSignatureReferences(contextName + "_" + rxa + "_type_RS", in, Codec.UNSIGNED5,
    959                    mbg[i].anno_N);
    960                mbg[i].pair_N = decodeBandInt(contextName + "_" + rxa + "_pair_N", in, Codec.UNSIGNED5, mbg[i].anno_N);
    961                for (final int[] element : mbg[i].pair_N) {
    962                    for (final int element2 : element) {
    963                        pairCount += element2;
    964                    }
    965                }
    966
    967                mbg[i].name_RU = parseCPUTF8References(contextName + "_" + rxa + "_name_RU", in, Codec.UNSIGNED5,
    968                    pairCount);
    969            } else {
    970                pairCount = RxACount[i];
    971            }
    972            mbg[i].T = decodeBandInt(contextName + "_" + rxa + "_T", in, Codec.BYTE1,
    973                pairCount + backwardsCallCounts[i]);
    974            int ICount = 0, DCount = 0, FCount = 0, JCount = 0, cCount = 0, eCount = 0, sCount = 0, arrayCount = 0,
    975                atCount = 0;
    976            for (final int element : mbg[i].T) {
    977                final char c = (char) element;
    978                switch (c) {
    979                case 'B':
    980                case 'C':
    981                case 'I':
    982                case 'S':
    983                case 'Z':
    984                    ICount++;
    985                    break;
    986                case 'D':
    987                    DCount++;
    988                    break;
    989                case 'F':
    990                    FCount++;
    991                    break;
    992                case 'J':
    993                    JCount++;
    994                    break;
    995                case 'c':
    996                    cCount++;
    997                    break;
    998                case 'e':
    999                    eCount++;
    1000                    break;
    1001                case 's':
    1002                    sCount++;
    1003                    break;
    1004                case '[':
    1005                    arrayCount++;
    1006                    break;
    1007                case '@':
    1008                    atCount++;
    1009                    break;
    1010                }
    1011            }
    1012            mbg[i].caseI_KI = parseCPIntReferences(contextName + "_" + rxa + "_caseI_KI", in, Codec.UNSIGNED5, ICount);
    1013            mbg[i].caseD_KD = parseCPDoubleReferences(contextName + "_" + rxa + "_caseD_KD", in, Codec.UNSIGNED5,
    1014                DCount);
    1015            mbg[i].caseF_KF = parseCPFloatReferences(contextName + "_" + rxa + "_caseF_KF", in, Codec.UNSIGNED5,
    1016                FCount);
    1017            mbg[i].caseJ_KJ = parseCPLongReferences(contextName + "_" + rxa + "_caseJ_KJ", in, Codec.UNSIGNED5, JCount);
    1018            mbg[i].casec_RS = parseCPSignatureReferences(contextName + "_" + rxa + "_casec_RS", in, Codec.UNSIGNED5,
    1019                cCount);
    1020            mbg[i].caseet_RS = parseReferences(contextName + "_" + rxa + "_caseet_RS", in, Codec.UNSIGNED5, eCount,
    1021                cpBands.getCpSignature());
    1022            mbg[i].caseec_RU = parseReferences(contextName + "_" + rxa + "_caseec_RU", in, Codec.UNSIGNED5, eCount,
    1023                cpBands.getCpUTF8());
    1024            mbg[i].cases_RU = parseCPUTF8References(contextName + "_" + rxa + "_cases_RU", in, Codec.UNSIGNED5, sCount);
    1025            mbg[i].casearray_N = decodeBandInt(contextName + "_" + rxa + "_casearray_N", in, Codec.UNSIGNED5,
    1026                arrayCount);
    1027            mbg[i].nesttype_RS = parseCPUTF8References(contextName + "_" + rxa + "_nesttype_RS", in, Codec.UNSIGNED5,
    1028                atCount);
    1029            mbg[i].nestpair_N = decodeBandInt(contextName + "_" + rxa + "_nestpair_N", in, Codec.UNSIGNED5, atCount);
    1030            int nestPairCount = 0;
    1031            for (final int element : mbg[i].nestpair_N) {
    1032                nestPairCount += element;
    1033            }
    1034            mbg[i].nestname_RU = parseCPUTF8References(contextName + "_" + rxa + "_nestname_RU", in, Codec.UNSIGNED5,
    1035                nestPairCount);
    1036        }
    1037        return mbg;
    1038    }
    1039
    1040    private void parseMethodAttrBands(final InputStream in) throws IOException, Pack200Exception {
    1041        methodFlags = parseFlags("method_flags", in, classMethodCount, Codec.UNSIGNED5, options.hasMethodFlagsHi());
    1042        final int methodAttrCount = SegmentUtils.countBit16(methodFlags);
    1043        final int[] methodAttrCounts = decodeBandInt("method_attr_count", in, Codec.UNSIGNED5, methodAttrCount);
    1044        final int[][] methodAttrIndexes = decodeBandInt("method_attr_indexes", in, Codec.UNSIGNED5, methodAttrCounts);
    1045        final int callCount = getCallCount(methodAttrIndexes, methodFlags, AttributeLayout.CONTEXT_METHOD);
    1046        methodAttrCalls = decodeBandInt("method_attr_calls", in, Codec.UNSIGNED5, callCount);
    1047
    1048        // assign empty method attributes
    1049        methodAttributes = new ArrayList[classCount][];
    1050        for (int i = 0; i < classCount; i++) {
    1051            methodAttributes[i] = new ArrayList[methodFlags[i].length];
    1052            for (int j = 0; j < methodFlags[i].length; j++) {
    1053                methodAttributes[i][j] = new ArrayList<>();
    1054            }
    1055        }
    1056
    1057        // Parse method exceptions attributes
    1058        final AttributeLayout methodExceptionsLayout = attrMap.getAttributeLayout(AttributeLayout.ATTRIBUTE_EXCEPTIONS,
    1059            AttributeLayout.CONTEXT_METHOD);
    1060        final int count = SegmentUtils.countMatches(methodFlags, methodExceptionsLayout);
    1061        final int[] numExceptions = decodeBandInt("method_Exceptions_n", in, Codec.UNSIGNED5, count);
    1062        final int[][] methodExceptionsRS = decodeBandInt("method_Exceptions_RC", in, Codec.UNSIGNED5, numExceptions);
    1063
    1064        // Parse method signature attributes
    1065        final AttributeLayout methodSignatureLayout = attrMap.getAttributeLayout(AttributeLayout.ATTRIBUTE_SIGNATURE,
    1066            AttributeLayout.CONTEXT_METHOD);
    1067        final int count1 = SegmentUtils.countMatches(methodFlags, methodSignatureLayout);
    1068        final int[] methodSignatureRS = decodeBandInt("method_signature_RS", in, Codec.UNSIGNED5, count1);
    1069
    1070        final AttributeLayout deprecatedLayout = attrMap.getAttributeLayout(AttributeLayout.ATTRIBUTE_DEPRECATED,
    1071            AttributeLayout.CONTEXT_METHOD);
    1072
    1073        // Add attributes to the attribute arrays
    1074        int methodExceptionsIndex = 0;
    1075        int methodSignatureIndex = 0;
    1076        for (int i = 0; i < methodAttributes.length; i++) {
    1077            for (int j = 0; j < methodAttributes[i].length; j++) {
    1078                final long flag = methodFlags[i][j];
    1079                if (methodExceptionsLayout.matches(flag)) {
    1080                    final int n = numExceptions[methodExceptionsIndex];
    1081                    final int[] exceptions = methodExceptionsRS[methodExceptionsIndex];
    1082                    final CPClass[] exceptionClasses = new CPClass[n];
    1083                    for (int k = 0; k < n; k++) {
    1084                        exceptionClasses[k] = cpBands.cpClassValue(exceptions[k]);
    1085                    }
    1086                    methodAttributes[i][j].add(new ExceptionsAttribute(exceptionClasses));
    1087                    methodExceptionsIndex++;
    1088                }
    1089                if (methodSignatureLayout.matches(flag)) {
    1090                    // We've got a signature attribute
    1091                    final long result = methodSignatureRS[methodSignatureIndex];
    1092                    final String desc = methodDescr[i][j];
    1093                    final int colon = desc.indexOf(':');
    1094                    String type = desc.substring(colon + 1);
    1095                    // TODO Got to get better at this ... in any case, it should
    1096                    // be e.g. KIB or KIH
    1097                    if (type.equals("B") || type.equals("H")) {
    1098                        type = "I";
    1099                    }
    1100                    final CPUTF8 value = (CPUTF8) methodSignatureLayout.getValue(result, type,
    1101                        cpBands.getConstantPool());
    1102                    methodAttributes[i][j].add(new SignatureAttribute(value));
    1103                    methodSignatureIndex++;
    1104                }
    1105                if (deprecatedLayout.matches(flag)) {
    1106                    methodAttributes[i][j].add(new DeprecatedAttribute());
    1107                }
    1108            }
    1109        }
    1110
    1111        // Parse non-predefined attribute bands
    1112        int backwardsCallIndex = parseMethodMetadataBands(in, methodAttrCalls);
    1113        final int limit = options.hasMethodFlagsHi() ? 62 : 31;
    1114        final AttributeLayout[] otherLayouts = new AttributeLayout[limit + 1];
    1115        final int[] counts = new int[limit + 1];
    1116        for (int i = 0; i < limit; i++) {
    1117            final AttributeLayout layout = attrMap.getAttributeLayout(i, AttributeLayout.CONTEXT_METHOD);
    1118            if (layout != null && !(layout.isDefaultLayout())) {
    1119                otherLayouts[i] = layout;
    1120                counts[i] = SegmentUtils.countMatches(methodFlags, layout);
    1121            }
    1122        }
    1123        final List<Attribute>[] otherAttributes = new List[limit + 1];
    1124        for (int i = 0; i < counts.length; i++) {
    1125            if (counts[i] > 0) {
    1126                final NewAttributeBands bands = attrMap.getAttributeBands(otherLayouts[i]);
    1127                otherAttributes[i] = bands.parseAttributes(in, counts[i]);
    1128                final int numBackwardsCallables = otherLayouts[i].numBackwardsCallables();
    1129                if (numBackwardsCallables > 0) {
    1130                    final int[] backwardsCalls = new int[numBackwardsCallables];
    1131                    System.arraycopy(methodAttrCalls, backwardsCallIndex, backwardsCalls, 0, numBackwardsCallables);
    1132                    bands.setBackwardsCalls(backwardsCalls);
    1133                    backwardsCallIndex += numBackwardsCallables;
    1134                }
    1135            }
    1136        }
    1137
    1138        // Non-predefined attributes
    1139        for (int i = 0; i < methodAttributes.length; i++) {
    1140            for (int j = 0; j < methodAttributes[i].length; j++) {
    1141                final long flag = methodFlags[i][j];
    1142                int othersAddedAtStart = 0;
    1143                for (int k = 0; k < otherLayouts.length; k++) {
    1144                    if (otherLayouts[k] != null && otherLayouts[k].matches(flag)) {
    1145                        // Add the next attribute
    1146                        if (otherLayouts[k].getIndex() < 15) {
    1147                            methodAttributes[i][j].add(othersAddedAtStart++, otherAttributes[k].get(0));
    1148                        } else {
    1149                            methodAttributes[i][j].add(otherAttributes[k].get(0));
    1150                        }
    1151                        otherAttributes[k].remove(0);
    1152                    }
    1153                }
    1154            }
    1155        }
    1156    }
    1157
    1158    private void parseMethodBands(final InputStream in) throws IOException, Pack200Exception {
    1159        methodDescrInts = decodeBandInt("method_descr", in, Codec.MDELTA5, classMethodCount);
    1160        methodDescr = getReferences(methodDescrInts, cpBands.getCpDescriptor());
    1161        parseMethodAttrBands(in);
    1162    }
    1163
    1164    private int parseMethodMetadataBands(final InputStream in, final int[] methodAttrCalls)
    1165        throws Pack200Exception, IOException {
    1166        int backwardsCallsUsed = 0;
    1167        final String[] RxA = {"RVA", "RIA", "RVPA", "RIPA", "AD"};
    1168        final int[] rxaCounts = {0, 0, 0, 0, 0};
    1169
    1170        final AttributeLayout rvaLayout = attrMap
    1171            .getAttributeLayout(AttributeLayout.ATTRIBUTE_RUNTIME_VISIBLE_ANNOTATIONS, AttributeLayout.CONTEXT_METHOD);
    1172        final AttributeLayout riaLayout = attrMap.getAttributeLayout(
    1173            AttributeLayout.ATTRIBUTE_RUNTIME_INVISIBLE_ANNOTATIONS, AttributeLayout.CONTEXT_METHOD);
    1174        final AttributeLayout rvpaLayout = attrMap.getAttributeLayout(
    1175            AttributeLayout.ATTRIBUTE_RUNTIME_VISIBLE_PARAMETER_ANNOTATIONS, AttributeLayout.CONTEXT_METHOD);
    1176        final AttributeLayout ripaLayout = attrMap.getAttributeLayout(
    1177            AttributeLayout.ATTRIBUTE_RUNTIME_INVISIBLE_PARAMETER_ANNOTATIONS, AttributeLayout.CONTEXT_METHOD);
    1178        final AttributeLayout adLayout = attrMap.getAttributeLayout(AttributeLayout.ATTRIBUTE_ANNOTATION_DEFAULT,
    1179            AttributeLayout.CONTEXT_METHOD);
    1180        final AttributeLayout[] rxaLayouts = {rvaLayout, riaLayout, rvpaLayout, ripaLayout, adLayout};
    1181
    1182        Arrays.setAll(rxaCounts, i -> SegmentUtils.countMatches(methodFlags, rxaLayouts[i]));
    1183        final int[] backwardsCalls = new int[5];
    1184        int methodAttrIndex = 0;
    1185        for (int i = 0; i < backwardsCalls.length; i++) {
    1186            if (rxaCounts[i] > 0) {
    1187                backwardsCallsUsed++;
    1188                backwardsCalls[i] = methodAttrCalls[methodAttrIndex];
    1189                methodAttrIndex++;
    1190            } else {
    1191                backwardsCalls[i] = 0;
    1192            }
    1193        }
    1194        final MetadataBandGroup[] mbgs = parseMetadata(in, RxA, rxaCounts, backwardsCalls, "method");
    1195        final List<Attribute>[] attributeLists = new List[RxA.length];
    1196        final int[] attributeListIndexes = new int[RxA.length];
    1197        for (int i = 0; i < mbgs.length; i++) {
    1198            attributeLists[i] = mbgs[i].getAttributes();
    1199            attributeListIndexes[i] = 0;
    1200        }
    1201        for (int i = 0; i < methodFlags.length; i++) {
    1202            for (int j = 0; j < methodFlags[i].length; j++) {
    1203                for (int k = 0; k < rxaLayouts.length; k++) {
    1204                    if (rxaLayouts[k].matches(methodFlags[i][j])) {
    1205                        methodAttributes[i][j].add(attributeLists[k].get(attributeListIndexes[k]++));
    1206                    }
    1207                }
    1208            }
    1209        }
    1210        return backwardsCallsUsed;
    1211    }
    1212
    1213    /*
    1214     * (non-Javadoc)
    1215     *
    1216     * @see org.apache.commons.compress.harmony.unpack200.BandSet#unpack(java.io.InputStream)
    1217     */
    1218    @Override
    1219    public void read(final InputStream in) throws IOException, Pack200Exception {
    1220        final int classCount = header.getClassCount();
    1221        classThisInts = decodeBandInt("class_this", in, Codec.DELTA5, classCount);
    1222        classThis = getReferences(classThisInts, cpBands.getCpClass());
    1223        classSuperInts = decodeBandInt("class_super", in, Codec.DELTA5, classCount);
    1224        final int[] classInterfaceLengths = decodeBandInt("class_interface_count", in, Codec.DELTA5, classCount);
    1225        classInterfacesInts = decodeBandInt("class_interface", in, Codec.DELTA5, classInterfaceLengths);
    1226        classFieldCount = decodeBandInt("class_field_count", in, Codec.DELTA5, classCount);
    1227        classMethodCount = decodeBandInt("class_method_count", in, Codec.DELTA5, classCount);
    1228        parseFieldBands(in);
    1229        parseMethodBands(in);
    1230        parseClassAttrBands(in);
    1231        parseCodeBands(in);
    1232
    1233    }
    1234
    1235    @Override
    1236    public void unpack() {
    1237
    1238    }
    1239
    1240}