Skip to content

Commit 0dbc003

Browse files
committed
[Xamarin.Android.Tools.Bytecode] Add EnclosingMethod, SourceFile
Context: dotnet#466 While looking at `class-parse --dump` output for various Kotlin-compiled `.class` files, providing support for the `EnclosingMethod` and `SourceFile` annotation blobs looks to be useful. Add support for parsing the `EnclosingMethod` and `SourceFile` annotations. Update the generated XML so that the `SourceFile` annotation is avaialble from the `//class/@source-file-name` or `//interface/@source-file-name` attributes. Update the generated XML so that the `EnclosingMethod` annotation is available from these new attributes on `//class` or `//interface`: * `enclosing-method-jni-type`: The JNI signature of the type declaring the enclosing method. * `enclosing-method-name`: The name of the enclosing method. * `enclosing-method-signature`: The JNI signature of the enclosing method. For example, if `TestType.action()` had an anonymous inner class: class TestType { public void action (Object value) { Runnable r = new Runnable () { public void run() { System.out.println ("foo"); } }; } } This could result in the creation of a new `TestType$1` class for the anonymous inner class, with the resulting `enclosing-*` attributes: enclosing-method-jni-type="Lcom/xamarin/JavaType;" enclosing-method-name="action" enclosing-method-signature="(Ljava/lang/Object;)V"
1 parent be58159 commit 0dbc003

23 files changed

+254
-14
lines changed

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

Lines changed: 56 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -40,10 +40,12 @@ public class AttributeInfo {
4040
public const string ConstantValue = "ConstantValue";
4141
public const string Deprecated = "Deprecated";
4242
public const string Exceptions = "Exceptions";
43+
public const string EnclosingMethod = "EnclosingMethod";
4344
public const string InnerClasses = "InnerClasses";
4445
public const string LocalVariableTable = "LocalVariableTable";
4546
public const string MethodParameters = "MethodParameters";
4647
public const string Signature = "Signature";
48+
public const string SourceFile = "SourceFile";
4749
public const string StackMapTable = "StackMapTable";
4850

4951
ushort nameIndex;
@@ -69,11 +71,13 @@ public string Name {
6971
{ typeof (CodeAttribute), Code },
7072
{ typeof (ConstantValueAttribute), ConstantValue },
7173
{ typeof (DeprecatedAttribute), Deprecated },
74+
{ typeof (EnclosingMethodAttribute), EnclosingMethod },
7275
{ typeof (ExceptionsAttribute), Exceptions },
7376
{ typeof (InnerClassesAttribute), InnerClasses },
7477
{ typeof (LocalVariableTableAttribute), LocalVariableTable },
7578
{ typeof (MethodParametersAttribute), MethodParameters },
7679
{ typeof (SignatureAttribute), Signature },
80+
{ typeof (SourceFileAttribute), SourceFile },
7781
{ typeof (StackMapTableAttribute), StackMapTable },
7882
};
7983

@@ -100,11 +104,13 @@ static AttributeInfo CreateAttribute (string name, ConstantPool constantPool, us
100104
case Code: return new CodeAttribute (constantPool, nameIndex, stream);
101105
case ConstantValue: return new ConstantValueAttribute (constantPool, nameIndex, stream);
102106
case Deprecated: return new DeprecatedAttribute (constantPool, nameIndex, stream);
107+
case EnclosingMethod: return new EnclosingMethodAttribute (constantPool, nameIndex, stream);
103108
case Exceptions: return new ExceptionsAttribute (constantPool, nameIndex, stream);
104109
case InnerClasses: return new InnerClassesAttribute (constantPool, nameIndex, stream);
105110
case LocalVariableTable: return new LocalVariableTableAttribute (constantPool, nameIndex, stream);
106111
case MethodParameters: return new MethodParametersAttribute (constantPool, nameIndex, stream);
107112
case Signature: return new SignatureAttribute (constantPool, nameIndex, stream);
113+
case SourceFile: return new SourceFileAttribute (constantPool, nameIndex, stream);
108114
case StackMapTable: return new StackMapTableAttribute (constantPool, nameIndex, stream);
109115
default: return new UnknownAttribute (constantPool, nameIndex, stream);
110116
}
@@ -221,6 +227,33 @@ public override string ToString ()
221227
}
222228
}
223229

