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 major version numberss to use for each
200     * 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 minor version numberss to use for each
210     * 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, AttributeLayout.CONTEXT_CLASS);
345
346        final AttributeLayout sourceFileLayout = attrMap.getAttributeLayout(AttributeLayout.ATTRIBUTE_SOURCE_FILE, AttributeLayout.CONTEXT_CLASS);
347        final int sourceFileCount = SegmentUtils.countMatches(classFlags, sourceFileLayout);
348        final int[] classSourceFile = decodeBandInt("class_SourceFile_RUN", in, Codec.UNSIGNED5, sourceFileCount);
349
350        final AttributeLayout enclosingMethodLayout = attrMap.getAttributeLayout(AttributeLayout.ATTRIBUTE_ENCLOSING_METHOD, AttributeLayout.CONTEXT_CLASS);
351        final int enclosingMethodCount = SegmentUtils.countMatches(classFlags, enclosingMethodLayout);
352        final int[] enclosingMethodRC = decodeBandInt("class_EnclosingMethod_RC", in, Codec.UNSIGNED5, enclosingMethodCount);
353        final int[] enclosingMethodRDN = decodeBandInt("class_EnclosingMethod_RDN", in, Codec.UNSIGNED5, enclosingMethodCount);
354
355        final AttributeLayout signatureLayout = attrMap.getAttributeLayout(AttributeLayout.ATTRIBUTE_SIGNATURE, AttributeLayout.CONTEXT_CLASS);
356        final int signatureCount = SegmentUtils.countMatches(classFlags, signatureLayout);
357        final int[] classSignature = decodeBandInt("class_Signature_RS", in, Codec.UNSIGNED5, signatureCount);
358
359        final int backwardsCallsUsed = parseClassMetadataBands(in, classAttrCalls);
360
361        final AttributeLayout innerClassLayout = attrMap.getAttributeLayout(AttributeLayout.ATTRIBUTE_INNER_CLASSES, AttributeLayout.CONTEXT_CLASS);
362        final int innerClassCount = SegmentUtils.countMatches(classFlags, innerClassLayout);
363        final int[] classInnerClassesN = decodeBandInt("class_InnerClasses_N", in, Codec.UNSIGNED5, innerClassCount);
364        final int[][] classInnerClassesRC = decodeBandInt("class_InnerClasses_RC", in, Codec.UNSIGNED5, classInnerClassesN);
365        final int[][] classInnerClassesF = decodeBandInt("class_InnerClasses_F", in, Codec.UNSIGNED5, classInnerClassesN);
366        int flagsCount = 0;
367        for (final int[] element : classInnerClassesF) {
368            for (final int element2 : element) {
369                if (element2 != 0) {
370                    flagsCount++;
371                }
372            }
373        }
374        final int[] classInnerClassesOuterRCN = decodeBandInt("class_InnerClasses_outer_RCN", in, Codec.UNSIGNED5, flagsCount);
375        final int[] classInnerClassesNameRUN = decodeBandInt("class_InnerClasses_name_RUN", in, Codec.UNSIGNED5, flagsCount);
376
377        final AttributeLayout versionLayout = attrMap.getAttributeLayout(AttributeLayout.ATTRIBUTE_CLASS_FILE_VERSION, AttributeLayout.CONTEXT_CLASS);
378        final int versionCount = SegmentUtils.countMatches(classFlags, versionLayout);
379        final int[] classFileVersionMinorH = decodeBandInt("class_file_version_minor_H", in, Codec.UNSIGNED5, versionCount);
380        final int[] classFileVersionMajorH = decodeBandInt("class_file_version_major_H", in, Codec.UNSIGNED5, versionCount);
381        if (versionCount > 0) {
382            classVersionMajor = new int[classCount];
383            classVersionMinor = new int[classCount];
384        }
385        final int defaultVersionMajor = header.getDefaultClassMajorVersion();
386        final int defaultVersionMinor = header.getDefaultClassMinorVersion();
387
388        // Parse non-predefined attribute bands
389        int backwardsCallIndex = backwardsCallsUsed;
390        final int limit = options.hasClassFlagsHi() ? 62 : 31;
391        final AttributeLayout[] otherLayouts = new AttributeLayout[limit + 1];
392        final int[] counts = new int[limit + 1];
393        final List<Attribute>[] otherAttributes = new List[limit + 1];
394        for (int i = 0; i < limit; i++) {
395            final AttributeLayout layout = attrMap.getAttributeLayout(i, AttributeLayout.CONTEXT_CLASS);
396            if (layout != null && !layout.isDefaultLayout()) {
397                otherLayouts[i] = layout;
398                counts[i] = SegmentUtils.countMatches(classFlags, layout);
399            }
400        }
401        for (int i = 0; i < counts.length; i++) {
402            if (counts[i] > 0) {
403                final NewAttributeBands bands = attrMap.getAttributeBands(otherLayouts[i]);
404                otherAttributes[i] = bands.parseAttributes(in, counts[i]);
405                final int numBackwardsCallables = otherLayouts[i].numBackwardsCallables();
406                if (numBackwardsCallables > 0) {
407                    final int[] backwardsCalls = new int[numBackwardsCallables];
408                    System.arraycopy(classAttrCalls, backwardsCallIndex, backwardsCalls, 0, numBackwardsCallables);
409                    bands.setBackwardsCalls(backwardsCalls);
410                    backwardsCallIndex += numBackwardsCallables;
411                }
412            }
413        }
414
415        // Now process the attribute bands we have parsed
416        int sourceFileIndex = 0;
417        int enclosingMethodIndex = 0;
418        int signatureIndex = 0;
419        int innerClassIndex = 0;
420        int innerClassC2NIndex = 0;
421        int versionIndex = 0;
422        icLocal = new IcTuple[classCount][];
423        for (int i = 0; i < classCount; i++) {
424            final long flag = classFlags[i];
425            if (deprecatedLayout.matches(classFlags[i])) {
426                classAttributes[i].add(new DeprecatedAttribute());
427            }
428            if (sourceFileLayout.matches(flag)) {
429                final long result = classSourceFile[sourceFileIndex];
430                ClassFileEntry value = sourceFileLayout.getValue(result, cpBands.getConstantPool());
431                if (value == null) {
432                    // Remove package prefix
433                    String className = classThis[i].substring(classThis[i].lastIndexOf('/') + 1);
434                    className = className.substring(className.lastIndexOf('.') + 1);
435
436                    // Remove mangled nested class names
437                    final char[] chars = className.toCharArray();
438                    int index = -1;
439                    for (int j = 0; j < chars.length; j++) {
440                        if (chars[j] <= 0x2D) {
441                            index = j;
442                            break;
443                        }
444                    }
445                    if (index > -1) {
446                        className = className.substring(0, index);
447                    }
448                    // Add .java to the end
449                    value = cpBands.cpUTF8Value(className + ".java", true);
450                }
451                classAttributes[i].add(new SourceFileAttribute((CPUTF8) value));
452                sourceFileIndex++;
453            }
454            if (enclosingMethodLayout.matches(flag)) {
455                final CPClass theClass = cpBands.cpClassValue(enclosingMethodRC[enclosingMethodIndex]);
456                CPNameAndType theMethod = null;
457                if (enclosingMethodRDN[enclosingMethodIndex] != 0) {
458                    theMethod = cpBands.cpNameAndTypeValue(enclosingMethodRDN[enclosingMethodIndex] - 1);
459                }
460                classAttributes[i].add(new EnclosingMethodAttribute(theClass, theMethod));
461                enclosingMethodIndex++;
462            }
463            if (signatureLayout.matches(flag)) {
464                final long result = classSignature[signatureIndex];
465                final CPUTF8 value = (CPUTF8) signatureLayout.getValue(result, cpBands.getConstantPool());
466                classAttributes[i].add(new SignatureAttribute(value));
467                signatureIndex++;
468            }
469            if (innerClassLayout.matches(flag)) {
470                // Just create the tuples for now because the attributes are
471                // decided at the end when creating class constant pools
472                icLocal[i] = new IcTuple[classInnerClassesN[innerClassIndex]];
473                for (int j = 0; j < icLocal[i].length; j++) {
474                    final int icTupleCIndex = classInnerClassesRC[innerClassIndex][j];
475                    int icTupleC2Index = -1;
476                    int icTupleNIndex = -1;
477
478                    final String icTupleC = cpClass[icTupleCIndex];
479                    int icTupleF = classInnerClassesF[innerClassIndex][j];
480                    String icTupleC2 = null;
481                    String icTupleN = null;
482
483                    if (icTupleF != 0) {
484                        icTupleC2Index = classInnerClassesOuterRCN[innerClassC2NIndex];
485                        icTupleNIndex = classInnerClassesNameRUN[innerClassC2NIndex];
486                        icTupleC2 = cpClass[icTupleC2Index];
487                        icTupleN = cpUTF8[icTupleNIndex];
488                        innerClassC2NIndex++;
489                    } else {
490                        // Get from icBands
491                        final IcBands icBands = segment.getIcBands();
492                        final IcTuple[] icAll = icBands.getIcTuples();
493                        for (final IcTuple element : icAll) {
494                            if (element.getC().equals(icTupleC)) {
495                                icTupleF = element.getF();
496                                icTupleC2 = element.getC2();
497                                icTupleN = element.getN();
498                                break;
499                            }
500                        }
501                    }
502
503                    final IcTuple icTuple = new IcTuple(icTupleC, icTupleF, icTupleC2, icTupleN, icTupleCIndex, icTupleC2Index, icTupleNIndex, j);
504                    icLocal[i][j] = icTuple;
505                }
506                innerClassIndex++;
507            }
508            if (versionLayout.matches(flag)) {
509                classVersionMajor[i] = classFileVersionMajorH[versionIndex];
510                classVersionMinor[i] = classFileVersionMinorH[versionIndex];
511                versionIndex++;
512            } else if (classVersionMajor != null) {
513                // Fill in with defaults
514                classVersionMajor[i] = defaultVersionMajor;
515                classVersionMinor[i] = defaultVersionMinor;
516            }
517            // Non-predefined attributes
518            for (int j = 0; j < otherLayouts.length; j++) {
519                if (otherLayouts[j] != null && otherLayouts[j].matches(flag)) {
520                    // Add the next attribute
521                    classAttributes[i].add(otherAttributes[j].get(0));
522                    otherAttributes[j].remove(0);
523                }
524            }
525        }
526    }
527
528    /**
529     * Parse the class metadata bands and return the number of backwards callables.
530     *
531     * @param in             TODO
532     * @param classAttrCalls TODO
533     * @return the number of backwards callables.
534     * @throws Pack200Exception TODO
535     * @throws IOException      If an I/O error occurs.
536     */
537    private int parseClassMetadataBands(final InputStream in, final int[] classAttrCalls) throws Pack200Exception, IOException {
538        int numBackwardsCalls = 0;
539        final String[] RxA = { "RVA", "RIA" };
540
541        final AttributeLayout rvaLayout = attrMap.getAttributeLayout(AttributeLayout.ATTRIBUTE_RUNTIME_VISIBLE_ANNOTATIONS, AttributeLayout.CONTEXT_CLASS);
542        final AttributeLayout riaLayout = attrMap.getAttributeLayout(AttributeLayout.ATTRIBUTE_RUNTIME_INVISIBLE_ANNOTATIONS, AttributeLayout.CONTEXT_CLASS);
543        final int rvaCount = SegmentUtils.countMatches(classFlags, rvaLayout);
544        final int riaCount = SegmentUtils.countMatches(classFlags, riaLayout);
545        final int[] RxACount = { rvaCount, riaCount };
546        final int[] backwardsCalls = { 0, 0 };
547        if (rvaCount > 0) {
548            numBackwardsCalls++;
549            backwardsCalls[0] = classAttrCalls[0];
550            if (riaCount > 0) {
551                numBackwardsCalls++;
552                backwardsCalls[1] = classAttrCalls[1];
553            }
554        } else if (riaCount > 0) {
555            numBackwardsCalls++;
556            backwardsCalls[1] = classAttrCalls[0];
557        }
558        final MetadataBandGroup[] mbgs = parseMetadata(in, RxA, RxACount, backwardsCalls, "class");
559        final List<Attribute> rvaAttributes = mbgs[0].getAttributes();
560        final List<Attribute> riaAttributes = mbgs[1].getAttributes();
561        int rvaAttributesIndex = 0;
562        int riaAttributesIndex = 0;
563        for (int i = 0; i < classFlags.length; i++) {
564            if (rvaLayout.matches(classFlags[i])) {
565                classAttributes[i].add(rvaAttributes.get(rvaAttributesIndex++));
566            }
567            if (riaLayout.matches(classFlags[i])) {
568                classAttributes[i].add(riaAttributes.get(riaAttributesIndex++));
569            }
570        }
571        return numBackwardsCalls;
572    }
573
574    private void parseCodeAttrBands(final InputStream in, final int codeFlagsCount) throws IOException, Pack200Exception {
575        final long[] codeFlags = parseFlags("code_flags", in, codeFlagsCount, Codec.UNSIGNED5, segment.getSegmentHeader().getOptions().hasCodeFlagsHi());
576        final int codeAttrCount = SegmentUtils.countBit16(codeFlags);
577        final int[] codeAttrCounts = decodeBandInt("code_attr_count", in, Codec.UNSIGNED5, codeAttrCount);
578        final int[][] codeAttrIndexes = decodeBandInt("code_attr_indexes", in, Codec.UNSIGNED5, codeAttrCounts);
579        int callCount = 0;
580        for (final int[] element : codeAttrIndexes) {
581            for (final int index : element) {
582                final AttributeLayout layout = attrMap.getAttributeLayout(index, AttributeLayout.CONTEXT_CODE);
583                callCount += layout.numBackwardsCallables();
584            }
585        }
586        final int[] codeAttrCalls = decodeBandInt("code_attr_calls", in, Codec.UNSIGNED5, callCount);
587
588        final AttributeLayout lineNumberTableLayout = attrMap.getAttributeLayout(AttributeLayout.ATTRIBUTE_LINE_NUMBER_TABLE, AttributeLayout.CONTEXT_CODE);
589        final int lineNumberTableCount = SegmentUtils.countMatches(codeFlags, lineNumberTableLayout);
590        final int[] lineNumberTableN = decodeBandInt("code_LineNumberTable_N", in, Codec.UNSIGNED5, lineNumberTableCount);
591        final int[][] lineNumberTableBciP = decodeBandInt("code_LineNumberTable_bci_P", in, Codec.BCI5, lineNumberTableN);
592        final int[][] lineNumberTableLine = decodeBandInt("code_LineNumberTable_line", in, Codec.UNSIGNED5, lineNumberTableN);
593
594        final AttributeLayout localVariableTableLayout = attrMap.getAttributeLayout(AttributeLayout.ATTRIBUTE_LOCAL_VARIABLE_TABLE,
595                AttributeLayout.CONTEXT_CODE);
596        final AttributeLayout localVariableTypeTableLayout = attrMap.getAttributeLayout(AttributeLayout.ATTRIBUTE_LOCAL_VARIABLE_TYPE_TABLE,
597                AttributeLayout.CONTEXT_CODE);
598
599        final int lengthLocalVariableNBand = SegmentUtils.countMatches(codeFlags, localVariableTableLayout);
600        final int[] localVariableTableN = decodeBandInt("code_LocalVariableTable_N", in, Codec.UNSIGNED5, lengthLocalVariableNBand);
601        final int[][] localVariableTableBciP = decodeBandInt("code_LocalVariableTable_bci_P", in, Codec.BCI5, localVariableTableN);
602        final int[][] localVariableTableSpanO = decodeBandInt("code_LocalVariableTable_span_O", in, Codec.BRANCH5, localVariableTableN);
603        final CPUTF8[][] localVariableTableNameRU = parseCPUTF8References("code_LocalVariableTable_name_RU", in, Codec.UNSIGNED5, localVariableTableN);
604        final CPUTF8[][] localVariableTableTypeRS = parseCPSignatureReferences("code_LocalVariableTable_type_RS", in, Codec.UNSIGNED5, localVariableTableN);
605        final int[][] localVariableTableSlot = decodeBandInt("code_LocalVariableTable_slot", in, Codec.UNSIGNED5, localVariableTableN);
606
607        final int lengthLocalVariableTypeTableNBand = SegmentUtils.countMatches(codeFlags, localVariableTypeTableLayout);
608        final int[] localVariableTypeTableN = decodeBandInt("code_LocalVariableTypeTable_N", in, Codec.UNSIGNED5, lengthLocalVariableTypeTableNBand);
609        final int[][] localVariableTypeTableBciP = decodeBandInt("code_LocalVariableTypeTable_bci_P", in, Codec.BCI5, localVariableTypeTableN);
610        final int[][] localVariableTypeTableSpanO = decodeBandInt("code_LocalVariableTypeTable_span_O", in, Codec.BRANCH5, localVariableTypeTableN);
611        final CPUTF8[][] localVariableTypeTableNameRU = parseCPUTF8References("code_LocalVariableTypeTable_name_RU", in, Codec.UNSIGNED5,
612                localVariableTypeTableN);
613        final CPUTF8[][] localVariableTypeTableTypeRS = parseCPSignatureReferences("code_LocalVariableTypeTable_type_RS", in, Codec.UNSIGNED5,
614                localVariableTypeTableN);
615        final int[][] localVariableTypeTableSlot = decodeBandInt("code_LocalVariableTypeTable_slot", in, Codec.UNSIGNED5, localVariableTypeTableN);
616
617        // Parse non-predefined attribute bands
618        int backwardsCallIndex = 0;
619        final int limit = options.hasCodeFlagsHi() ? 62 : 31;
620        final AttributeLayout[] otherLayouts = new AttributeLayout[limit + 1];
621        final int[] counts = new int[limit + 1];
622        final List<Attribute>[] otherAttributes = new List[limit + 1];
623        for (int i = 0; i < limit; i++) {
624            final AttributeLayout layout = attrMap.getAttributeLayout(i, AttributeLayout.CONTEXT_CODE);
625            if (layout != null && !layout.isDefaultLayout()) {
626                otherLayouts[i] = layout;
627                counts[i] = SegmentUtils.countMatches(codeFlags, layout);
628            }
629        }
630        for (int i = 0; i < counts.length; i++) {
631            if (counts[i] > 0) {
632                final NewAttributeBands bands = attrMap.getAttributeBands(otherLayouts[i]);
633                otherAttributes[i] = bands.parseAttributes(in, counts[i]);
634                final int numBackwardsCallables = otherLayouts[i].numBackwardsCallables();
635                if (numBackwardsCallables > 0) {
636                    final int[] backwardsCalls = new int[numBackwardsCallables];
637                    System.arraycopy(codeAttrCalls, backwardsCallIndex, backwardsCalls, 0, numBackwardsCallables);
638                    bands.setBackwardsCalls(backwardsCalls);
639                    backwardsCallIndex += numBackwardsCallables;
640                }
641            }
642        }
643
644        int lineNumberIndex = 0;
645        int lvtIndex = 0;
646        int lvttIndex = 0;
647        for (int i = 0; i < codeFlagsCount; i++) {
648            if (lineNumberTableLayout.matches(codeFlags[i])) {
649                final LineNumberTableAttribute lnta = new LineNumberTableAttribute(lineNumberTableN[lineNumberIndex], lineNumberTableBciP[lineNumberIndex],
650                        lineNumberTableLine[lineNumberIndex]);
651                lineNumberIndex++;
652                codeAttributes[i].add(lnta);
653            }
654            if (localVariableTableLayout.matches(codeFlags[i])) {
655                final LocalVariableTableAttribute lvta = new LocalVariableTableAttribute(localVariableTableN[lvtIndex], localVariableTableBciP[lvtIndex],
656                        localVariableTableSpanO[lvtIndex], localVariableTableNameRU[lvtIndex], localVariableTableTypeRS[lvtIndex],
657                        localVariableTableSlot[lvtIndex]);
658                lvtIndex++;
659                codeAttributes[i].add(lvta);
660            }
661            if (localVariableTypeTableLayout.matches(codeFlags[i])) {
662                final LocalVariableTypeTableAttribute lvtta = new LocalVariableTypeTableAttribute(localVariableTypeTableN[lvttIndex],
663                        localVariableTypeTableBciP[lvttIndex], localVariableTypeTableSpanO[lvttIndex], localVariableTypeTableNameRU[lvttIndex],
664                        localVariableTypeTableTypeRS[lvttIndex], localVariableTypeTableSlot[lvttIndex]);
665                lvttIndex++;
666                codeAttributes[i].add(lvtta);
667            }
668            // Non-predefined attributes
669            for (int j = 0; j < otherLayouts.length; j++) {
670                if (otherLayouts[j] != null && otherLayouts[j].matches(codeFlags[i])) {
671                    // Add the next attribute
672                    codeAttributes[i].add(otherAttributes[j].get(0));
673                    otherAttributes[j].remove(0);
674                }
675            }
676        }
677
678    }
679
680    private void parseCodeBands(final InputStream in) throws Pack200Exception, IOException {
681        final AttributeLayout layout = attrMap.getAttributeLayout(AttributeLayout.ATTRIBUTE_CODE, AttributeLayout.CONTEXT_METHOD);
682
683        final int codeCount = SegmentUtils.countMatches(methodFlags, layout);
684        final int[] codeHeaders = decodeBandInt("code_headers", in, Codec.BYTE1, codeCount);
685
686        final boolean allCodeHasFlags = segment.getSegmentHeader().getOptions().hasAllCodeFlags();
687        if (!allCodeHasFlags) {
688            codeHasAttributes = new boolean[codeCount];
689        }
690        int codeSpecialHeader = 0;
691        for (int i = 0; i < codeCount; i++) {
692            if (codeHeaders[i] == 0) {
693                codeSpecialHeader++;
694                if (!allCodeHasFlags) {
695                    codeHasAttributes[i] = true;
696                }
697            }
698        }
699        final int[] codeMaxStackSpecials = decodeBandInt("code_max_stack", in, Codec.UNSIGNED5, codeSpecialHeader);
700        final int[] codeMaxNALocalsSpecials = decodeBandInt("code_max_na_locals", in, Codec.UNSIGNED5, codeSpecialHeader);
701        final int[] codeHandlerCountSpecials = decodeBandInt("code_handler_count", in, Codec.UNSIGNED5, codeSpecialHeader);
702
703        codeMaxStack = new int[codeCount];
704        codeMaxNALocals = new int[codeCount];
705        codeHandlerCount = new int[codeCount];
706        int special = 0;
707        for (int i = 0; i < codeCount; i++) {
708            final int header = 0xff & codeHeaders[i];
709            if (header < 0) {
710                throw new IllegalStateException("Shouldn't get here");
711            }
712            if (header == 0) {
713                codeMaxStack[i] = codeMaxStackSpecials[special];
714                codeMaxNALocals[i] = codeMaxNALocalsSpecials[special];
715                codeHandlerCount[i] = codeHandlerCountSpecials[special];
716                special++;
717            } else if (header <= 144) {
718                codeMaxStack[i] = (header - 1) % 12;
719                codeMaxNALocals[i] = (header - 1) / 12;
720                codeHandlerCount[i] = 0;
721            } else if (header <= 208) {
722                codeMaxStack[i] = (header - 145) % 8;
723                codeMaxNALocals[i] = (header - 145) / 8;
724                codeHandlerCount[i] = 1;
725            } else if (header <= 255) {
726                codeMaxStack[i] = (header - 209) % 7;
727                codeMaxNALocals[i] = (header - 209) / 7;
728                codeHandlerCount[i] = 2;
729            } else {
730                throw new IllegalStateException("Shouldn't get here either");
731            }
732        }
733        codeHandlerStartP = decodeBandInt("code_handler_start_P", in, Codec.BCI5, codeHandlerCount);
734        codeHandlerEndPO = decodeBandInt("code_handler_end_PO", in, Codec.BRANCH5, codeHandlerCount);
735        codeHandlerCatchPO = decodeBandInt("code_handler_catch_PO", in, Codec.BRANCH5, codeHandlerCount);
736        codeHandlerClassRCN = decodeBandInt("code_handler_class_RCN", in, Codec.UNSIGNED5, codeHandlerCount);
737
738        final int codeFlagsCount = allCodeHasFlags ? codeCount : codeSpecialHeader;
739
740        codeAttributes = new List[codeFlagsCount];
741        Arrays.setAll(codeAttributes, i -> new ArrayList<>());
742        parseCodeAttrBands(in, codeFlagsCount);
743    }
744
745    private void parseFieldAttrBands(final InputStream in) throws IOException, Pack200Exception {
746        fieldFlags = parseFlags("field_flags", in, classFieldCount, Codec.UNSIGNED5, options.hasFieldFlagsHi());
747        final int fieldAttrCount = SegmentUtils.countBit16(fieldFlags);
748        final int[] fieldAttrCounts = decodeBandInt("field_attr_count", in, Codec.UNSIGNED5, fieldAttrCount);
749        final int[][] fieldAttrIndexes = decodeBandInt("field_attr_indexes", in, Codec.UNSIGNED5, fieldAttrCounts);
750        final int callCount = getCallCount(fieldAttrIndexes, fieldFlags, AttributeLayout.CONTEXT_FIELD);
751        final int[] fieldAttrCalls = decodeBandInt("field_attr_calls", in, Codec.UNSIGNED5, callCount);
752
753        // Assign empty field attributes
754        fieldAttributes = new ArrayList[classCount][];
755        for (int i = 0; i < classCount; i++) {
756            fieldAttributes[i] = new ArrayList[fieldFlags[i].length];
757            for (int j = 0; j < fieldFlags[i].length; j++) {
758                fieldAttributes[i][j] = new ArrayList<>();
759            }
760        }
761
762        final AttributeLayout constantValueLayout = attrMap.getAttributeLayout("ConstantValue", AttributeLayout.CONTEXT_FIELD);
763        final int constantCount = SegmentUtils.countMatches(fieldFlags, constantValueLayout);
764        final int[] field_constantValue_KQ = decodeBandInt("field_ConstantValue_KQ", in, Codec.UNSIGNED5, constantCount);
765        int constantValueIndex = 0;
766
767        final AttributeLayout signatureLayout = attrMap.getAttributeLayout(AttributeLayout.ATTRIBUTE_SIGNATURE, AttributeLayout.CONTEXT_FIELD);
768        final int signatureCount = SegmentUtils.countMatches(fieldFlags, signatureLayout);
769        final int[] fieldSignatureRS = decodeBandInt("field_Signature_RS", in, Codec.UNSIGNED5, signatureCount);
770        int signatureIndex = 0;
771
772        final AttributeLayout deprecatedLayout = attrMap.getAttributeLayout(AttributeLayout.ATTRIBUTE_DEPRECATED, AttributeLayout.CONTEXT_FIELD);
773
774        for (int i = 0; i < classCount; i++) {
775            for (int j = 0; j < fieldFlags[i].length; j++) {
776                final long flag = fieldFlags[i][j];
777                if (deprecatedLayout.matches(flag)) {
778                    fieldAttributes[i][j].add(new DeprecatedAttribute());
779                }
780                if (constantValueLayout.matches(flag)) {
781                    // we've got a value to read
782                    final long result = field_constantValue_KQ[constantValueIndex];
783                    final String desc = fieldDescr[i][j];
784                    final int colon = desc.indexOf(':');
785                    String type = desc.substring(colon + 1);
786                    if (type.equals("B") || type.equals("S") || type.equals("C") || type.equals("Z")) {
787                        type = "I";
788                    }
789                    final ClassFileEntry value = constantValueLayout.getValue(result, type, cpBands.getConstantPool());
790                    fieldAttributes[i][j].add(new ConstantValueAttribute(value));
791                    constantValueIndex++;
792                }
793                if (signatureLayout.matches(flag)) {
794                    // we've got a signature attribute
795                    final long result = fieldSignatureRS[signatureIndex];
796                    final String desc = fieldDescr[i][j];
797                    final int colon = desc.indexOf(':');
798                    final String type = desc.substring(colon + 1);
799                    final CPUTF8 value = (CPUTF8) signatureLayout.getValue(result, type, cpBands.getConstantPool());
800                    fieldAttributes[i][j].add(new SignatureAttribute(value));
801                    signatureIndex++;
802                }
803            }
804        }
805
806        // Parse non-predefined attribute bands
807        int backwardsCallIndex = parseFieldMetadataBands(in, fieldAttrCalls);
808        final int limit = options.hasFieldFlagsHi() ? 62 : 31;
809        final AttributeLayout[] otherLayouts = new AttributeLayout[limit + 1];
810        final int[] counts = new int[limit + 1];
811        final List<Attribute>[] otherAttributes = new List[limit + 1];
812        for (int i = 0; i < limit; i++) {
813            final AttributeLayout layout = attrMap.getAttributeLayout(i, AttributeLayout.CONTEXT_FIELD);
814            if (layout != null && !layout.isDefaultLayout()) {
815                otherLayouts[i] = layout;
816                counts[i] = SegmentUtils.countMatches(fieldFlags, layout);
817            }
818        }
819        for (int i = 0; i < counts.length; i++) {
820            if (counts[i] > 0) {
821                final NewAttributeBands bands = attrMap.getAttributeBands(otherLayouts[i]);
822                otherAttributes[i] = bands.parseAttributes(in, counts[i]);
823                final int numBackwardsCallables = otherLayouts[i].numBackwardsCallables();
824                if (numBackwardsCallables > 0) {
825                    final int[] backwardsCalls = new int[numBackwardsCallables];
826                    System.arraycopy(fieldAttrCalls, backwardsCallIndex, backwardsCalls, 0, numBackwardsCallables);
827                    bands.setBackwardsCalls(backwardsCalls);
828                    backwardsCallIndex += numBackwardsCallables;
829                }
830            }
831        }
832
833        // Non-predefined attributes
834        for (int i = 0; i < classCount; i++) {
835            for (int j = 0; j < fieldFlags[i].length; j++) {
836                final long flag = fieldFlags[i][j];
837                int othersAddedAtStart = 0;
838                for (int k = 0; k < otherLayouts.length; k++) {
839                    if (otherLayouts[k] != null && otherLayouts[k].matches(flag)) {
840                        // Add the next attribute
841                        if (otherLayouts[k].getIndex() < 15) {
842                            fieldAttributes[i][j].add(othersAddedAtStart++, otherAttributes[k].get(0));
843                        } else {
844                            fieldAttributes[i][j].add(otherAttributes[k].get(0));
845                        }
846                        otherAttributes[k].remove(0);
847                    }
848                }
849            }
850        }
851    }
852
853    private void parseFieldBands(final InputStream in) throws IOException, Pack200Exception {
854        fieldDescrInts = decodeBandInt("field_descr", in, Codec.DELTA5, classFieldCount);
855        fieldDescr = getReferences(fieldDescrInts, cpBands.getCpDescriptor());
856        parseFieldAttrBands(in);
857    }
858
859    private int parseFieldMetadataBands(final InputStream in, final int[] fieldAttrCalls) throws Pack200Exception, IOException {
860        int backwardsCallsUsed = 0;
861        final String[] RxA = { "RVA", "RIA" };
862
863        final AttributeLayout rvaLayout = attrMap.getAttributeLayout(AttributeLayout.ATTRIBUTE_RUNTIME_VISIBLE_ANNOTATIONS, AttributeLayout.CONTEXT_FIELD);
864        final AttributeLayout riaLayout = attrMap.getAttributeLayout(AttributeLayout.ATTRIBUTE_RUNTIME_INVISIBLE_ANNOTATIONS, AttributeLayout.CONTEXT_FIELD);
865
866        final int rvaCount = SegmentUtils.countMatches(fieldFlags, rvaLayout);
867        final int riaCount = SegmentUtils.countMatches(fieldFlags, riaLayout);
868        final int[] RxACount = { rvaCount, riaCount };
869        final int[] backwardsCalls = { 0, 0 };
870        if (rvaCount > 0) {
871            backwardsCalls[0] = fieldAttrCalls[0];
872            backwardsCallsUsed++;
873            if (riaCount > 0) {
874                backwardsCalls[1] = fieldAttrCalls[1];
875                backwardsCallsUsed++;
876            }
877        } else if (riaCount > 0) {
878            backwardsCalls[1] = fieldAttrCalls[0];
879            backwardsCallsUsed++;
880        }
881        final MetadataBandGroup[] mb = parseMetadata(in, RxA, RxACount, backwardsCalls, "field");
882        final List<Attribute> rvaAttributes = mb[0].getAttributes();
883        final List<Attribute> riaAttributes = mb[1].getAttributes();
884        int rvaAttributesIndex = 0;
885        int riaAttributesIndex = 0;
886        for (int i = 0; i < fieldFlags.length; i++) {
887            for (int j = 0; j < fieldFlags[i].length; j++) {
888                if (rvaLayout.matches(fieldFlags[i][j])) {
889                    fieldAttributes[i][j].add(rvaAttributes.get(rvaAttributesIndex++));
890                }
891                if (riaLayout.matches(fieldFlags[i][j])) {
892                    fieldAttributes[i][j].add(riaAttributes.get(riaAttributesIndex++));
893                }
894            }
895        }
896        return backwardsCallsUsed;
897    }
898
899    private MetadataBandGroup[] parseMetadata(final InputStream in, final String[] RxA, final int[] RxACount, final int[] backwardsCallCounts,
900            final String contextName) throws IOException, Pack200Exception {
901        final MetadataBandGroup[] mbg = new MetadataBandGroup[RxA.length];
902        for (int i = 0; i < RxA.length; i++) {
903            mbg[i] = new MetadataBandGroup(RxA[i], cpBands);
904            final String rxa = RxA[i];
905            if (rxa.indexOf('P') >= 0) {
906                mbg[i].param_NB = decodeBandInt(contextName + "_" + rxa + "_param_NB", in, Codec.BYTE1, RxACount[i]);
907            }
908            int pairCount = 0;
909            if (!rxa.equals("AD")) {
910                mbg[i].anno_N = decodeBandInt(contextName + "_" + rxa + "_anno_N", in, Codec.UNSIGNED5, RxACount[i]);
911                mbg[i].type_RS = parseCPSignatureReferences(contextName + "_" + rxa + "_type_RS", in, Codec.UNSIGNED5, mbg[i].anno_N);
912                mbg[i].pair_N = decodeBandInt(contextName + "_" + rxa + "_pair_N", in, Codec.UNSIGNED5, mbg[i].anno_N);
913                for (final int[] element : mbg[i].pair_N) {
914                    for (final int element2 : element) {
915                        pairCount += element2;
916                    }
917                }
918
919                mbg[i].name_RU = parseCPUTF8References(contextName + "_" + rxa + "_name_RU", in, Codec.UNSIGNED5, pairCount);
920            } else {
921                pairCount = RxACount[i];
922            }
923            mbg[i].T = decodeBandInt(contextName + "_" + rxa + "_T", in, Codec.BYTE1, pairCount + backwardsCallCounts[i]);
924            int ICount = 0, DCount = 0, FCount = 0, JCount = 0, cCount = 0, eCount = 0, sCount = 0, arrayCount = 0, atCount = 0;
925            for (final int element : mbg[i].T) {
926                final char c = (char) element;
927                switch (c) {
928                case 'B':
929                case 'C':
930                case 'I':
931                case 'S':
932                case 'Z':
933                    ICount++;
934                    break;
935                case 'D':
936                    DCount++;
937                    break;
938                case 'F':
939                    FCount++;
940                    break;
941                case 'J':
942                    JCount++;
943                    break;
944                case 'c':
945                    cCount++;
946                    break;
947                case 'e':
948                    eCount++;
949                    break;
950                case 's':
951                    sCount++;
952                    break;
953                case '[':
954                    arrayCount++;
955                    break;
956                case '@':
957                    atCount++;
958                    break;
959                }
960            }
961            mbg[i].caseI_KI = parseCPIntReferences(contextName + "_" + rxa + "_caseI_KI", in, Codec.UNSIGNED5, ICount);
962            mbg[i].caseD_KD = parseCPDoubleReferences(contextName + "_" + rxa + "_caseD_KD", in, Codec.UNSIGNED5, DCount);
963            mbg[i].caseF_KF = parseCPFloatReferences(contextName + "_" + rxa + "_caseF_KF", in, Codec.UNSIGNED5, FCount);
964            mbg[i].caseJ_KJ = parseCPLongReferences(contextName + "_" + rxa + "_caseJ_KJ", in, Codec.UNSIGNED5, JCount);
965            mbg[i].casec_RS = parseCPSignatureReferences(contextName + "_" + rxa + "_casec_RS", in, Codec.UNSIGNED5, cCount);
966            mbg[i].caseet_RS = parseReferences(contextName + "_" + rxa + "_caseet_RS", in, Codec.UNSIGNED5, eCount, cpBands.getCpSignature());
967            mbg[i].caseec_RU = parseReferences(contextName + "_" + rxa + "_caseec_RU", in, Codec.UNSIGNED5, eCount, cpBands.getCpUTF8());
968            mbg[i].cases_RU = parseCPUTF8References(contextName + "_" + rxa + "_cases_RU", in, Codec.UNSIGNED5, sCount);
969            mbg[i].casearray_N = decodeBandInt(contextName + "_" + rxa + "_casearray_N", in, Codec.UNSIGNED5, arrayCount);
970            mbg[i].nesttype_RS = parseCPUTF8References(contextName + "_" + rxa + "_nesttype_RS", in, Codec.UNSIGNED5, atCount);
971            mbg[i].nestpair_N = decodeBandInt(contextName + "_" + rxa + "_nestpair_N", in, Codec.UNSIGNED5, atCount);
972            int nestPairCount = 0;
973            for (final int element : mbg[i].nestpair_N) {
974                nestPairCount += element;
975            }
976            mbg[i].nestname_RU = parseCPUTF8References(contextName + "_" + rxa + "_nestname_RU", in, Codec.UNSIGNED5, nestPairCount);
977        }
978        return mbg;
979    }
980
981    private void parseMethodAttrBands(final InputStream in) throws IOException, Pack200Exception {
982        methodFlags = parseFlags("method_flags", in, classMethodCount, Codec.UNSIGNED5, options.hasMethodFlagsHi());
983        final int methodAttrCount = SegmentUtils.countBit16(methodFlags);
984        final int[] methodAttrCounts = decodeBandInt("method_attr_count", in, Codec.UNSIGNED5, methodAttrCount);
985        final int[][] methodAttrIndexes = decodeBandInt("method_attr_indexes", in, Codec.UNSIGNED5, methodAttrCounts);
986        final int callCount = getCallCount(methodAttrIndexes, methodFlags, AttributeLayout.CONTEXT_METHOD);
987        methodAttrCalls = decodeBandInt("method_attr_calls", in, Codec.UNSIGNED5, callCount);
988
989        // assign empty method attributes
990        methodAttributes = new ArrayList[classCount][];
991        for (int i = 0; i < classCount; i++) {
992            methodAttributes[i] = new ArrayList[methodFlags[i].length];
993            for (int j = 0; j < methodFlags[i].length; j++) {
994                methodAttributes[i][j] = new ArrayList<>();
995            }
996        }
997
998        // Parse method exceptions attributes
999        final AttributeLayout methodExceptionsLayout = attrMap.getAttributeLayout(AttributeLayout.ATTRIBUTE_EXCEPTIONS, AttributeLayout.CONTEXT_METHOD);
1000        final int count = SegmentUtils.countMatches(methodFlags, methodExceptionsLayout);
1001        final int[] numExceptions = decodeBandInt("method_Exceptions_n", in, Codec.UNSIGNED5, count);
1002        final int[][] methodExceptionsRS = decodeBandInt("method_Exceptions_RC", in, Codec.UNSIGNED5, numExceptions);
1003
1004        // Parse method signature attributes
1005        final AttributeLayout methodSignatureLayout = attrMap.getAttributeLayout(AttributeLayout.ATTRIBUTE_SIGNATURE, AttributeLayout.CONTEXT_METHOD);
1006        final int count1 = SegmentUtils.countMatches(methodFlags, methodSignatureLayout);
1007        final int[] methodSignatureRS = decodeBandInt("method_signature_RS", in, Codec.UNSIGNED5, count1);
1008
1009        final AttributeLayout deprecatedLayout = attrMap.getAttributeLayout(AttributeLayout.ATTRIBUTE_DEPRECATED, AttributeLayout.CONTEXT_METHOD);
1010
1011        // Add attributes to the attribute arrays
1012        int methodExceptionsIndex = 0;
1013        int methodSignatureIndex = 0;
1014        for (int i = 0; i < methodAttributes.length; i++) {
1015            for (int j = 0; j < methodAttributes[i].length; j++) {
1016                final long flag = methodFlags[i][j];
1017                if (methodExceptionsLayout.matches(flag)) {
1018                    final int n = numExceptions[methodExceptionsIndex];
1019                    final int[] exceptions = methodExceptionsRS[methodExceptionsIndex];
1020                    final CPClass[] exceptionClasses = new CPClass[n];
1021                    for (int k = 0; k < n; k++) {
1022                        exceptionClasses[k] = cpBands.cpClassValue(exceptions[k]);
1023                    }
1024                    methodAttributes[i][j].add(new ExceptionsAttribute(exceptionClasses));
1025                    methodExceptionsIndex++;
1026                }
1027                if (methodSignatureLayout.matches(flag)) {
1028                    // We've got a signature attribute
1029                    final long result = methodSignatureRS[methodSignatureIndex];
1030                    final String desc = methodDescr[i][j];
1031                    final int colon = desc.indexOf(':');
1032                    String type = desc.substring(colon + 1);
1033                    // TODO Got to get better at this ... in any case, it should
1034                    // be e.g. KIB or KIH
1035                    if (type.equals("B") || type.equals("H")) {
1036                        type = "I";
1037                    }
1038                    final CPUTF8 value = (CPUTF8) methodSignatureLayout.getValue(result, type, cpBands.getConstantPool());
1039                    methodAttributes[i][j].add(new SignatureAttribute(value));
1040                    methodSignatureIndex++;
1041                }
1042                if (deprecatedLayout.matches(flag)) {
1043                    methodAttributes[i][j].add(new DeprecatedAttribute());
1044                }
1045            }
1046        }
1047
1048        // Parse non-predefined attribute bands
1049        int backwardsCallIndex = parseMethodMetadataBands(in, methodAttrCalls);
1050        final int limit = options.hasMethodFlagsHi() ? 62 : 31;
1051        final AttributeLayout[] otherLayouts = new AttributeLayout[limit + 1];
1052        final int[] counts = new int[limit + 1];
1053        for (int i = 0; i < limit; i++) {
1054            final AttributeLayout layout = attrMap.getAttributeLayout(i, AttributeLayout.CONTEXT_METHOD);
1055            if (layout != null && !layout.isDefaultLayout()) {
1056                otherLayouts[i] = layout;
1057                counts[i] = SegmentUtils.countMatches(methodFlags, layout);
1058            }
1059        }
1060        final List<Attribute>[] otherAttributes = new List[limit + 1];
1061        for (int i = 0; i < counts.length; i++) {
1062            if (counts[i] > 0) {
1063                final NewAttributeBands bands = attrMap.getAttributeBands(otherLayouts[i]);
1064                otherAttributes[i] = bands.parseAttributes(in, counts[i]);
1065                final int numBackwardsCallables = otherLayouts[i].numBackwardsCallables();
1066                if (numBackwardsCallables > 0) {
1067                    final int[] backwardsCalls = new int[numBackwardsCallables];
1068                    System.arraycopy(methodAttrCalls, backwardsCallIndex, backwardsCalls, 0, numBackwardsCallables);
1069                    bands.setBackwardsCalls(backwardsCalls);
1070                    backwardsCallIndex += numBackwardsCallables;
1071                }
1072            }
1073        }
1074
1075        // Non-predefined attributes
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                int othersAddedAtStart = 0;
1080                for (int k = 0; k < otherLayouts.length; k++) {
1081                    if (otherLayouts[k] != null && otherLayouts[k].matches(flag)) {
1082                        // Add the next attribute
1083                        if (otherLayouts[k].getIndex() < 15) {
1084                            methodAttributes[i][j].add(othersAddedAtStart++, otherAttributes[k].get(0));
1085                        } else {
1086                            methodAttributes[i][j].add(otherAttributes[k].get(0));
1087                        }
1088                        otherAttributes[k].remove(0);
1089                    }
1090                }
1091            }
1092        }
1093    }
1094
1095    private void parseMethodBands(final InputStream in) throws IOException, Pack200Exception {
1096        methodDescrInts = decodeBandInt("method_descr", in, Codec.MDELTA5, classMethodCount);
1097        methodDescr = getReferences(methodDescrInts, cpBands.getCpDescriptor());
1098        parseMethodAttrBands(in);
1099    }
1100
1101    private int parseMethodMetadataBands(final InputStream in, final int[] methodAttrCalls) throws Pack200Exception, IOException {
1102        int backwardsCallsUsed = 0;
1103        final String[] RxA = { "RVA", "RIA", "RVPA", "RIPA", "AD" };
1104        final int[] rxaCounts = { 0, 0, 0, 0, 0 };
1105
1106        final AttributeLayout rvaLayout = attrMap.getAttributeLayout(AttributeLayout.ATTRIBUTE_RUNTIME_VISIBLE_ANNOTATIONS, AttributeLayout.CONTEXT_METHOD);
1107        final AttributeLayout riaLayout = attrMap.getAttributeLayout(AttributeLayout.ATTRIBUTE_RUNTIME_INVISIBLE_ANNOTATIONS, AttributeLayout.CONTEXT_METHOD);
1108        final AttributeLayout rvpaLayout = attrMap.getAttributeLayout(AttributeLayout.ATTRIBUTE_RUNTIME_VISIBLE_PARAMETER_ANNOTATIONS,
1109                AttributeLayout.CONTEXT_METHOD);
1110        final AttributeLayout ripaLayout = attrMap.getAttributeLayout(AttributeLayout.ATTRIBUTE_RUNTIME_INVISIBLE_PARAMETER_ANNOTATIONS,
1111                AttributeLayout.CONTEXT_METHOD);
1112        final AttributeLayout adLayout = attrMap.getAttributeLayout(AttributeLayout.ATTRIBUTE_ANNOTATION_DEFAULT, AttributeLayout.CONTEXT_METHOD);
1113        final AttributeLayout[] rxaLayouts = { rvaLayout, riaLayout, rvpaLayout, ripaLayout, adLayout };
1114
1115        Arrays.setAll(rxaCounts, i -> SegmentUtils.countMatches(methodFlags, rxaLayouts[i]));
1116        final int[] backwardsCalls = new int[5];
1117        int methodAttrIndex = 0;
1118        for (int i = 0; i < backwardsCalls.length; i++) {
1119            if (rxaCounts[i] > 0) {
1120                backwardsCallsUsed++;
1121                backwardsCalls[i] = methodAttrCalls[methodAttrIndex];
1122                methodAttrIndex++;
1123            } else {
1124                backwardsCalls[i] = 0;
1125            }
1126        }
1127        final MetadataBandGroup[] mbgs = parseMetadata(in, RxA, rxaCounts, backwardsCalls, "method");
1128        final List<Attribute>[] attributeLists = new List[RxA.length];
1129        final int[] attributeListIndexes = new int[RxA.length];
1130        for (int i = 0; i < mbgs.length; i++) {
1131            attributeLists[i] = mbgs[i].getAttributes();
1132            attributeListIndexes[i] = 0;
1133        }
1134        for (int i = 0; i < methodFlags.length; i++) {
1135            for (int j = 0; j < methodFlags[i].length; j++) {
1136                for (int k = 0; k < rxaLayouts.length; k++) {
1137                    if (rxaLayouts[k].matches(methodFlags[i][j])) {
1138                        methodAttributes[i][j].add(attributeLists[k].get(attributeListIndexes[k]++));
1139                    }
1140                }
1141            }
1142        }
1143        return backwardsCallsUsed;
1144    }
1145
1146    /*
1147     * (non-Javadoc)
1148     *
1149     * @see org.apache.commons.compress.harmony.unpack200.BandSet#unpack(java.io.InputStream)
1150     */
1151    @Override
1152    public void read(final InputStream in) throws IOException, Pack200Exception {
1153        final int classCount = header.getClassCount();
1154        classThisInts = decodeBandInt("class_this", in, Codec.DELTA5, classCount);
1155        classThis = getReferences(classThisInts, cpBands.getCpClass());
1156        classSuperInts = decodeBandInt("class_super", in, Codec.DELTA5, classCount);
1157        final int[] classInterfaceLengths = decodeBandInt("class_interface_count", in, Codec.DELTA5, classCount);
1158        classInterfacesInts = decodeBandInt("class_interface", in, Codec.DELTA5, classInterfaceLengths);
1159        classFieldCount = decodeBandInt("class_field_count", in, Codec.DELTA5, classCount);
1160        classMethodCount = decodeBandInt("class_method_count", in, Codec.DELTA5, classCount);
1161        parseFieldBands(in);
1162        parseMethodBands(in);
1163        parseClassAttrBands(in);
1164        parseCodeBands(in);
1165
1166    }
1167
1168    @Override
1169    public void unpack() {
1170
1171    }
1172
1173}