View Javadoc
1   /*
2    * Licensed to the Apache Software Foundation (ASF) under one
3    * or more contributor license agreements.  See the NOTICE file
4    * distributed with this work for additional information
5    * regarding copyright ownership.  The ASF licenses this file
6    * to you under the Apache License, Version 2.0 (the
7    * "License"); you may not use this file except in compliance
8    * with the License.  You may obtain a copy of the License at
9    *
10   *   https://www.apache.org/licenses/LICENSE-2.0
11   *
12   * Unless required by applicable law or agreed to in writing,
13   * software distributed under the License is distributed on an
14   * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
15   * KIND, either express or implied.  See the License for the
16   * specific language governing permissions and limitations
17   * under the License.
18   */
19  package org.apache.commons.compress.harmony.unpack200;
20  
21  import java.io.ByteArrayOutputStream;
22  import java.io.IOException;
23  import java.io.InputStream;
24  import java.util.ArrayList;
25  import java.util.Collections;
26  import java.util.List;
27  
28  import org.apache.commons.compress.harmony.pack200.Codec;
29  import org.apache.commons.compress.harmony.pack200.Pack200Exception;
30  import org.apache.commons.compress.harmony.unpack200.bytecode.Attribute;
31  import org.apache.commons.compress.harmony.unpack200.bytecode.BCIRenumberedAttribute;
32  import org.apache.commons.compress.harmony.unpack200.bytecode.ByteCode;
33  import org.apache.commons.compress.harmony.unpack200.bytecode.CPClass;
34  import org.apache.commons.compress.harmony.unpack200.bytecode.CodeAttribute;
35  import org.apache.commons.compress.harmony.unpack200.bytecode.ExceptionTableEntry;
36  import org.apache.commons.compress.harmony.unpack200.bytecode.NewAttribute;
37  import org.apache.commons.compress.harmony.unpack200.bytecode.OperandManager;
38  
39  /**
40   * Bytecode bands.
41   */
42  public class BcBands extends BandSet {
43  
44      /** The bytecodes for each method in each class as they come (i.e. in their packed format). */
45      private byte[][][] methodByteCodePacked;
46  
47      // The bands
48      // TODO: Haven't resolved references yet. Do we want to?
49      private int[] bcCaseCount;
50      private int[] bcCaseValue;
51      private int[] bcByte;
52      private int[] bcLocal;
53      private int[] bcShort;
54      private int[] bcLabel;
55      private int[] bcIntRef;
56      private int[] bcFloatRef;
57      private int[] bcLongRef;
58      private int[] bcDoubleRef;
59      private int[] bcStringRef;
60      private int[] bcClassRef;
61      private int[] bcFieldRef;
62      private int[] bcMethodRef;
63      private int[] bcIMethodRef;
64      private int[] bcThisField;
65      private int[] bcSuperField;
66      private int[] bcThisMethod;
67      private int[] bcSuperMethod;
68      private int[] bcInitRef;
69      private int[] bcEscRef;
70      private int[] bcEscRefSize;
71      private int[] bcEscSize;
72      private int[][] bcEscByte;
73  
74      private List<Integer> wideByteCodes;
75  
76      /**
77       * Constructs a new instance for the given segment.
78       *
79       * @param segment The segment.
80       */
81      public BcBands(final Segment segment) {
82          super(segment);
83      }
84  
85      private boolean endsWithLoad(final int codePacked) {
86          return codePacked >= 21 && codePacked <= 25;
87      }
88  
89      private boolean endsWithStore(final int codePacked) {
90          return codePacked >= 54 && codePacked <= 58;
91      }
92  
93      public int[] getBcByte() {
94          return bcByte;
95      }
96  
97      public int[] getBcCaseCount() {
98          return bcCaseCount;
99      }
100 
101     public int[] getBcCaseValue() {
102         return bcCaseValue;
103     }
104 
105     public int[] getBcClassRef() {
106         return bcClassRef;
107     }
108 
109     public int[] getBcDoubleRef() {
110         return bcDoubleRef;
111     }
112 
113     public int[] getBcFieldRef() {
114         return bcFieldRef;
115     }
116 
117     public int[] getBcFloatRef() {
118         return bcFloatRef;
119     }
120 
121     public int[] getBcIMethodRef() {
122         return bcIMethodRef;
123     }
124 
125     public int[] getBcInitRef() {
126         return bcInitRef;
127     }
128 
129     public int[] getBcIntRef() {
130         return bcIntRef;
131     }
132 
133     public int[] getBcLabel() {
134         return bcLabel;
135     }
136 
137     public int[] getBcLocal() {
138         return bcLocal;
139     }
140 
141     public int[] getBcLongRef() {
142         return bcLongRef;
143     }
144 
145     public int[] getBcMethodRef() {
146         return bcMethodRef;
147     }
148 
149     public int[] getBcShort() {
150         return bcShort;
151     }
152 
153     public int[] getBcStringRef() {
154         return bcStringRef;
155     }
156 
157     public int[] getBcSuperField() {
158         return bcSuperField;
159     }
160 
161     public int[] getBcSuperMethod() {
162         return bcSuperMethod;
163     }
164 
165     public int[] getBcThisField() {
166         return bcThisField;
167     }
168 
169     public int[] getBcThisMethod() {
170         return bcThisMethod;
171     }
172 
173     public byte[][][] getMethodByteCodePacked() {
174         return methodByteCodePacked;
175     }
176 
177     /*
178      * (non-Javadoc)
179      *
180      * @see org.apache.commons.compress.harmony.unpack200.BandSet#unpack(java.io.InputStream)
181      */
182     @Override
183     public void read(final InputStream in) throws IOException, Pack200Exception {
184 
185         final AttributeLayoutMap attributeDefinitionMap = segment.getAttrDefinitionBands().getAttributeDefinitionMap();
186         final int classCount = header.getClassCount();
187         final long[][] methodFlags = segment.getClassBands().getMethodFlags();
188 
189         int bcCaseCountCount = 0;
190         int bcByteCount = 0;
191         int bcShortCount = 0;
192         int bcLocalCount = 0;
193         int bcLabelCount = 0;
194         int bcIntRefCount = 0;
195         int bcFloatRefCount = 0;
196         int bcLongRefCount = 0;
197         int bcDoubleRefCount = 0;
198         int bcStringRefCount = 0;
199         int bcClassRefCount = 0;
200         int bcFieldRefCount = 0;
201         int bcMethodRefCount = 0;
202         int bcIMethodRefCount = 0;
203         int bcThisFieldCount = 0;
204         int bcSuperFieldCount = 0;
205         int bcThisMethodCount = 0;
206         int bcSuperMethodCount = 0;
207         int bcInitRefCount = 0;
208         int bcEscCount = 0;
209         int bcEscRefCount = 0;
210 
211         final AttributeLayout abstractModifier = attributeDefinitionMap.getAttributeLayout(AttributeLayout.ACC_ABSTRACT, AttributeLayout.CONTEXT_METHOD);
212         final AttributeLayout nativeModifier = attributeDefinitionMap.getAttributeLayout(AttributeLayout.ACC_NATIVE, AttributeLayout.CONTEXT_METHOD);
213 
214         methodByteCodePacked = new byte[classCount][][];
215 
216         final List<Boolean> switchIsTableSwitch = new ArrayList<>();
217         wideByteCodes = new ArrayList<>();
218         for (int c = 0; c < classCount; c++) {
219             final int numberOfMethods = methodFlags[c].length;
220             methodByteCodePacked[c] = new byte[numberOfMethods][];
221             for (int m = 0; m < numberOfMethods; m++) {
222                 final long methodFlag = methodFlags[c][m];
223                 if (!abstractModifier.matches(methodFlag) && !nativeModifier.matches(methodFlag)) {
224                     final ByteArrayOutputStream codeBytes = new ByteArrayOutputStream();
225                     byte code;
226                     while ((code = (byte) (0xff & in.read())) != -1) {
227                         codeBytes.write(code);
228                     }
229                     methodByteCodePacked[c][m] = codeBytes.toByteArray();
230                     final int[] codes = new int[methodByteCodePacked[c][m].length];
231                     for (int i = 0; i < codes.length; i++) {
232                         codes[i] = methodByteCodePacked[c][m][i] & 0xff;
233                     }
234                     for (int i = 0; i < methodByteCodePacked[c][m].length; i++) {
235                         final int codePacked = 0xff & methodByteCodePacked[c][m][i];
236                         switch (codePacked) {
237                         case 16: // bipush
238                         case 188: // newarray
239                             bcByteCount++;
240                             break;
241                         case 17: // sipush
242                             bcShortCount++;
243                             break;
244                         case 18: // (a)ldc
245                         case 19: // aldc_w
246                             bcStringRefCount++;
247                             break;
248                         case 234: // ildc
249                         case 237: // ildc_w
250                             bcIntRefCount++;
251                             break;
252                         case 235: // fldc
253                         case 238: // fldc_w
254                             bcFloatRefCount++;
255                             break;
256                         case 197: // multianewarray
257                             bcByteCount++;
258                             // falls-through
259                         case 233: // cldc
260                         case 236: // cldc_w
261                         case 187: // new
262                         case 189: // anewarray
263                         case 192: // checkcast
264                         case 193: // instanceof
265                             bcClassRefCount++;
266                             break;
267                         case 20: // lldc2_w
268                             bcLongRefCount++;
269                             break;
270                         case 239: // dldc2_w
271                             bcDoubleRefCount++;
272                             break;
273                         case 169: // ret
274                             bcLocalCount++;
275                             break;
276                         case 167: // goto
277                         case 168: // jsr
278                         case 200: // goto_w
279                         case 201: // jsr_w
280                             bcLabelCount++;
281                             break;
282                         case 170: // tableswitch
283                             switchIsTableSwitch.add(Boolean.TRUE);
284                             bcCaseCountCount++;
285                             bcLabelCount++;
286                             break;
287                         case 171: // lookupswitch
288                             switchIsTableSwitch.add(Boolean.FALSE);
289                             bcCaseCountCount++;
290                             bcLabelCount++;
291                             break;
292                         case 178: // getstatic
293                         case 179: // putstatic
294                         case 180: // getfield
295                         case 181: // putfield
296                             bcFieldRefCount++;
297                             break;
298                         case 182: // invokevirtual
299                         case 183: // invokespecial
300                         case 184: // invokestatic
301                             bcMethodRefCount++;
302                             break;
303                         case 185: // invokeinterface
304                             bcIMethodRefCount++;
305                             break;
306                         case 202: // getstatic_this
307                         case 203: // putstatic_this
308                         case 204: // getfield_this
309                         case 205: // putfield_this
310                         case 209: // aload_0_getstatic_this
311                         case 210: // aload_0_putstatic_this
312                         case 211: // aload_0_putfield_this
313                         case 212: // aload_0_putfield_this
314                             bcThisFieldCount++;
315                             break;
316                         case 206: // invokevirtual_this
317                         case 207: // invokespecial_this
318                         case 208: // invokestatic_this
319                         case 213: // aload_0_invokevirtual_this
320                         case 214: // aload_0_invokespecial_this
321                         case 215: // aload_0_invokestatic_this
322                             bcThisMethodCount++;
323                             break;
324                         case 216: // getstatic_super
325                         case 217: // putstatic_super
326                         case 218: // getfield_super
327                         case 219: // putfield_super
328                         case 223: // aload_0_getstatic_super
329                         case 224: // aload_0_putstatic_super
330                         case 225: // aload_0_getfield_super
331                         case 226: // aload_0_putfield_super
332                             bcSuperFieldCount++;
333                             break;
334                         case 220: // invokevirtual_super
335                         case 221: // invokespecial_super
336                         case 222: // invokestatic_super
337                         case 227: // aload_0_invokevirtual_super
338                         case 228: // aload_0_invokespecial_super
339                         case 229: // aload_0_invokestatic_super
340                             bcSuperMethodCount++;
341                             break;
342                         case 132: // iinc
343                             bcLocalCount++;
344                             bcByteCount++;
345                             break;
346                         case 196: // wide
347                             final int nextInstruction = 0xff & methodByteCodePacked[c][m][i + 1];
348                             wideByteCodes.add(Integer.valueOf(nextInstruction));
349                             if (nextInstruction == 132) { // iinc
350                                 bcLocalCount++;
351                                 bcShortCount++;
352                             } else if (endsWithLoad(nextInstruction) || endsWithStore(nextInstruction) || nextInstruction == 169) {
353                                 bcLocalCount++;
354                             } else {
355                                 segment.log(Segment.LOG_LEVEL_VERBOSE, "Found unhandled " + ByteCode.getByteCode(nextInstruction));
356                             }
357                             i++;
358                             break;
359                         case 230: // invokespecial_this_init
360                         case 231: // invokespecial_super_init
361                         case 232: // invokespecial_new_init
362                             bcInitRefCount++;
363                             break;
364                         case 253: // ref_escape
365                             bcEscRefCount++;
366                             break;
367                         case 254: // byte_escape
368                             bcEscCount++;
369                             break;
370                         default:
371                             if (endsWithLoad(codePacked) || endsWithStore(codePacked)) {
372                                 bcLocalCount++;
373                             } else if (startsWithIf(codePacked)) {
374                                 bcLabelCount++;
375                             }
376                         }
377                     }
378                 }
379             }
380         }
381         // other bytecode bands
382         bcCaseCount = decodeBandInt("bc_case_count", in, Codec.UNSIGNED5, bcCaseCountCount);
383         int bcCaseValueCount = 0;
384         for (int i = 0; i < bcCaseCount.length; i++) {
385             final boolean isTableSwitch = switchIsTableSwitch.get(i).booleanValue();
386             if (isTableSwitch) {
387                 bcCaseValueCount += 1;
388             } else {
389                 bcCaseValueCount += bcCaseCount[i];
390             }
391         }
392         bcCaseValue = decodeBandInt("bc_case_value", in, Codec.DELTA5, bcCaseValueCount);
393         // Every case value needs a label. We weren't able to count these
394         // above, because we didn't know how many cases there were.
395         // Have to correct it now.
396         for (int index = 0; index < bcCaseCountCount; index++) {
397             bcLabelCount += bcCaseCount[index];
398         }
399         bcByte = decodeBandInt("bc_byte", in, Codec.BYTE1, bcByteCount);
400         bcShort = decodeBandInt("bc_short", in, Codec.DELTA5, bcShortCount);
401         bcLocal = decodeBandInt("bc_local", in, Codec.UNSIGNED5, bcLocalCount);
402         bcLabel = decodeBandInt("bc_label", in, Codec.BRANCH5, bcLabelCount);
403         bcIntRef = decodeBandInt("bc_intref", in, Codec.DELTA5, bcIntRefCount);
404         bcFloatRef = decodeBandInt("bc_floatref", in, Codec.DELTA5, bcFloatRefCount);
405         bcLongRef = decodeBandInt("bc_longref", in, Codec.DELTA5, bcLongRefCount);
406         bcDoubleRef = decodeBandInt("bc_doubleref", in, Codec.DELTA5, bcDoubleRefCount);
407         bcStringRef = decodeBandInt("bc_stringref", in, Codec.DELTA5, bcStringRefCount);
408         bcClassRef = decodeBandInt("bc_classref", in, Codec.UNSIGNED5, bcClassRefCount);
409         bcFieldRef = decodeBandInt("bc_fieldref", in, Codec.DELTA5, bcFieldRefCount);
410         bcMethodRef = decodeBandInt("bc_methodref", in, Codec.UNSIGNED5, bcMethodRefCount);
411         bcIMethodRef = decodeBandInt("bc_imethodref", in, Codec.DELTA5, bcIMethodRefCount);
412         bcThisField = decodeBandInt("bc_thisfield", in, Codec.UNSIGNED5, bcThisFieldCount);
413         bcSuperField = decodeBandInt("bc_superfield", in, Codec.UNSIGNED5, bcSuperFieldCount);
414         bcThisMethod = decodeBandInt("bc_thismethod", in, Codec.UNSIGNED5, bcThisMethodCount);
415         bcSuperMethod = decodeBandInt("bc_supermethod", in, Codec.UNSIGNED5, bcSuperMethodCount);
416         bcInitRef = decodeBandInt("bc_initref", in, Codec.UNSIGNED5, bcInitRefCount);
417         bcEscRef = decodeBandInt("bc_escref", in, Codec.UNSIGNED5, bcEscRefCount);
418         bcEscRefSize = decodeBandInt("bc_escrefsize", in, Codec.UNSIGNED5, bcEscRefCount);
419         bcEscSize = decodeBandInt("bc_escsize", in, Codec.UNSIGNED5, bcEscCount);
420         bcEscByte = decodeBandInt("bc_escbyte", in, Codec.BYTE1, bcEscSize);
421     }
422 
423     private boolean startsWithIf(final int codePacked) {
424         return codePacked >= 153 && codePacked <= 166 || codePacked == 198 || codePacked == 199;
425     }
426 
427     @Override
428     public void unpack() throws Pack200Exception {
429         final int classCount = header.getClassCount();
430         final long[][] methodFlags = segment.getClassBands().getMethodFlags();
431         final int[] codeMaxNALocals = segment.getClassBands().getCodeMaxNALocals();
432         final int[] codeMaxStack = segment.getClassBands().getCodeMaxStack();
433         final ArrayList<Attribute>[][] methodAttributes = segment.getClassBands().getMethodAttributes();
434         final String[][] methodDescr = segment.getClassBands().getMethodDescr();
435 
436         final AttributeLayoutMap attributeDefinitionMap = segment.getAttrDefinitionBands().getAttributeDefinitionMap();
437 
438         final AttributeLayout abstractModifier = attributeDefinitionMap.getAttributeLayout(AttributeLayout.ACC_ABSTRACT, AttributeLayout.CONTEXT_METHOD);
439         final AttributeLayout nativeModifier = attributeDefinitionMap.getAttributeLayout(AttributeLayout.ACC_NATIVE, AttributeLayout.CONTEXT_METHOD);
440         final AttributeLayout staticModifier = attributeDefinitionMap.getAttributeLayout(AttributeLayout.ACC_STATIC, AttributeLayout.CONTEXT_METHOD);
441 
442         final int[] wideByteCodeArray = new int[wideByteCodes.size()];
443         for (int index = 0; index < wideByteCodeArray.length; index++) {
444             wideByteCodeArray[index] = wideByteCodes.get(index).intValue();
445         }
446         final OperandManager operandManager = new OperandManager(bcCaseCount, bcCaseValue, bcByte, bcShort, bcLocal, bcLabel, bcIntRef, bcFloatRef, bcLongRef,
447                 bcDoubleRef, bcStringRef, bcClassRef, bcFieldRef, bcMethodRef, bcIMethodRef, bcThisField, bcSuperField, bcThisMethod, bcSuperMethod, bcInitRef,
448                 wideByteCodeArray);
449         operandManager.setSegment(segment);
450 
451         int i = 0;
452         final ArrayList<List<Attribute>> orderedCodeAttributes = segment.getClassBands().getOrderedCodeAttributes();
453         int codeAttributeIndex = 0;
454 
455         // Exception table fields
456         final int[] handlerCount = segment.getClassBands().getCodeHandlerCount();
457         final int[][] handlerStartPCs = segment.getClassBands().getCodeHandlerStartP();
458         final int[][] handlerEndPCs = segment.getClassBands().getCodeHandlerEndPO();
459         final int[][] handlerCatchPCs = segment.getClassBands().getCodeHandlerCatchPO();
460         final int[][] handlerClassTypes = segment.getClassBands().getCodeHandlerClassRCN();
461 
462         final boolean allCodeHasFlags = segment.getSegmentHeader().getOptions().hasAllCodeFlags();
463         final boolean[] codeHasFlags = segment.getClassBands().getCodeHasAttributes();
464 
465         for (int c = 0; c < classCount; c++) {
466             final int numberOfMethods = methodFlags[c].length;
467             for (int m = 0; m < numberOfMethods; m++) {
468                 final long methodFlag = methodFlags[c][m];
469                 if (!abstractModifier.matches(methodFlag) && !nativeModifier.matches(methodFlag)) {
470                     final int maxStack = codeMaxStack[i];
471                     int maxLocal = codeMaxNALocals[i];
472                     if (!staticModifier.matches(methodFlag)) {
473                         maxLocal++; // one for 'this' parameter
474                     }
475                     // I believe this has to take wide arguments into account
476                     maxLocal += SegmentUtils.countInvokeInterfaceArgs(methodDescr[c][m]);
477                     final String[] cpClass = segment.getCpBands().getCpClass();
478                     operandManager.setCurrentClass(cpClass[segment.getClassBands().getClassThisInts()[c]]);
479                     operandManager.setSuperClass(cpClass[segment.getClassBands().getClassSuperInts()[c]]);
480                     final List<ExceptionTableEntry> exceptionTable = new ArrayList<>();
481                     if (handlerCount != null) {
482                         for (int j = 0; j < handlerCount[i]; j++) {
483                             final int handlerClass = handlerClassTypes[i][j] - 1;
484                             CPClass cpHandlerClass = null;
485                             if (handlerClass != -1) {
486                                 // The handlerClass will be null if the
487                                 // catch is a finally (that is, the
488                                 // exception table catch_type should be 0
489                                 cpHandlerClass = segment.getCpBands().cpClassValue(handlerClass);
490                             }
491                             final ExceptionTableEntry entry = new ExceptionTableEntry(handlerStartPCs[i][j], handlerEndPCs[i][j], handlerCatchPCs[i][j],
492                                     cpHandlerClass);
493                             exceptionTable.add(entry);
494                         }
495                     }
496                     final CodeAttribute codeAttr = new CodeAttribute(maxStack, maxLocal, methodByteCodePacked[c][m], segment, operandManager, exceptionTable);
497                     final List<Attribute> methodAttributesList = methodAttributes[c][m];
498                     // Make sure we add the code attribute in the right place
499                     int indexForCodeAttr = 0;
500                     for (final Attribute attribute : methodAttributesList) {
501                         if (!(attribute instanceof NewAttribute) || ((NewAttribute) attribute).getLayoutIndex() >= 15) {
502                             break;
503                         }
504                         indexForCodeAttr++;
505                     }
506                     methodAttributesList.add(indexForCodeAttr, codeAttr);
507                     codeAttr.renumber(codeAttr.byteCodeOffsets);
508                     final List<Attribute> currentAttributes;
509                     if (allCodeHasFlags) {
510                         currentAttributes = orderedCodeAttributes.get(i);
511                     } else if (codeHasFlags[i]) {
512                         currentAttributes = orderedCodeAttributes.get(codeAttributeIndex);
513                         codeAttributeIndex++;
514                     } else {
515                         currentAttributes = Collections.EMPTY_LIST;
516                     }
517                     for (final Attribute currentAttribute : currentAttributes) {
518                         codeAttr.addAttribute(currentAttribute);
519                         // Fix up the line numbers if needed
520                         if (currentAttribute.hasBCIRenumbering()) {
521                             ((BCIRenumberedAttribute) currentAttribute).renumber(codeAttr.byteCodeOffsets);
522                         }
523                     }
524                     i++;
525                 }
526             }
527         }
528     }
529 }