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}