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