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