Changed the implementation details for the sparse-switch and packed-switch pseudo opcodes to use an int[], instead of a List<Integer>.

Also added additional validity checking to SparseSwitchData.make()

git-svn-id: https://smali.googlecode.com/svn/trunk@36 55b6fa8a-2a1e-11de-a435-ffa8d773f76a
This commit is contained in:
JesusFreke@JesusFreke.com 2009-05-11 00:40:03 +00:00
parent 061ba3a71c
commit 8a116bba98
4 changed files with 98 additions and 46 deletions

View File

@ -54,9 +54,11 @@ tokens {
I_ARRAY_ELEMENTS;
I_PACKED_SWITCH_START_KEY;
I_PACKED_SWITCH_BASE_OFFSET;
I_PACKED_SWITCH_TARGET_COUNT;
I_PACKED_SWITCH_TARGETS;
I_SPARSE_SWITCH_BASE_OFFSET;
I_SPARSE_SWITCH_KEYS;
I_SPARSE_SWITCH_TARGET_COUNT;
I_SPARSE_SWITCH_TARGETS;
I_STATEMENTS;
I_STATEMENT_FORMAT10t;
@ -261,6 +263,7 @@ instruction returns [int size]
|
PACKED_SWITCH_DIRECTIVE
{
int targetCount = 0;
if (($statements::currentAddress \% 2) != 0) {
needsNop = true;
$size = 2;
@ -273,18 +276,28 @@ instruction returns [int size]
fixed_32bit_literal
(switch_target += offset_or_label {$size+=4;})*
(switch_target += offset_or_label {$size+=4; targetCount++;})*
END_PACKED_SWITCH_DIRECTIVE {$size = $size + 8;}
/*add a nop statement before this if needed to force the correct alignment*/
-> {needsNop}? ^(I_STATEMENT_FORMAT10x[$start, "I_STATEMENT_FORMAT10x"] INSTRUCTION_FORMAT10x[$start, "nop"])
^(I_STATEMENT_PACKED_SWITCH ^(I_PACKED_SWITCH_BASE_OFFSET $base_offset) ^(I_PACKED_SWITCH_START_KEY fixed_32bit_literal) ^(I_PACKED_SWITCH_TARGETS $switch_target*))
-> ^(I_STATEMENT_PACKED_SWITCH ^(I_PACKED_SWITCH_BASE_OFFSET $base_offset) ^(I_PACKED_SWITCH_START_KEY fixed_32bit_literal) ^(I_PACKED_SWITCH_TARGETS $switch_target*))
^(I_STATEMENT_PACKED_SWITCH[$start, "I_STATEMENT_PACKED_SWITCH"]
^(I_PACKED_SWITCH_BASE_OFFSET[$start, "I_PACKED_SWITCH_BASE_OFFSET"] $base_offset)
^(I_PACKED_SWITCH_START_KEY[$start, "I_PACKED_SWITCH_START_KEY"] fixed_32bit_literal)
^(I_PACKED_SWITCH_TARGETS[$start, "I_PACKED_SWITCH_TARGETS"] I_PACKED_SWITCH_TARGET_COUNT[$start, Integer.toString(targetCount)] $switch_target*)
)
-> ^(I_STATEMENT_PACKED_SWITCH[$start, "I_STATEMENT_PACKED_SWITCH"]
^(I_PACKED_SWITCH_BASE_OFFSET[$start, "I_PACKED_SWITCH_BASE_OFFSET"] $base_offset)
^(I_PACKED_SWITCH_START_KEY[$start, "I_PACKED_SWITCH_START_KEY"] fixed_32bit_literal)
^(I_PACKED_SWITCH_TARGETS[$start, "I_PACKED_SWITCH_TARGETS"] I_PACKED_SWITCH_TARGET_COUNT[$start, Integer.toString(targetCount)] $switch_target*)
)
|
SPARSE_SWITCH_DIRECTIVE
{
int targetCount = 0;
if (($statements::currentAddress \% 2) != 0) {
needsNop = true;
$size = 2;
@ -295,15 +308,22 @@ instruction returns [int size]
base_offset = offset_or_label
(fixed_32bit_literal switch_target += offset_or_label {$size += 8;})*
(fixed_32bit_literal switch_target += offset_or_label {$size += 8; targetCount++;})*
END_SPARSE_SWITCH_DIRECTIVE {$size = $size + 4;}
/*add a nop statement before this if needed to force the correct alignment*/
-> {needsNop}? ^(I_STATEMENT_FORMAT10x[$start, "I_STATEMENT_FORMAT10x"] INSTRUCTION_FORMAT10x[$start, "nop"])
^(I_STATEMENT_SPARSE_SWITCH ^(I_SPARSE_SWITCH_BASE_OFFSET $base_offset) ^(I_SPARSE_SWITCH_KEYS fixed_32bit_literal*) ^(I_SPARSE_SWITCH_TARGETS $switch_target*))
-> ^(I_STATEMENT_SPARSE_SWITCH ^(I_SPARSE_SWITCH_BASE_OFFSET $base_offset) ^(I_SPARSE_SWITCH_KEYS fixed_32bit_literal*) ^(I_SPARSE_SWITCH_TARGETS $switch_target*))
^(I_STATEMENT_SPARSE_SWITCH[$start, "I_STATEMENT_SPARSE_SWITCH"]
^(I_SPARSE_SWITCH_BASE_OFFSET[$start, "I_SPARSE_SWITCH_BASE_OFFSET"] $base_offset)
I_SPARSE_SWITCH_TARGET_COUNT[$start, Integer.toString(targetCount)]
^(I_SPARSE_SWITCH_KEYS[$start, "I_SPARSE_SWITCH_KEYS"] fixed_32bit_literal*)
^(I_SPARSE_SWITCH_TARGETS $switch_target*))
-> ^(I_STATEMENT_SPARSE_SWITCH[$start, "I_STATEMENT_SPARSE_SWITCH"]
^(I_SPARSE_SWITCH_BASE_OFFSET[$start, "I_SPARSE_SWITCH_BASE_OFFSET"] $base_offset)
I_SPARSE_SWITCH_TARGET_COUNT[$start, Integer.toString(targetCount)]
^(I_SPARSE_SWITCH_KEYS[$start, "I_SPARSE_SWITCH_KEYS"] fixed_32bit_literal*)
^(I_SPARSE_SWITCH_TARGETS $switch_target*))
;
offset_or_label

View File

@ -212,31 +212,50 @@ array_elements returns[List<byte[\]> values]
{
$values.add($fixed_size_literal.value);
})*);
packed_switch_target_count returns[int targetCount]
: I_PACKED_SWITCH_TARGET_COUNT {$targetCount = Integer.parseInt($I_PACKED_SWITCH_TARGET_COUNT.text);};
packed_switch_targets[int baseOffset] returns[List<Integer> targets]
: {$targets = new ArrayList<Integer>();}
packed_switch_targets[int baseOffset] returns[int[\] targets]
:
^(I_PACKED_SWITCH_TARGETS
packed_switch_target_count
{
int targetCount = $packed_switch_target_count.targetCount;
$targets = new int[targetCount];
int targetsPosition = 0;
}
(offset_or_label
{
$targets.add($offset_or_label.offsetValue - $baseOffset);
targets[targetsPosition++] = $offset_or_label.offsetValue - $baseOffset;
})*
);
sparse_switch_target_count returns[int targetCount]
: I_SPARSE_SWITCH_TARGET_COUNT {$targetCount = Integer.parseInt($I_SPARSE_SWITCH_TARGET_COUNT.text);};
sparse_switch_keys returns[List<Integer> keys]
: {$keys = new ArrayList<Integer>();}
sparse_switch_keys[int targetCount] returns[int[\] keys]
: {
$keys = new int[$targetCount];
int keysPosition = 0;
}
^(I_SPARSE_SWITCH_KEYS
(fixed_32bit_literal
{
$keys.add($fixed_32bit_literal.value);
$keys[keysPosition++] = $fixed_32bit_literal.value;
})*
);
sparse_switch_targets[int baseOffset] returns[List<Integer> targets]
: {$targets = new ArrayList<Integer>();}
sparse_switch_targets[int baseOffset, int targetCount] returns[int[\] targets]
: {
$targets = new int[$targetCount];
int targetsPosition = 0;
}
^(I_SPARSE_SWITCH_TARGETS
(offset_or_label
{
$targets.add($offset_or_label.offsetValue - $baseOffset);
$targets[targetsPosition++] = $offset_or_label.offsetValue - $baseOffset;
})*
);
@ -703,15 +722,15 @@ instruction returns[Instruction instruction]
^(I_STATEMENT_PACKED_SWITCH ^(I_PACKED_SWITCH_BASE_OFFSET base_offset=offset_or_label) ^(I_PACKED_SWITCH_START_KEY fixed_32bit_literal) packed_switch_targets[$base_offset.offsetValue])
{
int startKey = $fixed_32bit_literal.value;
List<Integer> targets = $packed_switch_targets.targets;
int[] targets = $packed_switch_targets.targets;
$instruction = PackedSwitchData.make(dexFile, startKey, targets);
}
|
^(I_STATEMENT_SPARSE_SWITCH ^(I_SPARSE_SWITCH_BASE_OFFSET base_offset=offset_or_label) sparse_switch_keys sparse_switch_targets[$base_offset.offsetValue])
^(I_STATEMENT_SPARSE_SWITCH ^(I_SPARSE_SWITCH_BASE_OFFSET base_offset=offset_or_label) sparse_switch_target_count sparse_switch_keys[$sparse_switch_target_count.targetCount] sparse_switch_targets[$base_offset.offsetValue, $sparse_switch_target_count.targetCount])
{
List<Integer> keys = $sparse_switch_keys.keys;
List<Integer> targets = $sparse_switch_targets.targets;
int[] keys = $sparse_switch_keys.keys;
int[] targets = $sparse_switch_targets.targets;
$instruction = SparseSwitchData.make(dexFile, keys, targets);
}

View File

@ -36,16 +36,15 @@ import java.util.List;
public class PackedSwitchData
{
//TODO: switch from List<Integer> to int[]
public static Instruction make(DexFile dexFile, int firstKey, List<Integer> targets) {
public static Instruction make(DexFile dexFile, int firstKey, int[] targets) {
byte[] bytes;
if (targets.size() > 0xFFFF) {
if (targets.length > 0xFFFF) {
throw new RuntimeException("The packed-switch data contains too many elements. " +
"The maximum number of switch elements is 65535");
}
bytes = new byte[targets.size() * 4 + 8];
bytes = new byte[targets.length * 4 + 8];
int position = 8;
for (int target: targets) {
@ -59,8 +58,8 @@ public class PackedSwitchData
bytes[0] = 0x00;
bytes[1] = 0x01;
bytes[2] = (byte)targets.size();
bytes[3] = (byte)(targets.size() >> 8);
bytes[2] = (byte)targets.length;
bytes[3] = (byte)(targets.length >> 8);
bytes[4] = (byte)firstKey;
bytes[5] = (byte)(firstKey >> 8);

View File

@ -36,46 +36,60 @@ import java.util.List;
public class SparseSwitchData
{
//TODO: change from 2 List<Integer> to int[,]
public static Instruction make(DexFile dexFile, List<Integer> keys, List<Integer> targets) {
public static Instruction make(DexFile dexFile, int[] keys, int[] targets) {
byte[] bytes;
if (keys.size() != targets.size()) {
if (keys.length != targets.length) {
throw new RuntimeException("The number of keys and offsets don't match");
}
if (targets.length == 0) {
throw new RuntimeException("The sparse-switch data must contain at least 1 key/target");
}
if (targets.size() > 0xFFFF) {
if (targets.length > 0xFFFF) {
throw new RuntimeException("The sparse-switch data contains too many elements. " +
"The maximum number of switch elements is 65535");
}
//TODO: need to sort the switch elements based on key, in ascending order
bytes = new byte[targets.length * 8 + 4];
int position = 8;
bytes = new byte[targets.size() * 8 + 4];
int position = 4;
//TODO: should we throw an error if there are no switch elements?
if (targets.length > 0) {
int key = keys[0];
bytes[4] = (byte)key;
bytes[5] = (byte)(key >> 8);
bytes[6] = (byte)(key >> 16);
bytes[7] = (byte)(key >> 24);
for (int i=1; i<keys.length; i++) {
key = keys[i];
if (key <= keys[i-1]) {
throw new RuntimeException("The targets in a sparse switch block must be sorted in ascending" +
"order, by key");
}
for (int key: keys) {
bytes[position++] = (byte)key;
bytes[position++] = (byte)(key >> 8);
bytes[position++] = (byte)(key >> 16);
bytes[position++] = (byte)(key >> 24);
}
bytes[position++] = (byte)key;
bytes[position++] = (byte)(key >> 8);
bytes[position++] = (byte)(key >> 16);
bytes[position++] = (byte)(key >> 24);
}
for (int target: targets) {
bytes[position++] = (byte)target;
bytes[position++] = (byte)(target >> 8);
bytes[position++] = (byte)(target >> 16);
bytes[position++] = (byte)(target >> 24);
for (int target: targets) {
bytes[position++] = (byte)target;
bytes[position++] = (byte)(target >> 8);
bytes[position++] = (byte)(target >> 16);
bytes[position++] = (byte)(target >> 24);
}
}
//sparse-switch psuedo-opcode
bytes[0] = 0x00;
bytes[1] = 0x02;
bytes[2] = (byte)targets.size();
bytes[3] = (byte)(targets.size() >> 8);
bytes[2] = (byte)targets.length;
bytes[3] = (byte)(targets.length >> 8);
return new Instruction(dexFile, bytes, null);
}