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