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

View File

@ -213,30 +213,49 @@ array_elements returns[List<byte[\]> values]
$values.add($fixed_size_literal.value); $values.add($fixed_size_literal.value);
})*); })*);
packed_switch_targets[int baseOffset] returns[List<Integer> targets] packed_switch_target_count returns[int targetCount]
: {$targets = new ArrayList<Integer>();} : I_PACKED_SWITCH_TARGET_COUNT {$targetCount = Integer.parseInt($I_PACKED_SWITCH_TARGET_COUNT.text);};
packed_switch_targets[int baseOffset] returns[int[\] targets]
:
^(I_PACKED_SWITCH_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 (offset_or_label
{ {
$targets.add($offset_or_label.offsetValue - $baseOffset); targets[targetsPosition++] = $offset_or_label.offsetValue - $baseOffset;
})* })*
); );
sparse_switch_keys returns[List<Integer> keys] sparse_switch_target_count returns[int targetCount]
: {$keys = new ArrayList<Integer>();} : I_SPARSE_SWITCH_TARGET_COUNT {$targetCount = Integer.parseInt($I_SPARSE_SWITCH_TARGET_COUNT.text);};
sparse_switch_keys[int targetCount] returns[int[\] keys]
: {
$keys = new int[$targetCount];
int keysPosition = 0;
}
^(I_SPARSE_SWITCH_KEYS ^(I_SPARSE_SWITCH_KEYS
(fixed_32bit_literal (fixed_32bit_literal
{ {
$keys.add($fixed_32bit_literal.value); $keys[keysPosition++] = $fixed_32bit_literal.value;
})* })*
); );
sparse_switch_targets[int baseOffset] returns[List<Integer> targets] sparse_switch_targets[int baseOffset, int targetCount] returns[int[\] targets]
: {$targets = new ArrayList<Integer>();} : {
$targets = new int[$targetCount];
int targetsPosition = 0;
}
^(I_SPARSE_SWITCH_TARGETS ^(I_SPARSE_SWITCH_TARGETS
(offset_or_label (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]) ^(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; 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); $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; int[] keys = $sparse_switch_keys.keys;
List<Integer> targets = $sparse_switch_targets.targets; int[] targets = $sparse_switch_targets.targets;
$instruction = SparseSwitchData.make(dexFile, keys, targets); $instruction = SparseSwitchData.make(dexFile, keys, targets);
} }

View File

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

View File

@ -36,46 +36,60 @@ import java.util.List;
public class SparseSwitchData public class SparseSwitchData
{ {
//TODO: change from 2 List<Integer> to int[,] public static Instruction make(DexFile dexFile, int[] keys, int[] targets) {
public static Instruction make(DexFile dexFile, List<Integer> keys, List<Integer> targets) {
byte[] bytes; byte[] bytes;
if (keys.size() != targets.size()) { if (keys.length != targets.length) {
throw new RuntimeException("The number of keys and offsets don't match"); 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. " + throw new RuntimeException("The sparse-switch data contains too many elements. " +
"The maximum number of switch elements is 65535"); "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]; //TODO: should we throw an error if there are no switch elements?
int position = 4; 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; bytes[position++] = (byte)(key >> 8);
bytes[position++] = (byte)(key >> 8); bytes[position++] = (byte)(key >> 16);
bytes[position++] = (byte)(key >> 16); bytes[position++] = (byte)(key >> 24);
bytes[position++] = (byte)(key >> 24); }
}
for (int target: targets) { for (int target: targets) {
bytes[position++] = (byte)target; bytes[position++] = (byte)target;
bytes[position++] = (byte)(target >> 8); bytes[position++] = (byte)(target >> 8);
bytes[position++] = (byte)(target >> 16); bytes[position++] = (byte)(target >> 16);
bytes[position++] = (byte)(target >> 24); bytes[position++] = (byte)(target >> 24);
}
} }
//sparse-switch psuedo-opcode //sparse-switch psuedo-opcode
bytes[0] = 0x00; bytes[0] = 0x00;
bytes[1] = 0x02; bytes[1] = 0x02;
bytes[2] = (byte)targets.size(); bytes[2] = (byte)targets.length;
bytes[3] = (byte)(targets.size() >> 8); bytes[3] = (byte)(targets.length >> 8);
return new Instruction(dexFile, bytes, null); return new Instruction(dexFile, bytes, null);
} }