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.pack200;
020
021import java.io.IOException;
022import java.io.OutputStream;
023import java.util.ArrayList;
024import java.util.Arrays;
025import java.util.Comparator;
026import java.util.HashMap;
027import java.util.HashSet;
028import java.util.List;
029import java.util.Map;
030import java.util.Set;
031
032import org.apache.commons.compress.harmony.pack200.AttributeDefinitionBands.AttributeDefinition;
033import org.apache.commons.compress.harmony.pack200.IcBands.IcTuple;
034import org.apache.commons.compress.harmony.pack200.Segment.SegmentMethodVisitor;
035import org.objectweb.asm.Label;
036import org.objectweb.asm.Opcodes;
037
038/**
039 * Class bands (corresponds to the {@code class_bands} set of bands in the pack200 specification)
040 */
041public class ClassBands extends BandSet {
042
043    private static final class TempParamAnnotation {
044
045        int numParams;
046        int[] annoN;
047        IntList pairN = new IntList();
048        List<String> typeRS = new ArrayList<>();
049        List<String> nameRU = new ArrayList<>();
050        List<String> tags = new ArrayList<>();
051        List<Object> values = new ArrayList<>();
052        List<Integer> caseArrayN = new ArrayList<>();
053        List<String> nestTypeRS = new ArrayList<>();
054        List<String> nestNameRU = new ArrayList<>();
055        List<Integer> nestPairN = new ArrayList<>();
056
057        TempParamAnnotation(final int numParams) {
058            this.numParams = numParams;
059            annoN = new int[numParams];
060        }
061
062        void addParameterAnnotation(final int parameter, final String desc, final List<String> nameRU, final List<String> tags,
063                final List<Object> values, final List<Integer> caseArrayN, final List<String> nestTypeRS, final List<String> nestNameRU,
064                final List<Integer> nestPairN) {
065            annoN[parameter]++;
066            typeRS.add(desc);
067            pairN.add(nameRU.size());
068            this.nameRU.addAll(nameRU);
069            this.tags.addAll(tags);
070            this.values.addAll(values);
071            this.caseArrayN.addAll(caseArrayN);
072            this.nestTypeRS.addAll(nestTypeRS);
073            this.nestNameRU.addAll(nestNameRU);
074            this.nestPairN.addAll(nestPairN);
075        }
076    }
077
078    private static final long[] EMPTY_LONG_ARRAY = {};
079
080    /**
081     * Counts arguments in a descriptor.
082     *
083     * @param descriptor the descriptor to parse.
084     * @return the argument count.
085     */
086    protected static int countArgs(final String descriptor) {
087        final int bra = descriptor.indexOf('(');
088        final int ket = descriptor.indexOf(')');
089        if (bra == -1 || ket == -1 || ket < bra) {
090            throw new IllegalArgumentException("No arguments");
091        }
092
093        boolean inType = false;
094        boolean consumingNextType = false;
095        int count = 0;
096        for (int i = bra + 1; i < ket; i++) {
097            final char charAt = descriptor.charAt(i);
098            if (inType && charAt == ';') {
099                inType = false;
100                consumingNextType = false;
101            } else if (!inType && charAt == 'L') {
102                inType = true;
103                count++;
104            } else if (charAt == '[') {
105                consumingNextType = true;
106            } else if (inType) {
107                // NOP
108            } else if (consumingNextType) {
109                count++;
110                consumingNextType = false;
111            } else if (charAt == 'D' || charAt == 'J') {
112                count += 2;
113            } else {
114                count++;
115            }
116        }
117        return count;
118    }
119
120    private final CpBands cpBands;
121    private final AttributeDefinitionBands attrBands;
122    private final CPClass[] class_this;
123    private final CPClass[] class_super;
124
125    private final CPClass[][] class_interface;
126
127    private final int[] class_interface_count;
128    private final int[] major_versions;
129    private final long[] class_flags;
130    private int[] class_attr_calls;
131    private final List<CPUTF8> classSourceFile = new ArrayList<>();
132    private final List<ConstantPoolEntry> classEnclosingMethodClass = new ArrayList<>();
133
134    private final List<ConstantPoolEntry> classEnclosingMethodDesc = new ArrayList<>();
135    private final List<CPSignature> classSignature = new ArrayList<>();
136
137    private final IntList classFileVersionMinor = new IntList();
138    private final IntList classFileVersionMajor = new IntList();
139    private final int[] class_field_count;
140    private final CPNameAndType[][] field_descr;
141    private final long[][] field_flags;
142    private int[] field_attr_calls;
143
144    private final List<CPConstant<?>> fieldConstantValueKQ = new ArrayList<>();
145    private final List<CPSignature> fieldSignature = new ArrayList<>();
146    private final int[] class_method_count;
147    private final CPNameAndType[][] method_descr;
148    private final long[][] method_flags;
149    private int[] method_attr_calls;
150    private final List<CPSignature> methodSignature = new ArrayList<>();
151
152    private final IntList methodExceptionNumber = new IntList();
153    private final List<CPClass> methodExceptionClasses = new ArrayList<>();
154    private int[] codeHeaders;
155    private final IntList codeMaxStack = new IntList();
156    private final IntList codeMaxLocals = new IntList();
157    private final IntList codeHandlerCount = new IntList();
158    private final List codeHandlerStartP = new ArrayList();
159    private final List codeHandlerEndPO = new ArrayList();
160    private final List codeHandlerCatchPO = new ArrayList();
161    private final List<CPClass> codeHandlerClass = new ArrayList<>();
162    private final List<Long> codeFlags = new ArrayList<>();
163    private int[] code_attr_calls;
164    private final IntList codeLineNumberTableN = new IntList();
165    private final List codeLineNumberTableBciP = new ArrayList();
166    private final IntList codeLineNumberTableLine = new IntList();
167    private final IntList codeLocalVariableTableN = new IntList();
168    private final List codeLocalVariableTableBciP = new ArrayList();
169    private final List codeLocalVariableTableSpanO = new ArrayList();
170    private final List<ConstantPoolEntry> codeLocalVariableTableNameRU = new ArrayList<>();
171    private final List<ConstantPoolEntry> codeLocalVariableTableTypeRS = new ArrayList<>();
172    private final IntList codeLocalVariableTableSlot = new IntList();
173    private final IntList codeLocalVariableTypeTableN = new IntList();
174    private final List codeLocalVariableTypeTableBciP = new ArrayList();
175    private final List codeLocalVariableTypeTableSpanO = new ArrayList();
176    private final List<ConstantPoolEntry> codeLocalVariableTypeTableNameRU = new ArrayList<>();
177
178    private final List<ConstantPoolEntry> codeLocalVariableTypeTableTypeRS = new ArrayList<>();
179    private final IntList codeLocalVariableTypeTableSlot = new IntList();
180    private final MetadataBandGroup class_RVA_bands;
181    private final MetadataBandGroup class_RIA_bands;
182    private final MetadataBandGroup field_RVA_bands;
183    private final MetadataBandGroup field_RIA_bands;
184    private final MetadataBandGroup method_RVA_bands;
185    private final MetadataBandGroup method_RIA_bands;
186    private final MetadataBandGroup method_RVPA_bands;
187
188    private final MetadataBandGroup method_RIPA_bands;
189    private final MetadataBandGroup method_AD_bands;
190    private final List<NewAttributeBands> classAttributeBands = new ArrayList<>();
191    private final List<NewAttributeBands> methodAttributeBands = new ArrayList<>();
192
193    private final List<NewAttributeBands> fieldAttributeBands = new ArrayList<>();
194    private final List<NewAttributeBands> codeAttributeBands = new ArrayList<>();
195    private final List<Long> tempFieldFlags = new ArrayList<>();
196    private final List<CPNameAndType> tempFieldDesc = new ArrayList<>();
197    private final List<Long> tempMethodFlags = new ArrayList<>();
198    private final List<CPNameAndType> tempMethodDesc = new ArrayList<>();
199
200    private TempParamAnnotation tempMethodRVPA;
201    private TempParamAnnotation tempMethodRIPA;
202    private boolean anySyntheticClasses;
203    private boolean anySyntheticFields;
204
205    private boolean anySyntheticMethods;
206    private final Segment segment;
207
208    private final Map<CPClass, Set<CPClass>> classReferencesInnerClass = new HashMap<>();
209
210    private final boolean stripDebug;
211    private int index;
212    private int numMethodArgs;
213    private int[] class_InnerClasses_N;
214    private CPClass[] class_InnerClasses_RC;
215    private int[] class_InnerClasses_F;
216
217    private List<CPClass> classInnerClassesOuterRCN;
218
219    private List<CPUTF8> classInnerClassesNameRUN;
220
221    public ClassBands(final Segment segment, final int numClasses, final int effort, final boolean stripDebug) throws IOException {
222        super(effort, segment.getSegmentHeader());
223        this.stripDebug = stripDebug;
224        this.segment = segment;
225        this.cpBands = segment.getCpBands();
226        this.attrBands = segment.getAttrBands();
227        class_this = new CPClass[numClasses];
228        class_super = new CPClass[numClasses];
229        class_interface_count = new int[numClasses];
230        class_interface = new CPClass[numClasses][];
231        class_field_count = new int[numClasses];
232        class_method_count = new int[numClasses];
233        field_descr = new CPNameAndType[numClasses][];
234        field_flags = new long[numClasses][];
235        method_descr = new CPNameAndType[numClasses][];
236        method_flags = new long[numClasses][];
237        for (int i = 0; i < numClasses; i++) {
238            field_flags[i] = EMPTY_LONG_ARRAY;
239            method_flags[i] = EMPTY_LONG_ARRAY;
240        }
241        // minor_versions = new int[numClasses];
242        major_versions = new int[numClasses];
243        class_flags = new long[numClasses];
244
245        class_RVA_bands = new MetadataBandGroup("RVA", MetadataBandGroup.CONTEXT_CLASS, cpBands, segmentHeader, effort);
246        class_RIA_bands = new MetadataBandGroup("RIA", MetadataBandGroup.CONTEXT_CLASS, cpBands, segmentHeader, effort);
247        field_RVA_bands = new MetadataBandGroup("RVA", MetadataBandGroup.CONTEXT_FIELD, cpBands, segmentHeader, effort);
248        field_RIA_bands = new MetadataBandGroup("RIA", MetadataBandGroup.CONTEXT_FIELD, cpBands, segmentHeader, effort);
249        method_RVA_bands = new MetadataBandGroup("RVA", MetadataBandGroup.CONTEXT_METHOD, cpBands, segmentHeader, effort);
250        method_RIA_bands = new MetadataBandGroup("RIA", MetadataBandGroup.CONTEXT_METHOD, cpBands, segmentHeader, effort);
251        method_RVPA_bands = new MetadataBandGroup("RVPA", MetadataBandGroup.CONTEXT_METHOD, cpBands, segmentHeader, effort);
252        method_RIPA_bands = new MetadataBandGroup("RIPA", MetadataBandGroup.CONTEXT_METHOD, cpBands, segmentHeader, effort);
253        method_AD_bands = new MetadataBandGroup("AD", MetadataBandGroup.CONTEXT_METHOD, cpBands, segmentHeader, effort);
254
255        createNewAttributeBands();
256    }
257
258    public void addAnnotation(final int context, final String desc, final boolean visible, final List<String> nameRU, final List<String> tags,
259            final List<Object> values, final List<Integer> caseArrayN, final List<String> nestTypeRS, final List<String> nestNameRU,
260            final List<Integer> nestPairN) {
261        switch (context) {
262        case MetadataBandGroup.CONTEXT_CLASS:
263            if (visible) {
264                class_RVA_bands.addAnnotation(desc, nameRU, tags, values, caseArrayN, nestTypeRS, nestNameRU, nestPairN);
265                if ((class_flags[index] & 1 << 21) != 0) {
266                    class_RVA_bands.incrementAnnoN();
267                } else {
268                    class_RVA_bands.newEntryInAnnoN();
269                    class_flags[index] = class_flags[index] | 1 << 21;
270                }
271            } else {
272                class_RIA_bands.addAnnotation(desc, nameRU, tags, values, caseArrayN, nestTypeRS, nestNameRU, nestPairN);
273                if ((class_flags[index] & 1 << 22) != 0) {
274                    class_RIA_bands.incrementAnnoN();
275                } else {
276                    class_RIA_bands.newEntryInAnnoN();
277                    class_flags[index] = class_flags[index] | 1 << 22;
278                }
279            }
280            break;
281        case MetadataBandGroup.CONTEXT_FIELD:
282            if (visible) {
283                field_RVA_bands.addAnnotation(desc, nameRU, tags, values, caseArrayN, nestTypeRS, nestNameRU, nestPairN);
284                final Long flag = tempFieldFlags.remove(tempFieldFlags.size() - 1);
285                if ((flag.intValue() & 1 << 21) != 0) {
286                    field_RVA_bands.incrementAnnoN();
287                } else {
288                    field_RVA_bands.newEntryInAnnoN();
289                }
290                tempFieldFlags.add(Long.valueOf(flag.intValue() | 1 << 21));
291            } else {
292                field_RIA_bands.addAnnotation(desc, nameRU, tags, values, caseArrayN, nestTypeRS, nestNameRU, nestPairN);
293                final Long flag = tempFieldFlags.remove(tempFieldFlags.size() - 1);
294                if ((flag.intValue() & 1 << 22) != 0) {
295                    field_RIA_bands.incrementAnnoN();
296                } else {
297                    field_RIA_bands.newEntryInAnnoN();
298                }
299                tempFieldFlags.add(Long.valueOf(flag.intValue() | 1 << 22));
300            }
301            break;
302        case MetadataBandGroup.CONTEXT_METHOD:
303            if (visible) {
304                method_RVA_bands.addAnnotation(desc, nameRU, tags, values, caseArrayN, nestTypeRS, nestNameRU, nestPairN);
305                final Long flag = tempMethodFlags.remove(tempMethodFlags.size() - 1);
306                if ((flag.intValue() & 1 << 21) != 0) {
307                    method_RVA_bands.incrementAnnoN();
308                } else {
309                    method_RVA_bands.newEntryInAnnoN();
310                }
311                tempMethodFlags.add(Long.valueOf(flag.intValue() | 1 << 21));
312            } else {
313                method_RIA_bands.addAnnotation(desc, nameRU, tags, values, caseArrayN, nestTypeRS, nestNameRU, nestPairN);
314                final Long flag = tempMethodFlags.remove(tempMethodFlags.size() - 1);
315                if ((flag.intValue() & 1 << 22) != 0) {
316                    method_RIA_bands.incrementAnnoN();
317                } else {
318                    method_RIA_bands.newEntryInAnnoN();
319                }
320                tempMethodFlags.add(Long.valueOf(flag.intValue() | 1 << 22));
321            }
322            break;
323        }
324    }
325
326    public void addAnnotationDefault(final List<String> nameRU, final List<String> tags, final List<Object> values, final List<Integer> caseArrayN,
327            final List<String> nestTypeRS, final List<String> nestNameRU, final List<Integer> nestPairN) {
328        method_AD_bands.addAnnotation(null, nameRU, tags, values, caseArrayN, nestTypeRS, nestNameRU, nestPairN);
329        final Long flag = tempMethodFlags.remove(tempMethodFlags.size() - 1);
330        tempMethodFlags.add(Long.valueOf(flag.longValue() | 1 << 25));
331    }
332
333    public void addClass(final int major, final int flags, final String className, final String signature, final String superName, final String[] interfaces) {
334        class_this[index] = cpBands.getCPClass(className);
335        class_super[index] = cpBands.getCPClass(superName);
336        class_interface_count[index] = interfaces.length;
337        class_interface[index] = new CPClass[interfaces.length];
338        Arrays.setAll(class_interface[index], i -> cpBands.getCPClass(interfaces[i]));
339        major_versions[index] = major;
340        class_flags[index] = flags;
341        if (!anySyntheticClasses && (flags & 1 << 12) != 0 && segment.getCurrentClassReader().hasSyntheticAttributes()) {
342            cpBands.addCPUtf8("Synthetic");
343            anySyntheticClasses = true;
344        }
345//        if ((flags & Opcodes.ACC_DEPRECATED) != 0) { // ASM uses (1<<17) flag for deprecated
346//            flags &= ~Opcodes.ACC_DEPRECATED;
347//            flags |= (1 << 20);
348//        }
349        if (signature != null) {
350            class_flags[index] |= 1 << 19;
351            classSignature.add(cpBands.getCPSignature(signature));
352        }
353    }
354
355    /**
356     * Adds a new class attribute.
357     *
358     * @param attribute the new class attribute.
359     */
360    public void addClassAttribute(final NewAttribute attribute) {
361        // TODO: backwards calls
362        final String attributeName = attribute.type;
363        for (final NewAttributeBands bands : classAttributeBands) {
364            if (bands.getAttributeName().equals(attributeName)) {
365                bands.addAttribute(attribute);
366                final int flagIndex = bands.getFlagIndex();
367                class_flags[index] |= 1 << flagIndex;
368                return;
369            }
370        }
371        throw new IllegalArgumentException("No suitable definition for " + attributeName);
372    }
373
374    public void addCode() {
375        codeHandlerCount.add(0);
376        if (!stripDebug) {
377            codeFlags.add(Long.valueOf(1 << 2));
378            codeLocalVariableTableN.add(0);
379        }
380    }
381
382    /**
383     * Adds a new code attribute.
384     *
385     * @param attribute the new code attribute.
386     */
387    public void addCodeAttribute(final NewAttribute attribute) {
388        final String attributeName = attribute.type;
389        for (final NewAttributeBands bands : codeAttributeBands) {
390            if (bands.getAttributeName().equals(attributeName)) {
391                bands.addAttribute(attribute);
392                final int flagIndex = bands.getFlagIndex();
393                final Long flags = codeFlags.remove(codeFlags.size() - 1);
394                codeFlags.add(Long.valueOf(flags.longValue() | 1 << flagIndex));
395                return;
396            }
397        }
398        throw new IllegalArgumentException("No suitable definition for " + attributeName);
399    }
400
401    /**
402     * Adds an enclosing method.
403     *
404     * @param ownerClassName a fully-qualifed class name.
405     * @param name a method name.
406     * @param signature a signature in the constant pool.
407     */
408    public void addEnclosingMethod(final String ownerClassName, final String name, final String signature) {
409        class_flags[index] |= 1 << 18;
410        classEnclosingMethodClass.add(cpBands.getCPClass(ownerClassName));
411        classEnclosingMethodDesc.add(name == null ? null : cpBands.getCPNameAndType(name, signature));
412    }
413
414    public void addField(int flags, final String name, final String desc, final String signature, final Object value) {
415        flags &= 0xFFFF;
416        tempFieldDesc.add(cpBands.getCPNameAndType(name, desc));
417        if (signature != null) {
418            fieldSignature.add(cpBands.getCPSignature(signature));
419            flags |= 1 << 19;
420        }
421        if ((flags & Opcodes.ACC_DEPRECATED) != 0) { // ASM uses (1<<17) flag for deprecated
422            flags &= ~Opcodes.ACC_DEPRECATED;
423            flags |= 1 << 20;
424        }
425        if (value != null) {
426            fieldConstantValueKQ.add(cpBands.getConstant(value));
427            flags |= 1 << 17;
428        }
429        if (!anySyntheticFields && (flags & 1 << 12) != 0 && segment.getCurrentClassReader().hasSyntheticAttributes()) {
430            cpBands.addCPUtf8("Synthetic");
431            anySyntheticFields = true;
432        }
433        tempFieldFlags.add(Long.valueOf(flags));
434    }
435
436    /**
437     * Adds a new field attribute.
438     *
439     * @param attribute the new field attribute.
440     */
441    public void addFieldAttribute(final NewAttribute attribute) {
442        final String attributeName = attribute.type;
443        for (final NewAttributeBands bands : fieldAttributeBands) {
444            if (bands.getAttributeName().equals(attributeName)) {
445                bands.addAttribute(attribute);
446                final int flagIndex = bands.getFlagIndex();
447                final Long flags = tempFieldFlags.remove(tempFieldFlags.size() - 1);
448                tempFieldFlags.add(Long.valueOf(flags.longValue() | 1 << flagIndex));
449                return;
450            }
451        }
452        throw new IllegalArgumentException("No suitable definition for " + attributeName);
453    }
454
455    public void addHandler(final Label start, final Label end, final Label handler, final String type) {
456        final int handlers = codeHandlerCount.remove(codeHandlerCount.size() - 1);
457        codeHandlerCount.add(handlers + 1);
458        codeHandlerStartP.add(start);
459        codeHandlerEndPO.add(end);
460        codeHandlerCatchPO.add(handler);
461        codeHandlerClass.add(type == null ? null : cpBands.getCPClass(type));
462    }
463
464    public void addLineNumber(final int line, final Label start) {
465        final Long latestCodeFlag = codeFlags.get(codeFlags.size() - 1);
466        if ((latestCodeFlag.intValue() & 1 << 1) == 0) {
467            codeFlags.remove(codeFlags.size() - 1);
468            codeFlags.add(Long.valueOf(latestCodeFlag.intValue() | 1 << 1));
469            codeLineNumberTableN.add(1);
470        } else {
471            codeLineNumberTableN.increment(codeLineNumberTableN.size() - 1);
472        }
473        codeLineNumberTableLine.add(line);
474        codeLineNumberTableBciP.add(start);
475    }
476
477    public void addLocalVariable(final String name, final String desc, final String signature, final Label start, final Label end, final int indx) {
478        if (signature != null) { // LocalVariableTypeTable attribute
479            final Long latestCodeFlag = codeFlags.get(codeFlags.size() - 1);
480            if ((latestCodeFlag.intValue() & 1 << 3) == 0) {
481                codeFlags.remove(codeFlags.size() - 1);
482                codeFlags.add(Long.valueOf(latestCodeFlag.intValue() | 1 << 3));
483                codeLocalVariableTypeTableN.add(1);
484            } else {
485                codeLocalVariableTypeTableN.increment(codeLocalVariableTypeTableN.size() - 1);
486            }
487            codeLocalVariableTypeTableBciP.add(start);
488            codeLocalVariableTypeTableSpanO.add(end);
489            codeLocalVariableTypeTableNameRU.add(cpBands.getCPUtf8(name));
490            codeLocalVariableTypeTableTypeRS.add(cpBands.getCPSignature(signature));
491            codeLocalVariableTypeTableSlot.add(indx);
492        }
493        // LocalVariableTable attribute
494        codeLocalVariableTableN.increment(codeLocalVariableTableN.size() - 1);
495        codeLocalVariableTableBciP.add(start);
496        codeLocalVariableTableSpanO.add(end);
497        codeLocalVariableTableNameRU.add(cpBands.getCPUtf8(name));
498        codeLocalVariableTableTypeRS.add(cpBands.getCPSignature(desc));
499        codeLocalVariableTableSlot.add(indx);
500    }
501
502    public void addMaxStack(final int maxStack, int maxLocals) {
503        final Long latestFlag = tempMethodFlags.remove(tempMethodFlags.size() - 1);
504        final Long newFlag = Long.valueOf(latestFlag.intValue() | 1 << 17);
505        tempMethodFlags.add(newFlag);
506        codeMaxStack.add(maxStack);
507        if ((newFlag.longValue() & 1 << 3) == 0) { // not static
508            maxLocals--; // minus 'this' local
509        }
510        maxLocals -= numMethodArgs;
511        codeMaxLocals.add(maxLocals);
512    }
513
514    public void addMethod(int flags, final String name, final String desc, final String signature, final String[] exceptions) {
515        final CPNameAndType nt = cpBands.getCPNameAndType(name, desc);
516        tempMethodDesc.add(nt);
517        if (signature != null) {
518            methodSignature.add(cpBands.getCPSignature(signature));
519            flags |= 1 << 19;
520        }
521        if (exceptions != null) {
522            methodExceptionNumber.add(exceptions.length);
523            for (final String exception : exceptions) {
524                methodExceptionClasses.add(cpBands.getCPClass(exception));
525            }
526            flags |= 1 << 18;
527        }
528        if ((flags & Opcodes.ACC_DEPRECATED) != 0) { // ASM uses (1<<17) flag for deprecated
529            flags &= ~Opcodes.ACC_DEPRECATED;
530            flags |= 1 << 20;
531        }
532        tempMethodFlags.add(Long.valueOf(flags));
533        numMethodArgs = countArgs(desc);
534        if (!anySyntheticMethods && (flags & 1 << 12) != 0 && segment.getCurrentClassReader().hasSyntheticAttributes()) {
535            cpBands.addCPUtf8("Synthetic");
536            anySyntheticMethods = true;
537        }
538    }
539
540    /**
541     * Adds a new method attribute.
542     *
543     * @param attribute the new method attribute.
544     */
545    public void addMethodAttribute(final NewAttribute attribute) {
546        final String attributeName = attribute.type;
547        for (final NewAttributeBands bands : methodAttributeBands) {
548            if (bands.getAttributeName().equals(attributeName)) {
549                bands.addAttribute(attribute);
550                final int flagIndex = bands.getFlagIndex();
551                final Long flags = tempMethodFlags.remove(tempMethodFlags.size() - 1);
552                tempMethodFlags.add(Long.valueOf(flags.longValue() | 1 << flagIndex));
553                return;
554            }
555        }
556        throw new IllegalArgumentException("No suitable definition for " + attributeName);
557    }
558
559    public void addParameterAnnotation(final int parameter, final String desc, final boolean visible, final List<String> nameRU, final List<String> tags,
560            final List<Object> values, final List<Integer> caseArrayN, final List<String> nestTypeRS, final List<String> nestNameRU,
561            final List<Integer> nestPairN) {
562        if (visible) {
563            if (tempMethodRVPA == null) {
564                tempMethodRVPA = new TempParamAnnotation(numMethodArgs);
565                tempMethodRVPA.addParameterAnnotation(parameter, desc, nameRU, tags, values, caseArrayN, nestTypeRS, nestNameRU, nestPairN);
566            }
567            final Long flag = tempMethodFlags.remove(tempMethodFlags.size() - 1);
568            tempMethodFlags.add(Long.valueOf(flag.longValue() | 1 << 23));
569        } else {
570            if (tempMethodRIPA == null) {
571                tempMethodRIPA = new TempParamAnnotation(numMethodArgs);
572                tempMethodRIPA.addParameterAnnotation(parameter, desc, nameRU, tags, values, caseArrayN, nestTypeRS, nestNameRU, nestPairN);
573            }
574            final Long flag = tempMethodFlags.remove(tempMethodFlags.size() - 1);
575            tempMethodFlags.add(Long.valueOf(flag.longValue() | 1 << 24));
576        }
577    }
578
579    /**
580     * Adds a source file path.
581     *
582     * @param source a source file path.
583     */
584    public void addSourceFile(final String source) {
585        String implicitSourceFileName = class_this[index].toString();
586        if (implicitSourceFileName.indexOf('$') != -1) {
587            implicitSourceFileName = implicitSourceFileName.substring(0, implicitSourceFileName.indexOf('$'));
588        }
589        implicitSourceFileName = implicitSourceFileName.substring(implicitSourceFileName.lastIndexOf('/') + 1) + ".java";
590        if (source.equals(implicitSourceFileName)) {
591            classSourceFile.add(null);
592        } else {
593            classSourceFile.add(cpBands.getCPUtf8(source));
594        }
595        class_flags[index] |= 1 << 17;
596    }
597
598    private void createNewAttributeBands() throws IOException {
599        for (final AttributeDefinition def : attrBands.getClassAttributeLayouts()) {
600            classAttributeBands.add(new NewAttributeBands(effort, cpBands, segment.getSegmentHeader(), def));
601        }
602        for (final AttributeDefinition def : attrBands.getMethodAttributeLayouts()) {
603            methodAttributeBands.add(new NewAttributeBands(effort, cpBands, segment.getSegmentHeader(), def));
604        }
605        for (final AttributeDefinition def : attrBands.getFieldAttributeLayouts()) {
606            fieldAttributeBands.add(new NewAttributeBands(effort, cpBands, segment.getSegmentHeader(), def));
607        }
608        for (final AttributeDefinition def : attrBands.getCodeAttributeLayouts()) {
609            codeAttributeBands.add(new NewAttributeBands(effort, cpBands, segment.getSegmentHeader(), def));
610        }
611    }
612
613    public void currentClassReferencesInnerClass(final CPClass inner) {
614        if (!(index >= class_this.length)) {
615            final CPClass currentClass = class_this[index];
616            if (currentClass != null && !currentClass.equals(inner) && !isInnerClassOf(currentClass.toString(), inner)) {
617                classReferencesInnerClass.computeIfAbsent(currentClass, c -> new HashSet<>()).add(inner);
618            }
619        }
620    }
621
622    public void doBciRenumbering(final IntList bciRenumbering, final Map<Label, Integer> labelsToOffsets) {
623        renumberBci(codeLineNumberTableBciP, bciRenumbering, labelsToOffsets);
624        renumberBci(codeLocalVariableTableBciP, bciRenumbering, labelsToOffsets);
625        renumberOffsetBci(codeLocalVariableTableBciP, codeLocalVariableTableSpanO, bciRenumbering, labelsToOffsets);
626        renumberBci(codeLocalVariableTypeTableBciP, bciRenumbering, labelsToOffsets);
627        renumberOffsetBci(codeLocalVariableTypeTableBciP, codeLocalVariableTypeTableSpanO, bciRenumbering, labelsToOffsets);
628        renumberBci(codeHandlerStartP, bciRenumbering, labelsToOffsets);
629        renumberOffsetBci(codeHandlerStartP, codeHandlerEndPO, bciRenumbering, labelsToOffsets);
630        renumberDoubleOffsetBci(codeHandlerStartP, codeHandlerEndPO, codeHandlerCatchPO, bciRenumbering, labelsToOffsets);
631
632        for (final NewAttributeBands newAttributeBandSet : classAttributeBands) {
633            newAttributeBandSet.renumberBci(bciRenumbering, labelsToOffsets);
634        }
635        for (final NewAttributeBands newAttributeBandSet : methodAttributeBands) {
636            newAttributeBandSet.renumberBci(bciRenumbering, labelsToOffsets);
637        }
638        for (final NewAttributeBands newAttributeBandSet : fieldAttributeBands) {
639            newAttributeBandSet.renumberBci(bciRenumbering, labelsToOffsets);
640        }
641        for (final NewAttributeBands newAttributeBandSet : codeAttributeBands) {
642            newAttributeBandSet.renumberBci(bciRenumbering, labelsToOffsets);
643        }
644    }
645
646    /**
647     * Called when all the data for the current class has been.
648     *
649     * @see Segment#visitEnd()
650     */
651    public void endOfClass() {
652        final int numFields = tempFieldDesc.size();
653        class_field_count[index] = numFields;
654        field_descr[index] = new CPNameAndType[numFields];
655        field_flags[index] = new long[numFields];
656        for (int i = 0; i < numFields; i++) {
657            field_descr[index][i] = tempFieldDesc.get(i);
658            field_flags[index][i] = tempFieldFlags.get(i).longValue();
659        }
660        final int numMethods = tempMethodDesc.size();
661        class_method_count[index] = numMethods;
662        method_descr[index] = new CPNameAndType[numMethods];
663        method_flags[index] = new long[numMethods];
664        for (int i = 0; i < numMethods; i++) {
665            method_descr[index][i] = tempMethodDesc.get(i);
666            method_flags[index][i] = tempMethodFlags.get(i).longValue();
667        }
668        tempFieldDesc.clear();
669        tempFieldFlags.clear();
670        tempMethodDesc.clear();
671        tempMethodFlags.clear();
672        index++;
673    }
674
675    /**
676     * Called when all the data for the current method has been.
677     *
678     * @see SegmentMethodVisitor#visitEnd()
679     */
680    public void endOfMethod() {
681        if (tempMethodRVPA != null) {
682            method_RVPA_bands.addParameterAnnotation(tempMethodRVPA.numParams, tempMethodRVPA.annoN, tempMethodRVPA.pairN, tempMethodRVPA.typeRS,
683                    tempMethodRVPA.nameRU, tempMethodRVPA.tags, tempMethodRVPA.values, tempMethodRVPA.caseArrayN, tempMethodRVPA.nestTypeRS,
684                    tempMethodRVPA.nestNameRU, tempMethodRVPA.nestPairN);
685            tempMethodRVPA = null;
686        }
687        if (tempMethodRIPA != null) {
688            method_RIPA_bands.addParameterAnnotation(tempMethodRIPA.numParams, tempMethodRIPA.annoN, tempMethodRIPA.pairN, tempMethodRIPA.typeRS,
689                    tempMethodRIPA.nameRU, tempMethodRIPA.tags, tempMethodRIPA.values, tempMethodRIPA.caseArrayN, tempMethodRIPA.nestTypeRS,
690                    tempMethodRIPA.nestNameRU, tempMethodRIPA.nestPairN);
691            tempMethodRIPA = null;
692        }
693        if (codeFlags.size() > 0) {
694            final long latestCodeFlag = codeFlags.get(codeFlags.size() - 1).longValue();
695            final int latestLocalVariableTableN = codeLocalVariableTableN.get(codeLocalVariableTableN.size() - 1);
696            if (latestCodeFlag == 1 << 2 && latestLocalVariableTableN == 0) {
697                codeLocalVariableTableN.remove(codeLocalVariableTableN.size() - 1);
698                codeFlags.remove(codeFlags.size() - 1);
699                codeFlags.add(Long.valueOf(0));
700            }
701        }
702    }
703
704    /**
705     * All input classes for the segment have now been read in, so this method is called so that this class can calculate/complete anything it could not do
706     * while classes were being read.
707     */
708    public void finaliseBands() {
709        final int defaultMajorVersion = segmentHeader.getDefaultMajorVersion();
710        for (int i = 0; i < class_flags.length; i++) {
711            final int major = major_versions[i];
712            if (major != defaultMajorVersion) {
713                class_flags[i] |= 1 << 24;
714                classFileVersionMajor.add(major);
715                classFileVersionMinor.add(0);
716            }
717        }
718        // Calculate code headers
719        codeHeaders = new int[codeHandlerCount.size()];
720        int removed = 0;
721        for (int i = 0; i < codeHeaders.length; i++) {
722            final int numHandlers = codeHandlerCount.get(i - removed);
723            final int maxLocals = codeMaxLocals.get(i - removed);
724            final int maxStack = codeMaxStack.get(i - removed);
725            switch (numHandlers) {
726            case 0: {
727                final int header = maxLocals * 12 + maxStack + 1;
728                if (header < 145 && maxStack < 12) {
729                    codeHeaders[i] = header;
730                }
731                break;
732            }
733            case 1: {
734                final int header = maxLocals * 8 + maxStack + 145;
735                if (header < 209 && maxStack < 8) {
736                    codeHeaders[i] = header;
737                }
738                break;
739            }
740            case 2: {
741                final int header = maxLocals * 7 + maxStack + 209;
742                if (header < 256 && maxStack < 7) {
743                    codeHeaders[i] = header;
744                }
745                break;
746            }
747            default:
748                break;
749            }
750            if (codeHeaders[i] != 0) { // Remove the redundant values from
751                                       // codeHandlerCount, codeMaxLocals and
752                                       // codeMaxStack
753                codeHandlerCount.remove(i - removed);
754                codeMaxLocals.remove(i - removed);
755                codeMaxStack.remove(i - removed);
756                removed++;
757            } else if (!segment.getSegmentHeader().have_all_code_flags()) {
758                codeFlags.add(Long.valueOf(0));
759            }
760        }
761
762        // Compute any required IcLocals
763        final IntList innerClassesN = new IntList();
764        final List<IcTuple> icLocal = new ArrayList<>();
765        for (int i = 0; i < class_this.length; i++) {
766            final CPClass cpClass = class_this[i];
767            final Set<CPClass> referencedInnerClasses = classReferencesInnerClass.get(cpClass);
768            if (referencedInnerClasses != null) {
769                int innerN = 0;
770                final List<IcTuple> innerClasses = segment.getIcBands().getInnerClassesForOuter(cpClass.toString());
771                if (innerClasses != null) {
772                    for (final IcTuple element : innerClasses) {
773                        referencedInnerClasses.remove(element.C);
774                    }
775                }
776                for (final CPClass inner : referencedInnerClasses) {
777                    final IcTuple icTuple = segment.getIcBands().getIcTuple(inner);
778                    if (icTuple != null && !icTuple.isAnonymous()) {
779                        // should transmit an icLocal entry
780                        icLocal.add(icTuple);
781                        innerN++;
782                    }
783                }
784                if (innerN != 0) {
785                    innerClassesN.add(innerN);
786                    class_flags[i] |= 1 << 23;
787                }
788            }
789        }
790        class_InnerClasses_N = innerClassesN.toArray();
791        class_InnerClasses_RC = new CPClass[icLocal.size()];
792        class_InnerClasses_F = new int[icLocal.size()];
793        classInnerClassesOuterRCN = new ArrayList<>();
794        classInnerClassesNameRUN = new ArrayList<>();
795        for (int i = 0; i < class_InnerClasses_RC.length; i++) {
796            final IcTuple icTuple = icLocal.get(i);
797            class_InnerClasses_RC[i] = icTuple.C;
798            if (icTuple.C2 == null && icTuple.N == null) {
799                class_InnerClasses_F[i] = 0;
800            } else {
801                if (icTuple.F == 0) {
802                    class_InnerClasses_F[i] = 0x00010000;
803                } else {
804                    class_InnerClasses_F[i] = icTuple.F;
805                }
806                classInnerClassesOuterRCN.add(icTuple.C2);
807                classInnerClassesNameRUN.add(icTuple.N);
808            }
809        }
810        // Calculate any backwards calls from metadata bands
811        final IntList classAttrCalls = new IntList();
812        final IntList fieldAttrCalls = new IntList();
813        final IntList methodAttrCalls = new IntList();
814        final IntList codeAttrCalls = new IntList();
815
816        if (class_RVA_bands.hasContent()) {
817            classAttrCalls.add(class_RVA_bands.numBackwardsCalls());
818        }
819        if (class_RIA_bands.hasContent()) {
820            classAttrCalls.add(class_RIA_bands.numBackwardsCalls());
821        }
822        if (field_RVA_bands.hasContent()) {
823            fieldAttrCalls.add(field_RVA_bands.numBackwardsCalls());
824        }
825        if (field_RIA_bands.hasContent()) {
826            fieldAttrCalls.add(field_RIA_bands.numBackwardsCalls());
827        }
828        if (method_RVA_bands.hasContent()) {
829            methodAttrCalls.add(method_RVA_bands.numBackwardsCalls());
830        }
831        if (method_RIA_bands.hasContent()) {
832            methodAttrCalls.add(method_RIA_bands.numBackwardsCalls());
833        }
834        if (method_RVPA_bands.hasContent()) {
835            methodAttrCalls.add(method_RVPA_bands.numBackwardsCalls());
836        }
837        if (method_RIPA_bands.hasContent()) {
838            methodAttrCalls.add(method_RIPA_bands.numBackwardsCalls());
839        }
840        if (method_AD_bands.hasContent()) {
841            methodAttrCalls.add(method_AD_bands.numBackwardsCalls());
842        }
843
844        // Sort non-predefined attribute bands
845        final Comparator<NewAttributeBands> comparator = (arg0, arg1) -> arg0.getFlagIndex() - arg1.getFlagIndex();
846        classAttributeBands.sort(comparator);
847        methodAttributeBands.sort(comparator);
848        fieldAttributeBands.sort(comparator);
849        codeAttributeBands.sort(comparator);
850
851        for (final NewAttributeBands bands : classAttributeBands) {
852            if (bands.isUsedAtLeastOnce()) {
853                for (final int backwardsCallCount : bands.numBackwardsCalls()) {
854                    classAttrCalls.add(backwardsCallCount);
855                }
856            }
857        }
858        for (final NewAttributeBands bands : methodAttributeBands) {
859            if (bands.isUsedAtLeastOnce()) {
860                for (final int backwardsCallCount : bands.numBackwardsCalls()) {
861                    methodAttrCalls.add(backwardsCallCount);
862                }
863            }
864        }
865        for (final NewAttributeBands bands : fieldAttributeBands) {
866            if (bands.isUsedAtLeastOnce()) {
867                for (final int backwardsCallCount : bands.numBackwardsCalls()) {
868                    fieldAttrCalls.add(backwardsCallCount);
869                }
870            }
871        }
872        for (final NewAttributeBands bands : codeAttributeBands) {
873            if (bands.isUsedAtLeastOnce()) {
874                for (final int backwardsCallCount : bands.numBackwardsCalls()) {
875                    codeAttrCalls.add(backwardsCallCount);
876                }
877            }
878        }
879
880        class_attr_calls = classAttrCalls.toArray();
881        field_attr_calls = fieldAttrCalls.toArray();
882        method_attr_calls = methodAttrCalls.toArray();
883        code_attr_calls = codeAttrCalls.toArray();
884    }
885
886    private int[] getInts(final CPClass[] cpClasses) {
887        final int[] ints = new int[cpClasses.length];
888        for (int i = 0; i < ints.length; i++) {
889            if (cpClasses[i] != null) {
890                ints[i] = cpClasses[i].getIndex();
891            }
892        }
893        return ints;
894    }
895
896    /**
897     * Tests whether there are any synthetic classes.
898     *
899     * @return whether there are any synthetic classes.
900     */
901    public boolean isAnySyntheticClasses() {
902        return anySyntheticClasses;
903    }
904
905    /**
906     * Tests whether there are any synthetic fields.
907     *
908     * @return whether there are any synthetic fields.
909     */
910    public boolean isAnySyntheticFields() {
911        return anySyntheticFields;
912    }
913
914    /**
915     * Tests whether there are any synthetic methods.
916     *
917     * @return whether there are any synthetic methods.
918     */
919    public boolean isAnySyntheticMethods() {
920        return anySyntheticMethods;
921    }
922
923    private boolean isInnerClass(final String possibleInner) {
924        return possibleInner.indexOf('$') != -1;
925    }
926
927    private boolean isInnerClassOf(final String possibleInner, final CPClass possibleOuter) {
928        if (isInnerClass(possibleInner)) {
929            final String superClassName = possibleInner.substring(0, possibleInner.lastIndexOf('$'));
930            if (superClassName.equals(possibleOuter.toString())) {
931                return true;
932            }
933            return isInnerClassOf(superClassName, possibleOuter);
934        }
935        return false;
936    }
937
938    /**
939     * Gets the number of classes processed.
940     *
941     * @return the number of classes processed.
942     */
943    public int numClassesProcessed() {
944        return index;
945    }
946
947    @Override
948    public void pack(final OutputStream out) throws IOException, Pack200Exception {
949        PackingUtils.log("Writing class bands...");
950
951        byte[] encodedBand = encodeBandInt("class_this", getInts(class_this), Codec.DELTA5);
952        out.write(encodedBand);
953        PackingUtils.log("Wrote " + encodedBand.length + " bytes from class_this[" + class_this.length + "]");
954
955        encodedBand = encodeBandInt("class_super", getInts(class_super), Codec.DELTA5);
956        out.write(encodedBand);
957        PackingUtils.log("Wrote " + encodedBand.length + " bytes from class_super[" + class_super.length + "]");
958
959        encodedBand = encodeBandInt("class_interface_count", class_interface_count, Codec.DELTA5);
960        out.write(encodedBand);
961        PackingUtils.log("Wrote " + encodedBand.length + " bytes from class_interface_count[" + class_interface_count.length + "]");
962
963        final int totalInterfaces = sum(class_interface_count);
964        final int[] classInterface = new int[totalInterfaces];
965        int k = 0;
966        for (final CPClass[] element : class_interface) {
967            if (element != null) {
968                for (final CPClass cpClass : element) {
969                    classInterface[k] = cpClass.getIndex();
970                    k++;
971                }
972            }
973        }
974
975        encodedBand = encodeBandInt("class_interface", classInterface, Codec.DELTA5);
976        out.write(encodedBand);
977        PackingUtils.log("Wrote " + encodedBand.length + " bytes from class_interface[" + classInterface.length + "]");
978
979        encodedBand = encodeBandInt("class_field_count", class_field_count, Codec.DELTA5);
980        out.write(encodedBand);
981        PackingUtils.log("Wrote " + encodedBand.length + " bytes from class_field_count[" + class_field_count.length + "]");
982
983        encodedBand = encodeBandInt("class_method_count", class_method_count, Codec.DELTA5);
984        out.write(encodedBand);
985        PackingUtils.log("Wrote " + encodedBand.length + " bytes from class_method_count[" + class_method_count.length + "]");
986
987        final int totalFields = sum(class_field_count);
988        final int[] fieldDescr = new int[totalFields];
989        k = 0;
990        for (int i = 0; i < index; i++) {
991            for (int j = 0; j < field_descr[i].length; j++) {
992                final CPNameAndType descr = field_descr[i][j];
993                fieldDescr[k] = descr.getIndex();
994                k++;
995            }
996        }
997
998        encodedBand = encodeBandInt("field_descr", fieldDescr, Codec.DELTA5);
999        out.write(encodedBand);
1000        PackingUtils.log("Wrote " + encodedBand.length + " bytes from field_descr[" + fieldDescr.length + "]");
1001
1002        writeFieldAttributeBands(out);
1003
1004        final int totalMethods = sum(class_method_count);
1005        final int[] methodDescr = new int[totalMethods];
1006        k = 0;
1007        for (int i = 0; i < index; i++) {
1008            for (int j = 0; j < method_descr[i].length; j++) {
1009                final CPNameAndType descr = method_descr[i][j];
1010                methodDescr[k] = descr.getIndex();
1011                k++;
1012            }
1013        }
1014
1015        encodedBand = encodeBandInt("method_descr", methodDescr, Codec.MDELTA5);
1016        out.write(encodedBand);
1017        PackingUtils.log("Wrote " + encodedBand.length + " bytes from method_descr[" + methodDescr.length + "]");
1018
1019        writeMethodAttributeBands(out);
1020        writeClassAttributeBands(out);
1021        writeCodeBands(out);
1022    }
1023
1024    /**
1025     * Remove all entries for the current class
1026     */
1027    public void removeCurrentClass() {
1028        // Note - this doesn't remove any entries added to the constant pool but
1029        // that shouldn't be a problem
1030        if ((class_flags[index] & 1 << 17) != 0) {
1031            classSourceFile.remove(classSourceFile.size() - 1);
1032        }
1033        if ((class_flags[index] & 1 << 18) != 0) {
1034            classEnclosingMethodClass.remove(classEnclosingMethodClass.size() - 1);
1035            classEnclosingMethodDesc.remove(classEnclosingMethodDesc.size() - 1);
1036        }
1037        if ((class_flags[index] & 1 << 19) != 0) {
1038            classSignature.remove(classSignature.size() - 1);
1039        }
1040        if ((class_flags[index] & 1 << 21) != 0) {
1041            class_RVA_bands.removeLatest();
1042        }
1043        if ((class_flags[index] & 1 << 22) != 0) {
1044            class_RIA_bands.removeLatest();
1045        }
1046        for (final Long flagsL : tempFieldFlags) {
1047            final long flags = flagsL.longValue();
1048            if ((flags & 1 << 19) != 0) {
1049                fieldSignature.remove(fieldSignature.size() - 1);
1050            }
1051            if ((flags & 1 << 17) != 0) {
1052                fieldConstantValueKQ.remove(fieldConstantValueKQ.size() - 1);
1053            }
1054            if ((flags & 1 << 21) != 0) {
1055                field_RVA_bands.removeLatest();
1056            }
1057            if ((flags & 1 << 22) != 0) {
1058                field_RIA_bands.removeLatest();
1059            }
1060        }
1061        for (final Long flagsL : tempMethodFlags) {
1062            final long flags = flagsL.longValue();
1063            if ((flags & 1 << 19) != 0) {
1064                methodSignature.remove(methodSignature.size() - 1);
1065            }
1066            if ((flags & 1 << 18) != 0) {
1067                final int exceptions = methodExceptionNumber.remove(methodExceptionNumber.size() - 1);
1068                for (int i = 0; i < exceptions; i++) {
1069                    methodExceptionClasses.remove(methodExceptionClasses.size() - 1);
1070                }
1071            }
1072            if ((flags & 1 << 17) != 0) { // has code attribute
1073                codeMaxLocals.remove(codeMaxLocals.size() - 1);
1074                codeMaxStack.remove(codeMaxStack.size() - 1);
1075                final int handlers = codeHandlerCount.remove(codeHandlerCount.size() - 1);
1076                for (int i = 0; i < handlers; i++) {
1077                    final int index = codeHandlerStartP.size() - 1;
1078                    codeHandlerStartP.remove(index);
1079                    codeHandlerEndPO.remove(index);
1080                    codeHandlerCatchPO.remove(index);
1081                    codeHandlerClass.remove(index);
1082                }
1083                if (!stripDebug) {
1084                    final long cdeFlags = codeFlags.remove(codeFlags.size() - 1).longValue();
1085                    final int numLocalVariables = codeLocalVariableTableN.remove(codeLocalVariableTableN.size() - 1);
1086                    for (int i = 0; i < numLocalVariables; i++) {
1087                        final int location = codeLocalVariableTableBciP.size() - 1;
1088                        codeLocalVariableTableBciP.remove(location);
1089                        codeLocalVariableTableSpanO.remove(location);
1090                        codeLocalVariableTableNameRU.remove(location);
1091                        codeLocalVariableTableTypeRS.remove(location);
1092                        codeLocalVariableTableSlot.remove(location);
1093                    }
1094                    if ((cdeFlags & 1 << 3) != 0) {
1095                        final int numLocalVariablesInTypeTable = codeLocalVariableTypeTableN.remove(codeLocalVariableTypeTableN.size() - 1);
1096                        for (int i = 0; i < numLocalVariablesInTypeTable; i++) {
1097                            final int location = codeLocalVariableTypeTableBciP.size() - 1;
1098                            codeLocalVariableTypeTableBciP.remove(location);
1099                            codeLocalVariableTypeTableSpanO.remove(location);
1100                            codeLocalVariableTypeTableNameRU.remove(location);
1101                            codeLocalVariableTypeTableTypeRS.remove(location);
1102                            codeLocalVariableTypeTableSlot.remove(location);
1103                        }
1104                    }
1105                    if ((cdeFlags & 1 << 1) != 0) {
1106                        final int numLineNumbers = codeLineNumberTableN.remove(codeLineNumberTableN.size() - 1);
1107                        for (int i = 0; i < numLineNumbers; i++) {
1108                            final int location = codeLineNumberTableBciP.size() - 1;
1109                            codeLineNumberTableBciP.remove(location);
1110                            codeLineNumberTableLine.remove(location);
1111                        }
1112                    }
1113                }
1114            }
1115            if ((flags & 1 << 21) != 0) {
1116                method_RVA_bands.removeLatest();
1117            }
1118            if ((flags & 1 << 22) != 0) {
1119                method_RIA_bands.removeLatest();
1120            }
1121            if ((flags & 1 << 23) != 0) {
1122                method_RVPA_bands.removeLatest();
1123            }
1124            if ((flags & 1 << 24) != 0) {
1125                method_RIPA_bands.removeLatest();
1126            }
1127            if ((flags & 1 << 25) != 0) {
1128                method_AD_bands.removeLatest();
1129            }
1130        }
1131        class_this[index] = null;
1132        class_super[index] = null;
1133        class_interface_count[index] = 0;
1134        class_interface[index] = null;
1135        major_versions[index] = 0;
1136        class_flags[index] = 0;
1137        tempFieldDesc.clear();
1138        tempFieldFlags.clear();
1139        tempMethodDesc.clear();
1140        tempMethodFlags.clear();
1141        if (index > 0) {
1142            index--;
1143        }
1144    }
1145
1146    private void renumberBci(final List<Integer> list, final IntList bciRenumbering, final Map<Label, Integer> labelsToOffsets) {
1147        for (int i = list.size() - 1; i >= 0; i--) {
1148            final Object label = list.get(i);
1149            if (label instanceof Integer) {
1150                break;
1151            }
1152            if (label instanceof Label) {
1153                list.remove(i);
1154                final Integer bytecodeIndex = labelsToOffsets.get(label);
1155                list.add(i, Integer.valueOf(bciRenumbering.get(bytecodeIndex.intValue())));
1156            }
1157        }
1158    }
1159
1160    private void renumberDoubleOffsetBci(final List<Integer> relative, final List<Integer> firstOffset, final List<Object> list, final IntList bciRenumbering,
1161            final Map<Label, Integer> labelsToOffsets) {
1162        // TODO: There's probably a nicer way of doing this...
1163        for (int i = list.size() - 1; i >= 0; i--) {
1164            final Object label = list.get(i);
1165            if (label instanceof Integer) {
1166                break;
1167            }
1168            if (label instanceof Label) {
1169                list.remove(i);
1170                final Integer bytecodeIndex = labelsToOffsets.get(label);
1171                final Integer renumberedOffset = Integer
1172                        .valueOf(bciRenumbering.get(bytecodeIndex.intValue()) - relative.get(i).intValue() - firstOffset.get(i).intValue());
1173                list.add(i, renumberedOffset);
1174            }
1175        }
1176    }
1177
1178    private void renumberOffsetBci(final List<Integer> relative, final List<Integer> list, final IntList bciRenumbering,
1179            final Map<Label, Integer> labelsToOffsets) {
1180        for (int i = list.size() - 1; i >= 0; i--) {
1181            final Object label = list.get(i);
1182            if (label instanceof Integer) {
1183                break;
1184            }
1185            if (label instanceof Label) {
1186                list.remove(i);
1187                final Integer bytecodeIndex = labelsToOffsets.get(label);
1188                final Integer renumberedOffset = Integer.valueOf(bciRenumbering.get(bytecodeIndex.intValue()) - relative.get(i).intValue());
1189                list.add(i, renumberedOffset);
1190            }
1191        }
1192    }
1193
1194    private int sum(final int[] ints) {
1195        int sum = 0;
1196        for (final int j : ints) {
1197            sum += j;
1198        }
1199        return sum;
1200    }
1201
1202    private void writeClassAttributeBands(final OutputStream out) throws IOException, Pack200Exception {
1203        byte[] encodedBand = encodeFlags("class_flags", class_flags, Codec.UNSIGNED5, Codec.UNSIGNED5, segmentHeader.have_class_flags_hi());
1204        out.write(encodedBand);
1205        PackingUtils.log("Wrote " + encodedBand.length + " bytes from class_flags[" + class_flags.length + "]");
1206
1207        // These bands are not needed, but could be used to reduce the size of
1208        // the archive if there are enough different non-standard attributes
1209        // defined that segmentHeader.have_class_flags_hi() is true. The same
1210        // applies to method_attr_count, field_attr_count, code_attr_count etc.
1211
1212        // *class_attr_count :UNSIGNED5 [COUNT(1<<16,...)]
1213        // *class_attr_indexes :UNSIGNED5 [SUM(*class_attr_count)]
1214
1215        encodedBand = encodeBandInt("class_attr_calls", class_attr_calls, Codec.UNSIGNED5);
1216        out.write(encodedBand);
1217        PackingUtils.log("Wrote " + encodedBand.length + " bytes from class_attr_calls[" + class_attr_calls.length + "]");
1218
1219        encodedBand = encodeBandInt("classSourceFile", cpEntryOrNullListToArray(classSourceFile), Codec.UNSIGNED5);
1220        out.write(encodedBand);
1221        PackingUtils.log("Wrote " + encodedBand.length + " bytes from classSourceFile[" + classSourceFile.size() + "]");
1222
1223        encodedBand = encodeBandInt("class_enclosing_method_RC", cpEntryListToArray(classEnclosingMethodClass), Codec.UNSIGNED5);
1224        out.write(encodedBand);
1225        PackingUtils.log("Wrote " + encodedBand.length + " bytes from class_enclosing_method_RC[" + classEnclosingMethodClass.size() + "]");
1226
1227        encodedBand = encodeBandInt("class_EnclosingMethod_RDN", cpEntryOrNullListToArray(classEnclosingMethodDesc), Codec.UNSIGNED5);
1228        out.write(encodedBand);
1229        PackingUtils.log("Wrote " + encodedBand.length + " bytes from class_EnclosingMethod_RDN[" + classEnclosingMethodDesc.size() + "]");
1230
1231        encodedBand = encodeBandInt("class_Signature_RS", cpEntryListToArray(classSignature), Codec.UNSIGNED5);
1232        out.write(encodedBand);
1233        PackingUtils.log("Wrote " + encodedBand.length + " bytes from class_Signature_RS[" + classSignature.size() + "]");
1234
1235        class_RVA_bands.pack(out);
1236        class_RIA_bands.pack(out);
1237
1238        encodedBand = encodeBandInt("class_InnerClasses_N", class_InnerClasses_N, Codec.UNSIGNED5);
1239        out.write(encodedBand);
1240        PackingUtils.log("Wrote " + encodedBand.length + " bytes from class_InnerClasses_N[" + class_InnerClasses_N.length + "]");
1241
1242        encodedBand = encodeBandInt("class_InnerClasses_RC", getInts(class_InnerClasses_RC), Codec.UNSIGNED5);
1243        out.write(encodedBand);
1244        PackingUtils.log("Wrote " + encodedBand.length + " bytes from class_InnerClasses_RC[" + class_InnerClasses_RC.length + "]");
1245
1246        encodedBand = encodeBandInt("class_InnerClasses_F", class_InnerClasses_F, Codec.UNSIGNED5);
1247        out.write(encodedBand);
1248        PackingUtils.log("Wrote " + encodedBand.length + " bytes from class_InnerClasses_F[" + class_InnerClasses_F.length + "]");
1249
1250        encodedBand = encodeBandInt("class_InnerClasses_outer_RCN", cpEntryOrNullListToArray(classInnerClassesOuterRCN), Codec.UNSIGNED5);
1251        out.write(encodedBand);
1252        PackingUtils.log("Wrote " + encodedBand.length + " bytes from class_InnerClasses_outer_RCN[" + classInnerClassesOuterRCN.size() + "]");
1253
1254        encodedBand = encodeBandInt("class_InnerClasses_name_RUN", cpEntryOrNullListToArray(classInnerClassesNameRUN), Codec.UNSIGNED5);
1255        out.write(encodedBand);
1256        PackingUtils.log("Wrote " + encodedBand.length + " bytes from class_InnerClasses_name_RUN[" + classInnerClassesNameRUN.size() + "]");
1257
1258        encodedBand = encodeBandInt("classFileVersionMinor", classFileVersionMinor.toArray(), Codec.UNSIGNED5);
1259        out.write(encodedBand);
1260        PackingUtils.log("Wrote " + encodedBand.length + " bytes from classFileVersionMinor[" + classFileVersionMinor.size() + "]");
1261
1262        encodedBand = encodeBandInt("classFileVersionMajor", classFileVersionMajor.toArray(), Codec.UNSIGNED5);
1263        out.write(encodedBand);
1264        PackingUtils.log("Wrote " + encodedBand.length + " bytes from classFileVersionMajor[" + classFileVersionMajor.size() + "]");
1265
1266        for (final NewAttributeBands classAttributeBand : classAttributeBands) {
1267            classAttributeBand.pack(out);
1268        }
1269    }
1270
1271    private void writeCodeAttributeBands(final OutputStream out) throws IOException, Pack200Exception {
1272        byte[] encodedBand = encodeFlags("codeFlags", longListToArray(codeFlags), Codec.UNSIGNED5, Codec.UNSIGNED5, segmentHeader.have_code_flags_hi());
1273        out.write(encodedBand);
1274        PackingUtils.log("Wrote " + encodedBand.length + " bytes from codeFlags[" + codeFlags.size() + "]");
1275
1276        // *code_attr_count :UNSIGNED5 [COUNT(1<<16,...)]
1277        // *code_attr_indexes :UNSIGNED5 [SUM(*code_attr_count)]
1278        encodedBand = encodeBandInt("code_attr_calls", code_attr_calls, Codec.UNSIGNED5);
1279        out.write(encodedBand);
1280        PackingUtils.log("Wrote " + encodedBand.length + " bytes from code_attr_calls[" + code_attr_calls.length + "]");
1281
1282        encodedBand = encodeBandInt("code_LineNumberTable_N", codeLineNumberTableN.toArray(), Codec.UNSIGNED5);
1283        out.write(encodedBand);
1284        PackingUtils.log("Wrote " + encodedBand.length + " bytes from code_LineNumberTable_N[" + codeLineNumberTableN.size() + "]");
1285
1286        encodedBand = encodeBandInt("code_LineNumberTable_bci_P", integerListToArray(codeLineNumberTableBciP), Codec.BCI5);
1287        out.write(encodedBand);
1288        PackingUtils.log("Wrote " + encodedBand.length + " bytes from code_LineNumberTable_bci_P[" + codeLineNumberTableBciP.size() + "]");
1289
1290        encodedBand = encodeBandInt("code_LineNumberTable_line", codeLineNumberTableLine.toArray(), Codec.UNSIGNED5);
1291        out.write(encodedBand);
1292        PackingUtils.log("Wrote " + encodedBand.length + " bytes from code_LineNumberTable_line[" + codeLineNumberTableLine.size() + "]");
1293
1294        encodedBand = encodeBandInt("code_LocalVariableTable_N", codeLocalVariableTableN.toArray(), Codec.UNSIGNED5);
1295        out.write(encodedBand);
1296        PackingUtils.log("Wrote " + encodedBand.length + " bytes from code_LocalVariableTable_N[" + codeLocalVariableTableN.size() + "]");
1297
1298        encodedBand = encodeBandInt("code_LocalVariableTable_bci_P", integerListToArray(codeLocalVariableTableBciP), Codec.BCI5);
1299        out.write(encodedBand);
1300        PackingUtils.log("Wrote " + encodedBand.length + " bytes from code_LocalVariableTable_bci_P[" + codeLocalVariableTableBciP.size() + "]");
1301
1302        encodedBand = encodeBandInt("code_LocalVariableTable_span_O", integerListToArray(codeLocalVariableTableSpanO), Codec.BRANCH5);
1303        out.write(encodedBand);
1304        PackingUtils.log("Wrote " + encodedBand.length + " bytes from code_LocalVariableTable_span_O[" + codeLocalVariableTableSpanO.size() + "]");
1305
1306        encodedBand = encodeBandInt("code_LocalVariableTable_name_RU", cpEntryListToArray(codeLocalVariableTableNameRU), Codec.UNSIGNED5);
1307        out.write(encodedBand);
1308        PackingUtils.log("Wrote " + encodedBand.length + " bytes from code_LocalVariableTable_name_RU[" + codeLocalVariableTableNameRU.size() + "]");
1309
1310        encodedBand = encodeBandInt("code_LocalVariableTable_type_RS", cpEntryListToArray(codeLocalVariableTableTypeRS), Codec.UNSIGNED5);
1311        out.write(encodedBand);
1312        PackingUtils.log("Wrote " + encodedBand.length + " bytes from code_LocalVariableTable_type_RS[" + codeLocalVariableTableTypeRS.size() + "]");
1313
1314        encodedBand = encodeBandInt("code_LocalVariableTable_slot", codeLocalVariableTableSlot.toArray(), Codec.UNSIGNED5);
1315        out.write(encodedBand);
1316        PackingUtils.log("Wrote " + encodedBand.length + " bytes from code_LocalVariableTable_slot[" + codeLocalVariableTableSlot.size() + "]");
1317
1318        encodedBand = encodeBandInt("code_LocalVariableTypeTable_N", codeLocalVariableTypeTableN.toArray(), Codec.UNSIGNED5);
1319        out.write(encodedBand);
1320        PackingUtils.log("Wrote " + encodedBand.length + " bytes from code_LocalVariableTypeTable_N[" + codeLocalVariableTypeTableN.size() + "]");
1321
1322        encodedBand = encodeBandInt("code_LocalVariableTypeTable_bci_P", integerListToArray(codeLocalVariableTypeTableBciP), Codec.BCI5);
1323        out.write(encodedBand);
1324        PackingUtils.log("Wrote " + encodedBand.length + " bytes from code_LocalVariableTypeTable_bci_P[" + codeLocalVariableTypeTableBciP.size() + "]");
1325
1326        encodedBand = encodeBandInt("code_LocalVariableTypeTable_span_O", integerListToArray(codeLocalVariableTypeTableSpanO), Codec.BRANCH5);
1327        out.write(encodedBand);
1328        PackingUtils.log("Wrote " + encodedBand.length + " bytes from code_LocalVariableTypeTable_span_O[" + codeLocalVariableTypeTableSpanO.size() + "]");
1329
1330        encodedBand = encodeBandInt("code_LocalVariableTypeTable_name_RU", cpEntryListToArray(codeLocalVariableTypeTableNameRU), Codec.UNSIGNED5);
1331        out.write(encodedBand);
1332        PackingUtils.log("Wrote " + encodedBand.length + " bytes from code_LocalVariableTypeTable_name_RU[" + codeLocalVariableTypeTableNameRU.size() + "]");
1333
1334        encodedBand = encodeBandInt("code_LocalVariableTypeTable_type_RS", cpEntryListToArray(codeLocalVariableTypeTableTypeRS), Codec.UNSIGNED5);
1335        out.write(encodedBand);
1336        PackingUtils.log("Wrote " + encodedBand.length + " bytes from code_LocalVariableTypeTable_type_RS[" + codeLocalVariableTypeTableTypeRS.size() + "]");
1337
1338        encodedBand = encodeBandInt("code_LocalVariableTypeTable_slot", codeLocalVariableTypeTableSlot.toArray(), Codec.UNSIGNED5);
1339        out.write(encodedBand);
1340        PackingUtils.log("Wrote " + encodedBand.length + " bytes from code_LocalVariableTypeTable_slot[" + codeLocalVariableTypeTableSlot.size() + "]");
1341
1342        for (final NewAttributeBands bands : codeAttributeBands) {
1343            bands.pack(out);
1344        }
1345    }
1346
1347    private void writeCodeBands(final OutputStream out) throws IOException, Pack200Exception {
1348        byte[] encodedBand = encodeBandInt("codeHeaders", codeHeaders, Codec.BYTE1);
1349        out.write(encodedBand);
1350        PackingUtils.log("Wrote " + encodedBand.length + " bytes from codeHeaders[" + codeHeaders.length + "]");
1351
1352        encodedBand = encodeBandInt("codeMaxStack", codeMaxStack.toArray(), Codec.UNSIGNED5);
1353        out.write(encodedBand);
1354        PackingUtils.log("Wrote " + encodedBand.length + " bytes from codeMaxStack[" + codeMaxStack.size() + "]");
1355
1356        encodedBand = encodeBandInt("codeMaxLocals", codeMaxLocals.toArray(), Codec.UNSIGNED5);
1357        out.write(encodedBand);
1358        PackingUtils.log("Wrote " + encodedBand.length + " bytes from codeMaxLocals[" + codeMaxLocals.size() + "]");
1359
1360        encodedBand = encodeBandInt("codeHandlerCount", codeHandlerCount.toArray(), Codec.UNSIGNED5);
1361        out.write(encodedBand);
1362        PackingUtils.log("Wrote " + encodedBand.length + " bytes from codeHandlerCount[" + codeHandlerCount.size() + "]");
1363
1364        encodedBand = encodeBandInt("codeHandlerStartP", integerListToArray(codeHandlerStartP), Codec.BCI5);
1365        out.write(encodedBand);
1366        PackingUtils.log("Wrote " + encodedBand.length + " bytes from codeHandlerStartP[" + codeHandlerStartP.size() + "]");
1367
1368        encodedBand = encodeBandInt("codeHandlerEndPO", integerListToArray(codeHandlerEndPO), Codec.BRANCH5);
1369        out.write(encodedBand);
1370        PackingUtils.log("Wrote " + encodedBand.length + " bytes from codeHandlerEndPO[" + codeHandlerEndPO.size() + "]");
1371
1372        encodedBand = encodeBandInt("codeHandlerCatchPO", integerListToArray(codeHandlerCatchPO), Codec.BRANCH5);
1373        out.write(encodedBand);
1374        PackingUtils.log("Wrote " + encodedBand.length + " bytes from codeHandlerCatchPO[" + codeHandlerCatchPO.size() + "]");
1375
1376        encodedBand = encodeBandInt("codeHandlerClass", cpEntryOrNullListToArray(codeHandlerClass), Codec.UNSIGNED5);
1377        out.write(encodedBand);
1378        PackingUtils.log("Wrote " + encodedBand.length + " bytes from codeHandlerClass[" + codeHandlerClass.size() + "]");
1379
1380        writeCodeAttributeBands(out);
1381    }
1382
1383    private void writeFieldAttributeBands(final OutputStream out) throws IOException, Pack200Exception {
1384        byte[] encodedBand = encodeFlags("field_flags", field_flags, Codec.UNSIGNED5, Codec.UNSIGNED5, segmentHeader.have_field_flags_hi());
1385        out.write(encodedBand);
1386        PackingUtils.log("Wrote " + encodedBand.length + " bytes from field_flags[" + field_flags.length + "]");
1387
1388        // *field_attr_count :UNSIGNED5 [COUNT(1<<16,...)]
1389        // *field_attr_indexes :UNSIGNED5 [SUM(*field_attr_count)]
1390        encodedBand = encodeBandInt("field_attr_calls", field_attr_calls, Codec.UNSIGNED5);
1391        out.write(encodedBand);
1392        PackingUtils.log("Wrote " + encodedBand.length + " bytes from field_attr_calls[" + field_attr_calls.length + "]");
1393
1394        encodedBand = encodeBandInt("fieldConstantValueKQ", cpEntryListToArray(fieldConstantValueKQ), Codec.UNSIGNED5);
1395        out.write(encodedBand);
1396        PackingUtils.log("Wrote " + encodedBand.length + " bytes from fieldConstantValueKQ[" + fieldConstantValueKQ.size() + "]");
1397
1398        encodedBand = encodeBandInt("fieldSignature", cpEntryListToArray(fieldSignature), Codec.UNSIGNED5);
1399        out.write(encodedBand);
1400        PackingUtils.log("Wrote " + encodedBand.length + " bytes from fieldSignature[" + fieldSignature.size() + "]");
1401
1402        field_RVA_bands.pack(out);
1403        field_RIA_bands.pack(out);
1404        for (final NewAttributeBands bands : fieldAttributeBands) {
1405            bands.pack(out);
1406        }
1407    }
1408
1409    private void writeMethodAttributeBands(final OutputStream out) throws IOException, Pack200Exception {
1410        byte[] encodedBand = encodeFlags("method_flags", method_flags, Codec.UNSIGNED5, Codec.UNSIGNED5, segmentHeader.have_method_flags_hi());
1411        out.write(encodedBand);
1412        PackingUtils.log("Wrote " + encodedBand.length + " bytes from method_flags[" + method_flags.length + "]");
1413
1414        // *method_attr_count :UNSIGNED5 [COUNT(1<<16,...)]
1415        // *method_attr_indexes :UNSIGNED5 [SUM(*method_attr_count)]
1416        encodedBand = encodeBandInt("method_attr_calls", method_attr_calls, Codec.UNSIGNED5);
1417        out.write(encodedBand);
1418        PackingUtils.log("Wrote " + encodedBand.length + " bytes from method_attr_calls[" + method_attr_calls.length + "]");
1419
1420        encodedBand = encodeBandInt("methodExceptionNumber", methodExceptionNumber.toArray(), Codec.UNSIGNED5);
1421        out.write(encodedBand);
1422        PackingUtils.log("Wrote " + encodedBand.length + " bytes from methodExceptionNumber[" + methodExceptionNumber.size() + "]");
1423
1424        encodedBand = encodeBandInt("methodExceptionClasses", cpEntryListToArray(methodExceptionClasses), Codec.UNSIGNED5);
1425        out.write(encodedBand);
1426        PackingUtils.log("Wrote " + encodedBand.length + " bytes from methodExceptionClasses[" + methodExceptionClasses.size() + "]");
1427
1428        encodedBand = encodeBandInt("methodSignature", cpEntryListToArray(methodSignature), Codec.UNSIGNED5);
1429        out.write(encodedBand);
1430        PackingUtils.log("Wrote " + encodedBand.length + " bytes from methodSignature[" + methodSignature.size() + "]");
1431
1432        method_RVA_bands.pack(out);
1433        method_RIA_bands.pack(out);
1434        method_RVPA_bands.pack(out);
1435        method_RIPA_bands.pack(out);
1436        method_AD_bands.pack(out);
1437        for (final NewAttributeBands bands : methodAttributeBands) {
1438            bands.pack(out);
1439        }
1440    }
1441}