1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
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
39
40 public class Segment extends ClassVisitor {
41
42 public class ArrayVisitor extends AnnotationVisitor {
43
44 private final int indexInCaseArrayN;
45 private final List<Integer> caseArrayN;
46 private final List<Object> values;
47 private final List<String> nameRU;
48 private final List<String> tags;
49
50 public ArrayVisitor(final List<Integer> caseArrayN, final List<String> tags, final List<String> nameRU, final List<Object> values) {
51 super(ASM_API);
52
53 this.caseArrayN = caseArrayN;
54 this.tags = tags;
55 this.nameRU = nameRU;
56 this.values = values;
57 this.indexInCaseArrayN = caseArrayN.size() - 1;
58 }
59
60 @Override
61 public void visit(String name, final Object value) {
62 final Integer numCases = caseArrayN.remove(indexInCaseArrayN);
63 caseArrayN.add(indexInCaseArrayN, Integer.valueOf(numCases.intValue() + 1));
64 if (name == null) {
65 name = "";
66 }
67 addValueAndTag(value, tags, values);
68 }
69
70 @Override
71 public AnnotationVisitor visitAnnotation(final String arg0, final String arg1) {
72 throw new UnsupportedOperationException("Not yet supported");
73 }
74
75 @Override
76 public AnnotationVisitor visitArray(String name) {
77 tags.add("[");
78 if (name == null) {
79 name = "";
80 }
81 nameRU.add(name);
82 caseArrayN.add(Integer.valueOf(0));
83 return new ArrayVisitor(caseArrayN, tags, nameRU, values);
84 }
85
86 @Override
87 public void visitEnd() {
88
89 }
90
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
103
104
105 public static class PassException extends RuntimeException {
106
107 private static final long serialVersionUID = 1L;
108
109 }
110
111
112
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<>();
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
180 }
181
182 @Override
183 public AnnotationVisitor visitArray(final String arg0) {
184 throw new UnsupportedOperationException("Not yet supported");
185
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
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 }
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 }
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
284
285
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 }
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 }
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 }
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
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
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
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
594
595
596
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
641
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 }
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 }
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 }