/*
* Copyright (C) 2012 Sony Mobile Communications AB
*
* This file is part of ApkAnalyser.
*
* Licensed 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 mereflect.bytecode;
import java.util.HashMap;
import java.util.Map;
import mereflect.CorruptBytecodeException;
import mereflect.MEMethod;
public class Bytecode
{
protected int m_bytecode;
protected int m_index;
protected int m_bcIndex;
protected int m_len;
protected String m_bcString;
/**
* Returns index of the bytecode in bytecode-array
* @return
*/
public int getBytecodeIndex()
{
return m_bcIndex;
}
/**
* Returns textual description of bytecode
* @return
*/
public String getBytecodeOpString()
{
return m_bcString;
}
/**
* Returns the bytecode
* @return
*/
public int getBytecode()
{
return m_bytecode;
}
/**
* Returns index of the bytecode in a byte-array
* @return
*/
public int getIndex()
{
return m_index;
}
/**
* Returns length of bytecode in bytes
* @return
*/
public int getLength()
{
return m_len;
}
public static int getBytecodeIndexForOffset(MEMethod method, int offset)
throws CorruptBytecodeException {
int pc = 0;
int index = 0;
byte[] code = method.getByteCodes();
if (code != null)
{
while (pc < code.length && pc < offset)
{
int bytecode = (code[pc] & 0xff);
int len = Bytecodes.BC_LENGTHS[bytecode];
if (bytecode == 170) // tableswitch
{
len = ((Integer) Bytecode.getTableSwitch(code, pc).get(Bytecode.SWITCH_OP_LENGTH)).intValue();
}
else if (bytecode == 171) // lookupswitch
{
len = ((Integer) Bytecode.getLookupSwitch(code, pc).get(Bytecode.SWITCH_OP_LENGTH)).intValue();
}
if (len < 0 || pc + len > code.length)
{
throw new CorruptBytecodeException(pc, len, bytecode);
}
pc += len;
index++;
}
}
return index;
}
public static final String SWITCH_OP_LENGTH = "switch_len";
public static final String SWITCH_DEFAULT = "default";
public static final String SWITCH_LOW = "low";
public static final String SWITCH_HIGH = "high";
public static final String SWITCH_PAIRS = "pairs";
public static class Pair
{
public int vCase;
public int vJump;
public Pair(int vCase, int vJump)
{
this.vCase = vCase;
this.vJump = vJump;
}
}
public static Map<Object, Object> getTableSwitch(byte[] code, int pc)
{
Map<Object, Object> map = new HashMap<Object, Object>();
int oldPc = pc;
pc++;
int padding = 4 - (pc & 3);
if (padding == 4) {
padding = 0;
}
pc += padding;
int vDefault = read32(code, pc);
pc += 4;
map.put(SWITCH_DEFAULT, new Integer(vDefault));
int vLow = read32(code, pc);
pc += 4;
map.put(SWITCH_LOW, new Integer(vLow));
int vHigh = read32(code, pc);
pc += 4;
map.put(SWITCH_HIGH, new Integer(vHigh));
for (int i = vLow; i <= vHigh; i++)
{
int vJump = read32(code, pc);
pc += 4;
map.put(new Integer(i), new Integer(vJump));
}
map.put(SWITCH_OP_LENGTH, new Integer(pc - oldPc));
return map;
}
public static Map<Object, Object> getLookupSwitch(byte[] code, int pc)
{
Map<Object, Object> map = new HashMap<Object, Object>();
int oldPc = pc;
pc++;
int padding = 4 - (pc & 3);
if (padding == 4) {
padding = 0;
}
pc += padding;
int vDefault = read32(code, pc);
map.put(SWITCH_DEFAULT, new Integer(vDefault));
pc += 4;
int nPairs = read32(code, pc);
pc += 4;
map.put(SWITCH_PAIRS, new Integer(nPairs));
for (int i = 0; i < nPairs; i++)
{
int vData = read32(code, pc);
pc += 4;
int vJump = read32(code, pc);
pc += 4;
map.put(new Integer(i), new Pair(vData, vJump));
}
map.put(SWITCH_OP_LENGTH, new Integer(pc - oldPc));
return map;
}
protected static int read32(byte[] data, int offset)
{
int v = 0;
v = ((data[offset] & 0xff) << 24) | ((data[offset + 1] & 0xff) << 16)
| ((data[offset + 2] & 0xff) << 8) | ((data[offset + 3] & 0xff));
return v;
}
}