Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
51 changes: 51 additions & 0 deletions src/Xamarin.Android.Tools.Bytecode/Annotation.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,51 @@
using System.Collections.Generic;
using System.IO;
using System.Text;

namespace Xamarin.Android.Tools.Bytecode
{
public sealed class Annotation
{
public ConstantPool ConstantPool { get; }

ushort typeIndex;
public string Type => ((ConstantPoolUtf8Item) ConstantPool [typeIndex]).Value;

public IList<KeyValuePair<string, AnnotationElementValue>>
Values { get; } = new List<KeyValuePair<string, AnnotationElementValue>> ();

public Annotation (ConstantPool constantPool, Stream stream)
{
ConstantPool = constantPool;
typeIndex = stream.ReadNetworkUInt16 ();

var count = stream.ReadNetworkUInt16 ();
for (int i = 0; i < count; ++i) {
var elementNameIndex = stream.ReadNetworkUInt16 ();
var elementName = ((ConstantPoolUtf8Item) ConstantPool [elementNameIndex]).Value;
var elementValue = AnnotationElementValue.Create (constantPool, stream);
Values.Add (new KeyValuePair<string, AnnotationElementValue> (elementName, elementValue));
}
}

public override string ToString ()
{
var values = new StringBuilder ("{");
if (Values.Count > 0) {
Append (Values [0]);
}
for (int i = 1; i < Values.Count; ++i) {
values.Append (", ");
Append (Values [i]);
}
values.Append ("}");
return $"Annotation('{Type}', {values})";

void Append (KeyValuePair<string, AnnotationElementValue> value)
{
values.Append (value.Key).Append (": ");
values.Append (value.Value);
}
}
}
}
127 changes: 127 additions & 0 deletions src/Xamarin.Android.Tools.Bytecode/AnnotationElementValue.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,127 @@
using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Text;