230+
// https://docs.oracle.com/javase/specs/jvms/se7/html/jvms-4.html#jvms-4.7.7
231+
public sealed class EnclosingMethodAttribute : AttributeInfo {
232+
233+
ushort classIndex, methodIndex;
234+
235+
public ConstantPoolClassItem Class {
236+
get {return (ConstantPoolClassItem) ConstantPool [classIndex];}
237+
}
238+
239+
public ConstantPoolNameAndTypeItem Method {
240+
get {return methodIndex == 0 ? null : (ConstantPoolNameAndTypeItem) ConstantPool [methodIndex];}
241+
}
242+
243+
public EnclosingMethodAttribute (ConstantPool constantPool, ushort nameIndex, Stream stream)
244+
: base (constantPool, nameIndex, stream)
245+
{
246+
var length = stream.ReadNetworkUInt32 ();
247+
classIndex = stream.ReadNetworkUInt16 ();
248+
methodIndex = stream.ReadNetworkUInt16 ();
249+
}
250+
251+
public override string ToString ()
252+
{
253+
return $"EnclosingMethod({Class}, {Method})";
254+
}
255+
}
256+
224257
// http://docs.oracle.com/javase/specs/jvms/se7/html/jvms-4.html#jvms-4.7.5
225258
public sealed class ExceptionsAttribute : AttributeInfo {
226259

@@ -483,6 +516,29 @@ public override string ToString ()
483516
}
484517
}
485518

519+
// https://docs.oracle.com/javase/specs/jvms/se7/html/jvms-4.html#jvms-4.7.10
520+
public sealed class SourceFileAttribute : AttributeInfo {
521+
522+
ushort sourceFileIndex;
523+
524+
public string FileName {
525+
get {return ((ConstantPoolUtf8Item) ConstantPool [sourceFileIndex]).Value;}
526+
}
527+
528+
public SourceFileAttribute (ConstantPool constantPool, ushort nameIndex, Stream stream)
529+
: base (constantPool, nameIndex, stream)
530+
{
531+
var length = stream.ReadNetworkUInt32 ();
532+
sourceFileIndex = stream.ReadNetworkUInt16 ();
533+
}
534+
535+
public override string ToString ()
536+
{
537+
return $"SourceFile('{FileName}')";
538+
}
539+
}
540+
541+
486542
// http://docs.oracle.com/javase/specs/jvms/se7/html/jvms-4.html#jvms-4.7.4
487543
public sealed class StackMapTableAttribute : AttributeInfo {
488544

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

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -98,6 +98,28 @@ public string PackageName {
9898
}
9999
}
100100

101+
public string SourceFileName {
102+
get {
103+
var sourceFile = Attributes.Get<SourceFileAttribute> ();
104+
return sourceFile == null ? null : sourceFile.FileName;
105+
}
106+
}
107+
108+
public bool TryGetEnclosingMethodInfo (out string declaringClass, out string declaringMethod, out string declaringDescriptor)
109+
{
110+
declaringClass = declaringMethod = declaringDescriptor = null;
111+
112+
var enclosingMethod = Attributes.Get<EnclosingMethodAttribute> ();
113+
if (enclosingMethod == null) {
114+
return false;
115+
}
116+
117+
declaringClass = enclosingMethod.Class.Name.Value;
118+
declaringMethod = enclosingMethod.Method.Name.Value;
119+
declaringDescriptor = enclosingMethod.Method.Descriptor.Value;
120+
return true;
121+
}
122+
101123
public ClassSignature GetSignature ()
102124
{
103125
if (this.signature != null)

src/Xamarin.Android.Tools.Bytecode/Tests/ExpectedInnerClassInfo.cs

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -15,10 +15,10 @@ class ExpectedInnerClassInfo {
1515

1616
public void Assert (InnerClassInfo info)
1717
{
18-
NAssert.AreEqual (InnerClassName, info.InnerClass.Name.Value);
19-
NAssert.AreEqual (OuterClassName, info.OuterClass.Name.Value);
20-
NAssert.AreEqual (InnerName, info.InnerName);
21-
NAssert.AreEqual (AccessFlags, info.InnerClassAccessFlags);
18+
NAssert.AreEqual (InnerClassName, info.InnerClass?.Name?.Value, $"InnerClassName");
19+
NAssert.AreEqual (OuterClassName, info.OuterClass?.Name?.Value, $"OuterClassName");
20+
NAssert.AreEqual (InnerName, info.InnerName, $"InnerName");
21+
NAssert.AreEqual (AccessFlags, info.InnerClassAccessFlags, $"InnerClassAccessFlags");
2222
}
2323
}
2424
}

