1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19 package org.apache.commons.compress.harmony.pack200;
20
21 import java.io.IOException;
22 import java.io.OutputStream;
23 import java.util.ArrayList;
24 import java.util.HashMap;
25 import java.util.List;
26 import java.util.Map;
27 import java.util.stream.Collectors;
28
29 import org.objectweb.asm.Label;
30
31
32
33
34 public class BcBands extends BandSet {
35
36 private static final int MULTIANEWARRAY = 197;
37 private static final int ALOAD_0 = 42;
38
39 private static final int WIDE = 196;
40
41 private static final int INVOKEINTERFACE = 185;
42 private static final int TABLESWITCH = 170;
43 private static final int IINC = 132;
44 private static final int LOOKUPSWITCH = 171;
45 private static final int endMarker = 255;
46 private final CpBands cpBands;
47
48 private final Segment segment;
49 private final IntList bcCodes = new IntList();
50 private final IntList bcCaseCount = new IntList();
51 private final IntList bcCaseValue = new IntList();
52 private final IntList bcByte = new IntList();
53 private final IntList bcShort = new IntList();
54 private final IntList bcLocal = new IntList();
55
56
57 private final List bcLabel = new ArrayList();
58 private final List<CPInt> bcIntref = new ArrayList<>();
59 private final List<CPFloat> bcFloatRef = new ArrayList<>();
60 private final List<CPLong> bcLongRef = new ArrayList<>();
61 private final List<CPDouble> bcDoubleRef = new ArrayList<>();
62 private final List<CPString> bcStringRef = new ArrayList<>();
63 private final List<CPClass> bcClassRef = new ArrayList<>();
64 private final List<CPMethodOrField> bcFieldRef = new ArrayList<>();
65
66 private final List<CPMethodOrField> bcMethodRef = new ArrayList<>();
67 private final List<CPMethodOrField> bcIMethodRef = new ArrayList<>();
68
69
70 private List bcThisField = new ArrayList<>();
71
72 private final List bcSuperField = new ArrayList<>();
73 private List bcThisMethod = new ArrayList<>();
74 private List bcSuperMethod = new ArrayList<>();
75 private List bcInitRef = new ArrayList<>();
76 private String currentClass;
77 private String superClass;
78 private String currentNewClass;
79 private final IntList bciRenumbering = new IntList();
80
81 private final Map<Label, Integer> labelsToOffsets = new HashMap<>();
82 private int byteCodeOffset;
83 private int renumberedOffset;
84 private final IntList bcLabelRelativeOffsets = new IntList();
85
86 public BcBands(final CpBands cpBands, final Segment segment, final int effort) {
87 super(effort, segment.getSegmentHeader());
88 this.cpBands = cpBands;
89 this.segment = segment;
90 }
91
92
93
94
95
96 public void finaliseBands() {
97 bcThisField = getIndexInClass(bcThisField);
98 bcThisMethod = getIndexInClass(bcThisMethod);
99 bcSuperMethod = getIndexInClass(bcSuperMethod);
100 bcInitRef = getIndexInClassForConstructor(bcInitRef);
101 }
102
103 private List<Integer> getIndexInClass(final List<CPMethodOrField> cPMethodOrFieldList) {
104 return cPMethodOrFieldList.stream().collect(Collectors.mapping(CPMethodOrField::getIndexInClass, Collectors.toList()));
105 }
106
107 private List<Integer> getIndexInClassForConstructor(final List<CPMethodOrField> cPMethodList) {
108 return cPMethodList.stream().collect(Collectors.mapping(CPMethodOrField::getIndexInClassForConstructor, Collectors.toList()));
109 }
110
111 @Override
112 public void pack(final OutputStream out) throws IOException, Pack200Exception {
113 PackingUtils.log("Writing byte code bands...");
114 byte[] encodedBand = encodeBandInt("bcCodes", bcCodes.toArray(), Codec.BYTE1);
115 out.write(encodedBand);
116 PackingUtils.log("Wrote " + encodedBand.length + " bytes from bcCodes[" + bcCodes.size() + "]");
117
118 encodedBand = encodeBandInt("bcCaseCount", bcCaseCount.toArray(), Codec.UNSIGNED5);
119 out.write(encodedBand);
120 PackingUtils.log("Wrote " + encodedBand.length + " bytes from bcCaseCount[" + bcCaseCount.size() + "]");
121
122 encodedBand = encodeBandInt("bcCaseValue", bcCaseValue.toArray(), Codec.DELTA5);
123 out.write(encodedBand);
124 PackingUtils.log("Wrote " + encodedBand.length + " bytes from bcCaseValue[" + bcCaseValue.size() + "]");
125
126 encodedBand = encodeBandInt("bcByte", bcByte.toArray(), Codec.BYTE1);
127 out.write(encodedBand);
128 PackingUtils.log("Wrote " + encodedBand.length + " bytes from bcByte[" + bcByte.size() + "]");
129
130 encodedBand = encodeBandInt("bcShort", bcShort.toArray(), Codec.DELTA5);
131 out.write(encodedBand);
132 PackingUtils.log("Wrote " + encodedBand.length + " bytes from bcShort[" + bcShort.size() + "]");
133
134 encodedBand = encodeBandInt("bcLocal", bcLocal.toArray(), Codec.UNSIGNED5);
135 out.write(encodedBand);
136 PackingUtils.log("Wrote " + encodedBand.length + " bytes from bcLocal[" + bcLocal.size() + "]");
137
138 encodedBand = encodeBandInt("bcLabel", integerListToArray(bcLabel), Codec.BRANCH5);
139 out.write(encodedBand);
140 PackingUtils.log("Wrote " + encodedBand.length + " bytes from bcLabel[" + bcLabel.size() + "]");
141
142 encodedBand = encodeBandInt("bcIntref", cpEntryListToArray(bcIntref), Codec.DELTA5);
143 out.write(encodedBand);
144 PackingUtils.log("Wrote " + encodedBand.length + " bytes from bcIntref[" + bcIntref.size() + "]");
145
146 encodedBand = encodeBandInt("bcFloatRef", cpEntryListToArray(bcFloatRef), Codec.DELTA5);
147 out.write(encodedBand);
148 PackingUtils.log("Wrote " + encodedBand.length + " bytes from bcFloatRef[" + bcFloatRef.size() + "]");
149
150 encodedBand = encodeBandInt("bcLongRef", cpEntryListToArray(bcLongRef), Codec.DELTA5);
151 out.write(encodedBand);
152 PackingUtils.log("Wrote " + encodedBand.length + " bytes from bcLongRef[" + bcLongRef.size() + "]");
153
154 encodedBand = encodeBandInt("bcDoubleRef", cpEntryListToArray(bcDoubleRef), Codec.DELTA5);
155 out.write(encodedBand);
156 PackingUtils.log("Wrote " + encodedBand.length + " bytes from bcDoubleRef[" + bcDoubleRef.size() + "]");
157
158 encodedBand = encodeBandInt("bcStringRef", cpEntryListToArray(bcStringRef), Codec.DELTA5);
159 out.write(encodedBand);
160 PackingUtils.log("Wrote " + encodedBand.length + " bytes from bcStringRef[" + bcStringRef.size() + "]");
161
162 encodedBand = encodeBandInt("bcClassRef", cpEntryOrNullListToArray(bcClassRef), Codec.UNSIGNED5);
163 out.write(encodedBand);
164 PackingUtils.log("Wrote " + encodedBand.length + " bytes from bcClassRef[" + bcClassRef.size() + "]");
165
166 encodedBand = encodeBandInt("bcFieldRef", cpEntryListToArray(bcFieldRef), Codec.DELTA5);
167 out.write(encodedBand);
168 PackingUtils.log("Wrote " + encodedBand.length + " bytes from bcFieldRef[" + bcFieldRef.size() + "]");
169
170 encodedBand = encodeBandInt("bcMethodRef", cpEntryListToArray(bcMethodRef), Codec.UNSIGNED5);
171 out.write(encodedBand);
172 PackingUtils.log("Wrote " + encodedBand.length + " bytes from bcMethodRef[" + bcMethodRef.size() + "]");
173
174 encodedBand = encodeBandInt("bcIMethodRef", cpEntryListToArray(bcIMethodRef), Codec.DELTA5);
175 out.write(encodedBand);
176 PackingUtils.log("Wrote " + encodedBand.length + " bytes from bcIMethodRef[" + bcIMethodRef.size() + "]");
177
178 encodedBand = encodeBandInt("bcThisField", integerListToArray(bcThisField), Codec.UNSIGNED5);
179 out.write(encodedBand);
180 PackingUtils.log("Wrote " + encodedBand.length + " bytes from bcThisField[" + bcThisField.size() + "]");
181
182 encodedBand = encodeBandInt("bcSuperField", integerListToArray(bcSuperField), Codec.UNSIGNED5);
183 out.write(encodedBand);
184 PackingUtils.log("Wrote " + encodedBand.length + " bytes from bcSuperField[" + bcSuperField.size() + "]");
185
186 encodedBand = encodeBandInt("bcThisMethod", integerListToArray(bcThisMethod), Codec.UNSIGNED5);
187 out.write(encodedBand);
188 PackingUtils.log("Wrote " + encodedBand.length + " bytes from bcThisMethod[" + bcThisMethod.size() + "]");
189
190 encodedBand = encodeBandInt("bcSuperMethod", integerListToArray(bcSuperMethod), Codec.UNSIGNED5);
191 out.write(encodedBand);
192 PackingUtils.log("Wrote " + encodedBand.length + " bytes from bcSuperMethod[" + bcSuperMethod.size() + "]");
193
194 encodedBand = encodeBandInt("bcInitRef", integerListToArray(bcInitRef), Codec.UNSIGNED5);
195 out.write(encodedBand);
196 PackingUtils.log("Wrote " + encodedBand.length + " bytes from bcInitRef[" + bcInitRef.size() + "]");
197
198
199
200
201
202
203
204
205 }
206
207 public void setCurrentClass(final String name, final String superName) {
208 currentClass = name;
209 superClass = superName;
210 }
211
212 private void updateRenumbering() {
213 if (bciRenumbering.isEmpty()) {
214 bciRenumbering.add(0);
215 }
216 renumberedOffset++;
217 for (int i = bciRenumbering.size(); i < byteCodeOffset; i++) {
218 bciRenumbering.add(-1);
219 }
220 bciRenumbering.add(renumberedOffset);
221 }
222
223 public void visitEnd() {
224 for (int i = 0; i < bciRenumbering.size(); i++) {
225 if (bciRenumbering.get(i) == -1) {
226 bciRenumbering.remove(i);
227 bciRenumbering.add(i, ++renumberedOffset);
228 }
229 }
230 if (renumberedOffset != 0) {
231 if (renumberedOffset + 1 != bciRenumbering.size()) {
232 throw new IllegalStateException("Mistake made with renumbering");
233 }
234 for (int i = bcLabel.size() - 1; i >= 0; i--) {
235 final Object label = bcLabel.get(i);
236 if (label instanceof Integer) {
237 break;
238 }
239 if (label instanceof Label) {
240 bcLabel.remove(i);
241 final Integer offset = labelsToOffsets.get(label);
242 final int relativeOffset = bcLabelRelativeOffsets.get(i);
243 bcLabel.add(i, Integer.valueOf(bciRenumbering.get(offset.intValue()) - bciRenumbering.get(relativeOffset)));
244 }
245 }
246 bcCodes.add(endMarker);
247 segment.getClassBands().doBciRenumbering(bciRenumbering, labelsToOffsets);
248 bciRenumbering.clear();
249 labelsToOffsets.clear();
250 byteCodeOffset = 0;
251 renumberedOffset = 0;
252 }
253 }
254
255 public void visitFieldInsn(int opcode, final String owner, final String name, final String desc) {
256 byteCodeOffset += 3;
257 updateRenumbering();
258 boolean aload_0 = false;
259 if (bcCodes.size() > 0 && bcCodes.get(bcCodes.size() - 1) == ALOAD_0) {
260 bcCodes.remove(bcCodes.size() - 1);
261 aload_0 = true;
262 }
263 final CPMethodOrField cpField = cpBands.getCPField(owner, name, desc);
264 if (aload_0) {
265 opcode += 7;
266 }
267 if (owner.equals(currentClass)) {
268 opcode += 24;
269 bcThisField.add(cpField);
270
271
272
273 } else {
274 if (aload_0) {
275 opcode -= 7;
276 bcCodes.add(ALOAD_0);
277
278
279 }
280 bcFieldRef.add(cpField);
281 }
282 aload_0 = false;
283 bcCodes.add(opcode);
284 }
285
286 public void visitIincInsn(final int var, final int increment) {
287 if (var > 255 || increment > 255) {
288 byteCodeOffset += 6;
289 bcCodes.add(WIDE);
290 bcCodes.add(IINC);
291 bcLocal.add(var);
292 bcShort.add(increment);
293 } else {
294 byteCodeOffset += 3;
295 bcCodes.add(IINC);
296 bcLocal.add(var);
297 bcByte.add(increment & 0xFF);
298 }
299 updateRenumbering();
300 }
301
302 public void visitInsn(final int opcode) {
303 if (opcode >= 202) {
304 throw new IllegalArgumentException("Non-standard bytecode instructions not supported");
305 }
306 bcCodes.add(opcode);
307 byteCodeOffset++;
308 updateRenumbering();
309 }
310
311 public void visitIntInsn(final int opcode, final int operand) {
312 switch (opcode) {
313 case 17:
314 bcCodes.add(opcode);
315 bcShort.add(operand);
316 byteCodeOffset += 3;
317 break;
318 case 16:
319 case 188:
320 bcCodes.add(opcode);
321 bcByte.add(operand & 0xFF);
322 byteCodeOffset += 2;
323 }
324 updateRenumbering();
325 }
326
327 public void visitJumpInsn(final int opcode, final Label label) {
328 bcCodes.add(opcode);
329 bcLabel.add(label);
330 bcLabelRelativeOffsets.add(byteCodeOffset);
331 byteCodeOffset += 3;
332 updateRenumbering();
333 }
334
335 public void visitLabel(final Label label) {
336 labelsToOffsets.put(label, Integer.valueOf(byteCodeOffset));
337 }
338
339 public void visitLdcInsn(final Object cst) {
340 final CPConstant<?> constant = cpBands.getConstant(cst);
341 if (segment.lastConstantHadWideIndex() || constant instanceof CPLong || constant instanceof CPDouble) {
342 byteCodeOffset += 3;
343 if (constant instanceof CPInt) {
344 bcCodes.add(237);
345 bcIntref.add((CPInt) constant);
346 } else if (constant instanceof CPFloat) {
347 bcCodes.add(238);
348 bcFloatRef.add((CPFloat) constant);
349 } else if (constant instanceof CPLong) {
350 bcCodes.add(20);
351 bcLongRef.add((CPLong) constant);
352 } else if (constant instanceof CPDouble) {
353 bcCodes.add(239);
354 bcDoubleRef.add((CPDouble) constant);
355 } else if (constant instanceof CPString) {
356 bcCodes.add(19);
357 bcStringRef.add((CPString) constant);
358 } else if (constant instanceof CPClass) {
359 bcCodes.add(236);
360 bcClassRef.add((CPClass) constant);
361 } else {
362 throw new IllegalArgumentException("Constant should not be null");
363 }
364 } else {
365 byteCodeOffset += 2;
366 if (constant instanceof CPInt) {
367 bcCodes.add(234);
368 bcIntref.add((CPInt) constant);
369 } else if (constant instanceof CPFloat) {
370 bcCodes.add(235);
371 bcFloatRef.add((CPFloat) constant);
372 } else if (constant instanceof CPString) {
373 bcCodes.add(18);
374 bcStringRef.add((CPString) constant);
375 } else if (constant instanceof CPClass) {
376 bcCodes.add(233);
377 bcClassRef.add((CPClass) constant);
378 }
379 }
380 updateRenumbering();
381 }
382
383 public void visitLookupSwitchInsn(final Label dflt, final int[] keys, final Label[] labels) {
384 bcCodes.add(LOOKUPSWITCH);
385 bcLabel.add(dflt);
386 bcLabelRelativeOffsets.add(byteCodeOffset);
387 bcCaseCount.add(keys.length);
388 for (int i = 0; i < labels.length; i++) {
389 bcCaseValue.add(keys[i]);
390 bcLabel.add(labels[i]);
391 bcLabelRelativeOffsets.add(byteCodeOffset);
392 }
393 final int padding = (byteCodeOffset + 1) % 4 == 0 ? 0 : 4 - (byteCodeOffset + 1) % 4;
394 byteCodeOffset += 1 + padding + 8 + 8 * keys.length;
395 updateRenumbering();
396 }
397
398 public void visitMethodInsn(int opcode, final String owner, final String name, final String desc) {
399 byteCodeOffset += 3;
400 switch (opcode) {
401 case 182:
402 case 183:
403 case 184:
404 boolean aload_0 = false;
405 if (bcCodes.size() > 0 && bcCodes.get(bcCodes.size() - 1) == ALOAD_0) {
406 bcCodes.remove(bcCodes.size() - 1);
407 aload_0 = true;
408 opcode += 7;
409 }
410 if (owner.equals(currentClass)) {
411 opcode += 24;
412
413
414 if (name.equals("<init>") && opcode == 207) {
415 opcode = 230;
416 bcInitRef.add(cpBands.getCPMethod(owner, name, desc));
417 } else {
418 bcThisMethod.add(cpBands.getCPMethod(owner, name, desc));
419 }
420 } else if (owner.equals(superClass)) {
421 opcode += 38;
422
423 if (name.equals("<init>") && opcode == 221) {
424 opcode = 231;
425 bcInitRef.add(cpBands.getCPMethod(owner, name, desc));
426 } else {
427 bcSuperMethod.add(cpBands.getCPMethod(owner, name, desc));
428 }
429 } else {
430 if (aload_0) {
431 opcode -= 7;
432 bcCodes.add(ALOAD_0);
433
434
435
436 }
437 if (name.equals("<init>") && opcode == 183 && owner.equals(currentNewClass)) {
438 opcode = 232;
439 bcInitRef.add(cpBands.getCPMethod(owner, name, desc));
440 } else {
441 bcMethodRef.add(cpBands.getCPMethod(owner, name, desc));
442 }
443 }
444 bcCodes.add(opcode);
445 break;
446 case 185:
447 byteCodeOffset += 2;
448 final CPMethodOrField cpIMethod = cpBands.getCPIMethod(owner, name, desc);
449 bcIMethodRef.add(cpIMethod);
450 bcCodes.add(INVOKEINTERFACE);
451 break;
452 }
453 updateRenumbering();
454 }
455
456 public void visitMultiANewArrayInsn(final String desc, final int dimensions) {
457 byteCodeOffset += 4;
458 updateRenumbering();
459 bcCodes.add(MULTIANEWARRAY);
460 bcClassRef.add(cpBands.getCPClass(desc));
461 bcByte.add(dimensions & 0xFF);
462 }
463
464 public void visitTableSwitchInsn(final int min, final int max, final Label dflt, final Label... labels) {
465 bcCodes.add(TABLESWITCH);
466 bcLabel.add(dflt);
467 bcLabelRelativeOffsets.add(byteCodeOffset);
468 bcCaseValue.add(min);
469 final int count = labels.length;
470 bcCaseCount.add(count);
471 for (int i = 0; i < count; i++) {
472 bcLabel.add(labels[i]);
473 bcLabelRelativeOffsets.add(byteCodeOffset);
474 }
475 final int padding = byteCodeOffset % 4 == 0 ? 0 : 4 - byteCodeOffset % 4;
476 byteCodeOffset += padding + 12 + 4 * labels.length;
477 updateRenumbering();
478 }
479
480 public void visitTypeInsn(final int opcode, final String type) {
481
482 byteCodeOffset += 3;
483 updateRenumbering();
484 bcCodes.add(opcode);
485 bcClassRef.add(cpBands.getCPClass(type));
486 if (opcode == 187) {
487 currentNewClass = type;
488 }
489 }
490
491 public void visitVarInsn(final int opcode, final int var) {
492
493 if (var > 255) {
494 byteCodeOffset += 4;
495 bcCodes.add(WIDE);
496 bcCodes.add(opcode);
497 bcLocal.add(var);
498 } else if (var > 3 || opcode == 169 ) {
499 byteCodeOffset += 2;
500 bcCodes.add(opcode);
501 bcLocal.add(var);
502 } else {
503 byteCodeOffset += 1;
504 switch (opcode) {
505 case 21:
506 case 54:
507 bcCodes.add(opcode + 5 + var);
508 break;
509 case 22:
510 case 55:
511 bcCodes.add(opcode + 8 + var);
512 break;
513 case 23:
514 case 56:
515 bcCodes.add(opcode + 11 + var);
516 break;
517 case 24:
518 case 57:
519 bcCodes.add(opcode + 14 + var);
520 break;
521 case 25:
522 case 58:
523 bcCodes.add(opcode + 17 + var);
524 break;
525 }
526 }
527 updateRenumbering();
528 }
529
530 }