1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20 package org.apache.commons.compress.harmony.pack200;
21
22 import java.io.ByteArrayOutputStream;
23 import java.io.IOException;
24 import java.io.OutputStream;
25 import java.util.ArrayList;
26 import java.util.List;
27
28 import org.apache.commons.compress.harmony.pack200.Archive.PackingFile;
29 import org.apache.commons.compress.harmony.pack200.Archive.SegmentUnit;
30 import org.objectweb.asm.AnnotationVisitor;
31 import org.objectweb.asm.Attribute;
32 import org.objectweb.asm.ClassReader;
33 import org.objectweb.asm.ClassVisitor;
34 import org.objectweb.asm.FieldVisitor;
35 import org.objectweb.asm.Label;
36 import org.objectweb.asm.MethodVisitor;
37 import org.objectweb.asm.Opcodes;
38 import org.objectweb.asm.Type;
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60 public class Segment extends ClassVisitor {
61
62 public class ArrayVisitor extends AnnotationVisitor {
63
64 private final int indexInCaseArrayN;
65 private final List<Integer> caseArrayN;
66 private final List<Object> values;
67 private final List<String> nameRU;
68 private final List<String> tags;
69
70 public ArrayVisitor(final List<Integer> caseArrayN, final List<String> tags, final List<String> nameRU, final List<Object> values) {
71 super(ASM_API);
72
73 this.caseArrayN = caseArrayN;
74 this.tags = tags;
75 this.nameRU = nameRU;
76 this.values = values;
77 this.indexInCaseArrayN = caseArrayN.size() - 1;
78 }
79
80 @Override
81 public void visit(String name, final Object value) {
82 final Integer numCases = caseArrayN.remove(indexInCaseArrayN);
83 caseArrayN.add(indexInCaseArrayN, Integer.valueOf(numCases.intValue() + 1));
84 if (name == null) {
85 name = "";
86 }
87 addValueAndTag(value, tags, values);
88 }
89
90 @Override
91 public AnnotationVisitor visitAnnotation(final String arg0, final String arg1) {
92 throw new UnsupportedOperationException("Not yet supported");
93 }
94
95 @Override
96 public AnnotationVisitor visitArray(String name) {
97 tags.add("[");
98 if (name == null) {
99 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
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
123
124
125 public static class PassException extends RuntimeException {
126
127 private static final long serialVersionUID = 1L;
128
129 }
130
131
132
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<>();
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
200 }
201
202 @Override
203 public AnnotationVisitor visitArray(final String arg0) {
204 throw new UnsupportedOperationException("Not yet supported");
205
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
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 }
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 }
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
304
305
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 }
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 }
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 }
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
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
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
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
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
614
615
616
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
661
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 }
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 }
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 }