BcBands.java
/*
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership.
* The ASF licenses this file to You under the Apache License, Version 2.0
* (the "License"); you may not use this file except in compliance with
* the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.apache.commons.compress.harmony.unpack200;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import org.apache.commons.compress.harmony.pack200.Codec;
import org.apache.commons.compress.harmony.pack200.Pack200Exception;
import org.apache.commons.compress.harmony.unpack200.bytecode.Attribute;
import org.apache.commons.compress.harmony.unpack200.bytecode.BCIRenumberedAttribute;
import org.apache.commons.compress.harmony.unpack200.bytecode.ByteCode;
import org.apache.commons.compress.harmony.unpack200.bytecode.CPClass;
import org.apache.commons.compress.harmony.unpack200.bytecode.CodeAttribute;
import org.apache.commons.compress.harmony.unpack200.bytecode.ExceptionTableEntry;
import org.apache.commons.compress.harmony.unpack200.bytecode.NewAttribute;
import org.apache.commons.compress.harmony.unpack200.bytecode.OperandManager;
/**
* Bytecode bands
*/
public class BcBands extends BandSet {
// The bytecodes for each method in each class as they come (i.e. in their
// packed format)
private byte[][][] methodByteCodePacked;
// The bands
// TODO: Haven't resolved references yet. Do we want to?
private int[] bcCaseCount;
private int[] bcCaseValue;
private int[] bcByte;
private int[] bcLocal;
private int[] bcShort;
private int[] bcLabel;
private int[] bcIntRef;
private int[] bcFloatRef;
private int[] bcLongRef;
private int[] bcDoubleRef;
private int[] bcStringRef;
private int[] bcClassRef;
private int[] bcFieldRef;
private int[] bcMethodRef;
private int[] bcIMethodRef;
private int[] bcThisField;
private int[] bcSuperField;
private int[] bcThisMethod;
private int[] bcSuperMethod;
private int[] bcInitRef;
private int[] bcEscRef;
private int[] bcEscRefSize;
private int[] bcEscSize;
private int[][] bcEscByte;
private List<Integer> wideByteCodes;
/**
* @param segment TODO
*/
public BcBands(final Segment segment) {
super(segment);
}
private boolean endsWithLoad(final int codePacked) {
return codePacked >= 21 && codePacked <= 25;
}
private boolean endsWithStore(final int codePacked) {
return codePacked >= 54 && codePacked <= 58;
}
public int[] getBcByte() {
return bcByte;
}
public int[] getBcCaseCount() {
return bcCaseCount;
}
public int[] getBcCaseValue() {
return bcCaseValue;
}
public int[] getBcClassRef() {
return bcClassRef;
}
public int[] getBcDoubleRef() {
return bcDoubleRef;
}
public int[] getBcFieldRef() {
return bcFieldRef;
}
public int[] getBcFloatRef() {
return bcFloatRef;
}
public int[] getBcIMethodRef() {
return bcIMethodRef;
}
public int[] getBcInitRef() {
return bcInitRef;
}
public int[] getBcIntRef() {
return bcIntRef;
}
public int[] getBcLabel() {
return bcLabel;
}
public int[] getBcLocal() {
return bcLocal;
}
public int[] getBcLongRef() {
return bcLongRef;
}
public int[] getBcMethodRef() {
return bcMethodRef;
}
public int[] getBcShort() {
return bcShort;
}
public int[] getBcStringRef() {
return bcStringRef;
}
public int[] getBcSuperField() {
return bcSuperField;
}
public int[] getBcSuperMethod() {
return bcSuperMethod;
}
public int[] getBcThisField() {
return bcThisField;
}
public int[] getBcThisMethod() {
return bcThisMethod;
}
public byte[][][] getMethodByteCodePacked() {
return methodByteCodePacked;
}
/*
* (non-Javadoc)
*
* @see org.apache.commons.compress.harmony.unpack200.BandSet#unpack(java.io.InputStream)
*/
@Override
public void read(final InputStream in) throws IOException, Pack200Exception {
final AttributeLayoutMap attributeDefinitionMap = segment.getAttrDefinitionBands().getAttributeDefinitionMap();
final int classCount = header.getClassCount();
final long[][] methodFlags = segment.getClassBands().getMethodFlags();
int bcCaseCountCount = 0;
int bcByteCount = 0;
int bcShortCount = 0;
int bcLocalCount = 0;
int bcLabelCount = 0;
int bcIntRefCount = 0;
int bcFloatRefCount = 0;
int bcLongRefCount = 0;
int bcDoubleRefCount = 0;
int bcStringRefCount = 0;
int bcClassRefCount = 0;
int bcFieldRefCount = 0;
int bcMethodRefCount = 0;
int bcIMethodRefCount = 0;
int bcThisFieldCount = 0;
int bcSuperFieldCount = 0;
int bcThisMethodCount = 0;
int bcSuperMethodCount = 0;
int bcInitRefCount = 0;
int bcEscCount = 0;
int bcEscRefCount = 0;
final AttributeLayout abstractModifier = attributeDefinitionMap.getAttributeLayout(AttributeLayout.ACC_ABSTRACT, AttributeLayout.CONTEXT_METHOD);
final AttributeLayout nativeModifier = attributeDefinitionMap.getAttributeLayout(AttributeLayout.ACC_NATIVE, AttributeLayout.CONTEXT_METHOD);
methodByteCodePacked = new byte[classCount][][];
final List<Boolean> switchIsTableSwitch = new ArrayList<>();
wideByteCodes = new ArrayList<>();
for (int c = 0; c < classCount; c++) {
final int numberOfMethods = methodFlags[c].length;
methodByteCodePacked[c] = new byte[numberOfMethods][];
for (int m = 0; m < numberOfMethods; m++) {
final long methodFlag = methodFlags[c][m];
if (!abstractModifier.matches(methodFlag) && !nativeModifier.matches(methodFlag)) {
final ByteArrayOutputStream codeBytes = new ByteArrayOutputStream();
byte code;
while ((code = (byte) (0xff & in.read())) != -1) {
codeBytes.write(code);
}
methodByteCodePacked[c][m] = codeBytes.toByteArray();
final int[] codes = new int[methodByteCodePacked[c][m].length];
for (int i = 0; i < codes.length; i++) {
codes[i] = methodByteCodePacked[c][m][i] & 0xff;
}
for (int i = 0; i < methodByteCodePacked[c][m].length; i++) {
final int codePacked = 0xff & methodByteCodePacked[c][m][i];
switch (codePacked) {
case 16: // bipush
case 188: // newarray
bcByteCount++;
break;
case 17: // sipush
bcShortCount++;
break;
case 18: // (a)ldc
case 19: // aldc_w
bcStringRefCount++;
break;
case 234: // ildc
case 237: // ildc_w
bcIntRefCount++;
break;
case 235: // fldc
case 238: // fldc_w
bcFloatRefCount++;
break;
case 197: // multianewarray
bcByteCount++;
// fallthrough intended
case 233: // cldc
case 236: // cldc_w
case 187: // new
case 189: // anewarray
case 192: // checkcast
case 193: // instanceof
bcClassRefCount++;
break;
case 20: // lldc2_w
bcLongRefCount++;
break;
case 239: // dldc2_w
bcDoubleRefCount++;
break;
case 169: // ret
bcLocalCount++;
break;
case 167: // goto
case 168: // jsr
case 200: // goto_w
case 201: // jsr_w
bcLabelCount++;
break;
case 170: // tableswitch
switchIsTableSwitch.add(Boolean.TRUE);
bcCaseCountCount++;
bcLabelCount++;
break;
case 171: // lookupswitch
switchIsTableSwitch.add(Boolean.FALSE);
bcCaseCountCount++;
bcLabelCount++;
break;
case 178: // getstatic
case 179: // putstatic
case 180: // getfield
case 181: // putfield
bcFieldRefCount++;
break;
case 182: // invokevirtual
case 183: // invokespecial
case 184: // invokestatic
bcMethodRefCount++;
break;
case 185: // invokeinterface
bcIMethodRefCount++;
break;
case 202: // getstatic_this
case 203: // putstatic_this
case 204: // getfield_this
case 205: // putfield_this
case 209: // aload_0_getstatic_this
case 210: // aload_0_putstatic_this
case 211: // aload_0_putfield_this
case 212: // aload_0_putfield_this
bcThisFieldCount++;
break;
case 206: // invokevirtual_this
case 207: // invokespecial_this
case 208: // invokestatic_this
case 213: // aload_0_invokevirtual_this
case 214: // aload_0_invokespecial_this
case 215: // aload_0_invokestatic_this
bcThisMethodCount++;
break;
case 216: // getstatic_super
case 217: // putstatic_super
case 218: // getfield_super
case 219: // putfield_super
case 223: // aload_0_getstatic_super
case 224: // aload_0_putstatic_super
case 225: // aload_0_getfield_super
case 226: // aload_0_putfield_super
bcSuperFieldCount++;
break;
case 220: // invokevirtual_super
case 221: // invokespecial_super
case 222: // invokestatic_super
case 227: // aload_0_invokevirtual_super
case 228: // aload_0_invokespecial_super
case 229: // aload_0_invokestatic_super
bcSuperMethodCount++;
break;
case 132: // iinc
bcLocalCount++;
bcByteCount++;
break;
case 196: // wide
final int nextInstruction = 0xff & methodByteCodePacked[c][m][i + 1];
wideByteCodes.add(Integer.valueOf(nextInstruction));
if (nextInstruction == 132) { // iinc
bcLocalCount++;
bcShortCount++;
} else if (endsWithLoad(nextInstruction) || endsWithStore(nextInstruction) || nextInstruction == 169) {
bcLocalCount++;
} else {
segment.log(Segment.LOG_LEVEL_VERBOSE, "Found unhandled " + ByteCode.getByteCode(nextInstruction));
}
i++;
break;
case 230: // invokespecial_this_init
case 231: // invokespecial_super_init
case 232: // invokespecial_new_init
bcInitRefCount++;
break;
case 253: // ref_escape
bcEscRefCount++;
break;
case 254: // byte_escape
bcEscCount++;
break;
default:
if (endsWithLoad(codePacked) || endsWithStore(codePacked)) {
bcLocalCount++;
} else if (startsWithIf(codePacked)) {
bcLabelCount++;
}
}
}
}
}
}
// other bytecode bands
bcCaseCount = decodeBandInt("bc_case_count", in, Codec.UNSIGNED5, bcCaseCountCount);
int bcCaseValueCount = 0;
for (int i = 0; i < bcCaseCount.length; i++) {
final boolean isTableSwitch = switchIsTableSwitch.get(i).booleanValue();
if (isTableSwitch) {
bcCaseValueCount += 1;
} else {
bcCaseValueCount += bcCaseCount[i];
}
}
bcCaseValue = decodeBandInt("bc_case_value", in, Codec.DELTA5, bcCaseValueCount);
// Every case value needs a label. We weren't able to count these
// above, because we didn't know how many cases there were.
// Have to correct it now.
for (int index = 0; index < bcCaseCountCount; index++) {
bcLabelCount += bcCaseCount[index];
}
bcByte = decodeBandInt("bc_byte", in, Codec.BYTE1, bcByteCount);
bcShort = decodeBandInt("bc_short", in, Codec.DELTA5, bcShortCount);
bcLocal = decodeBandInt("bc_local", in, Codec.UNSIGNED5, bcLocalCount);
bcLabel = decodeBandInt("bc_label", in, Codec.BRANCH5, bcLabelCount);
bcIntRef = decodeBandInt("bc_intref", in, Codec.DELTA5, bcIntRefCount);
bcFloatRef = decodeBandInt("bc_floatref", in, Codec.DELTA5, bcFloatRefCount);
bcLongRef = decodeBandInt("bc_longref", in, Codec.DELTA5, bcLongRefCount);
bcDoubleRef = decodeBandInt("bc_doubleref", in, Codec.DELTA5, bcDoubleRefCount);
bcStringRef = decodeBandInt("bc_stringref", in, Codec.DELTA5, bcStringRefCount);
bcClassRef = decodeBandInt("bc_classref", in, Codec.UNSIGNED5, bcClassRefCount);
bcFieldRef = decodeBandInt("bc_fieldref", in, Codec.DELTA5, bcFieldRefCount);
bcMethodRef = decodeBandInt("bc_methodref", in, Codec.UNSIGNED5, bcMethodRefCount);
bcIMethodRef = decodeBandInt("bc_imethodref", in, Codec.DELTA5, bcIMethodRefCount);
bcThisField = decodeBandInt("bc_thisfield", in, Codec.UNSIGNED5, bcThisFieldCount);
bcSuperField = decodeBandInt("bc_superfield", in, Codec.UNSIGNED5, bcSuperFieldCount);
bcThisMethod = decodeBandInt("bc_thismethod", in, Codec.UNSIGNED5, bcThisMethodCount);
bcSuperMethod = decodeBandInt("bc_supermethod", in, Codec.UNSIGNED5, bcSuperMethodCount);
bcInitRef = decodeBandInt("bc_initref", in, Codec.UNSIGNED5, bcInitRefCount);
bcEscRef = decodeBandInt("bc_escref", in, Codec.UNSIGNED5, bcEscRefCount);
bcEscRefSize = decodeBandInt("bc_escrefsize", in, Codec.UNSIGNED5, bcEscRefCount);
bcEscSize = decodeBandInt("bc_escsize", in, Codec.UNSIGNED5, bcEscCount);
bcEscByte = decodeBandInt("bc_escbyte", in, Codec.BYTE1, bcEscSize);
}
private boolean startsWithIf(final int codePacked) {
return codePacked >= 153 && codePacked <= 166 || codePacked == 198 || codePacked == 199;
}
@Override
public void unpack() throws Pack200Exception {
final int classCount = header.getClassCount();
final long[][] methodFlags = segment.getClassBands().getMethodFlags();
final int[] codeMaxNALocals = segment.getClassBands().getCodeMaxNALocals();
final int[] codeMaxStack = segment.getClassBands().getCodeMaxStack();
final ArrayList<Attribute>[][] methodAttributes = segment.getClassBands().getMethodAttributes();
final String[][] methodDescr = segment.getClassBands().getMethodDescr();
final AttributeLayoutMap attributeDefinitionMap = segment.getAttrDefinitionBands().getAttributeDefinitionMap();
final AttributeLayout abstractModifier = attributeDefinitionMap.getAttributeLayout(AttributeLayout.ACC_ABSTRACT, AttributeLayout.CONTEXT_METHOD);
final AttributeLayout nativeModifier = attributeDefinitionMap.getAttributeLayout(AttributeLayout.ACC_NATIVE, AttributeLayout.CONTEXT_METHOD);
final AttributeLayout staticModifier = attributeDefinitionMap.getAttributeLayout(AttributeLayout.ACC_STATIC, AttributeLayout.CONTEXT_METHOD);
final int[] wideByteCodeArray = new int[wideByteCodes.size()];
for (int index = 0; index < wideByteCodeArray.length; index++) {
wideByteCodeArray[index] = wideByteCodes.get(index).intValue();
}
final OperandManager operandManager = new OperandManager(bcCaseCount, bcCaseValue, bcByte, bcShort, bcLocal, bcLabel, bcIntRef, bcFloatRef, bcLongRef,
bcDoubleRef, bcStringRef, bcClassRef, bcFieldRef, bcMethodRef, bcIMethodRef, bcThisField, bcSuperField, bcThisMethod, bcSuperMethod, bcInitRef,
wideByteCodeArray);
operandManager.setSegment(segment);
int i = 0;
final ArrayList<List<Attribute>> orderedCodeAttributes = segment.getClassBands().getOrderedCodeAttributes();
int codeAttributeIndex = 0;
// Exception table fields
final int[] handlerCount = segment.getClassBands().getCodeHandlerCount();
final int[][] handlerStartPCs = segment.getClassBands().getCodeHandlerStartP();
final int[][] handlerEndPCs = segment.getClassBands().getCodeHandlerEndPO();
final int[][] handlerCatchPCs = segment.getClassBands().getCodeHandlerCatchPO();
final int[][] handlerClassTypes = segment.getClassBands().getCodeHandlerClassRCN();
final boolean allCodeHasFlags = segment.getSegmentHeader().getOptions().hasAllCodeFlags();
final boolean[] codeHasFlags = segment.getClassBands().getCodeHasAttributes();
for (int c = 0; c < classCount; c++) {
final int numberOfMethods = methodFlags[c].length;
for (int m = 0; m < numberOfMethods; m++) {
final long methodFlag = methodFlags[c][m];
if (!abstractModifier.matches(methodFlag) && !nativeModifier.matches(methodFlag)) {
final int maxStack = codeMaxStack[i];
int maxLocal = codeMaxNALocals[i];
if (!staticModifier.matches(methodFlag)) {
maxLocal++; // one for 'this' parameter
}
// I believe this has to take wide arguments into account
maxLocal += SegmentUtils.countInvokeInterfaceArgs(methodDescr[c][m]);
final String[] cpClass = segment.getCpBands().getCpClass();
operandManager.setCurrentClass(cpClass[segment.getClassBands().getClassThisInts()[c]]);
operandManager.setSuperClass(cpClass[segment.getClassBands().getClassSuperInts()[c]]);
final List<ExceptionTableEntry> exceptionTable = new ArrayList<>();
if (handlerCount != null) {
for (int j = 0; j < handlerCount[i]; j++) {
final int handlerClass = handlerClassTypes[i][j] - 1;
CPClass cpHandlerClass = null;
if (handlerClass != -1) {
// The handlerClass will be null if the
// catch is a finally (that is, the
// exception table catch_type should be 0
cpHandlerClass = segment.getCpBands().cpClassValue(handlerClass);
}
final ExceptionTableEntry entry = new ExceptionTableEntry(handlerStartPCs[i][j], handlerEndPCs[i][j], handlerCatchPCs[i][j],
cpHandlerClass);
exceptionTable.add(entry);
}
}
final CodeAttribute codeAttr = new CodeAttribute(maxStack, maxLocal, methodByteCodePacked[c][m], segment, operandManager, exceptionTable);
final List<Attribute> methodAttributesList = methodAttributes[c][m];
// Make sure we add the code attribute in the right place
int indexForCodeAttr = 0;
for (final Attribute attribute : methodAttributesList) {
if (!(attribute instanceof NewAttribute) || ((NewAttribute) attribute).getLayoutIndex() >= 15) {
break;
}
indexForCodeAttr++;
}
methodAttributesList.add(indexForCodeAttr, codeAttr);
codeAttr.renumber(codeAttr.byteCodeOffsets);
List<Attribute> currentAttributes;
if (allCodeHasFlags) {
currentAttributes = orderedCodeAttributes.get(i);
} else if (codeHasFlags[i]) {
currentAttributes = orderedCodeAttributes.get(codeAttributeIndex);
codeAttributeIndex++;
} else {
currentAttributes = Collections.EMPTY_LIST;
}
for (final Attribute currentAttribute : currentAttributes) {
codeAttr.addAttribute(currentAttribute);
// Fix up the line numbers if needed
if (currentAttribute.hasBCIRenumbering()) {
((BCIRenumberedAttribute) currentAttribute).renumber(codeAttr.byteCodeOffsets);
}
}
i++;
}
}
}
}
}