    001/*
    002 * Licensed to the Apache Software Foundation (ASF) under one
    003 * or more contributor license agreements.  See the NOTICE file
    004 * distributed with this work for additional information
    005 * regarding copyright ownership.  The ASF licenses this file
    006 * to you under the Apache License, Version 2.0 (the
    007 * "License"); you may not use this file except in compliance
    008 * with the License.  You may obtain a copy of the License at
    009 *
    010 *   https://www.apache.org/licenses/LICENSE-2.0
    011 *
    012 * Unless required by applicable law or agreed to in writing,
    013 * software distributed under the License is distributed on an
    014 * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
    015 * KIND, either express or implied.  See the License for the
    016 * specific language governing permissions and limitations
    017 * under the License.
    018 */
    019
    020package org.apache.commons.compress.harmony.pack200;
    021
    022import java.io.ByteArrayOutputStream;
    023import java.io.IOException;
    024import java.io.OutputStream;
    025import java.util.ArrayList;
    026import java.util.List;
    027
    028import org.apache.commons.compress.harmony.pack200.Archive.PackingFile;
    029import org.apache.commons.compress.harmony.pack200.Archive.SegmentUnit;
    030import org.objectweb.asm.AnnotationVisitor;
    031import org.objectweb.asm.Attribute;
    032import org.objectweb.asm.ClassReader;
    033import org.objectweb.asm.ClassVisitor;
    034import org.objectweb.asm.FieldVisitor;
    035import org.objectweb.asm.Label;
    036import org.objectweb.asm.MethodVisitor;
    037import org.objectweb.asm.Opcodes;
    038import org.objectweb.asm.Type;
    039
    040/**
    041 * A Pack200 archive consists of one or more Segments.
    042 * <p>
    043 * Format:
    044 * </p>
    045 * <pre>
    046 *   pack200_archive:
    047 *      (pack200_segment)+
    048 *
    049 *   pack200_segment:
    050 *      segment_header
    051 *      *band_headers :BYTE1
    052 *      cp_bands
    053 *      attr_definition_bands
    054 *      ic_bands
    055 *      class_bands
    056 *      bc_bands
    057 *      file_bands
    058 * </pre>
    059 */
    060public class Segment extends ClassVisitor {
    061
    062    public class ArrayVisitor extends AnnotationVisitor {
    063
    064        private final int indexInCaseArrayN;
    065        private final List<Integer> caseArrayN;
    066        private final List<Object> values;
    067        private final List<String> nameRU;
    068        private final List<String> tags;
    069
    070        public ArrayVisitor(final List<Integer> caseArrayN, final List<String> tags, final List<String> nameRU, final List<Object> values) {
    071            super(ASM_API);
    072
    073            this.caseArrayN = caseArrayN;
    074            this.tags = tags;
    075            this.nameRU = nameRU;
    076            this.values = values;
    077            this.indexInCaseArrayN = caseArrayN.size() - 1;
    078        }
    079
    080        @Override
    081        public void visit(String name, final Object value) {
    082            final Integer numCases = caseArrayN.remove(indexInCaseArrayN);
    083            caseArrayN.add(indexInCaseArrayN, Integer.valueOf(numCases.intValue() + 1));
    084            if (name == null) {
    085                name = "";
    086            }
    087            addValueAndTag(value, tags, values);
    088        }
    089
    090        @Override
    091        public AnnotationVisitor visitAnnotation(final String arg0, final String arg1) {
    092            throw new UnsupportedOperationException("Not yet supported");
    093        }
    094
    095        @Override
    096        public AnnotationVisitor visitArray(String name) {
    097            tags.add("[");
    098            if (name == null) {
    099                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}