    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 wideByteCodes;
    074
    075    /**
    076     * @param segment TODO
    077     */
    078    public BcBands(final Segment segment) {
    079        super(segment);
    080    }
    081
    082    /*
    083     * (non-Javadoc)
    084     *
    085     * @see org.apache.commons.compress.harmony.unpack200.BandSet#unpack(java.io.InputStream)
    086     */
    087    @Override
    088    public void read(final InputStream in) throws IOException, Pack200Exception {
    089
    090        final AttributeLayoutMap attributeDefinitionMap = segment.getAttrDefinitionBands().getAttributeDefinitionMap();
    091        final int classCount = header.getClassCount();
    092        final long[][] methodFlags = segment.getClassBands().getMethodFlags();
    093
    094        int bcCaseCountCount = 0;
    095        int bcByteCount = 0;
    096        int bcShortCount = 0;
    097        int bcLocalCount = 0;
    098        int bcLabelCount = 0;
    099        int bcIntRefCount = 0;
    100        int bcFloatRefCount = 0;
    101        int bcLongRefCount = 0;
    102        int bcDoubleRefCount = 0;
    103        int bcStringRefCount = 0;
    104        int bcClassRefCount = 0;
    105        int bcFieldRefCount = 0;
    106        int bcMethodRefCount = 0;
    107        int bcIMethodRefCount = 0;
    108        int bcThisFieldCount = 0;
    109        int bcSuperFieldCount = 0;
    110        int bcThisMethodCount = 0;
    111        int bcSuperMethodCount = 0;
    112        int bcInitRefCount = 0;
    113        int bcEscCount = 0;
    114        int bcEscRefCount = 0;
    115
    116        final AttributeLayout abstractModifier = attributeDefinitionMap.getAttributeLayout(AttributeLayout.ACC_ABSTRACT,
    117            AttributeLayout.CONTEXT_METHOD);
    118        final AttributeLayout nativeModifier = attributeDefinitionMap.getAttributeLayout(AttributeLayout.ACC_NATIVE,
    119            AttributeLayout.CONTEXT_METHOD);
    120
    121        methodByteCodePacked = new byte[classCount][][];
    122        int bcParsed = 0;
    123
    124        final List switchIsTableSwitch = new ArrayList();
    125        wideByteCodes = new ArrayList();
    126        for (int c = 0; c < classCount; c++) {
    127            final int numberOfMethods = methodFlags[c].length;
    128            methodByteCodePacked[c] = new byte[numberOfMethods][];
    129            for (int m = 0; m < numberOfMethods; m++) {
    130                final long methodFlag = methodFlags[c][m];
    131                if (!abstractModifier.matches(methodFlag) && !nativeModifier.matches(methodFlag)) {
    132                    final ByteArrayOutputStream codeBytes = new ByteArrayOutputStream();
    133                    byte code;
    134                    while ((code = (byte) (0xff & in.read())) != -1) {
    135                        codeBytes.write(code);
    136                    }
    137                    methodByteCodePacked[c][m] = codeBytes.toByteArray();
    138                    bcParsed += methodByteCodePacked[c][m].length;
    139                    final int[] codes = new int[methodByteCodePacked[c][m].length];
    140                    for (int i = 0; i < codes.length; i++) {
    141                        codes[i] = methodByteCodePacked[c][m][i] & 0xff;
    142                    }
    143                    for (int i = 0; i < methodByteCodePacked[c][m].length; i++) {
    144                        final int codePacked = 0xff & methodByteCodePacked[c][m][i];
    145                        switch (codePacked) {
    146                        case 16: // bipush
    147                        case 188: // newarray
    148                            bcByteCount++;
    149                            break;
    150                        case 17: // sipush
    151                            bcShortCount++;
    152                            break;
    153                        case 18: // (a)ldc
    154                        case 19: // aldc_w
    155                            bcStringRefCount++;
    156                            break;
    157                        case 234: // ildc
    158                        case 237: // ildc_w
    159                            bcIntRefCount++;
    160                            break;
    161                        case 235: // fldc
    162                        case 238: // fldc_w
    163                            bcFloatRefCount++;
    164                            break;
    165                        case 197: // multianewarray
    166                            bcByteCount++;
    167                            // fallthrough intended
    168                        case 233: // cldc
    169                        case 236: // cldc_w
    170                        case 187: // new
    171                        case 189: // anewarray
    172                        case 192: // checkcast
    173                        case 193: // instanceof
    174                            bcClassRefCount++;
    175                            break;
    176                        case 20: // lldc2_w
    177                            bcLongRefCount++;
    178                            break;
    179                        case 239: // dldc2_w
    180                            bcDoubleRefCount++;
    181                            break;
    182                        case 169: // ret
    183                            bcLocalCount++;
    184                            break;
    185                        case 167: // goto
    186                        case 168: // jsr
    187                        case 200: // goto_w
    188                        case 201: // jsr_w
    189                            bcLabelCount++;
    190                            break;
    191                        case 170: // tableswitch
    192                            switchIsTableSwitch.add(Boolean.valueOf(true));
    193                            bcCaseCountCount++;
    194                            bcLabelCount++;
    195                            break;
    196                        case 171: // lookupswitch
    197                            switchIsTableSwitch.add(Boolean.valueOf(false));
    198                            bcCaseCountCount++;
    199                            bcLabelCount++;
    200                            break;
    201                        case 178: // getstatic
    202                        case 179: // putstatic
    203                        case 180: // getfield
    204                        case 181: // putfield
    205                            bcFieldRefCount++;
    206                            break;
    207                        case 182: // invokevirtual
    208                        case 183: // invokespecial
    209                        case 184: // invokestatic
    210                            bcMethodRefCount++;
    211                            break;
    212                        case 185: // invokeinterface
    213                            bcIMethodRefCount++;
    214                            break;
    215                        case 202: // getstatic_this
    216                        case 203: // putstatic_this
    217                        case 204: // getfield_this
    218                        case 205: // putfield_this
    219                        case 209: // aload_0_getstatic_this
    220                        case 210: // aload_0_putstatic_this
    221                        case 211: // aload_0_putfield_this
    222                        case 212: // aload_0_putfield_this
    223                            bcThisFieldCount++;
    224                            break;
    225                        case 206: // invokevirtual_this
    226                        case 207: // invokespecial_this
    227                        case 208: // invokestatic_this
    228                        case 213: // aload_0_invokevirtual_this
    229                        case 214: // aload_0_invokespecial_this
    230                        case 215: // aload_0_invokestatic_this
    231                            bcThisMethodCount++;
    232                            break;
    233                        case 216: // getstatic_super
    234                        case 217: // putstatic_super
    235                        case 218: // getfield_super
    236                        case 219: // putfield_super
    237                        case 223: // aload_0_getstatic_super
    238                        case 224: // aload_0_putstatic_super
    239                        case 225: // aload_0_getfield_super
    240                        case 226: // aload_0_putfield_super
    241                            bcSuperFieldCount++;
    242                            break;
    243                        case 220: // invokevirtual_super
    244                        case 221: // invokespecial_super
    245                        case 222: // invokestatic_super
    246                        case 227: // aload_0_invokevirtual_super
    247                        case 228: // aload_0_invokespecial_super
    248                        case 229: // aload_0_invokestatic_super
    249                            bcSuperMethodCount++;
    250                            break;
    251                        case 132: // iinc
    252                            bcLocalCount++;
    253                            bcByteCount++;
    254                            break;
    255                        case 196: // wide
    256                            final int nextInstruction = 0xff & methodByteCodePacked[c][m][i + 1];
    257                            wideByteCodes.add(Integer.valueOf(nextInstruction));
    258                            if (nextInstruction == 132) { // iinc
    259                                bcLocalCount++;
    260                                bcShortCount++;
    261                            } else if (endsWithLoad(nextInstruction) || endsWithStore(nextInstruction)
    262                                || nextInstruction == 169) {
    263                                bcLocalCount++;
    264                            } else {
    265                                segment.log(Segment.LOG_LEVEL_VERBOSE,
    266                                    "Found unhandled " + ByteCode.getByteCode(nextInstruction));
    267                            }
    268                            i++;
    269                            break;
    270                        case 230: // invokespecial_this_init
    271                        case 231: // invokespecial_super_init
    272                        case 232: // invokespecial_new_init
    273                            bcInitRefCount++;
    274                            break;
    275                        case 253: // ref_escape
    276                            bcEscRefCount++;
    277                            break;
    278                        case 254: // byte_escape
    279                            bcEscCount++;
    280                            break;
    281                        default:
    282                            if (endsWithLoad(codePacked) || endsWithStore(codePacked)) {
    283                                bcLocalCount++;
    284                            } else if (startsWithIf(codePacked)) {
    285                                bcLabelCount++;
    286                            }
    287                        }
    288                    }
    289                }
    290            }
    291        }
    292        // other bytecode bands
    293        bcCaseCount = decodeBandInt("bc_case_count", in, Codec.UNSIGNED5, bcCaseCountCount);
    294        int bcCaseValueCount = 0;
    295        for (int i = 0; i < bcCaseCount.length; i++) {
    296            final boolean isTableSwitch = ((Boolean) switchIsTableSwitch.get(i)).booleanValue();
    297            if (isTableSwitch) {
    298                bcCaseValueCount += 1;
    299            } else {
    300                bcCaseValueCount += bcCaseCount[i];
    301            }
    302        }
    303        bcCaseValue = decodeBandInt("bc_case_value", in, Codec.DELTA5, bcCaseValueCount);
    304        // Every case value needs a label. We weren't able to count these
    305        // above, because we didn't know how many cases there were.
    306        // Have to correct it now.
    307        for (int index = 0; index < bcCaseCountCount; index++) {
    308            bcLabelCount += bcCaseCount[index];
    309        }
    310        bcByte = decodeBandInt("bc_byte", in, Codec.BYTE1, bcByteCount);
    311        bcShort = decodeBandInt("bc_short", in, Codec.DELTA5, bcShortCount);
    312        bcLocal = decodeBandInt("bc_local", in, Codec.UNSIGNED5, bcLocalCount);
    313        bcLabel = decodeBandInt("bc_label", in, Codec.BRANCH5, bcLabelCount);
    314        bcIntRef = decodeBandInt("bc_intref", in, Codec.DELTA5, bcIntRefCount);
    315        bcFloatRef = decodeBandInt("bc_floatref", in, Codec.DELTA5, bcFloatRefCount);
    316        bcLongRef = decodeBandInt("bc_longref", in, Codec.DELTA5, bcLongRefCount);
    317        bcDoubleRef = decodeBandInt("bc_doubleref", in, Codec.DELTA5, bcDoubleRefCount);
    318        bcStringRef = decodeBandInt("bc_stringref", in, Codec.DELTA5, bcStringRefCount);
    319        bcClassRef = decodeBandInt("bc_classref", in, Codec.UNSIGNED5, bcClassRefCount);
    320        bcFieldRef = decodeBandInt("bc_fieldref", in, Codec.DELTA5, bcFieldRefCount);
    321        bcMethodRef = decodeBandInt("bc_methodref", in, Codec.UNSIGNED5, bcMethodRefCount);
    322        bcIMethodRef = decodeBandInt("bc_imethodref", in, Codec.DELTA5, bcIMethodRefCount);
    323        bcThisField = decodeBandInt("bc_thisfield", in, Codec.UNSIGNED5, bcThisFieldCount);
    324        bcSuperField = decodeBandInt("bc_superfield", in, Codec.UNSIGNED5, bcSuperFieldCount);
    325        bcThisMethod = decodeBandInt("bc_thismethod", in, Codec.UNSIGNED5, bcThisMethodCount);
    326        bcSuperMethod = decodeBandInt("bc_supermethod", in, Codec.UNSIGNED5, bcSuperMethodCount);
    327        bcInitRef = decodeBandInt("bc_initref", in, Codec.UNSIGNED5, bcInitRefCount);
    328        bcEscRef = decodeBandInt("bc_escref", in, Codec.UNSIGNED5, bcEscRefCount);
    329        bcEscRefSize = decodeBandInt("bc_escrefsize", in, Codec.UNSIGNED5, bcEscRefCount);
    330        bcEscSize = decodeBandInt("bc_escsize", in, Codec.UNSIGNED5, bcEscCount);
    331        bcEscByte = decodeBandInt("bc_escbyte", in, Codec.BYTE1, bcEscSize);
    332    }
    333
    334    @Override
    335    public void unpack() throws Pack200Exception {
    336        final int classCount = header.getClassCount();
    337        final long[][] methodFlags = segment.getClassBands().getMethodFlags();
    338        final int[] codeMaxNALocals = segment.getClassBands().getCodeMaxNALocals();
    339        final int[] codeMaxStack = segment.getClassBands().getCodeMaxStack();
    340        final ArrayList[][] methodAttributes = segment.getClassBands().getMethodAttributes();
    341        final String[][] methodDescr = segment.getClassBands().getMethodDescr();
    342
    343        final AttributeLayoutMap attributeDefinitionMap = segment.getAttrDefinitionBands().getAttributeDefinitionMap();
    344
    345        final AttributeLayout abstractModifier = attributeDefinitionMap.getAttributeLayout(AttributeLayout.ACC_ABSTRACT,
    346            AttributeLayout.CONTEXT_METHOD);
    347        final AttributeLayout nativeModifier = attributeDefinitionMap.getAttributeLayout(AttributeLayout.ACC_NATIVE,
    348            AttributeLayout.CONTEXT_METHOD);
    349        final AttributeLayout staticModifier = attributeDefinitionMap.getAttributeLayout(AttributeLayout.ACC_STATIC,
    350            AttributeLayout.CONTEXT_METHOD);
    351
    352        final int[] wideByteCodeArray = new int[wideByteCodes.size()];
    353        for (int index = 0; index < wideByteCodeArray.length; index++) {
    354            wideByteCodeArray[index] = ((Integer) wideByteCodes.get(index)).intValue();
    355        }
    356        final OperandManager operandManager = new OperandManager(bcCaseCount, bcCaseValue, bcByte, bcShort, bcLocal,
    357            bcLabel, bcIntRef, bcFloatRef, bcLongRef, bcDoubleRef, bcStringRef, bcClassRef, bcFieldRef, bcMethodRef,
    358            bcIMethodRef, bcThisField, bcSuperField, bcThisMethod, bcSuperMethod, bcInitRef, wideByteCodeArray);
    359        operandManager.setSegment(segment);
    360
    361        int i = 0;
    362        final ArrayList orderedCodeAttributes = segment.getClassBands().getOrderedCodeAttributes();
    363        int codeAttributeIndex = 0;
    364
    365        // Exception table fields
    366        final int[] handlerCount = segment.getClassBands().getCodeHandlerCount();
    367        final int[][] handlerStartPCs = segment.getClassBands().getCodeHandlerStartP();
    368        final int[][] handlerEndPCs = segment.getClassBands().getCodeHandlerEndPO();
    369        final int[][] handlerCatchPCs = segment.getClassBands().getCodeHandlerCatchPO();
    370        final int[][] handlerClassTypes = segment.getClassBands().getCodeHandlerClassRCN();
    371
    372        final boolean allCodeHasFlags = segment.getSegmentHeader().getOptions().hasAllCodeFlags();
    373        final boolean[] codeHasFlags = segment.getClassBands().getCodeHasAttributes();
    374
    375        for (int c = 0; c < classCount; c++) {
    376            final int numberOfMethods = methodFlags[c].length;
    377            for (int m = 0; m < numberOfMethods; m++) {
    378                final long methodFlag = methodFlags[c][m];
    379                if (!abstractModifier.matches(methodFlag) && !nativeModifier.matches(methodFlag)) {
    380                    final int maxStack = codeMaxStack[i];
    381                    int maxLocal = codeMaxNALocals[i];
    382                    if (!staticModifier.matches(methodFlag)) {
    383                        maxLocal++; // one for 'this' parameter
    384                    }
    385                    // I believe this has to take wide arguments into account
    386                    maxLocal += SegmentUtils.countInvokeInterfaceArgs(methodDescr[c][m]);
    387                    final String[] cpClass = segment.getCpBands().getCpClass();
    388                    operandManager.setCurrentClass(cpClass[segment.getClassBands().getClassThisInts()[c]]);
    389                    operandManager.setSuperClass(cpClass[segment.getClassBands().getClassSuperInts()[c]]);
    390                    final List exceptionTable = new ArrayList();
    391                    if (handlerCount != null) {
    392                        for (int j = 0; j < handlerCount[i]; j++) {
    393                            final int handlerClass = handlerClassTypes[i][j] - 1;
    394                            CPClass cpHandlerClass = null;
    395                            if (handlerClass != -1) {
    396                                // The handlerClass will be null if the
    397                                // catch is a finally (that is, the
    398                                // exception table catch_type should be 0
    399                                cpHandlerClass = segment.getCpBands().cpClassValue(handlerClass);
    400                            }
    401                            final ExceptionTableEntry entry = new ExceptionTableEntry(handlerStartPCs[i][j],
    402                                handlerEndPCs[i][j], handlerCatchPCs[i][j], cpHandlerClass);
    403                            exceptionTable.add(entry);
    404                        }
    405                    }
    406                    final CodeAttribute codeAttr = new CodeAttribute(maxStack, maxLocal, methodByteCodePacked[c][m],
    407                        segment, operandManager, exceptionTable);
    408                    final ArrayList methodAttributesList = methodAttributes[c][m];
    409                    // Make sure we add the code attribute in the right place
    410                    int indexForCodeAttr = 0;
    411                    for (int index = 0; index < methodAttributesList.size(); index++) {
    412                        final Attribute attribute = (Attribute) methodAttributesList.get(index);
    413                        if (!(attribute instanceof NewAttribute)
    414                            || (((NewAttribute) attribute).getLayoutIndex() >= 15)) {
    415                            break;
    416                        }
    417                        indexForCodeAttr++;
    418                    }
    419                    methodAttributesList.add(indexForCodeAttr, codeAttr);
    420                    codeAttr.renumber(codeAttr.byteCodeOffsets);
    421                    List currentAttributes;
    422                    if (allCodeHasFlags) {
    423                        currentAttributes = (List) orderedCodeAttributes.get(i);
    424                    } else if (codeHasFlags[i]) {
    425                        currentAttributes = (List) orderedCodeAttributes.get(codeAttributeIndex);
    426                        codeAttributeIndex++;
    427                    } else {
    428                        currentAttributes = Collections.EMPTY_LIST;
    429                    }
    430                    for (int index = 0; index < currentAttributes.size(); index++) {
    431                        final Attribute currentAttribute = (Attribute) currentAttributes.get(index);
    432                        codeAttr.addAttribute(currentAttribute);
    433                        // Fix up the line numbers if needed
    434                        if (currentAttribute.hasBCIRenumbering()) {
    435                            ((BCIRenumberedAttribute) currentAttribute).renumber(codeAttr.byteCodeOffsets);
    436                        }
    437                    }
    438                    i++;
    439                }
    440            }
    441        }
    442    }
    443
    444    private boolean startsWithIf(final int codePacked) {
    445        return (codePacked >= 153 && codePacked <= 166) || (codePacked == 198) || (codePacked == 199);
    446    }
    447
    448    private boolean endsWithLoad(final int codePacked) {
    449        return (codePacked >= 21 && codePacked <= 25);
    450    }
    451
    452    private boolean endsWithStore(final int codePacked) {
    453        return (codePacked >= 54 && codePacked <= 58);
    454    }
    455
    456    public byte[][][] getMethodByteCodePacked() {
    457        return methodByteCodePacked;
    458    }
    459
    460    public int[] getBcCaseCount() {
    461        return bcCaseCount;
    462    }
    463
    464    public int[] getBcCaseValue() {
    465        return bcCaseValue;
    466    }
    467
    468    public int[] getBcByte() {
    469        return bcByte;
    470    }
    471
    472    public int[] getBcClassRef() {
    473        return bcClassRef;
    474    }
    475
    476    public int[] getBcDoubleRef() {
    477        return bcDoubleRef;
    478    }
    479
    480    public int[] getBcFieldRef() {
    481        return bcFieldRef;
    482    }
    483
    484    public int[] getBcFloatRef() {
    485        return bcFloatRef;
    486    }
    487
    488    public int[] getBcIMethodRef() {
    489        return bcIMethodRef;
    490    }
    491
    492    public int[] getBcInitRef() {
    493        return bcInitRef;
    494    }
    495
    496    public int[] getBcIntRef() {
    497        return bcIntRef;
    498    }
    499
    500    public int[] getBcLabel() {
    501        return bcLabel;
    502    }
    503
    504    public int[] getBcLocal() {
    505        return bcLocal;
    506    }
    507
    508    public int[] getBcLongRef() {
    509        return bcLongRef;
    510    }
    511
    512    public int[] getBcMethodRef() {
    513        return bcMethodRef;
    514    }
    515
    516    public int[] getBcShort() {
    517        return bcShort;
    518    }
    519
    520    public int[] getBcStringRef() {
    521        return bcStringRef;
    522    }
    523
    524    public int[] getBcSuperField() {
    525        return bcSuperField;
    526    }
    527
    528    public int[] getBcSuperMethod() {
    529        return bcSuperMethod;
    530    }
    531
    532    public int[] getBcThisField() {
    533        return bcThisField;
    534    }
    535
    536    public int[] getBcThisMethod() {
    537        return bcThisMethod;
    538    }
    539}