Skip to content

Commit d4d173b

Browse files
jpobstjonpryor
authored andcommitted
[Xamarin.Android.Tools.Bytecode] Runtime[In]VisibleAnnotations Parsing (#502)
Context: #466 Context: #467 Context: #499 Add support for parsing the [`RuntimeVisibleAnnotations`][0] and [`RuntimeInvisibleAnnotations`][1] attributes in Java bytecode. These attributes store Java programming language [annotations][2] placed on language constructs such as types, fields, and methods, and are analogous with C# custom attributes. Kotlin stores various bits of information in these attributes, and it will be necessary to parse these attributes to read that information; see also PR #499. [0]: https://docs.oracle.com/javase/specs/jvms/se7/html/jvms-4.html#jvms-4.7.16 [1]: https://docs.oracle.com/javase/specs/jvms/se7/html/jvms-4.html#jvms-4.7.17 [2]: https://docs.oracle.com/javase/specs/jls/se7/html/jls-9.html#jls-9.7
1 parent bbd75c1 commit d4d173b

File tree

4 files changed

+234
-0
lines changed

4 files changed

+234
-0
lines changed
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,51 @@
1+
using System.Collections.Generic;
2+
using System.IO;
3+
using System.Text;
4+
5+
namespace Xamarin.Android.Tools.Bytecode
6+
{
7+
public sealed class Annotation
8+
{
9+
public ConstantPool ConstantPool { get; }
10+
11+
ushort typeIndex;
12+
public string Type => ((ConstantPoolUtf8Item) ConstantPool [typeIndex]).Value;
13+
14+
public IList<KeyValuePair<string, AnnotationElementValue>>
15+
Values { get; } = new List<KeyValuePair<string, AnnotationElementValue>> ();
16+
17+
public Annotation (ConstantPool constantPool, Stream stream)
18+
{
19+
ConstantPool = constantPool;
20+
typeIndex = stream.ReadNetworkUInt16 ();
21+
22+
var count = stream.ReadNetworkUInt16 ();
23+
for (int i = 0; i < count; ++i) {
24+
var elementNameIndex = stream.ReadNetworkUInt16 ();
25+
var elementName = ((ConstantPoolUtf8Item) ConstantPool [elementNameIndex]).Value;
26+
var elementValue = AnnotationElementValue.Create (constantPool, stream);
27+
Values.Add (new KeyValuePair<string, AnnotationElementValue> (elementName, elementValue));
28+
}
29+
}
30+
31+
public override string ToString ()
32+
{
33+
var values = new StringBuilder ("{");
34+
if (Values.Count > 0) {
35+
Append (Values [0]);
36+
}
37+
for (int i = 1; i < Values.Count; ++i) {
38+
values.Append (", ");
39+
Append (Values [i]);
40+
}
41+
values.Append ("}");
42+
return $"Annotation('{Type}', {values})";
43+
44+
void Append (KeyValuePair<string, AnnotationElementValue> value)
45+
{
46+
values.Append (value.Key).Append (": ");
47+
values.Append (value.Value);
48+
}
49+
}
50+
}
51+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,127 @@
1+
using System;
2+
using System.Collections.Generic;
3+
using System.IO;
4+
using System.Linq;
5+
using System.Text;
6+
7+
namespace Xamarin.Android.Tools.Bytecode
8+
{
9+
// https://docs.oracle.com/javase/specs/jvms/se7/html/jvms-4.html#jvms-4.7.16.1
10+
public abstract class AnnotationElementValue
11+
{
12+
public virtual string ToEncodedString () => ToString ();
13+
14+
public static AnnotationElementValue Create (ConstantPool constantPool, Stream stream)
15+
{
16+
var tag = stream.ReadNetworkByte ();
17+
18+
switch (tag) {
19+
case (byte) 'e': {
20+
var typeNameIndex = stream.ReadNetworkUInt16 ();
21+
var constNameIndex = stream.ReadNetworkUInt16 ();
22+
23+
var typeName = ((ConstantPoolUtf8Item) constantPool [typeNameIndex]).Value;
24+
var constName = ((ConstantPoolUtf8Item) constantPool [constNameIndex]).Value;
25+
26+
return new AnnotationElementEnum { TypeName = typeName, ConstantName = constName };
27+
}
28+
case (byte) 'c': {
29+
var classInfoIndex = stream.ReadNetworkUInt16 ();
30+
31+
return new AnnotationElementClassInfo { ClassInfo = ((ConstantPoolUtf8Item) constantPool [classInfoIndex]).Value };
32+
}
33+
case (byte) '@': {
34+
return new AnnotationElementAnnotation { Annotation = new Annotation (constantPool, stream) };
35+
}
36+
case (byte) '[': {
37+
var numValues = stream.ReadNetworkUInt16 ();
38+
39+
var values = new List<AnnotationElementValue> ();
40+
41+
for (var i = 0; i < numValues; i++)
42+
values.Add (Create (constantPool, stream));
43+
44+
return new AnnotationElementArray { Values = values.ToArray () };
45+
}
46+
case (byte) 'B':
47+
case (byte) 'C':
48+
case (byte) 'D':
49+
case (byte) 'F':
50+
case (byte) 'I':
51+
case (byte) 'J':
52+
case (byte) 'S':
53+
case (byte) 's':
54+
case (byte) 'Z': {
55+
var constValueIndex = stream.ReadNetworkUInt16 ();
56+
var poolItem = constantPool [constValueIndex];
57+
var value = poolItem.ToString ();
58+
59+
if (poolItem is ConstantPoolDoubleItem d)
60+
value = d.Value.ToString ();
61+
else if (poolItem is ConstantPoolFloatItem f)
62+
value = f.Value.ToString ();
63+
else if (poolItem is ConstantPoolIntegerItem i)
64+
value = i.Value.ToString ();
65+
else if (poolItem is ConstantPoolLongItem l)
66+
value = l.Value.ToString ();
67+
else if (poolItem is ConstantPoolStringItem s)
68+
return new AnnotationStringElementConstant { Value = s.StringData.Value.ToString () };
69+
else if (poolItem is ConstantPoolUtf8Item u)
70+
return new AnnotationStringElementConstant { Value = u.Value.ToString () };
71+
72+
return new AnnotationElementConstant { Value = value };
73+
}
74+
}
75+
76+
return null;
77+
}
78+
}
79+
80+
public class AnnotationElementEnum : AnnotationElementValue
81+
{
82+
public string TypeName { get; set; }
83+
public string ConstantName { get; set; }
84+
85+
public override string ToString () => $"Enum({TypeName}.{ConstantName})";
86+
}
87+
88+
public class AnnotationElementClassInfo : AnnotationElementValue
89+
{
90+
public string ClassInfo { get; set; }
91+
92+
public override string ToString () => ClassInfo;
93+
}
94+
95+
public class AnnotationElementAnnotation : AnnotationElementValue
96+
{
97+
public Annotation Annotation { get; set; }
98+
99+
public override string ToString () => Annotation.ToString ();
100+
}
101+
102+
public class AnnotationElementArray : AnnotationElementValue
103+
{
104+
public AnnotationElementValue[] Values { get; set; }
105+
106+
public override string ToString () => $"[{string.Join (", ", Values.Select (v => v.ToString ()))}]";
107+
108+
public override string ToEncodedString () => $"[{string.Join (", ", Values.Select (v => v.ToEncodedString ()))}]";
109+
}
110+
111+
public class AnnotationElementConstant : AnnotationElementValue
112+
{
113+
public string Value { get; set; }
114+
115+
public override string ToString () => Value;
116+
}
117+
118+
public class AnnotationStringElementConstant : AnnotationElementConstant
119+
{
120+
public override string ToString () => $"\"{Value}\"";
121+
122+
public override string ToEncodedString ()
123+
{
124+
return $"\"{Convert.ToBase64String (Encoding.UTF8.GetBytes (Value))}\"";
125+
}
126+
}
127+
}

src/Xamarin.Android.Tools.Bytecode/AttributeInfo.cs

+54
Original file line numberDiff line numberDiff line change
@@ -47,6 +47,8 @@ public class AttributeInfo {
4747
public const string Signature = "Signature";
4848
public const string SourceFile = "SourceFile";
4949
public const string StackMapTable = "StackMapTable";
50+
public const string RuntimeVisibleAnnotations = "RuntimeVisibleAnnotations";
51+
public const string RuntimeInvisibleAnnotations = "RuntimeInvisibleAnnotations";
5052

5153
ushort nameIndex;
5254

@@ -76,6 +78,8 @@ public string Name {
7678
{ typeof (InnerClassesAttribute), InnerClasses },
7779
{ typeof (LocalVariableTableAttribute), LocalVariableTable },
7880
{ typeof (MethodParametersAttribute), MethodParameters },
81+
{ typeof (RuntimeVisibleAnnotationsAttribute), RuntimeVisibleAnnotations },
82+
{ typeof (RuntimeInvisibleAnnotationsAttribute), RuntimeInvisibleAnnotations },
7983
{ typeof (SignatureAttribute), Signature },
8084
{ typeof (SourceFileAttribute), SourceFile },
8185
{ typeof (StackMapTableAttribute), StackMapTable },
@@ -109,6 +113,8 @@ static AttributeInfo CreateAttribute (string name, ConstantPool constantPool, us
109113
case InnerClasses: return new InnerClassesAttribute (constantPool, nameIndex, stream);
110114
case LocalVariableTable: return new LocalVariableTableAttribute (constantPool, nameIndex, stream);
111115
case MethodParameters: return new MethodParametersAttribute (constantPool, nameIndex, stream);
116+
case RuntimeVisibleAnnotations: return new RuntimeVisibleAnnotationsAttribute (constantPool, nameIndex, stream);
117+
case RuntimeInvisibleAnnotations: return new RuntimeInvisibleAnnotationsAttribute (constantPool, nameIndex, stream);
112118
case Signature: return new SignatureAttribute (constantPool, nameIndex, stream);
113119
case SourceFile: return new SourceFileAttribute (constantPool, nameIndex, stream);
114120
case StackMapTable: return new StackMapTableAttribute (constantPool, nameIndex, stream);
@@ -493,6 +499,54 @@ public override string ToString ()
493499
}
494500
}
495501

502+
// https://docs.oracle.com/javase/specs/jvms/se7/html/jvms-4.html#jvms-4.7.16
503+
public sealed class RuntimeVisibleAnnotationsAttribute : AttributeInfo
504+
{
505+
506+
public IList<Annotation> Annotations { get; } = new List<Annotation> ();
507+
508+
public RuntimeVisibleAnnotationsAttribute (ConstantPool constantPool, ushort nameIndex, Stream stream)
509+
: base (constantPool, nameIndex, stream)
510+
{
511+
var length = stream.ReadNetworkUInt32 ();
512+
var count = stream.ReadNetworkUInt16 ();
513+
514+
for (int i = 0; i < count; ++i) {
515+
Annotations.Add (new Annotation (constantPool, stream));
516+
}
517+
}
518+
519+
public override string ToString ()
520+
{
521+
var annotations = string.Join (", ", Annotations.Select (v => v.ToString ()));
522+
return $"RuntimeVisibleAnnotations({annotations})";
523+
}
524+
}
525+
526+
// https://docs.oracle.com/javase/specs/jvms/se7/html/jvms-4.html#jvms-4.7.17
527+
public sealed class RuntimeInvisibleAnnotationsAttribute : AttributeInfo
528+
{
529+
public IList<Annotation> Annotations { get; } = new List<Annotation> ();
530+
531+
public RuntimeInvisibleAnnotationsAttribute (ConstantPool constantPool, ushort nameIndex, Stream stream)
532+
: base (constantPool, nameIndex, stream)
533+
{
534+
var length = stream.ReadNetworkUInt32 ();
535+
var count = stream.ReadNetworkUInt16 ();
536+
537+
for (int i = 0; i < count; ++i) {
538+
var a = new Annotation (constantPool, stream);
539+
Annotations.Add (a);
540+
}
541+
}
542+
543+
public override string ToString ()
544+
{
545+
var annotations = string.Join (", ", Annotations.Select (v => v.ToString ()));
546+
return $"RuntimeVisibleAnnotations({annotations})";
547+
}
548+
}
549+
496550
// http://docs.oracle.com/javase/specs/jvms/se7/html/jvms-4.html#jvms-4.7.9
497551
public sealed class SignatureAttribute : AttributeInfo {
498552

src/Xamarin.Android.Tools.Bytecode/Xamarin.Android.Tools.Bytecode.csproj

+2
Original file line numberDiff line numberDiff line change
@@ -37,6 +37,8 @@
3737
<Reference Include="System.IO.Compression" />
3838
</ItemGroup>
3939
<ItemGroup>
40+
<Compile Include="Annotation.cs" />
41+
<Compile Include="AnnotationElementValue.cs" />
4042
<Compile Include="ClassFile.cs" />
4143
<Compile Include="ConstantPool.cs" />
4244
<Compile Include="StreamCoda.cs" />

0 commit comments

Comments
 (0)