View Javadoc
1   /*
2    * Licensed to the Apache Software Foundation (ASF) under one
3    * or more contributor license agreements.  See the NOTICE file
4    * distributed with this work for additional information
5    * regarding copyright ownership.  The ASF licenses this file
6    * to you under the Apache License, Version 2.0 (the
7    * "License"); you may not use this file except in compliance
8    * with the License.  You may obtain a copy of the License at
9    *
10   *   https://www.apache.org/licenses/LICENSE-2.0
11   *
12   * Unless required by applicable law or agreed to in writing,
13   * software distributed under the License is distributed on an
14   * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
15   * KIND, either express or implied.  See the License for the
16   * specific language governing permissions and limitations
17   * under the License.
18   */
19  package org.apache.commons.compress.harmony.pack200;
20  
21  import java.io.IOException;
22  import java.io.OutputStream;
23  import java.util.ArrayList;
24  import java.util.Arrays;
25  import java.util.Comparator;
26  import java.util.HashMap;
27  import java.util.HashSet;
28  import java.util.List;
29  import java.util.Map;
30  import java.util.Set;
31  
32  import org.apache.commons.compress.harmony.pack200.AttributeDefinitionBands.AttributeDefinition;
33  import org.apache.commons.compress.harmony.pack200.IcBands.IcTuple;
34  import org.apache.commons.compress.harmony.pack200.Segment.SegmentMethodVisitor;
35  import org.objectweb.asm.Label;
36  import org.objectweb.asm.Opcodes;
37  
38  /**
39   * Class bands (corresponds to the {@code class_bands} set of bands in the pack200 specification)
40   */
41  public class ClassBands extends BandSet {
42  
43      private static final class TempParamAnnotation {
44  
45          int numParams;
46          int[] annoN;
47          IntList pairN = new IntList();
48          List<String> typeRS = new ArrayList<>();
49          List<String> nameRU = new ArrayList<>();
50          List<String> tags = new ArrayList<>();
51          List<Object> values = new ArrayList<>();
52          List<Integer> caseArrayN = new ArrayList<>();
53          List<String> nestTypeRS = new ArrayList<>();
54          List<String> nestNameRU = new ArrayList<>();
55          List<Integer> nestPairN = new ArrayList<>();
56  
57          TempParamAnnotation(final int numParams) {
58              this.numParams = numParams;
59              annoN = new int[numParams];
60          }
61  
62          void addParameterAnnotation(final int parameter, final String desc, final List<String> nameRU, final List<String> tags,
63                  final List<Object> values, final List<Integer> caseArrayN, final List<String> nestTypeRS, final List<String> nestNameRU,
64                  final List<Integer> nestPairN) {
65              annoN[parameter]++;
66              typeRS.add(desc);
67              pairN.add(nameRU.size());
68              this.nameRU.addAll(nameRU);
69              this.tags.addAll(tags);
70              this.values.addAll(values);
71              this.caseArrayN.addAll(caseArrayN);
72              this.nestTypeRS.addAll(nestTypeRS);
73              this.nestNameRU.addAll(nestNameRU);
74              this.nestPairN.addAll(nestPairN);
75          }
76      }
77  
78      private static final long[] EMPTY_LONG_ARRAY = {};
79  
80      /**
81       * Counts arguments in a descriptor.
82       *
83       * @param descriptor the descriptor to parse.
84       * @return the argument count.
85       */
86      protected static int countArgs(final String descriptor) {
87          final int bra = descriptor.indexOf('(');
88          final int ket = descriptor.indexOf(')');
89          if (bra == -1 || ket == -1 || ket < bra) {
90              throw new IllegalArgumentException("No arguments");
91          }
92  
93          boolean inType = false;
94          boolean consumingNextType = false;
95          int count = 0;
96          for (int i = bra + 1; i < ket; i++) {
97              final char charAt = descriptor.charAt(i);
98              if (inType && charAt == ';') {
99                  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 }