Skip to content

Commit

Permalink
[class-parse] Add Kotlin metadata to -dump. (#1150)
Browse files Browse the repository at this point in the history
Context: 678c4bd
Context: https://discord.com/channels/732297728826277939/732297837953679412/1162418256615846030
Context: https://discord.com/channels/732297728826277939/732297837953679412/1162422742256205954

A customer reports that when binding
[androidx.emoji2.emoji2-emojipicker-1.4.0][0],
[`EmojiPickerView.setOnEmojiPickedListener()`][1] wasn't being bound.
Subsequent investigation showed that it wasn't being bound because it
had `//method[@visibility="kotlin-internal"]`.

…but *why* did `class-parse` indicate that
`EmojiPickerView.setOnEmojiPickedListener()` has visibility of
`kotlin-internal` and not `public`?

In order to assist this question, update `class-parse -dump` to also
dump out the parsed Kotlin metadata blob.

Example:

	% dotnet …/class-parse.dll -dump EmojiPickerView.class
	…
	Kotlin Class Metadata [1.8.0]: {
	  "$id": "1",
	  "CompanionObjectName": "Companion",
	  "Constructors": { ... },
	  "EnumEntries": {
	    "$id": "14",
	    "$values": []
	  },
	  "Flags": 6,
	  "FullyQualifiedName": null,
	  "Inheritability": 0,
	  "NestedClassNames": {
	    "$id": "15",
	    "$values": [
	      "Companion"
	    ]
	  },
	  "ObjectType": 0,
	  "SealedSubclassFullyQualifiedNames": null,
	  "SuperTypeIds": null,
	  "SuperTypes": {
	    "$id": "16",
	    "$values": [
	      {
	        "$id": "17",
	        "Arguments": {
	          "$id": "18",
	          "$values": []
	        },
	        "Nullable": false,
	        "FlexibleTypeCapabilitiesId": null,
	        "FlexibleUpperBound": null,
	        "FlexibleUpperBoundId": 0,
	        "ClassName": "android/widget/FrameLayout;",
	        "TypeParameter": null,
	        "TypeParameterName": null,
	        "TypeAliasName": null,
	        "OuterType": null,
	        "OuterTypeId": null,
	        "AbbreviatedType": null,
	        "AbbreviatedTypeId": null,
	        "Flags": 0
	      }
	    ]
	  },
	  "TypeParameters": {
	    "$id": "19",
	    "$values": []
	  },
	  "VersionRequirements": null,
	  "Visibility": 3,
	  "Functions": {
	    "$id": "20",
	    "$values": [
	      ...
	      {
	        "$id": "145",
	        "Name": "setOnEmojiPickedListener",
	        "JvmName": "setOnEmojiPickedListener",
	        "JvmSignature": null,
	        "Flags": 6,
	        "ReturnType": {
	          "$id": "146",
	          "Arguments": {
	            "$id": "147",
	            "$values": []
	          },
	          "Nullable": false,
	          "FlexibleTypeCapabilitiesId": null,
	          "FlexibleUpperBound": null,
	          "FlexibleUpperBoundId": 0,
	          "ClassName": "kotlin/Unit",
	          "TypeParameter": null,
	          "TypeParameterName": null,
	          "TypeAliasName": null,
	          "OuterType": null,
	          "OuterTypeId": null,
	          "AbbreviatedType": null,
	          "AbbreviatedTypeId": null,
	          "Flags": 0
	        },
	        "ReturnTypeId": 0,
	        "TypeParameters": {
	          "$id": "148",
	          "$values": []
	        },
	        "ReceiverType": null,
	        "ReceiverTypeId": 0,
	        "TypeTable": null,
	        "Contract": null,
	        "ValueParameters": {
	          "$id": "149",
	          "$values": [
	            {
	              "$id": "150",
	              "Flags": 0,
	              "Name": "onEmojiPickedListener",
	              "Type": {
	                "$id": "151",
	                "Arguments": {
	                  "$id": "152",
	                  "$values": [
	                    {
	                      "$id": "153",
	                      "Projection": 2,
	                      "Type": {
	                        "$id": "154",
	                        "Arguments": {
	                          "$id": "155",
	                          "$values": []
	                        },
	                        "Nullable": false,
	                        "FlexibleTypeCapabilitiesId": null,
	                        "FlexibleUpperBound": null,
	                        "FlexibleUpperBoundId": 0,
	                        "ClassName": "androidx/emoji2/emojipicker/EmojiViewItem;",
	                        "TypeParameter": null,
	                        "TypeParameterName": null,
	                        "TypeAliasName": null,
	                        "OuterType": null,
	                        "OuterTypeId": null,
	                        "AbbreviatedType": null,
	                        "AbbreviatedTypeId": null,
	                        "Flags": 0
	                      },
	                      "TypeId": 0
	                    }
	                  ]
	                },
	                "Nullable": true,
	                "FlexibleTypeCapabilitiesId": null,
	                "FlexibleUpperBound": null,
	                "FlexibleUpperBoundId": 0,
	                "ClassName": "androidx/core/util/Consumer;",
	                "TypeParameter": null,
	                "TypeParameterName": null,
	                "TypeAliasName": null,
	                "OuterType": null,
	                "OuterTypeId": null,
	                "AbbreviatedType": null,
	                "AbbreviatedTypeId": null,
	                "Flags": 0
	              },
	              "TypeId": 0,
	              "VarArgElementType": null,
	              "VarArgElementTypeId": 0
	            }
	          ]
	        },
	        "VersionRequirements": null
	      },
	  ...
	  },
	  "Properties": {... },
	  "TypeAliases": {
	    "$id": "230",
	    "$values": []
	  },
	  "TypeTable": null,
	  "VersionRequirementTable": {
	    "$id": "231",
	    "Requirements": {
	      "$id": "232",
	      "$values": [
	        {
	          "$id": "233",
	          "Version": 25,
	          "VersionFull": 0,
	          "Level": 1,
	          "ErrorCode": 0,
	          "Message": 0,
	          "VersionKind": 0
	        }
	      ]
	    }
	  }
	}

	Kotlin Metadata String Table: [
	  "Landroidx/emoji2/emojipicker/EmojiPickerView;",
	  "Landroid/widget/FrameLayout;",
	  "context",
	  "Landroid/content/Context;",
	  "attrs",
	  "Landroid/util/AttributeSet;",
	  "defStyleAttr",
	  "",
	  "(Landroid/content/Context;Landroid/util/AttributeSet;I)V",
	  "_emojiGridRows",
	  "",
	  "Ljava/lang/Float;",
	  "bodyAdapter",
	  "Landroidx/emoji2/emojipicker/EmojiPickerBodyAdapter;",
	  "value",
	  "emojiGridColumns",
	  "getEmojiGridColumns",
	  "()I",
	  "setEmojiGridColumns",
	  "(I)V",
	  "emojiGridRows",
	  "getEmojiGridRows",
	  "()F",
	  "setEmojiGridRows",
	  "(F)V",
	  "emojiPickerItems",
	  "Landroidx/emoji2/emojipicker/EmojiPickerItems;",
	  "onEmojiPickedListener",
	  "Landroidx/core/util/Consumer;",
	  "Landroidx/emoji2/emojipicker/EmojiViewItem;",
	  "recentEmojiProvider",
	  "Landroidx/emoji2/emojipicker/RecentEmojiProvider;",
	  "recentItemGroup",
	  "Landroidx/emoji2/emojipicker/ItemGroup;",
	  "recentItems",
	  "",
	  "Landroidx/emoji2/emojipicker/EmojiViewData;",
	  "recentNeedsRefreshing",
	  "",
	  "scope",
	  "Lkotlinx/coroutines/CoroutineScope;",
	  "stickyVariantProvider",
	  "Landroidx/emoji2/emojipicker/StickyVariantProvider;",
	  "addView",
	  "",
	  "child",
	  "Landroid/view/View;",
	  "params",
	  "Landroid/view/ViewGroup$LayoutParams;",
	  "index",
	  "width",
	  "height",
	  "buildEmojiPickerItems",
	  "buildEmojiPickerItems$emoji2_emojipicker_release",
	  "createEmojiPickerBodyAdapter",
	  "refreshRecent",
	  "refreshRecent$emoji2_emojipicker_release",
	  "(Lkotlin/coroutines/Continuation;)Ljava/lang/Object;",
	  "removeAllViews",
	  "removeView",
	  "removeViewAt",
	  "removeViewInLayout",
	  "removeViews",
	  "start",
	  "count",
	  "removeViewsInLayout",
	  "setOnEmojiPickedListener",
	  "setRecentEmojiProvider",
	  "showEmojiPickerView",
	  "Companion",
	  "emoji2-emojipicker_release"
	]


[0]: https://maven.google.com/web/index.html#androidx.emoji2:emoji2-emojipicker:1.4.0
[1]: https://developer.android.com/develop/ui/views/text-and-emoji/emoji-picker#how-use
  • Loading branch information
jpobst authored Oct 16, 2023
1 parent ed63d89 commit 3c83179
Showing 1 changed file with 28 additions and 0 deletions.
28 changes: 28 additions & 0 deletions tools/class-parse/Program.cs
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,8 @@
using Mono.Options;

using Xamarin.Android.Tools.Bytecode;
using System.Text.Json;
using System.Text.Json.Serialization;

namespace Xamarin.Android.Tools {

Expand Down Expand Up @@ -159,6 +161,32 @@ static void DumpClassFile (ClassFile c, TextWriter output)
output.WriteLine ("\t\t{0}", attr);
}
}

// Output Kotlin metadata if it exists
var kotlin_metadata = c.Attributes.OfType<RuntimeVisibleAnnotationsAttribute> ()
.FirstOrDefault ()?.Annotations
.FirstOrDefault (a => a.Type == "Lkotlin/Metadata;");

if (kotlin_metadata is not null) {
var meta = KotlinMetadata.FromAnnotation (kotlin_metadata);
var jopt = new JsonSerializerOptions {
ReferenceHandler = ReferenceHandler.Preserve,
WriteIndented = true,
};

if (meta.AsClassMetadata () is KotlinClass kc) {
output.WriteLine ();
var json = JsonSerializer.Serialize (kc, jopt);
output.WriteLine ($"Kotlin Class Metadata [{meta.MetadataVersion}]: {json}");
} else if (meta.AsFileMetadata () is KotlinFile kf) {
output.WriteLine ();
var json = JsonSerializer.Serialize (kf, jopt);
output.WriteLine ($"Kotlin File Metadata [{meta.MetadataVersion}]: {json}");
}

output.WriteLine ();
output.WriteLine ($"Kotlin Metadata String Table: {JsonSerializer.Serialize (meta.Data2, jopt)}");
}
}
}
}

0 comments on commit 3c83179

Please sign in to comment.