Fork of the Fernflower decompiler
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
fernflower/src/org/jetbrains/java/decompiler/struct/StructMethod.java

390 lines
12 KiB

// Copyright 2000-2017 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license that can be found in the LICENSE file.
package org.jetbrains.java.decompiler.struct;
11 years ago
import org.jetbrains.java.decompiler.code.*;
import org.jetbrains.java.decompiler.struct.attr.StructGeneralAttribute;
import org.jetbrains.java.decompiler.struct.attr.StructLocalVariableTableAttribute;
import org.jetbrains.java.decompiler.struct.consts.ConstantPool;
import org.jetbrains.java.decompiler.util.DataInputFullStream;
import org.jetbrains.java.decompiler.util.VBStyleCollection;
11 years ago
import java.io.IOException;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
11 years ago
import static org.jetbrains.java.decompiler.code.CodeConstants.*;
11 years ago
/*
method_info {
u2 access_flags;
u2 name_index;
u2 descriptor_index;
u2 attributes_count;
attribute_info attributes[attributes_count];
}
11 years ago
*/
public class StructMethod extends StructMember {
private static final int[] opr_iconst = {-1, 0, 1, 2, 3, 4, 5};
private static final int[] opr_loadstore = {0, 1, 2, 3, 0, 1, 2, 3, 0, 1, 2, 3, 0, 1, 2, 3, 0, 1, 2, 3};
private static final int[] opcs_load = {opc_iload, opc_lload, opc_fload, opc_dload, opc_aload};
private static final int[] opcs_store = {opc_istore, opc_lstore, opc_fstore, opc_dstore, opc_astore};
private final StructClass classStruct;
private final String name;
private final String descriptor;
private boolean containsCode = false;
private int localVariables = 0;
private int codeLength = 0;
private int codeFullLength = 0;
private InstructionSequence seq;
private boolean expanded = false;
private Map<String, StructGeneralAttribute> codeAttributes;
public StructMethod(DataInputFullStream in, StructClass clStruct) throws IOException {
classStruct = clStruct;
accessFlags = in.readUnsignedShort();
int nameIndex = in.readUnsignedShort();
int descriptorIndex = in.readUnsignedShort();
ConstantPool pool = clStruct.getPool();
String[] values = pool.getClassElement(ConstantPool.METHOD, clStruct.qualifiedName, nameIndex, descriptorIndex);
name = values[0];
descriptor = values[1];
attributes = readAttributes(in, pool);
if (codeAttributes != null) {
attributes.putAll(codeAttributes);
codeAttributes = null;
}
}
@Override
protected StructGeneralAttribute readAttribute(DataInputFullStream in, ConstantPool pool, String name) throws IOException {
if (StructGeneralAttribute.ATTRIBUTE_CODE.equals(name)) {
if (!classStruct.isOwn()) {
// skip code in foreign classes
in.discard(8);
in.discard(in.readInt());
in.discard(8 * in.readUnsignedShort());
}
else {
containsCode = true;
in.discard(6);
localVariables = in.readUnsignedShort();
codeLength = in.readInt();
in.discard(codeLength);
int excLength = in.readUnsignedShort();
in.discard(excLength * 8);
codeFullLength = codeLength + excLength * 8 + 2;
}
codeAttributes = readAttributes(in, pool);
return null;
}
return super.readAttribute(in, pool, name);
}
public void expandData() throws IOException {
if (containsCode && !expanded) {
byte[] code = classStruct.getLoader().loadBytecode(this, codeFullLength);
seq = parseBytecode(new DataInputFullStream(code), codeLength, classStruct.getPool());
expanded = true;
}
}
public void releaseResources() {
if (containsCode && expanded) {
seq = null;
expanded = false;
}
}
@SuppressWarnings("AssignmentToForLoopParameter")
private InstructionSequence parseBytecode(DataInputFullStream in, int length, ConstantPool pool) throws IOException {
VBStyleCollection<Instruction, Integer> instructions = new VBStyleCollection<>();
int bytecode_version = classStruct.getBytecodeVersion();
for (int i = 0; i < length; ) {
int offset = i;
int opcode = in.readUnsignedByte();
int group = GROUP_GENERAL;
boolean wide = (opcode == opc_wide);
if (wide) {
i++;
opcode = in.readUnsignedByte();
}
List<Integer> operands = new ArrayList<>();
if (opcode >= opc_iconst_m1 && opcode <= opc_iconst_5) {
operands.add(opr_iconst[opcode - opc_iconst_m1]);
opcode = opc_bipush;
}
else if (opcode >= opc_iload_0 && opcode <= opc_aload_3) {
operands.add(opr_loadstore[opcode - opc_iload_0]);
opcode = opcs_load[(opcode - opc_iload_0) / 4];
}
else if (opcode >= opc_istore_0 && opcode <= opc_astore_3) {
operands.add(opr_loadstore[opcode - opc_istore_0]);
opcode = opcs_store[(opcode - opc_istore_0) / 4];
}
else {
switch (opcode) {
case opc_bipush:
operands.add((int)in.readByte());
i++;
break;
case opc_ldc:
case opc_newarray:
operands.add(in.readUnsignedByte());
i++;
break;
case opc_sipush:
case opc_ifeq:
case opc_ifne:
case opc_iflt:
case opc_ifge:
case opc_ifgt:
case opc_ifle:
case opc_if_icmpeq:
case opc_if_icmpne:
case opc_if_icmplt:
case opc_if_icmpge:
case opc_if_icmpgt:
case opc_if_icmple:
case opc_if_acmpeq:
case opc_if_acmpne:
case opc_goto:
case opc_jsr:
case opc_ifnull:
case opc_ifnonnull:
if (opcode != opc_sipush) {
group = GROUP_JUMP;
}
operands.add((int)in.readShort());
i += 2;
break;
case opc_ldc_w:
case opc_ldc2_w:
case opc_getstatic:
case opc_putstatic:
case opc_getfield:
case opc_putfield:
case opc_invokevirtual:
case opc_invokespecial:
case opc_invokestatic:
case opc_new:
case opc_anewarray:
case opc_checkcast:
case opc_instanceof:
operands.add(in.readUnsignedShort());
i += 2;
if (opcode >= opc_getstatic && opcode <= opc_putfield) {
group = GROUP_FIELDACCESS;
}
else if (opcode >= opc_invokevirtual && opcode <= opc_invokestatic) {
group = GROUP_INVOCATION;
}
break;
case opc_invokedynamic:
if (classStruct.isVersionGE_1_7()) { // instruction unused in Java 6 and before
operands.add(in.readUnsignedShort());
in.discard(2);
group = GROUP_INVOCATION;
i += 4;
}
break;
case opc_iload:
case opc_lload:
case opc_fload:
case opc_dload:
case opc_aload:
case opc_istore:
case opc_lstore:
case opc_fstore:
case opc_dstore:
case opc_astore:
case opc_ret:
if (wide) {
operands.add(in.readUnsignedShort());
i += 2;
}
else {
operands.add(in.readUnsignedByte());
i++;
}
if (opcode == opc_ret) {
group = GROUP_RETURN;
}
break;
case opc_iinc:
if (wide) {
operands.add(in.readUnsignedShort());
operands.add((int)in.readShort());
i += 4;
}
else {
operands.add(in.readUnsignedByte());
operands.add((int)in.readByte());
i += 2;
}
break;
case opc_goto_w:
case opc_jsr_w:
opcode = opcode == opc_jsr_w ? opc_jsr : opc_goto;
operands.add(in.readInt());
group = GROUP_JUMP;
i += 4;
break;
case opc_invokeinterface:
operands.add(in.readUnsignedShort());
operands.add(in.readUnsignedByte());
in.discard(1);
group = GROUP_INVOCATION;
i += 4;
break;
case opc_multianewarray:
operands.add(in.readUnsignedShort());
operands.add(in.readUnsignedByte());
i += 3;
break;
case opc_tableswitch:
in.discard((4 - (i + 1) % 4) % 4);
i += ((4 - (i + 1) % 4) % 4); // padding
operands.add(in.readInt());
i += 4;
int low = in.readInt();
operands.add(low);
i += 4;
int high = in.readInt();
operands.add(high);
i += 4;
for (int j = 0; j < high - low + 1; j++) {
operands.add(in.readInt());
i += 4;
}
group = GROUP_SWITCH;
break;
case opc_lookupswitch:
in.discard((4 - (i + 1) % 4) % 4);
i += ((4 - (i + 1) % 4) % 4); // padding
operands.add(in.readInt());
i += 4;
int npairs = in.readInt();
operands.add(npairs);
i += 4;
for (int j = 0; j < npairs; j++) {
operands.add(in.readInt());
i += 4;
operands.add(in.readInt());
i += 4;
}
group = GROUP_SWITCH;
break;
case opc_ireturn:
case opc_lreturn:
case opc_freturn:
case opc_dreturn:
case opc_areturn:
case opc_return:
case opc_athrow:
group = GROUP_RETURN;
}
}
int[] ops = null;
if (!operands.isEmpty()) {
ops = new int[operands.size()];
for (int j = 0; j < operands.size(); j++) {
ops[j] = operands.get(j);
}
}
Instruction instr = Instruction.create(opcode, wide, group, bytecode_version, ops);
instructions.addWithKey(instr, offset);
i++;
}
// initialize exception table
List<ExceptionHandler> lstHandlers = new ArrayList<>();
int exception_count = in.readUnsignedShort();
for (int i = 0; i < exception_count; i++) {
ExceptionHandler handler = new ExceptionHandler();
handler.from = in.readUnsignedShort();
handler.to = in.readUnsignedShort();
handler.handler = in.readUnsignedShort();
int excclass = in.readUnsignedShort();
if (excclass != 0) {
handler.exceptionClass = pool.getPrimitiveConstant(excclass).getString();
}
lstHandlers.add(handler);
}
InstructionSequence seq = new FullInstructionSequence(instructions, new ExceptionTable(lstHandlers));
// initialize instructions
int i = seq.length() - 1;
seq.setPointer(i);
while (i >= 0) {
Instruction instr = seq.getInstr(i--);
if (instr.group != GROUP_GENERAL) {
instr.initInstruction(seq);
}
seq.addToPointer(-1);
}
return seq;
}
public StructClass getClassStruct() {
return classStruct;
}
public String getName() {
return name;
}
public String getDescriptor() {
return descriptor;
}
public boolean containsCode() {
return containsCode;
}
public int getLocalVariables() {
return localVariables;
}
public InstructionSequence getInstructionSequence() {
return seq;
}
public StructLocalVariableTableAttribute getLocalVariableAttr() {
return (StructLocalVariableTableAttribute)getAttribute(StructGeneralAttribute.ATTRIBUTE_LOCAL_VARIABLE_TABLE);
}
@Override
public String toString() {
return name;
}
}