Make sure elements are sorted by name when building an annotation in smali
diff --git a/smali-integration-tests/src/test/smali/junit-tests/AnnotationTests/AnnotationTests.smali b/smali-integration-tests/src/test/smali/junit-tests/AnnotationTests/AnnotationTests.smali
index 80e6643..711c34e 100644
--- a/smali-integration-tests/src/test/smali/junit-tests/AnnotationTests/AnnotationTests.smali
+++ b/smali-integration-tests/src/test/smali/junit-tests/AnnotationTests/AnnotationTests.smali
@@ -12,13 +12,19 @@
stringValue = "Class Annotation Test"
.end annotation
-
.field public testField:I
.annotation runtime LTestAnnotationClass;
stringValue = "Field Annotation Test"
.end annotation
.end field
+.field public testAnnotationValuesOutOfOrder:I
+ .annotation runtime LTestAnnotationClass;
+ stringValue = "stringValue Value"
+ anotherStringValue = "anotherStringValue Value"
+ .end annotation
+.end field
+
.method public testClassAnnotation()V
.registers 2
@@ -71,6 +77,41 @@
return-void
.end method
+.method public testAnnotationValuesOutOfOrder()V
+ .registers 5
+
+ .annotation runtime Lorg/junit/Test;
+ .end annotation
+
+ const-class v0, LAnnotationTests;
+ const-class v1, LTestAnnotationClass;
+ const-string v2, "testAnnotationValuesOutOfOrder"
+
+ invoke-virtual {v0, v2}, Ljava/lang/Class;->getField(Ljava/lang/String;)Ljava/lang/reflect/Field;
+ move-result-object v3
+
+ invoke-virtual {v3, v1}, Ljava/lang/reflect/Field;->getAnnotation(Ljava/lang/Class;)Ljava/lang/annotation/Annotation;
+ move-result-object v0
+
+ check-cast v0, LTestAnnotationClass;
+
+ invoke-interface {v0}, LTestAnnotationClass;->stringValue()Ljava/lang/String;
+ move-result-object v4
+
+ const-string v1, "stringValue Value"
+
+ invoke-static {v4, v1}, Lorg/junit/Assert;->assertEquals(Ljava/lang/Object;Ljava/lang/Object;)V
+
+ invoke-interface {v0}, LTestAnnotationClass;->anotherStringValue()Ljava/lang/String;
+ move-result-object v4
+
+ const-string v1, "anotherStringValue Value"
+
+ invoke-static {v4, v1}, Lorg/junit/Assert;->assertEquals(Ljava/lang/Object;Ljava/lang/Object;)V
+
+ return-void
+.end method
+
.method public testMethodAnnotation()V
.registers 4
diff --git a/smali-integration-tests/src/test/smali/junit-tests/AnnotationTests/TestAnnotationClass.smali b/smali-integration-tests/src/test/smali/junit-tests/AnnotationTests/TestAnnotationClass.smali
index eb2b245..fe1e823 100644
--- a/smali-integration-tests/src/test/smali/junit-tests/AnnotationTests/TestAnnotationClass.smali
+++ b/smali-integration-tests/src/test/smali/junit-tests/AnnotationTests/TestAnnotationClass.smali
@@ -2,11 +2,15 @@
.super Ljava/lang/Object;
.implements Ljava/lang/annotation/Annotation;
+.method public abstract anotherStringValue()Ljava/lang/String;
+.end method
+
.method public abstract stringValue()Ljava/lang/String;
.end method
.annotation system Ldalvik/annotation/AnnotationDefault;
value = .subannotation LAnnotationWithValues;
+ anotherStringValue = "Another String Value"
stringValue = "Test Annotation String Value"
.end subannotation
.end annotation
\ No newline at end of file
diff --git a/smali/build.gradle b/smali/build.gradle
index a60056f..d5f54ab 100644
--- a/smali/build.gradle
+++ b/smali/build.gradle
@@ -69,6 +69,7 @@
compile project(':dexlib')
compile 'org.antlr:antlr-runtime:3.2'
compile 'commons-cli:commons-cli:1.2'
+ compile 'com.google.guava:guava:13.0.1'
testCompile 'junit:junit:4.6'
diff --git a/smali/src/main/antlr3/smaliTreeWalker.g b/smali/src/main/antlr3/smaliTreeWalker.g
index fb14242..2fdf715 100644
--- a/smali/src/main/antlr3/smaliTreeWalker.g
+++ b/smali/src/main/antlr3/smaliTreeWalker.g
@@ -36,6 +36,7 @@
@header {
package org.jf.smali;
+import com.google.common.collect.ImmutableSortedMap;
import java.util.HashMap;
import java.util.LinkedList;
import java.util.regex.*;
@@ -1616,23 +1617,26 @@
};
subannotation returns[TypeIdItem annotationType, StringIdItem[\] elementNames, EncodedValue[\] elementValues]
- : {ArrayList<StringIdItem> elementNamesList = new ArrayList<StringIdItem>();
- ArrayList<EncodedValue> elementValuesList = new ArrayList<EncodedValue>();}
+ : {ImmutableSortedMap.Builder<StringIdItem, EncodedValue> elementBuilder =
+ ImmutableSortedMap.<StringIdItem, EncodedValue>naturalOrder();}
^(I_SUBANNOTATION
class_type_descriptor
(annotation_element
{
- elementNamesList.add($annotation_element.elementName);
- elementValuesList.add($annotation_element.elementValue);
+ elementBuilder.put($annotation_element.elementName, $annotation_element.elementValue);
}
)*
)
{
+ ImmutableSortedMap<StringIdItem, EncodedValue> elementMap = elementBuilder.build();
+
$annotationType = $class_type_descriptor.type;
- $elementNames = new StringIdItem[elementNamesList.size()];
- elementNamesList.toArray($elementNames);
- $elementValues = new EncodedValue[elementValuesList.size()];
- elementValuesList.toArray($elementValues);
+
+ $elementNames = new StringIdItem[elementMap.size()];
+ $elementValues = new EncodedValue[elementMap.size()];
+
+ elementMap.keySet().toArray($elementNames);
+ elementMap.values().toArray($elementValues);
};
field_literal returns[FieldIdItem value]