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