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