    001/*
    002 *  Licensed to the Apache Software Foundation (ASF) under one or more
    003 *  contributor license agreements.  See the NOTICE file distributed with
    004 *  this work for additional information regarding copyright ownership.
    005 *  The ASF licenses this file to You under the Apache License, Version 2.0
    006 *  (the "License"); you may not use this file except in compliance with
    007 *  the License.  You may obtain a copy of the License at
    008 *
    009 *     http://www.apache.org/licenses/LICENSE-2.0
    010 *
    011 *  Unless required by applicable law or agreed to in writing, software
    012 *  distributed under the License is distributed on an "AS IS" BASIS,
    013 *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    014 *  See the License for the specific language governing permissions and
    015 *  limitations under the License.
    016 */
    017package org.apache.commons.compress.harmony.unpack200;
    018
    019import java.io.ByteArrayOutputStream;
    020import java.io.IOException;
    021import java.io.InputStream;
    022import java.util.ArrayList;
    023import java.util.Collections;
    024import java.util.List;
    025
    026import org.apache.commons.compress.harmony.pack200.Codec;
    027import org.apache.commons.compress.harmony.pack200.Pack200Exception;
    028import org.apache.commons.compress.harmony.unpack200.bytecode.Attribute;
    029import org.apache.commons.compress.harmony.unpack200.bytecode.BCIRenumberedAttribute;
    030import org.apache.commons.compress.harmony.unpack200.bytecode.ByteCode;
    031import org.apache.commons.compress.harmony.unpack200.bytecode.CPClass;
    032import org.apache.commons.compress.harmony.unpack200.bytecode.CodeAttribute;
    033import org.apache.commons.compress.harmony.unpack200.bytecode.ExceptionTableEntry;
    034import org.apache.commons.compress.harmony.unpack200.bytecode.NewAttribute;
    035import org.apache.commons.compress.harmony.unpack200.bytecode.OperandManager;
    036
    037/**
    038 * Bytecode bands
    039 */
    040public class BcBands extends BandSet {
    041
    042    // The bytecodes for each method in each class as they come (i.e. in their
    043    // packed format)
    044    private byte[][][] methodByteCodePacked;
    045
    046    // The bands
    047    // TODO: Haven't resolved references yet. Do we want to?
    048    private int[] bcCaseCount;
    049    private int[] bcCaseValue;
    050    private int[] bcByte;
    051    private int[] bcLocal;
    052    private int[] bcShort;
    053    private int[] bcLabel;
    054    private int[] bcIntRef;
    055    private int[] bcFloatRef;
    056    private int[] bcLongRef;
    057    private int[] bcDoubleRef;
    058    private int[] bcStringRef;
    059    private int[] bcClassRef;
    060    private int[] bcFieldRef;
    061    private int[] bcMethodRef;
    062    private int[] bcIMethodRef;
    063    private int[] bcThisField;
    064    private int[] bcSuperField;
    065    private int[] bcThisMethod;
    066    private int[] bcSuperMethod;
    067    private int[] bcInitRef;
    068    private int[] bcEscRef;
    069    private int[] bcEscRefSize;
    070    private int[] bcEscSize;
    071    private int[][] bcEscByte;
    072
    073    private List<Integer> wideByteCodes;
    074
    075    /**
    076     * @param segment TODO
    077     */
    078    public BcBands(final Segment segment) {
    079        super(segment);
    080    }
    081
    082    private boolean endsWithLoad(final int codePacked) {
    083        return (codePacked >= 21 && codePacked <= 25);
    084    }
    085
    086    private boolean endsWithStore(final int codePacked) {
    087        return (codePacked >= 54 && codePacked <= 58);
    088    }
    089
    090    public int[] getBcByte() {
    091        return bcByte;
    092    }
    093
    094    public int[] getBcCaseCount() {
    095        return bcCaseCount;
    096    }
    097
    098    public int[] getBcCaseValue() {
    099        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,
    209            AttributeLayout.CONTEXT_METHOD);
    210        final AttributeLayout nativeModifier = attributeDefinitionMap.getAttributeLayout(AttributeLayout.ACC_NATIVE,
    211            AttributeLayout.CONTEXT_METHOD);
    212
    213        methodByteCodePacked = new byte[classCount][][];
    214        int bcParsed = 0;
    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                    bcParsed += methodByteCodePacked[c][m].length;
    231                    final int[] codes = new int[methodByteCodePacked[c][m].length];
    232                    for (int i = 0; i < codes.length; i++) {
    233                        codes[i] = methodByteCodePacked[c][m][i] & 0xff;
    234                    }
    235                    for (int i = 0; i < methodByteCodePacked[c][m].length; i++) {
    236                        final int codePacked = 0xff & methodByteCodePacked[c][m][i];
    237                        switch (codePacked) {
    238                        case 16: // bipush
    239                        case 188: // newarray
    240                            bcByteCount++;
    241                            break;
    242                        case 17: // sipush
    243                            bcShortCount++;
    244                            break;
    245                        case 18: // (a)ldc
    246                        case 19: // aldc_w
    247                            bcStringRefCount++;
    248                            break;
    249                        case 234: // ildc
    250                        case 237: // ildc_w
    251                            bcIntRefCount++;
    252                            break;
    253                        case 235: // fldc
    254                        case 238: // fldc_w
    255                            bcFloatRefCount++;
    256                            break;
    257                        case 197: // multianewarray
    258                            bcByteCount++;
    259                            // fallthrough intended
    260                        case 233: // cldc
    261                        case 236: // cldc_w
    262                        case 187: // new
    263                        case 189: // anewarray
    264                        case 192: // checkcast
    265                        case 193: // instanceof
    266                            bcClassRefCount++;
    267                            break;
    268                        case 20: // lldc2_w
    269                            bcLongRefCount++;
    270                            break;
    271                        case 239: // dldc2_w
    272                            bcDoubleRefCount++;
    273                            break;
    274                        case 169: // ret
    275                            bcLocalCount++;
    276                            break;
    277                        case 167: // goto
    278                        case 168: // jsr
    279                        case 200: // goto_w
    280                        case 201: // jsr_w
    281                            bcLabelCount++;
    282                            break;
    283                        case 170: // tableswitch
    284                            switchIsTableSwitch.add(Boolean.TRUE);
    285                            bcCaseCountCount++;
    286                            bcLabelCount++;
    287                            break;
    288                        case 171: // lookupswitch
    289                            switchIsTableSwitch.add(Boolean.FALSE);
    290                            bcCaseCountCount++;
    291                            bcLabelCount++;
    292                            break;
    293                        case 178: // getstatic
    294                        case 179: // putstatic
    295                        case 180: // getfield
    296                        case 181: // putfield
    297                            bcFieldRefCount++;
    298                            break;
    299                        case 182: // invokevirtual
    300                        case 183: // invokespecial
    301                        case 184: // invokestatic
    302                            bcMethodRefCount++;
    303                            break;
    304                        case 185: // invokeinterface
    305                            bcIMethodRefCount++;
    306                            break;
    307                        case 202: // getstatic_this
    308                        case 203: // putstatic_this
    309                        case 204: // getfield_this
    310                        case 205: // putfield_this
    311                        case 209: // aload_0_getstatic_this
    312                        case 210: // aload_0_putstatic_this
    313                        case 211: // aload_0_putfield_this
    314                        case 212: // aload_0_putfield_this
    315                            bcThisFieldCount++;
    316                            break;
    317                        case 206: // invokevirtual_this
    318                        case 207: // invokespecial_this
    319                        case 208: // invokestatic_this
    320                        case 213: // aload_0_invokevirtual_this
    321                        case 214: // aload_0_invokespecial_this
    322                        case 215: // aload_0_invokestatic_this
    323                            bcThisMethodCount++;
    324                            break;
    325                        case 216: // getstatic_super
    326                        case 217: // putstatic_super
    327                        case 218: // getfield_super
    328                        case 219: // putfield_super
    329                        case 223: // aload_0_getstatic_super
    330                        case 224: // aload_0_putstatic_super
    331                        case 225: // aload_0_getfield_super
    332                        case 226: // aload_0_putfield_super
    333                            bcSuperFieldCount++;
    334                            break;
    335                        case 220: // invokevirtual_super
    336                        case 221: // invokespecial_super
    337                        case 222: // invokestatic_super
    338                        case 227: // aload_0_invokevirtual_super
    339                        case 228: // aload_0_invokespecial_super
    340                        case 229: // aload_0_invokestatic_super
    341                            bcSuperMethodCount++;
    342                            break;
    343                        case 132: // iinc
    344                            bcLocalCount++;
    345                            bcByteCount++;
    346                            break;
    347                        case 196: // wide
    348                            final int nextInstruction = 0xff & methodByteCodePacked[c][m][i + 1];
    349                            wideByteCodes.add(Integer.valueOf(nextInstruction));
    350                            if (nextInstruction == 132) { // iinc
    351                                bcLocalCount++;
    352                                bcShortCount++;
    353                            } else if (endsWithLoad(nextInstruction) || endsWithStore(nextInstruction)
    354                                || nextInstruction == 169) {
    355                                bcLocalCount++;
    356                            } else {
    357                                segment.log(Segment.LOG_LEVEL_VERBOSE,
    358                                    "Found unhandled " + ByteCode.getByteCode(nextInstruction));
    359                            }
    360                            i++;
    361                            break;
    362                        case 230: // invokespecial_this_init
    363                        case 231: // invokespecial_super_init
    364                        case 232: // invokespecial_new_init
    365                            bcInitRefCount++;
    366                            break;
    367                        case 253: // ref_escape
    368                            bcEscRefCount++;
    369                            break;
    370                        case 254: // byte_escape
    371                            bcEscCount++;
    372                            break;
    373                        default:
    374                            if (endsWithLoad(codePacked) || endsWithStore(codePacked)) {
    375                                bcLocalCount++;
    376                            } else if (startsWithIf(codePacked)) {
    377                                bcLabelCount++;
    378                            }
    379                        }
    380                    }
    381                }
    382            }
    383        }
    384        // other bytecode bands
    385        bcCaseCount = decodeBandInt("bc_case_count", in, Codec.UNSIGNED5, bcCaseCountCount);
    386        int bcCaseValueCount = 0;
    387        for (int i = 0; i < bcCaseCount.length; i++) {
    388            final boolean isTableSwitch = switchIsTableSwitch.get(i).booleanValue();
    389            if (isTableSwitch) {
    390                bcCaseValueCount += 1;
    391            } else {
    392                bcCaseValueCount += bcCaseCount[i];
    393            }
    394        }
    395        bcCaseValue = decodeBandInt("bc_case_value", in, Codec.DELTA5, bcCaseValueCount);
    396        // Every case value needs a label. We weren't able to count these
    397        // above, because we didn't know how many cases there were.
    398        // Have to correct it now.
    399        for (int index = 0; index < bcCaseCountCount; index++) {
    400            bcLabelCount += bcCaseCount[index];
    401        }
    402        bcByte = decodeBandInt("bc_byte", in, Codec.BYTE1, bcByteCount);
    403        bcShort = decodeBandInt("bc_short", in, Codec.DELTA5, bcShortCount);
    404        bcLocal = decodeBandInt("bc_local", in, Codec.UNSIGNED5, bcLocalCount);
    405        bcLabel = decodeBandInt("bc_label", in, Codec.BRANCH5, bcLabelCount);
    406        bcIntRef = decodeBandInt("bc_intref", in, Codec.DELTA5, bcIntRefCount);
    407        bcFloatRef = decodeBandInt("bc_floatref", in, Codec.DELTA5, bcFloatRefCount);
    408        bcLongRef = decodeBandInt("bc_longref", in, Codec.DELTA5, bcLongRefCount);
    409        bcDoubleRef = decodeBandInt("bc_doubleref", in, Codec.DELTA5, bcDoubleRefCount);
    410        bcStringRef = decodeBandInt("bc_stringref", in, Codec.DELTA5, bcStringRefCount);
    411        bcClassRef = decodeBandInt("bc_classref", in, Codec.UNSIGNED5, bcClassRefCount);
    412        bcFieldRef = decodeBandInt("bc_fieldref", in, Codec.DELTA5, bcFieldRefCount);
    413        bcMethodRef = decodeBandInt("bc_methodref", in, Codec.UNSIGNED5, bcMethodRefCount);
    414        bcIMethodRef = decodeBandInt("bc_imethodref", in, Codec.DELTA5, bcIMethodRefCount);
    415        bcThisField = decodeBandInt("bc_thisfield", in, Codec.UNSIGNED5, bcThisFieldCount);
    416        bcSuperField = decodeBandInt("bc_superfield", in, Codec.UNSIGNED5, bcSuperFieldCount);
    417        bcThisMethod = decodeBandInt("bc_thismethod", in, Codec.UNSIGNED5, bcThisMethodCount);
    418        bcSuperMethod = decodeBandInt("bc_supermethod", in, Codec.UNSIGNED5, bcSuperMethodCount);
    419        bcInitRef = decodeBandInt("bc_initref", in, Codec.UNSIGNED5, bcInitRefCount);
    420        bcEscRef = decodeBandInt("bc_escref", in, Codec.UNSIGNED5, bcEscRefCount);
    421        bcEscRefSize = decodeBandInt("bc_escrefsize", in, Codec.UNSIGNED5, bcEscRefCount);
    422        bcEscSize = decodeBandInt("bc_escsize", in, Codec.UNSIGNED5, bcEscCount);
    423        bcEscByte = decodeBandInt("bc_escbyte", in, Codec.BYTE1, bcEscSize);
    424    }
    425
    426    private boolean startsWithIf(final int codePacked) {
    427        return (codePacked >= 153 && codePacked <= 166) || (codePacked == 198) || (codePacked == 199);
    428    }
    429
    430    @Override
    431    public void unpack() throws Pack200Exception {
    432        final int classCount = header.getClassCount();
    433        final long[][] methodFlags = segment.getClassBands().getMethodFlags();
    434        final int[] codeMaxNALocals = segment.getClassBands().getCodeMaxNALocals();
    435        final int[] codeMaxStack = segment.getClassBands().getCodeMaxStack();
    436        final ArrayList<Attribute>[][] methodAttributes = segment.getClassBands().getMethodAttributes();
    437        final String[][] methodDescr = segment.getClassBands().getMethodDescr();
    438
    439        final AttributeLayoutMap attributeDefinitionMap = segment.getAttrDefinitionBands().getAttributeDefinitionMap();
    440
    441        final AttributeLayout abstractModifier = attributeDefinitionMap.getAttributeLayout(AttributeLayout.ACC_ABSTRACT,
    442            AttributeLayout.CONTEXT_METHOD);
    443        final AttributeLayout nativeModifier = attributeDefinitionMap.getAttributeLayout(AttributeLayout.ACC_NATIVE,
    444            AttributeLayout.CONTEXT_METHOD);
    445        final AttributeLayout staticModifier = attributeDefinitionMap.getAttributeLayout(AttributeLayout.ACC_STATIC,
    446            AttributeLayout.CONTEXT_METHOD);
    447
    448        final int[] wideByteCodeArray = new int[wideByteCodes.size()];
    449        for (int index = 0; index < wideByteCodeArray.length; index++) {
    450            wideByteCodeArray[index] = wideByteCodes.get(index).intValue();
    451        }
    452        final OperandManager operandManager = new OperandManager(bcCaseCount, bcCaseValue, bcByte, bcShort, bcLocal,
    453            bcLabel, bcIntRef, bcFloatRef, bcLongRef, bcDoubleRef, bcStringRef, bcClassRef, bcFieldRef, bcMethodRef,
    454            bcIMethodRef, bcThisField, bcSuperField, bcThisMethod, bcSuperMethod, bcInitRef, wideByteCodeArray);
    455        operandManager.setSegment(segment);
    456
    457        int i = 0;
    458        final ArrayList<List<Attribute>> orderedCodeAttributes = segment.getClassBands().getOrderedCodeAttributes();
    459        int codeAttributeIndex = 0;
    460
    461        // Exception table fields
    462        final int[] handlerCount = segment.getClassBands().getCodeHandlerCount();
    463        final int[][] handlerStartPCs = segment.getClassBands().getCodeHandlerStartP();
    464        final int[][] handlerEndPCs = segment.getClassBands().getCodeHandlerEndPO();
    465        final int[][] handlerCatchPCs = segment.getClassBands().getCodeHandlerCatchPO();
    466        final int[][] handlerClassTypes = segment.getClassBands().getCodeHandlerClassRCN();
    467
    468        final boolean allCodeHasFlags = segment.getSegmentHeader().getOptions().hasAllCodeFlags();
    469        final boolean[] codeHasFlags = segment.getClassBands().getCodeHasAttributes();
    470
    471        for (int c = 0; c < classCount; c++) {
    472            final int numberOfMethods = methodFlags[c].length;
    473            for (int m = 0; m < numberOfMethods; m++) {
    474                final long methodFlag = methodFlags[c][m];
    475                if (!abstractModifier.matches(methodFlag) && !nativeModifier.matches(methodFlag)) {
    476                    final int maxStack = codeMaxStack[i];
    477                    int maxLocal = codeMaxNALocals[i];
    478                    if (!staticModifier.matches(methodFlag)) {
    479                        maxLocal++; // one for 'this' parameter
    480                    }
    481                    // I believe this has to take wide arguments into account
    482                    maxLocal += SegmentUtils.countInvokeInterfaceArgs(methodDescr[c][m]);
    483                    final String[] cpClass = segment.getCpBands().getCpClass();
    484                    operandManager.setCurrentClass(cpClass[segment.getClassBands().getClassThisInts()[c]]);
    485                    operandManager.setSuperClass(cpClass[segment.getClassBands().getClassSuperInts()[c]]);
    486                    final List<ExceptionTableEntry> exceptionTable = new ArrayList<>();
    487                    if (handlerCount != null) {
    488                        for (int j = 0; j < handlerCount[i]; j++) {
    489                            final int handlerClass = handlerClassTypes[i][j] - 1;
    490                            CPClass cpHandlerClass = null;
    491                            if (handlerClass != -1) {
    492                                // The handlerClass will be null if the
    493                                // catch is a finally (that is, the
    494                                // exception table catch_type should be 0
    495                                cpHandlerClass = segment.getCpBands().cpClassValue(handlerClass);
    496                            }
    497                            final ExceptionTableEntry entry = new ExceptionTableEntry(handlerStartPCs[i][j],
    498                                handlerEndPCs[i][j], handlerCatchPCs[i][j], cpHandlerClass);
    499                            exceptionTable.add(entry);
    500                        }
    501                    }
    502                    final CodeAttribute codeAttr = new CodeAttribute(maxStack, maxLocal, methodByteCodePacked[c][m],
    503                        segment, operandManager, exceptionTable);
    504                    final List<Attribute> methodAttributesList = methodAttributes[c][m];
    505                    // Make sure we add the code attribute in the right place
    506                    int indexForCodeAttr = 0;
    507                    for (final Attribute attribute : methodAttributesList) {
    508                        if (!(attribute instanceof NewAttribute)
    509                            || (((NewAttribute) attribute).getLayoutIndex() >= 15)) {
    510                            break;
    511                        }
    512                        indexForCodeAttr++;
    513                    }
    514                    methodAttributesList.add(indexForCodeAttr, codeAttr);
    515                    codeAttr.renumber(codeAttr.byteCodeOffsets);
    516                    List<Attribute> currentAttributes;
    517                    if (allCodeHasFlags) {
    518                        currentAttributes = orderedCodeAttributes.get(i);
    519                    } else if (codeHasFlags[i]) {
    520                        currentAttributes = orderedCodeAttributes.get(codeAttributeIndex);
    521                        codeAttributeIndex++;
    522                    } else {
    523                        currentAttributes = Collections.EMPTY_LIST;
    524                    }
    525                    for (final Attribute currentAttribute : currentAttributes) {
    526                        codeAttr.addAttribute(currentAttribute);
    527                        // Fix up the line numbers if needed
    528                        if (currentAttribute.hasBCIRenumbering()) {
    529                            ((BCIRenumberedAttribute) currentAttribute).renumber(codeAttr.byteCodeOffsets);
    530                        }
    531                    }
    532                    i++;
    533                }
    534            }
    535        }
    536    }
    537}