namespace Xamarin.Android.Tools.Bytecode
{
// https://docs.oracle.com/javase/specs/jvms/se7/html/jvms-4.html#jvms-4.7.16.1
public abstract class AnnotationElementValue
{
public virtual string ToEncodedString () => ToString ();

public static AnnotationElementValue Create (ConstantPool constantPool, Stream stream)
{
var tag = stream.ReadNetworkByte ();

switch (tag) {
case (byte) 'e': {
var typeNameIndex = stream.ReadNetworkUInt16 ();
var constNameIndex = stream.ReadNetworkUInt16 ();

var typeName = ((ConstantPoolUtf8Item) constantPool [typeNameIndex]).Value;
var constName = ((ConstantPoolUtf8Item) constantPool [constNameIndex]).Value;

return new AnnotationElementEnum { TypeName = typeName, ConstantName = constName };
}
case (byte) 'c': {
var classInfoIndex = stream.ReadNetworkUInt16 ();

return new AnnotationElementClassInfo { ClassInfo = ((ConstantPoolUtf8Item) constantPool [classInfoIndex]).Value };
}
case (byte) '@': {
return new AnnotationElementAnnotation { Annotation = new Annotation (constantPool, stream) };
}
case (byte) '[': {
var numValues = stream.ReadNetworkUInt16 ();

var values = new List<AnnotationElementValue> ();

for (var i = 0; i < numValues; i++)
values.Add (Create (constantPool, stream));

return new AnnotationElementArray { Values = values.ToArray () };
}
case (byte) 'B':
case (byte) 'C':
case (byte) 'D':
case (byte) 'F':
case (byte) 'I':
case (byte) 'J':
case (byte) 'S':
case (byte) 's':
case (byte) 'Z': {
var constValueIndex = stream.ReadNetworkUInt16 ();
var poolItem = constantPool [constValueIndex];
var value = poolItem.ToString ();

if (poolItem is ConstantPoolDoubleItem d)
value = d.Value.ToString ();
else if (poolItem is ConstantPoolFloatItem f)
value = f.Value.ToString ();
else if (poolItem is ConstantPoolIntegerItem i)
value = i.Value.ToString ();
else if (poolItem is ConstantPoolLongItem l)
value = l.Value.ToString ();
else if (poolItem is ConstantPoolStringItem s)
return new AnnotationStringElementConstant { Value = s.StringData.Value.ToString () };
else if (poolItem is ConstantPoolUtf8Item u)
return new AnnotationStringElementConstant { Value = u.Value.ToString () };

return new AnnotationElementConstant { Value = value };
}
}

return null;
}
}

public class AnnotationElementEnum : AnnotationElementValue
{
public string TypeName { get; set; }
public string ConstantName { get; set; }

public override string ToString () => $"Enum({TypeName}.{ConstantName})";
}

public class AnnotationElementClassInfo : AnnotationElementValue
{
public string ClassInfo { get; set; }

public override string ToString () => ClassInfo;
}

public class AnnotationElementAnnotation : AnnotationElementValue
{
public Annotation Annotation { get; set; }

public override string ToString () => Annotation.ToString ();
}

public class AnnotationElementArray : AnnotationElementValue
{
public AnnotationElementValue[] Values { get; set; }

public override string ToString () => $"[{string.Join (", ", Values.Select (v => v.ToString ()))}]";

public override string ToEncodedString () => $"[{string.Join (", ", Values.Select (v => v.ToEncodedString ()))}]";
}

public class AnnotationElementConstant : AnnotationElementValue
{
public string Value { get; set; }

public override string ToString () => Value;
}

public class AnnotationStringElementConstant : AnnotationElementConstant
{
public override string ToString () => $"\"{Value}\"";

public override string ToEncodedString ()
{
return $"\"{Convert.ToBase64String (Encoding.UTF8.GetBytes (Value))}\"";
}
}
}
54 changes: 54 additions & 0 deletions src/Xamarin.Android.Tools.Bytecode/AttributeInfo.cs
Original file line number Diff line number Diff line change
Expand Up @@ -47,6 +47,8 @@ public class AttributeInfo {
public const string Signature = "Signature";
public const string SourceFile = "SourceFile";
public const string StackMapTable = "StackMapTable";
public const string RuntimeVisibleAnnotations = "RuntimeVisibleAnnotations";
public const string RuntimeInvisibleAnnotations = "RuntimeInvisibleAnnotations";

ushort nameIndex;

Expand Down Expand Up @@ -76,6 +78,8 @@ public string Name {
{ typeof (InnerClassesAttribute), InnerClasses },
{ typeof (LocalVariableTableAttribute), LocalVariableTable },
{ typeof (MethodParametersAttribute), MethodParameters },
{ typeof (RuntimeVisibleAnnotationsAttribute), RuntimeVisibleAnnotations },
{ typeof (RuntimeInvisibleAnnotationsAttribute), RuntimeInvisibleAnnotations },
{ typeof (SignatureAttribute), Signature },
{ typeof (SourceFileAttribute), SourceFile },
{ typeof (StackMapTableAttribute), StackMapTable },
Expand Down Expand Up @@ -109,6 +113,8 @@ static AttributeInfo CreateAttribute (string name, ConstantPool constantPool, us
case InnerClasses: return new InnerClassesAttribute (constantPool, nameIndex, stream);
case LocalVariableTable: return new LocalVariableTableAttribute (constantPool, nameIndex, stream);
case MethodParameters: return new MethodParametersAttribute (constantPool, nameIndex, stream);
case RuntimeVisibleAnnotations: return new RuntimeVisibleAnnotationsAttribute (constantPool, nameIndex, stream);
case RuntimeInvisibleAnnotations: return new RuntimeInvisibleAnnotationsAttribute (constantPool, nameIndex, stream);
case Signature: return new SignatureAttribute (constantPool, nameIndex, stream);
case SourceFile: return new SourceFileAttribute (constantPool, nameIndex, stream);
case StackMapTable: return new StackMapTableAttribute (constantPool, nameIndex, stream);
Expand Down Expand Up @@ -493,6 +499,54 @@ public override string ToString ()
}
}

// https://docs.oracle.com/javase/specs/jvms/se7/html/jvms-4.html#jvms-4.7.16
public sealed class RuntimeVisibleAnnotationsAttribute : AttributeInfo
{

public IList<Annotation> Annotations { get; } = new List<Annotation> ();

public RuntimeVisibleAnnotationsAttribute (ConstantPool constantPool, ushort nameIndex, Stream stream)
: base (constantPool, nameIndex, stream)
{
var length = stream.ReadNetworkUInt32 ();
var count = stream.ReadNetworkUInt16 ();

for (int i = 0; i < count; ++i) {
Annotations.Add (new Annotation (constantPool, stream));
}
}

public override string ToString ()
{
var annotations = string.Join (", ", Annotations.Select (v => v.ToString ()));
return $"RuntimeVisibleAnnotations({annotations})";
}
}

// https://docs.oracle.com/javase/specs/jvms/se7/html/jvms-4.html#jvms-4.7.17
public sealed class RuntimeInvisibleAnnotationsAttribute : AttributeInfo
{
public IList<Annotation> Annotations { get; } = new List<Annotation> ();

public RuntimeInvisibleAnnotationsAttribute (ConstantPool constantPool, ushort nameIndex, Stream stream)
: base (constantPool, nameIndex, stream)
{
var length = stream.ReadNetworkUInt32 ();
var count = stream.ReadNetworkUInt16 ();

for (int i = 0; i < count; ++i) {
var a = new Annotation (constantPool, stream);
Annotations.Add (a);
}
}

public override string ToString ()
{
var annotations = string.Join (", ", Annotations.Select (v => v.ToString ()));
return $"RuntimeVisibleAnnotations({annotations})";
}
}

// http://docs.oracle.com/javase/specs/jvms/se7/html/jvms-4.html#jvms-4.7.9
public sealed class SignatureAttribute : AttributeInfo {

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,8 @@
<Reference Include="System.IO.Compression" />
</ItemGroup>
<ItemGroup>
<Compile Include="Annotation.cs" />
<Compile Include="AnnotationElementValue.cs" />
<Compile Include="ClassFile.cs" />
<Compile Include="ConstantPool.cs" />
<Compile Include="StreamCoda.cs" />
Expand Down