001/*
002 * Licensed to the Apache Software Foundation (ASF) under one
003 * or more contributor license agreements.  See the NOTICE file
004 * distributed with this work for additional information
005 * regarding copyright ownership.  The ASF licenses this file
006 * to you under the Apache License, Version 2.0 (the
007 * "License"); you may not use this file except in compliance
008 * with the License.  You may obtain a copy of the License at
009 *
010 *   https://www.apache.org/licenses/LICENSE-2.0
011 *
012 * Unless required by applicable law or agreed to in writing,
013 * software distributed under the License is distributed on an
014 * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
015 * KIND, either express or implied.  See the License for the
016 * specific language governing permissions and limitations
017 * under the License.
018 */
019package org.apache.commons.compress.harmony.unpack200;
020
021import java.io.ByteArrayOutputStream;
022import java.io.IOException;
023import java.io.InputStream;
024import java.util.ArrayList;
025import java.util.Collections;
026import java.util.List;
027
028import org.apache.commons.compress.harmony.pack200.Codec;
029import org.apache.commons.compress.harmony.pack200.Pack200Exception;
030import org.apache.commons.compress.harmony.unpack200.bytecode.Attribute;
031import org.apache.commons.compress.harmony.unpack200.bytecode.BCIRenumberedAttribute;
032import org.apache.commons.compress.harmony.unpack200.bytecode.ByteCode;
033import org.apache.commons.compress.harmony.unpack200.bytecode.CPClass;
034import org.apache.commons.compress.harmony.unpack200.bytecode.CodeAttribute;
035import org.apache.commons.compress.harmony.unpack200.bytecode.ExceptionTableEntry;
036import org.apache.commons.compress.harmony.unpack200.bytecode.NewAttribute;
037import org.apache.commons.compress.harmony.unpack200.bytecode.OperandManager;
038
039/**
040 * Bytecode bands.
041 */
042public class BcBands extends BandSet {
043
044    /** The bytecodes for each method in each class as they come (i.e. in their packed format). */
045    private byte[][][] methodByteCodePacked;
046
047    // The bands
048    // TODO: Haven't resolved references yet. Do we want to?
049    private int[] bcCaseCount;
050    private int[] bcCaseValue;
051    private int[] bcByte;
052    private int[] bcLocal;
053    private int[] bcShort;
054    private int[] bcLabel;
055    private int[] bcIntRef;
056    private int[] bcFloatRef;
057    private int[] bcLongRef;
058    private int[] bcDoubleRef;
059    private int[] bcStringRef;
060    private int[] bcClassRef;
061    private int[] bcFieldRef;
062    private int[] bcMethodRef;
063    private int[] bcIMethodRef;
064    private int[] bcThisField;
065    private int[] bcSuperField;
066    private int[] bcThisMethod;
067    private int[] bcSuperMethod;
068    private int[] bcInitRef;
069    private int[] bcEscRef;
070    private int[] bcEscRefSize;
071    private int[] bcEscSize;
072    private int[][] bcEscByte;
073
074    private List<Integer> wideByteCodes;
075
076    /**
077     * Constructs a new instance for the given segment.
078     *
079     * @param segment The segment.
080     */
081    public BcBands(final Segment segment) {
082        super(segment);
083    }
084
085    private boolean endsWithLoad(final int codePacked) {
086        return codePacked >= 21 && codePacked <= 25;
087    }
088
089    private boolean endsWithStore(final int codePacked) {
090        return codePacked >= 54 && codePacked <= 58;
091    }
092
093    public int[] getBcByte() {
094        return bcByte;
095    }
096
097    public int[] getBcCaseCount() {
098        return bcCaseCount;
099    }
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}