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