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