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