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