src/Xamarin.Android.Tools.Bytecode/Tests/ExpectedMethodDeclaration.cs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -43,7 +43,7 @@ public void Assert (MethodInfo method)
4343
}
4444

4545
var parameters = method.GetParameters ();
46-
NAssert.AreEqual (Parameters.Count, parameters.Length);
46+
NAssert.AreEqual (Parameters.Count, parameters.Length, $"Method {Name} Parameter Count");
4747
for (int i = 0; i < Parameters.Count; ++i) {
4848
NAssert.AreEqual (Parameters [i].Name, parameters [i].Name, message);
4949
NAssert.AreEqual (i, parameters [i].Position, message);

src/Xamarin.Android.Tools.Bytecode/Tests/ExpectedTypeDeclaration.cs

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -33,9 +33,9 @@ public void Assert (ClassFile classDeclaration)
3333

3434
NAssert.AreEqual (Deprecated, classDeclaration.Attributes.Get<DeprecatedAttribute> () != null, FullName + " Deprecated");
3535

36-
NAssert.AreEqual (Interfaces.Count, classDeclaration.Interfaces.Count);
36+
NAssert.AreEqual (Interfaces.Count, classDeclaration.Interfaces.Count, $"{FullName} Interfaces Count");
3737
for (int i = 0; i < Interfaces.Count; ++i) {
38-
NAssert.AreEqual (Interfaces [i].BinaryName, classDeclaration.Interfaces [i].Name.Value);
38+
NAssert.AreEqual (Interfaces [i].BinaryName, classDeclaration.Interfaces [i].Name.Value, $"{FullName} Interface {i}");
3939
}
4040

4141
var innerClasses = classDeclaration.InnerClasses;
@@ -45,8 +45,8 @@ public void Assert (ClassFile classDeclaration)
4545

4646
var interfaces = classDeclaration.GetInterfaces ();
4747
for (int i = 0; i < Interfaces.Count; ++i) {
48-
NAssert.AreEqual (Interfaces [i].BinaryName, interfaces [i].BinaryName, FullName + " Interfaces");
49-
NAssert.AreEqual (Interfaces [i].TypeSignature, interfaces [i].TypeSignature, FullName + " Interfaces");
48+
NAssert.AreEqual (Interfaces [i].BinaryName, interfaces [i].BinaryName, FullName + " Interfaces BinaryName");
49+
NAssert.AreEqual (Interfaces [i].TypeSignature, interfaces [i].TypeSignature, FullName + " Interfaces TypeSignature");
5050
}
5151

5252
var signature = classDeclaration.GetSignature ();

src/Xamarin.Android.Tools.Bytecode/Tests/IJavaInterface.xml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@
99
final="false"
1010
name="IJavaInterface"
1111
jni-signature="Lcom/xamarin/IJavaInterface;"
12+
source-file-name="IJavaInterface.java"
1213
static="false"
1314
visibility="">
1415
<typeParameters>

src/Xamarin.Android.Tools.Bytecode/Tests/JavaAnnotation.xml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@
99
final="false"
1010
name="JavaAnnotation"
1111
jni-signature="Lcom/xamarin/JavaAnnotation;"
12+
source-file-name="JavaAnnotation.java"
1213
static="false"
1314
visibility="public">
1415
<implements

src/Xamarin.Android.Tools.Bytecode/Tests/JavaEnum.xml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@
1212
final="true"
1313
name="JavaEnum"
1414
jni-signature="Lcom/xamarin/JavaEnum;"
15+
source-file-name="JavaType.java"
1516
static="false"
1617
visibility="">
1718
<method
Lines changed: 41 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,41 @@
1+
<api
2+
api-source="class-parse">
3+
<package
4+
name="com.xamarin"
5+
jni-name="com/xamarin">
6+
<class
7+
abstract="false"
8+
deprecated="not deprecated"
9+
enclosing-method-jni-type="Lcom/xamarin/JavaType;"
10+
enclosing-method-name="action"
11+
enclosing-method-signature="(Ljava/lang/Object;)V"
12+
jni-extends="Ljava/lang/Object;"
13+
extends="java.lang.Object"
14+
extends-generic-aware="java.lang.Object"
15+
final="false"
16+
name="JavaType.1"
17+
jni-signature="Lcom/xamarin/JavaType$1;"
18+
source-file-name="JavaType.java"
19+
static="false"
20+
visibility="">
21+
<implements
22+
name="java.lang.Runnable"
23+
name-generic-aware="java.lang.Runnable"
24+
jni-type="Ljava/lang/Runnable;" />
25+
<method
26+
abstract="false"
27+
deprecated="not deprecated"
28+
final="false"
29+
name="run"
30+
native="false"
31+
return="void"
32+
jni-return="V"
33+
static="false"
34+
synchronized="false"
35+
visibility="public"
36+
bridge="false"
37+
synthetic="false"
38+
jni-signature="()V" />
39+
</class>
40+
</package>
41+
</api>

src/Xamarin.Android.Tools.Bytecode/Tests/JavaType$ASC.xml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@
1212
final="false"
1313
name="JavaType.ASC"
1414
jni-signature="Lcom/xamarin/JavaType$ASC;"
15+
source-file-name="JavaType.java"
1516
static="true"
1617
visibility="" />
1718
</package>

src/Xamarin.Android.Tools.Bytecode/Tests/JavaType$PSC.xml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@
1212
final="false"
1313
name="JavaType.PSC"
1414
jni-signature="Lcom/xamarin/JavaType$PSC;"
15+
source-file-name="JavaType.java"
1516
static="true"
1617
visibility="public">
1718
<constructor

src/Xamarin.Android.Tools.Bytecode/Tests/JavaType$RNC$RPNC.xml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@
1212
final="false"
1313
name="JavaType.RNC.RPNC"
1414
jni-signature="Lcom/xamarin/JavaType$RNC$RPNC;"
15+
source-file-name="JavaType.java"
1516
static="false"
1617
visibility="public">
1718
<typeParameters>

src/Xamarin.Android.Tools.Bytecode/Tests/JavaType$RNC.xml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@
1212
final="false"
1313
name="JavaType.RNC"
1414
jni-signature="Lcom/xamarin/JavaType$RNC;"
15+
source-file-name="JavaType.java"
1516
static="false"
1617
visibility="protected">
1718
<typeParameters>
Lines changed: 64 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,64 @@
1+
using System;
2+
3+
using Xamarin.Android.Tools.Bytecode;
4+
5+
using NUnit.Framework;
6+
7+
namespace Xamarin.Android.Tools.BytecodeTests {
8+
9+
[TestFixture]
10+
public class JavaType_1Tests : ClassFileFixture {
11+
12+
const string JavaType = "JavaType$1";
13+
14+
[Test]
15+
public void ClassFileDescription ()
16+
{
17+
var c = LoadClassFile (JavaType + ".class");
18+
new ExpectedTypeDeclaration {
19+
MajorVersion = 0x32,
20+
MinorVersion = 0,
21+
ConstantPoolCount = 47,
22+
AccessFlags = ClassAccessFlags.Super,
23+
FullName = "com/xamarin/JavaType$1",
24+
Superclass = new TypeInfo ("java/lang/Object", "Ljava/lang/Object;"),
25+
InnerClasses = {
26+
new ExpectedInnerClassInfo {
27+
InnerClassName = "com/xamarin/JavaType$1",
28+
OuterClassName = null,
29+
InnerName = null,
30+
AccessFlags = 0,
31+
},
32+
},
33+
Interfaces = {
34+
new TypeInfo ("java/lang/Runnable"),
35+
},
36+
Fields = {
37+
new ExpectedFieldDeclaration {
38+
Name = "this$0",
39+
Descriptor = "Lcom/xamarin/JavaType;",
40+
AccessFlags = FieldAccessFlags.Final | FieldAccessFlags.Synthetic,
41+
},
42+
},
43+
Methods = {
44+
new ExpectedMethodDeclaration {
45+
Name = "<init>",
46+
AccessFlags = 0,
47+
ReturnDescriptor = "V",
48+
},
49+
new ExpectedMethodDeclaration {
50+
Name = "run",
51+
AccessFlags = MethodAccessFlags.Public,
52+
ReturnDescriptor = "V",
53+
},
54+
}
55+
}.Assert (c);
56+
}
57+
58+
[Test]
59+
public void XmlDescription ()
60+
{
61+
AssertXmlDeclaration (JavaType + ".class", JavaType + ".xml");
62+
}
63+
}
64+
}

src/Xamarin.Android.Tools.Bytecode/Tests/JavaType.xml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@
1212
final="false"
1313
name="JavaType"
1414
jni-signature="Lcom/xamarin/JavaType;"
15+
source-file-name="JavaType.java"
1516
static="false"
1617
visibility="public">
1718
<typeParameters>

src/Xamarin.Android.Tools.Bytecode/Tests/JavaTypeTests.cs

Lines changed: 8 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -20,7 +20,7 @@ public void ClassFile_WithJavaType_class ()
2020
new ExpectedTypeDeclaration {
2121
MajorVersion = 0x32,
2222
MinorVersion = 0,
23-
ConstantPoolCount = 184,
23+
ConstantPoolCount = 195,
2424
AccessFlags = ClassAccessFlags.Public | ClassAccessFlags.Super,
2525
FullName = "com/xamarin/JavaType",
2626
Superclass = new TypeInfo ("java/lang/Object", "Ljava/lang/Object;"),
@@ -54,6 +54,12 @@ public void ClassFile_WithJavaType_class ()
5454
InnerName = "PSC",
5555
AccessFlags = ClassAccessFlags.Public | ClassAccessFlags.Static | ClassAccessFlags.Abstract,
5656
},
57+
new ExpectedInnerClassInfo {
58+
InnerClassName = "com/xamarin/JavaType$1",
59+
OuterClassName = null,
60+
InnerName = null,
61+
AccessFlags = 0,
62+
},
5763
},
5864
Fields = {
5965
new ExpectedFieldDeclaration {
@@ -132,7 +138,7 @@ public void ClassFile_WithJavaType_class ()
132138
Name = "STATIC_FINAL_STRING",
133139
Descriptor = "Ljava/lang/String;",
134140
AccessFlags = FieldAccessFlags.Public | FieldAccessFlags.Static | FieldAccessFlags.Final,
135-
ConstantValue = "String(stringIndex=176 Utf8=\"Hello, \\\"embedded\0Nulls\" and \ud83d\udca9!\")",
141+
ConstantValue = "String(stringIndex=185 Utf8=\"Hello, \\\"embedded\0Nulls\" and \ud83d\udca9!\")",
136142
},
137143
new ExpectedFieldDeclaration {
138144
Name = "STATIC_FINAL_BOOL_FALSE",

src/Xamarin.Android.Tools.Bytecode/Tests/NonGenericGlobalType.xml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@
1212
final="false"
1313
name="NonGenericGlobalType"
1414
jni-signature="LNonGenericGlobalType;"
15+
source-file-name="NonGenericGlobalType.java"
1516
static="false"
1617
visibility="" />
1718
</package>

0 commit comments

Comments
 (0)