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