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