View Javadoc
1   /*
2    * Licensed to the Apache Software Foundation (ASF) under one
3    * or more contributor license agreements.  See the NOTICE file
4    * distributed with this work for additional information
5    * regarding copyright ownership.  The ASF licenses this file
6    * to you under the Apache License, Version 2.0 (the
7    * "License"); you may not use this file except in compliance
8    * with the License.  You may obtain a copy of the License at
9    *
10   *   https://www.apache.org/licenses/LICENSE-2.0
11   *
12   * Unless required by applicable law or agreed to in writing,
13   * software distributed under the License is distributed on an
14   * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
15   * KIND, either express or implied.  See the License for the
16   * specific language governing permissions and limitations
17   * under the License.
18   */
19  
20  package org.apache.commons.compress.harmony.pack200;
21  
22  import java.io.ByteArrayOutputStream;
23  import java.io.IOException;
24  import java.io.OutputStream;
25  import java.util.ArrayList;
26  import java.util.List;
27  
28  import org.apache.commons.compress.harmony.pack200.Archive.PackingFile;
29  import org.apache.commons.compress.harmony.pack200.Archive.SegmentUnit;
30  import org.objectweb.asm.AnnotationVisitor;
31  import org.objectweb.asm.Attribute;
32  import org.objectweb.asm.ClassReader;
33  import org.objectweb.asm.ClassVisitor;
34  import org.objectweb.asm.FieldVisitor;
35  import org.objectweb.asm.Label;
36  import org.objectweb.asm.MethodVisitor;
37  import org.objectweb.asm.Opcodes;
38  import org.objectweb.asm.Type;
39  
40  /**
41   * A Pack200 archive consists of one or more Segments.
42   * <p>
43   * Format:
44   * </p>
45   * <pre>
46   *   pack200_archive:
47   *      (pack200_segment)+
48   *
49   *   pack200_segment:
50   *      segment_header
51   *      *band_headers :BYTE1
52   *      cp_bands
53   *      attr_definition_bands
54   *      ic_bands
55   *      class_bands
56   *      bc_bands
57   *      file_bands
58   * </pre>
59   */
60  public class Segment extends ClassVisitor {
61  
62      public class ArrayVisitor extends AnnotationVisitor {
63  
64          private final int indexInCaseArrayN;
65          private final List<Integer> caseArrayN;
66          private final List<Object> values;
67          private final List<String> nameRU;
68          private final List<String> tags;
69  
70          public ArrayVisitor(final List<Integer> caseArrayN, final List<String> tags, final List<String> nameRU, final List<Object> values) {
71              super(ASM_API);
72  
73              this.caseArrayN = caseArrayN;
74              this.tags = tags;
75              this.nameRU = nameRU;
76              this.values = values;
77              this.indexInCaseArrayN = caseArrayN.size() - 1;
78          }
79  
80          @Override
81          public void visit(String name, final Object value) {
82              final Integer numCases = caseArrayN.remove(indexInCaseArrayN);
83              caseArrayN.add(indexInCaseArrayN, Integer.valueOf(numCases.intValue() + 1));
84              if (name == null) {
85                  name = "";
86              }
87              addValueAndTag(value, tags, values);
88          }
89  
90          @Override
91          public AnnotationVisitor visitAnnotation(final String arg0, final String arg1) {
92              throw new UnsupportedOperationException("Not yet supported");
93          }
94  
95          @Override
96          public AnnotationVisitor visitArray(String name) {
97              tags.add("[");
98              if (name == null) {
99                  name = "";
100             }
101             nameRU.add(name);
102             caseArrayN.add(Integer.valueOf(0));
103             return new ArrayVisitor(caseArrayN, tags, nameRU, values);
104         }
105 
106         @Override
107         public void visitEnd() {
108             // override to noop
109         }
110 
111         @Override
112         public void visitEnum(final String name, final String desc, final String value) {
113             final Integer numCases = caseArrayN.remove(caseArrayN.size() - 1);
114             caseArrayN.add(Integer.valueOf(numCases.intValue() + 1));
115             tags.add("e");
116             values.add(desc);
117             values.add(value);
118         }
119     }
120 
121     /**
122      * Exception indicating that the class currently being visited contains an unknown attribute, which means that by default the class file needs to be passed
123      * through as-is in the file_bands rather than being packed with pack200.
124      */
125     public static class PassException extends RuntimeException {
126 
127         private static final long serialVersionUID = 1L;
128 
129     }
130 
131     /**
132      * SegmentAnnotationVisitor implements {@code AnnotationVisitor} to visit Annotations found in a class file.
133      */
134     public class SegmentAnnotationVisitor extends AnnotationVisitor {
135 
136         private int context = -1;
137         private int parameter = -1;
138         private String desc;
139         private boolean visible;
140 
141         private final List<String> nameRU = new ArrayList<>();
142         private final List<String> tags = new ArrayList<>(); // tags
143         private final List<Object> values = new ArrayList<>();
144         private final List<Integer> caseArrayN = new ArrayList<>();
145         private final List<String> nestTypeRS = new ArrayList<>();
146         private final List<String> nestNameRU = new ArrayList<>();
147         private final List<Integer> nestPairN = new ArrayList<>();
148 
149         public SegmentAnnotationVisitor(final int context) {
150             super(ASM_API);
151             this.context = context;
152         }
153 
154         public SegmentAnnotationVisitor(final int context, final int parameter, final String desc, final boolean visible) {
155             super(ASM_API);
156             this.context = context;
157             this.parameter = parameter;
158             this.desc = desc;
159             this.visible = visible;
160         }
161 
162         public SegmentAnnotationVisitor(final int context, final String desc, final boolean visible) {
163             super(ASM_API);
164             this.context = context;
165             this.desc = desc;
166             this.visible = visible;
167         }
168 
169         @Override
170         public void visit(String name, final Object value) {
171             if (name == null) {
172                 name = "";
173             }
174             nameRU.add(name);
175             addValueAndTag(value, tags, values);
176         }
177 
178         @Override
179         public AnnotationVisitor visitAnnotation(String name, final String desc) {
180             tags.add("@");
181             if (name == null) {
182                 name = "";
183             }
184             nameRU.add(name);
185             nestTypeRS.add(desc);
186             nestPairN.add(Integer.valueOf(0));
187             return new AnnotationVisitor(context, av) {
188                 @Override
189                 public void visit(final String name, final Object value) {
190                     final Integer numPairs = nestPairN.remove(nestPairN.size() - 1);
191                     nestPairN.add(Integer.valueOf(numPairs.intValue() + 1));
192                     nestNameRU.add(name);
193                     addValueAndTag(value, tags, values);
194                 }
195 
196                 @Override
197                 public AnnotationVisitor visitAnnotation(final String arg0, final String arg1) {
198                     throw new UnsupportedOperationException("Not yet supported");
199 //                    return null;
200                 }
201 
202                 @Override
203                 public AnnotationVisitor visitArray(final String arg0) {
204                     throw new UnsupportedOperationException("Not yet supported");
205 //                    return null;
206                 }
207 
208                 @Override
209                 public void visitEnd() {
210                 }
211 
212                 @Override
213                 public void visitEnum(final String name, final String desc, final String value) {
214                     final Integer numPairs = nestPairN.remove(nestPairN.size() - 1);
215                     nestPairN.add(Integer.valueOf(numPairs.intValue() + 1));
216                     tags.add("e");
217                     nestNameRU.add(name);
218                     values.add(desc);
219                     values.add(value);
220                 }
221             };
222         }
223 
224         @Override
225         public AnnotationVisitor visitArray(String name) {
226             tags.add("[");
227             if (name == null) {
228                 name = "";
229             }
230             nameRU.add(name);
231             caseArrayN.add(Integer.valueOf(0));
232             return new ArrayVisitor(caseArrayN, tags, nameRU, values);
233         }
234 
235         @Override
236         public void visitEnd() {
237             if (desc == null) {
238                 Segment.this.classBands.addAnnotationDefault(nameRU, tags, values, caseArrayN, nestTypeRS, nestNameRU, nestPairN);
239             } else if (parameter != -1) {
240                 Segment.this.classBands.addParameterAnnotation(parameter, desc, visible, nameRU, tags, values, caseArrayN, nestTypeRS, nestNameRU, nestPairN);
241             } else {
242                 Segment.this.classBands.addAnnotation(context, desc, visible, nameRU, tags, values, caseArrayN, nestTypeRS, nestNameRU, nestPairN);
243             }
244         }
245 
246         @Override
247         public void visitEnum(String name, final String desc, final String value) {
248             tags.add("e");
249             if (name == null) {
250                 name = "";
251             }
252             nameRU.add(name);
253             values.add(desc);
254             values.add(value);
255         }
256     }
257 
258     /**
259      * SegmentFieldVisitor implements {@code FieldVisitor} to visit the metadata relating to fields in a class file.
260      */
261     public class SegmentFieldVisitor extends FieldVisitor {
262 
263         public SegmentFieldVisitor() {
264             super(ASM_API);
265         }
266 
267         @Override
268         public AnnotationVisitor visitAnnotation(final String desc, final boolean visible) {
269             return new SegmentAnnotationVisitor(MetadataBandGroup.CONTEXT_FIELD, desc, visible);
270         }
271 
272         @Override
273         public void visitAttribute(final Attribute attribute) {
274             if (attribute.isUnknown()) {
275                 final String action = options.getUnknownAttributeAction();
276                 if (action.equals(PackingOptions.PASS)) {
277                     passCurrentClass();
278                 } else if (action.equals(PackingOptions.ERROR)) {
279                     throw new Error("Unknown attribute encountered");
280                 } // else skip
281             } else if (attribute instanceof NewAttribute) {
282                 final NewAttribute newAttribute = (NewAttribute) attribute;
283                 if (newAttribute.isUnknown(AttributeDefinitionBands.CONTEXT_FIELD)) {
284                     final String action = options.getUnknownFieldAttributeAction(newAttribute.type);
285                     if (action.equals(PackingOptions.PASS)) {
286                         passCurrentClass();
287                     } else if (action.equals(PackingOptions.ERROR)) {
288                         throw new Error("Unknown attribute encountered");
289                     } // else skip
290                 }
291                 classBands.addFieldAttribute(newAttribute);
292             } else {
293                 throw new IllegalArgumentException("Unexpected attribute encountered: " + attribute.type);
294             }
295         }
296 
297         @Override
298         public void visitEnd() {
299         }
300     }
301 
302     /**
303      * This class implements MethodVisitor to visit the contents and metadata related to methods in a class file.
304      *
305      * It delegates to BcBands for bytecode related visits and to ClassBands for everything else.
306      */
307     public class SegmentMethodVisitor extends MethodVisitor {
308 
309         public SegmentMethodVisitor() {
310             super(ASM_API);
311         }
312 
313         @Override
314         public AnnotationVisitor visitAnnotation(final String desc, final boolean visible) {
315             return new SegmentAnnotationVisitor(MetadataBandGroup.CONTEXT_METHOD, desc, visible);
316         }
317 
318         @Override
319         public AnnotationVisitor visitAnnotationDefault() {
320             return new SegmentAnnotationVisitor(MetadataBandGroup.CONTEXT_METHOD);
321         }
322 
323         @Override
324         public void visitAttribute(final Attribute attribute) {
325             if (attribute.isUnknown()) {
326                 final String action = options.getUnknownAttributeAction();
327                 if (action.equals(PackingOptions.PASS)) {
328                     passCurrentClass();
329                 } else if (action.equals(PackingOptions.ERROR)) {
330                     throw new Error("Unknown attribute encountered");
331                 } // else skip
332             } else if (attribute instanceof NewAttribute) {
333                 final NewAttribute newAttribute = (NewAttribute) attribute;
334                 if (attribute.isCodeAttribute()) {
335                     if (newAttribute.isUnknown(AttributeDefinitionBands.CONTEXT_CODE)) {
336                         final String action = options.getUnknownCodeAttributeAction(newAttribute.type);
337                         if (action.equals(PackingOptions.PASS)) {
338                             passCurrentClass();
339                         } else if (action.equals(PackingOptions.ERROR)) {
340                             throw new Error("Unknown attribute encountered");
341                         } // else skip
342                     }
343                     classBands.addCodeAttribute(newAttribute);
344                 } else {
345                     if (newAttribute.isUnknown(AttributeDefinitionBands.CONTEXT_METHOD)) {
346                         final String action = options.getUnknownMethodAttributeAction(newAttribute.type);
347                         if (action.equals(PackingOptions.PASS)) {
348                             passCurrentClass();
349                         } else if (action.equals(PackingOptions.ERROR)) {
350                             throw new Error("Unknown attribute encountered");
351                         } // else skip
352                     }
353                     classBands.addMethodAttribute(newAttribute);
354                 }
355             } else {
356                 throw new IllegalArgumentException("Unexpected attribute encountered: " + attribute.type);
357             }
358         }
359 
360         @Override
361         public void visitCode() {
362             classBands.addCode();
363         }
364 
365         @Override
366         public void visitEnd() {
367             classBands.endOfMethod();
368             bcBands.visitEnd();
369         }
370 
371         @Override
372         public void visitFieldInsn(final int opcode, final String owner, final String name, final String desc) {
373             bcBands.visitFieldInsn(opcode, owner, name, desc);
374         }
375 
376         @Override
377         public void visitFrame(final int arg0, final int arg1, final Object[] arg2, final int arg3, final Object[] arg4) {
378             // TODO: Java 6 - implement support for this
379 
380         }
381 
382         @Override
383         public void visitIincInsn(final int var, final int increment) {
384             bcBands.visitIincInsn(var, increment);
385         }
386 
387         @Override
388         public void visitInsn(final int opcode) {
389             bcBands.visitInsn(opcode);
390         }
391 
392         @Override
393         public void visitIntInsn(final int opcode, final int operand) {
394             bcBands.visitIntInsn(opcode, operand);
395         }
396 
397         @Override
398         public void visitJumpInsn(final int opcode, final Label label) {
399             bcBands.visitJumpInsn(opcode, label);
400         }
401 
402         @Override
403         public void visitLabel(final Label label) {
404             bcBands.visitLabel(label);
405         }
406 
407         @Override
408         public void visitLdcInsn(final Object cst) {
409             bcBands.visitLdcInsn(cst);
410         }
411 
412         @Override
413         public void visitLineNumber(final int line, final Label start) {
414             if (!stripDebug) {
415                 classBands.addLineNumber(line, start);
416             }
417         }
418 
419         @Override
420         public void visitLocalVariable(final String name, final String desc, final String signature, final Label start, final Label end, final int index) {
421             if (!stripDebug) {
422                 classBands.addLocalVariable(name, desc, signature, start, end, index);
423             }
424         }
425 
426         @Override
427         public void visitLookupSwitchInsn(final Label dflt, final int[] keys, final Label[] labels) {
428             bcBands.visitLookupSwitchInsn(dflt, keys, labels);
429         }
430 
431         @Override
432         public void visitMaxs(final int maxStack, final int maxLocals) {
433             classBands.addMaxStack(maxStack, maxLocals);
434         }
435 
436         @Override
437         public void visitMethodInsn(final int opcode, final String owner, final String name, final String desc) {
438             bcBands.visitMethodInsn(opcode, owner, name, desc);
439         }
440 
441         @Override
442         public void visitMultiANewArrayInsn(final String desc, final int dimensions) {
443             bcBands.visitMultiANewArrayInsn(desc, dimensions);
444         }
445 
446         @Override
447         public AnnotationVisitor visitParameterAnnotation(final int parameter, final String desc, final boolean visible) {
448             return new SegmentAnnotationVisitor(MetadataBandGroup.CONTEXT_METHOD, parameter, desc, visible);
449         }
450 
451         @Override
452         public void visitTableSwitchInsn(final int min, final int max, final Label dflt, final Label... labels) {
453             bcBands.visitTableSwitchInsn(min, max, dflt, labels);
454         }
455 
456         @Override
457         public void visitTryCatchBlock(final Label start, final Label end, final Label handler, final String type) {
458             classBands.addHandler(start, end, handler, type);
459         }
460 
461         @Override
462         public void visitTypeInsn(final int opcode, final String type) {
463             bcBands.visitTypeInsn(opcode, type);
464         }
465 
466         @Override
467         public void visitVarInsn(final int opcode, final int var) {
468             bcBands.visitVarInsn(opcode, var);
469         }
470 
471     }
472 
473     /** See https://asm.ow2.io/Javadoc/org/objectweb/asm/Opcodes.html#ASM4 */
474     public static int ASM_API = Opcodes.ASM4;
475     private SegmentHeader segmentHeader;
476     private CpBands cpBands;
477     private AttributeDefinitionBands attributeDefinitionBands;
478 
479     private IcBands icBands;
480     private ClassBands classBands;
481     private BcBands bcBands;
482     private FileBands fileBands;
483     private final SegmentFieldVisitor fieldVisitor = new SegmentFieldVisitor();
484     private final SegmentMethodVisitor methodVisitor = new SegmentMethodVisitor();
485 
486     private Pack200ClassReader currentClassReader;
487 
488     private PackingOptions options;
489 
490     private boolean stripDebug;
491 
492     private Attribute[] nonStandardAttributePrototypes;
493 
494     public Segment() {
495         super(ASM_API);
496     }
497 
498     // helper method for annotation visitors
499     private void addValueAndTag(final Object value, final List<String> tags, final List<Object> values) {
500         if (value instanceof Integer) {
501             tags.add("I");
502             values.add(value);
503         } else if (value instanceof Double) {
504             tags.add("D");
505             values.add(value);
506         } else if (value instanceof Float) {
507             tags.add("F");
508             values.add(value);
509         } else if (value instanceof Long) {
510             tags.add("J");
511             values.add(value);
512         } else if (value instanceof Byte) {
513             tags.add("B");
514             values.add(Integer.valueOf(((Byte) value).intValue()));
515         } else if (value instanceof Character) {
516             tags.add("C");
517             values.add(Integer.valueOf(((Character) value).charValue()));
518         } else if (value instanceof Short) {
519             tags.add("S");
520             values.add(Integer.valueOf(((Short) value).intValue()));
521         } else if (value instanceof Boolean) {
522             tags.add("Z");
523             values.add(Integer.valueOf(((Boolean) value).booleanValue() ? 1 : 0));
524         } else if (value instanceof String) {
525             tags.add("s");
526             values.add(value);
527         } else if (value instanceof Type) {
528             tags.add("c");
529             values.add(((Type) value).toString());
530         }
531     }
532 
533     public AttributeDefinitionBands getAttrBands() {
534         return attributeDefinitionBands;
535     }
536 
537     public ClassBands getClassBands() {
538         return classBands;
539     }
540 
541     public CpBands getCpBands() {
542         return cpBands;
543     }
544 
545     public Pack200ClassReader getCurrentClassReader() {
546         return currentClassReader;
547     }
548 
549     public IcBands getIcBands() {
550         return icBands;
551     }
552 
553     public SegmentHeader getSegmentHeader() {
554         return segmentHeader;
555     }
556 
557     public boolean lastConstantHadWideIndex() {
558         return currentClassReader.lastConstantHadWideIndex();
559     }
560 
561     /**
562      * The main method on Segment. Reads in all the class files, packs them and then writes the packed segment out to the given OutputStream.
563      *
564      * @param segmentUnit TODO
565      * @param out         the OutputStream to write the packed Segment to.
566      * @param options     packing options.
567      * @throws IOException      If an I/O error occurs.
568      * @throws Pack200Exception If a Pack200 semantic error occurs.
569      */
570     public void pack(final SegmentUnit segmentUnit, final OutputStream out, final PackingOptions options) throws IOException, Pack200Exception {
571         this.options = options;
572         this.stripDebug = options.isStripDebug();
573         final int effort = options.getEffort();
574         nonStandardAttributePrototypes = options.getUnknownAttributePrototypes();
575 
576         PackingUtils.log("Start to pack a new segment with " + segmentUnit.fileListSize() + " files including " + segmentUnit.classListSize() + " classes");
577 
578         PackingUtils.log("Initialize a header for the segment");
579         segmentHeader = new SegmentHeader();
580         segmentHeader.setFile_count(segmentUnit.fileListSize());
581         segmentHeader.setHave_all_code_flags(!stripDebug);
582         if (!options.isKeepDeflateHint()) {
583             segmentHeader.setDeflate_hint(Boolean.parseBoolean(options.getDeflateHint()));
584         }
585 
586         PackingUtils.log("Setup constant pool bands for the segment");
587         cpBands = new CpBands(this, effort);
588 
589         PackingUtils.log("Setup attribute definition bands for the segment");
590         attributeDefinitionBands = new AttributeDefinitionBands(this, effort, nonStandardAttributePrototypes);
591 
592         PackingUtils.log("Setup internal class bands for the segment");
593         icBands = new IcBands(segmentHeader, cpBands, effort);
594 
595         PackingUtils.log("Setup class bands for the segment");
596         classBands = new ClassBands(this, segmentUnit.classListSize(), effort, stripDebug);
597 
598         PackingUtils.log("Setup byte code bands for the segment");
599         bcBands = new BcBands(cpBands, this, effort);
600 
601         PackingUtils.log("Setup file bands for the segment");
602         fileBands = new FileBands(cpBands, segmentHeader, options, segmentUnit, effort);
603 
604         processClasses(segmentUnit, nonStandardAttributePrototypes);
605 
606         cpBands.finaliseBands();
607         attributeDefinitionBands.finaliseBands();
608         icBands.finaliseBands();
609         classBands.finaliseBands();
610         bcBands.finaliseBands();
611         fileBands.finaliseBands();
612 
613         // Using a temporary stream because we have to pack the other bands
614         // before segmentHeader because the band_headers band is only created
615         // when the other bands are packed, but comes before them in the packed
616         // file.
617         final ByteArrayOutputStream bandsOutputStream = new ByteArrayOutputStream();
618 
619         PackingUtils.log("Packing...");
620         final int finalNumberOfClasses = classBands.numClassesProcessed();
621         segmentHeader.setClass_count(finalNumberOfClasses);
622         cpBands.pack(bandsOutputStream);
623         if (finalNumberOfClasses > 0) {
624             attributeDefinitionBands.pack(bandsOutputStream);
625             icBands.pack(bandsOutputStream);
626             classBands.pack(bandsOutputStream);
627             bcBands.pack(bandsOutputStream);
628         }
629         fileBands.pack(bandsOutputStream);
630 
631         final ByteArrayOutputStream headerOutputStream = new ByteArrayOutputStream();
632         segmentHeader.pack(headerOutputStream);
633 
634         headerOutputStream.writeTo(out);
635         bandsOutputStream.writeTo(out);
636 
637         segmentUnit.addPackedByteAmount(headerOutputStream.size());
638         segmentUnit.addPackedByteAmount(bandsOutputStream.size());
639 
640         PackingUtils.log("Wrote total of " + segmentUnit.getPackedByteAmount() + " bytes");
641         PackingUtils.log("Transmitted " + segmentUnit.fileListSize() + " files of " + segmentUnit.getByteAmount() + " input bytes in a segment of "
642                 + segmentUnit.getPackedByteAmount() + " bytes");
643     }
644 
645     private void passCurrentClass() {
646         throw new PassException();
647     }
648 
649     private void processClasses(final SegmentUnit segmentUnit, final Attribute[] attributes) throws Pack200Exception {
650         segmentHeader.setClass_count(segmentUnit.classListSize());
651         for (final Pack200ClassReader classReader : segmentUnit.getClassList()) {
652             currentClassReader = classReader;
653             int flags = 0;
654             if (stripDebug) {
655                 flags |= ClassReader.SKIP_DEBUG;
656             }
657             try {
658                 classReader.accept(this, attributes, flags);
659             } catch (final PassException pe) {
660                 // Pass this class through as-is rather than packing it
661                 // TODO: probably need to deal with any inner classes
662                 classBands.removeCurrentClass();
663                 final String name = classReader.getFileName();
664                 options.addPassFile(name);
665                 cpBands.addCPUtf8(name);
666                 boolean found = false;
667                 for (final PackingFile file : segmentUnit.getFileList()) {
668                     if (file.getName().equals(name)) {
669                         found = true;
670                         file.setContents(classReader.b);
671                         break;
672                     }
673                 }
674                 if (!found) {
675                     throw new Pack200Exception("Error passing file " + name);
676                 }
677             }
678         }
679     }
680 
681     @Override
682     public void visit(final int version, final int access, final String name, final String signature, final String superName, final String[] interfaces) {
683         bcBands.setCurrentClass(name, superName);
684         segmentHeader.addMajorVersion(version);
685         classBands.addClass(version, access, name, signature, superName, interfaces);
686     }
687 
688     @Override
689     public AnnotationVisitor visitAnnotation(final String desc, final boolean visible) {
690         return new SegmentAnnotationVisitor(MetadataBandGroup.CONTEXT_CLASS, desc, visible);
691     }
692 
693     @Override
694     public void visitAttribute(final Attribute attribute) {
695         if (attribute.isUnknown()) {
696             final String action = options.getUnknownAttributeAction();
697             if (action.equals(PackingOptions.PASS)) {
698                 passCurrentClass();
699             } else if (action.equals(PackingOptions.ERROR)) {
700                 throw new Error("Unknown attribute encountered");
701             } // else skip
702         } else if (attribute instanceof NewAttribute) {
703             final NewAttribute newAttribute = (NewAttribute) attribute;
704             if (newAttribute.isUnknown(AttributeDefinitionBands.CONTEXT_CLASS)) {
705                 final String action = options.getUnknownClassAttributeAction(newAttribute.type);
706                 if (action.equals(PackingOptions.PASS)) {
707                     passCurrentClass();
708                 } else if (action.equals(PackingOptions.ERROR)) {
709                     throw new Error("Unknown attribute encountered");
710                 } // else skip
711             }
712             classBands.addClassAttribute(newAttribute);
713         } else {
714             throw new IllegalArgumentException("Unexpected attribute encountered: " + attribute.type);
715         }
716     }
717 
718     @Override
719     public void visitEnd() {
720         classBands.endOfClass();
721     }
722 
723     @Override
724     public FieldVisitor visitField(final int flags, final String name, final String desc, final String signature, final Object value) {
725         classBands.addField(flags, name, desc, signature, value);
726         return fieldVisitor;
727     }
728 
729     @Override
730     public void visitInnerClass(final String name, final String outerName, final String innerName, final int flags) {
731         icBands.addInnerClass(name, outerName, innerName, flags);
732     }
733 
734     @Override
735     public MethodVisitor visitMethod(final int flags, final String name, final String desc, final String signature, final String[] exceptions) {
736         classBands.addMethod(flags, name, desc, signature, exceptions);
737         return methodVisitor;
738     }
739 
740     @Override
741     public void visitOuterClass(final String owner, final String name, final String desc) {
742         classBands.addEnclosingMethod(owner, name, desc);
743 
744     }
745 
746     @Override
747     public void visitSource(final String source, final String debug) {
748         if (!stripDebug) {
749             classBands.addSourceFile(source);
750         }
751     }
752 }