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}