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