diff --git a/src/Xamarin.Android.Tools.Bytecode/AttributeInfo.cs b/src/Xamarin.Android.Tools.Bytecode/AttributeInfo.cs index b9d882e78..4af73a9e5 100644 --- a/src/Xamarin.Android.Tools.Bytecode/AttributeInfo.cs +++ b/src/Xamarin.Android.Tools.Bytecode/AttributeInfo.cs @@ -40,10 +40,12 @@ public class AttributeInfo { public const string ConstantValue = "ConstantValue"; public const string Deprecated = "Deprecated"; public const string Exceptions = "Exceptions"; + public const string EnclosingMethod = "EnclosingMethod"; public const string InnerClasses = "InnerClasses"; public const string LocalVariableTable = "LocalVariableTable"; public const string MethodParameters = "MethodParameters"; public const string Signature = "Signature"; + public const string SourceFile = "SourceFile"; public const string StackMapTable = "StackMapTable"; ushort nameIndex; @@ -69,11 +71,13 @@ public string Name { { typeof (CodeAttribute), Code }, { typeof (ConstantValueAttribute), ConstantValue }, { typeof (DeprecatedAttribute), Deprecated }, + { typeof (EnclosingMethodAttribute), EnclosingMethod }, { typeof (ExceptionsAttribute), Exceptions }, { typeof (InnerClassesAttribute), InnerClasses }, { typeof (LocalVariableTableAttribute), LocalVariableTable }, { typeof (MethodParametersAttribute), MethodParameters }, { typeof (SignatureAttribute), Signature }, + { typeof (SourceFileAttribute), SourceFile }, { typeof (StackMapTableAttribute), StackMapTable }, }; @@ -100,11 +104,13 @@ static AttributeInfo CreateAttribute (string name, ConstantPool constantPool, us case Code: return new CodeAttribute (constantPool, nameIndex, stream); case ConstantValue: return new ConstantValueAttribute (constantPool, nameIndex, stream); case Deprecated: return new DeprecatedAttribute (constantPool, nameIndex, stream); + case EnclosingMethod: return new EnclosingMethodAttribute (constantPool, nameIndex, stream); case Exceptions: return new ExceptionsAttribute (constantPool, nameIndex, stream); 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 Signature: return new SignatureAttribute (constantPool, nameIndex, stream); + case SourceFile: return new SourceFileAttribute (constantPool, nameIndex, stream); case StackMapTable: return new StackMapTableAttribute (constantPool, nameIndex, stream); default: return new UnknownAttribute (constantPool, nameIndex, stream); } @@ -221,6 +227,33 @@ public override string ToString () } } + // https://docs.oracle.com/javase/specs/jvms/se7/html/jvms-4.html#jvms-4.7.7 + public sealed class EnclosingMethodAttribute : AttributeInfo { + + ushort classIndex, methodIndex; + + public ConstantPoolClassItem Class { + get {return (ConstantPoolClassItem) ConstantPool [classIndex];} + } + + public ConstantPoolNameAndTypeItem Method { + get {return methodIndex == 0 ? null : (ConstantPoolNameAndTypeItem) ConstantPool [methodIndex];} + } + + public EnclosingMethodAttribute (ConstantPool constantPool, ushort nameIndex, Stream stream) + : base (constantPool, nameIndex, stream) + { + var length = stream.ReadNetworkUInt32 (); + classIndex = stream.ReadNetworkUInt16 (); + methodIndex = stream.ReadNetworkUInt16 (); + } + + public override string ToString () + { + return $"EnclosingMethod({Class}, {Method})"; + } + } + // http://docs.oracle.com/javase/specs/jvms/se7/html/jvms-4.html#jvms-4.7.5 public sealed class ExceptionsAttribute : AttributeInfo { @@ -483,6 +516,29 @@ public override string ToString () } } + // https://docs.oracle.com/javase/specs/jvms/se7/html/jvms-4.html#jvms-4.7.10 + public sealed class SourceFileAttribute : AttributeInfo { + + ushort sourceFileIndex; + + public string FileName { + get {return ((ConstantPoolUtf8Item) ConstantPool [sourceFileIndex]).Value;} + } + + public SourceFileAttribute (ConstantPool constantPool, ushort nameIndex, Stream stream) + : base (constantPool, nameIndex, stream) + { + var length = stream.ReadNetworkUInt32 (); + sourceFileIndex = stream.ReadNetworkUInt16 (); + } + + public override string ToString () + { + return $"SourceFile('{FileName}')"; + } + } + + // http://docs.oracle.com/javase/specs/jvms/se7/html/jvms-4.html#jvms-4.7.4 public sealed class StackMapTableAttribute : AttributeInfo { diff --git a/src/Xamarin.Android.Tools.Bytecode/ClassFile.cs b/src/Xamarin.Android.Tools.Bytecode/ClassFile.cs index 30e94d92d..1e50293e8 100644 --- a/src/Xamarin.Android.Tools.Bytecode/ClassFile.cs +++ b/src/Xamarin.Android.Tools.Bytecode/ClassFile.cs @@ -98,6 +98,28 @@ public string PackageName { } } + public string SourceFileName { + get { + var sourceFile = Attributes.Get (); + return sourceFile == null ? null : sourceFile.FileName; + } + } + + public bool TryGetEnclosingMethodInfo (out string declaringClass, out string declaringMethod, out string declaringDescriptor) + { + declaringClass = declaringMethod = declaringDescriptor = null; + + var enclosingMethod = Attributes.Get (); + if (enclosingMethod == null) { + return false; + } + + declaringClass = enclosingMethod.Class.Name.Value; + declaringMethod = enclosingMethod.Method.Name.Value; + declaringDescriptor = enclosingMethod.Method.Descriptor.Value; + return true; + } + public ClassSignature GetSignature () { if (this.signature != null) diff --git a/src/Xamarin.Android.Tools.Bytecode/Tests/ExpectedInnerClassInfo.cs b/src/Xamarin.Android.Tools.Bytecode/Tests/ExpectedInnerClassInfo.cs index 717bda7bb..4775edabf 100644 --- a/src/Xamarin.Android.Tools.Bytecode/Tests/ExpectedInnerClassInfo.cs +++ b/src/Xamarin.Android.Tools.Bytecode/Tests/ExpectedInnerClassInfo.cs @@ -15,10 +15,10 @@ class ExpectedInnerClassInfo { public void Assert (InnerClassInfo info) { - NAssert.AreEqual (InnerClassName, info.InnerClass.Name.Value); - NAssert.AreEqual (OuterClassName, info.OuterClass.Name.Value); - NAssert.AreEqual (InnerName, info.InnerName); - NAssert.AreEqual (AccessFlags, info.InnerClassAccessFlags); + NAssert.AreEqual (InnerClassName, info.InnerClass?.Name?.Value, $"InnerClassName"); + NAssert.AreEqual (OuterClassName, info.OuterClass?.Name?.Value, $"OuterClassName"); + NAssert.AreEqual (InnerName, info.InnerName, $"InnerName"); + NAssert.AreEqual (AccessFlags, info.InnerClassAccessFlags, $"InnerClassAccessFlags"); } } } diff --git a/src/Xamarin.Android.Tools.Bytecode/Tests/ExpectedMethodDeclaration.cs b/src/Xamarin.Android.Tools.Bytecode/Tests/ExpectedMethodDeclaration.cs index 845a95031..9b2de077e 100644 --- a/src/Xamarin.Android.Tools.Bytecode/Tests/ExpectedMethodDeclaration.cs +++ b/src/Xamarin.Android.Tools.Bytecode/Tests/ExpectedMethodDeclaration.cs @@ -43,7 +43,7 @@ public void Assert (MethodInfo method) } var parameters = method.GetParameters (); - NAssert.AreEqual (Parameters.Count, parameters.Length); + NAssert.AreEqual (Parameters.Count, parameters.Length, $"Method {Name} Parameter Count"); for (int i = 0; i < Parameters.Count; ++i) { NAssert.AreEqual (Parameters [i].Name, parameters [i].Name, message); NAssert.AreEqual (i, parameters [i].Position, message); diff --git a/src/Xamarin.Android.Tools.Bytecode/Tests/ExpectedTypeDeclaration.cs b/src/Xamarin.Android.Tools.Bytecode/Tests/ExpectedTypeDeclaration.cs index 18e3846e7..98447adbf 100644 --- a/src/Xamarin.Android.Tools.Bytecode/Tests/ExpectedTypeDeclaration.cs +++ b/src/Xamarin.Android.Tools.Bytecode/Tests/ExpectedTypeDeclaration.cs @@ -33,9 +33,9 @@ public void Assert (ClassFile classDeclaration) NAssert.AreEqual (Deprecated, classDeclaration.Attributes.Get () != null, FullName + " Deprecated"); - NAssert.AreEqual (Interfaces.Count, classDeclaration.Interfaces.Count); + NAssert.AreEqual (Interfaces.Count, classDeclaration.Interfaces.Count, $"{FullName} Interfaces Count"); for (int i = 0; i < Interfaces.Count; ++i) { - NAssert.AreEqual (Interfaces [i].BinaryName, classDeclaration.Interfaces [i].Name.Value); + NAssert.AreEqual (Interfaces [i].BinaryName, classDeclaration.Interfaces [i].Name.Value, $"{FullName} Interface {i}"); } var innerClasses = classDeclaration.InnerClasses; @@ -45,8 +45,8 @@ public void Assert (ClassFile classDeclaration) var interfaces = classDeclaration.GetInterfaces (); for (int i = 0; i < Interfaces.Count; ++i) { - NAssert.AreEqual (Interfaces [i].BinaryName, interfaces [i].BinaryName, FullName + " Interfaces"); - NAssert.AreEqual (Interfaces [i].TypeSignature, interfaces [i].TypeSignature, FullName + " Interfaces"); + NAssert.AreEqual (Interfaces [i].BinaryName, interfaces [i].BinaryName, FullName + " Interfaces BinaryName"); + NAssert.AreEqual (Interfaces [i].TypeSignature, interfaces [i].TypeSignature, FullName + " Interfaces TypeSignature"); } var signature = classDeclaration.GetSignature (); diff --git a/src/Xamarin.Android.Tools.Bytecode/Tests/IJavaInterface.xml b/src/Xamarin.Android.Tools.Bytecode/Tests/IJavaInterface.xml index def9f29ca..0bee5c8a3 100644 --- a/src/Xamarin.Android.Tools.Bytecode/Tests/IJavaInterface.xml +++ b/src/Xamarin.Android.Tools.Bytecode/Tests/IJavaInterface.xml @@ -9,6 +9,7 @@ final="false" name="IJavaInterface" jni-signature="Lcom/xamarin/IJavaInterface;" + source-file-name="IJavaInterface.java" static="false" visibility=""> diff --git a/src/Xamarin.Android.Tools.Bytecode/Tests/JavaAnnotation.xml b/src/Xamarin.Android.Tools.Bytecode/Tests/JavaAnnotation.xml index 6709d3420..1e45c17b8 100644 --- a/src/Xamarin.Android.Tools.Bytecode/Tests/JavaAnnotation.xml +++ b/src/Xamarin.Android.Tools.Bytecode/Tests/JavaAnnotation.xml @@ -9,6 +9,7 @@ final="false" name="JavaAnnotation" jni-signature="Lcom/xamarin/JavaAnnotation;" + source-file-name="JavaAnnotation.java" static="false" visibility="public"> + + + + + + + diff --git a/src/Xamarin.Android.Tools.Bytecode/Tests/JavaType$ASC.xml b/src/Xamarin.Android.Tools.Bytecode/Tests/JavaType$ASC.xml index 352dce5c2..c46d3168c 100644 --- a/src/Xamarin.Android.Tools.Bytecode/Tests/JavaType$ASC.xml +++ b/src/Xamarin.Android.Tools.Bytecode/Tests/JavaType$ASC.xml @@ -12,6 +12,7 @@ final="false" name="JavaType.ASC" jni-signature="Lcom/xamarin/JavaType$ASC;" + source-file-name="JavaType.java" static="true" visibility="" /> diff --git a/src/Xamarin.Android.Tools.Bytecode/Tests/JavaType$PSC.xml b/src/Xamarin.Android.Tools.Bytecode/Tests/JavaType$PSC.xml index c3c613c88..89f2cec40 100644 --- a/src/Xamarin.Android.Tools.Bytecode/Tests/JavaType$PSC.xml +++ b/src/Xamarin.Android.Tools.Bytecode/Tests/JavaType$PSC.xml @@ -12,6 +12,7 @@ final="false" name="JavaType.PSC" jni-signature="Lcom/xamarin/JavaType$PSC;" + source-file-name="JavaType.java" static="true" visibility="public"> diff --git a/src/Xamarin.Android.Tools.Bytecode/Tests/JavaType$RNC.xml b/src/Xamarin.Android.Tools.Bytecode/Tests/JavaType$RNC.xml index 9b175914d..a9366418f 100644 --- a/src/Xamarin.Android.Tools.Bytecode/Tests/JavaType$RNC.xml +++ b/src/Xamarin.Android.Tools.Bytecode/Tests/JavaType$RNC.xml @@ -12,6 +12,7 @@ final="false" name="JavaType.RNC" jni-signature="Lcom/xamarin/JavaType$RNC;" + source-file-name="JavaType.java" static="false" visibility="protected"> diff --git a/src/Xamarin.Android.Tools.Bytecode/Tests/JavaType.1Tests.cs b/src/Xamarin.Android.Tools.Bytecode/Tests/JavaType.1Tests.cs new file mode 100644 index 000000000..83294c424 --- /dev/null +++ b/src/Xamarin.Android.Tools.Bytecode/Tests/JavaType.1Tests.cs @@ -0,0 +1,64 @@ +using System; + +using Xamarin.Android.Tools.Bytecode; + +using NUnit.Framework; + +namespace Xamarin.Android.Tools.BytecodeTests { + + [TestFixture] + public class JavaType_1Tests : ClassFileFixture { + + const string JavaType = "JavaType$1"; + + [Test] + public void ClassFileDescription () + { + var c = LoadClassFile (JavaType + ".class"); + new ExpectedTypeDeclaration { + MajorVersion = 0x32, + MinorVersion = 0, + ConstantPoolCount = 47, + AccessFlags = ClassAccessFlags.Super, + FullName = "com/xamarin/JavaType$1", + Superclass = new TypeInfo ("java/lang/Object", "Ljava/lang/Object;"), + InnerClasses = { + new ExpectedInnerClassInfo { + InnerClassName = "com/xamarin/JavaType$1", + OuterClassName = null, + InnerName = null, + AccessFlags = 0, + }, + }, + Interfaces = { + new TypeInfo ("java/lang/Runnable"), + }, + Fields = { + new ExpectedFieldDeclaration { + Name = "this$0", + Descriptor = "Lcom/xamarin/JavaType;", + AccessFlags = FieldAccessFlags.Final | FieldAccessFlags.Synthetic, + }, + }, + Methods = { + new ExpectedMethodDeclaration { + Name = "", + AccessFlags = 0, + ReturnDescriptor = "V", + }, + new ExpectedMethodDeclaration { + Name = "run", + AccessFlags = MethodAccessFlags.Public, + ReturnDescriptor = "V", + }, + } + }.Assert (c); + } + + [Test] + public void XmlDescription () + { + AssertXmlDeclaration (JavaType + ".class", JavaType + ".xml"); + } + } +} diff --git a/src/Xamarin.Android.Tools.Bytecode/Tests/JavaType.xml b/src/Xamarin.Android.Tools.Bytecode/Tests/JavaType.xml index 3e34f096e..428d687ab 100644 --- a/src/Xamarin.Android.Tools.Bytecode/Tests/JavaType.xml +++ b/src/Xamarin.Android.Tools.Bytecode/Tests/JavaType.xml @@ -12,6 +12,7 @@ final="false" name="JavaType" jni-signature="Lcom/xamarin/JavaType;" + source-file-name="JavaType.java" static="false" visibility="public"> diff --git a/src/Xamarin.Android.Tools.Bytecode/Tests/JavaTypeTests.cs b/src/Xamarin.Android.Tools.Bytecode/Tests/JavaTypeTests.cs index 234825b31..00ed89f87 100644 --- a/src/Xamarin.Android.Tools.Bytecode/Tests/JavaTypeTests.cs +++ b/src/Xamarin.Android.Tools.Bytecode/Tests/JavaTypeTests.cs @@ -20,7 +20,7 @@ public void ClassFile_WithJavaType_class () new ExpectedTypeDeclaration { MajorVersion = 0x32, MinorVersion = 0, - ConstantPoolCount = 184, + ConstantPoolCount = 195, AccessFlags = ClassAccessFlags.Public | ClassAccessFlags.Super, FullName = "com/xamarin/JavaType", Superclass = new TypeInfo ("java/lang/Object", "Ljava/lang/Object;"), @@ -54,6 +54,12 @@ public void ClassFile_WithJavaType_class () InnerName = "PSC", AccessFlags = ClassAccessFlags.Public | ClassAccessFlags.Static | ClassAccessFlags.Abstract, }, + new ExpectedInnerClassInfo { + InnerClassName = "com/xamarin/JavaType$1", + OuterClassName = null, + InnerName = null, + AccessFlags = 0, + }, }, Fields = { new ExpectedFieldDeclaration { @@ -132,7 +138,7 @@ public void ClassFile_WithJavaType_class () Name = "STATIC_FINAL_STRING", Descriptor = "Ljava/lang/String;", AccessFlags = FieldAccessFlags.Public | FieldAccessFlags.Static | FieldAccessFlags.Final, - ConstantValue = "String(stringIndex=176 Utf8=\"Hello, \\\"embedded\0Nulls\" and \ud83d\udca9!\")", + ConstantValue = "String(stringIndex=185 Utf8=\"Hello, \\\"embedded\0Nulls\" and \ud83d\udca9!\")", }, new ExpectedFieldDeclaration { Name = "STATIC_FINAL_BOOL_FALSE", diff --git a/src/Xamarin.Android.Tools.Bytecode/Tests/NonGenericGlobalType.xml b/src/Xamarin.Android.Tools.Bytecode/Tests/NonGenericGlobalType.xml index c363398d4..e785908f2 100644 --- a/src/Xamarin.Android.Tools.Bytecode/Tests/NonGenericGlobalType.xml +++ b/src/Xamarin.Android.Tools.Bytecode/Tests/NonGenericGlobalType.xml @@ -12,6 +12,7 @@ final="false" name="NonGenericGlobalType" jni-signature="LNonGenericGlobalType;" + source-file-name="NonGenericGlobalType.java" static="false" visibility="" /> diff --git a/src/Xamarin.Android.Tools.Bytecode/Tests/ParameterFixup.xml b/src/Xamarin.Android.Tools.Bytecode/Tests/ParameterFixup.xml index 70f2bdfc1..af03cb2ee 100644 --- a/src/Xamarin.Android.Tools.Bytecode/Tests/ParameterFixup.xml +++ b/src/Xamarin.Android.Tools.Bytecode/Tests/ParameterFixup.xml @@ -9,6 +9,7 @@ final="false" name="IParameterInterface" jni-signature="Lcom/xamarin/IParameterInterface;" + source-file-name="IParameterInterface.java" static="false" visibility=""> diff --git a/src/Xamarin.Android.Tools.Bytecode/Tests/Xamarin.Android.Tools.Bytecode-Tests.csproj b/src/Xamarin.Android.Tools.Bytecode/Tests/Xamarin.Android.Tools.Bytecode-Tests.csproj index 865341605..df7c1560b 100644 --- a/src/Xamarin.Android.Tools.Bytecode/Tests/Xamarin.Android.Tools.Bytecode-Tests.csproj +++ b/src/Xamarin.Android.Tools.Bytecode/Tests/Xamarin.Android.Tools.Bytecode-Tests.csproj @@ -49,6 +49,7 @@ + @@ -81,6 +82,9 @@ JavaType.xml + + JavaType$1.xml + IJavaInterface.xml @@ -102,6 +106,9 @@ JavaType.class + + JavaType$1.class + JavaType$ASC.class diff --git a/src/Xamarin.Android.Tools.Bytecode/Tests/java/com/xamarin/JavaType.java b/src/Xamarin.Android.Tools.Bytecode/Tests/java/com/xamarin/JavaType.java index 42da822a6..34f2d3e28 100644 --- a/src/Xamarin.Android.Tools.Bytecode/Tests/java/com/xamarin/JavaType.java +++ b/src/Xamarin.Android.Tools.Bytecode/Tests/java/com/xamarin/JavaType.java @@ -107,9 +107,15 @@ public void run () { @Deprecated public void action (Object value) { - Object local = new Object (); - local.toString (); - int i = 42; + Object local = new Object (); + local.toString (); + int i = 42; + Runnable r = new Runnable () { + public void run() { + System.out.println ("foo"); + } + }; + r.run(); } public java.lang.Integer func (String[] values) { diff --git a/src/Xamarin.Android.Tools.Bytecode/XmlClassDeclarationBuilder.cs b/src/Xamarin.Android.Tools.Bytecode/XmlClassDeclarationBuilder.cs index 6dd0d653e..a9626a5ea 100644 --- a/src/Xamarin.Android.Tools.Bytecode/XmlClassDeclarationBuilder.cs +++ b/src/Xamarin.Android.Tools.Bytecode/XmlClassDeclarationBuilder.cs @@ -34,11 +34,13 @@ public XElement ToXElement () return new XElement (GetElementName (), new XAttribute ("abstract", (classFile.AccessFlags & ClassAccessFlags.Abstract) != 0), new XAttribute ("deprecated", GetDeprecatedValue (classFile.Attributes)), + GetEnclosingMethod (), GetExtends (), GetExtendsGenericAware (), new XAttribute ("final", (classFile.AccessFlags & ClassAccessFlags.Final) != 0), new XAttribute ("name", GetThisClassName ()), new XAttribute ("jni-signature", "L" + classFile.ThisClass.Name.Value + ";"), + GetSourceFile (), new XAttribute ("static", classFile.IsStatic), new XAttribute ("visibility", GetVisibility (classFile.Visibility)), GetTypeParmeters (signature == null ? null : signature.TypeParameters), @@ -63,6 +65,19 @@ static string GetDeprecatedValue (AttributeCollection attributes) return "deprecated"; } + XAttribute[] GetEnclosingMethod () + { + string declaringClass, declaringMethod, declaringDescriptor; + if (!classFile.TryGetEnclosingMethodInfo (out declaringClass, out declaringMethod, out declaringDescriptor)) { + return null; + } + return new []{ + new XAttribute ("enclosing-method-jni-type", "L" + declaringClass + ";"), + new XAttribute ("enclosing-method-name", declaringMethod), + new XAttribute ("enclosing-method-signature", declaringDescriptor), + }; + } + XAttribute[] GetExtends () { if (IsInterface) @@ -254,6 +269,14 @@ static string GetBuiltinName (string value, ref int index) return null; } + XAttribute GetSourceFile () + { + var sourceFile = classFile.SourceFileName; + if (sourceFile == null) + return null; + return new XAttribute ("source-file-name", sourceFile); + } + XElement GetTypeParmeters (TypeParameterInfoCollection typeParameters) { if (typeParameters == null || typeParameters.Count == 0)