Skip to content

Commit

Permalink
[Java.Interop.Tools.TypeNameMappings] improve `ToJniNameFromAttribute…
Browse files Browse the repository at this point in the history
…sForAndroid` (#1064)

Reviewing `dotnet-trace` output:

	dotnet trace collect --format speedscope -- C:\src\xamarin-android\bin\Release\dotnet\dotnet.exe build -bl --no-restore bar.csproj

On a `dotnet new maui` application, I noticed:

	79.40ms java.interop.tools.javacallablewrappers!Java.Interop.Tools.TypeNameMappings.JavaNativeTypeManager.ToJniNameFromAttribute

This method just appears to be called *a lot*, and so things I changed:

  * Check `type.HasCustomAttributes`, `attributeType.HasInterfaces`,
    and `attr.HasProperties` where appropriate.

  * Unroll the `System.Linq` usage one level, and use plain `foreach`

  * Create a `IsIJniNameProviderAttribute` method that checks against
    a list of known attribute types as a "fast path".

  * The "slow path" can continue to check for the
    `IJniNameProviderAttribute` interface

Other changes:

  * Delete `#region`!

After these changes, I get:

	51.64ms (1.2%)	0.13ns (<0.01%)	java.interop.tools.javacallablewrappers!Java.Interop.Tools.TypeNameMappings.JavaNativeTypeManager.ToJniNameFromAttribute

This should save ~27ms on incremental builds of the `dotnet new maui`
project template and likely even more on larger projects.
  • Loading branch information
jonathanpeppers authored Dec 8, 2022
1 parent 09f8da2 commit 8ab9d33
Showing 1 changed file with 32 additions and 10 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -567,25 +567,47 @@ public static string ToJniName (TypeDefinition type, IMetadataResolver? resolver

static string? ToJniNameFromAttributesForAndroid (TypeDefinition type, IMetadataResolver? resolver)
{
#region CustomAttribute alternate name support
var attrs = type.CustomAttributes.Where (a => Resolve (resolver, a.AttributeType).Interfaces.Any (it => it.InterfaceType.FullName == typeof (IJniNameProviderAttribute).FullName));
return attrs.Select (attr => {
var ap = attr.Properties.FirstOrDefault (p => p.Name == "Name");
if (!type.HasCustomAttributes)
return null;
foreach (var attr in type.CustomAttributes) {
if (!IsIJniNameProviderAttribute (attr, resolver))
continue;
var ap = attr.HasProperties ? attr.Properties.FirstOrDefault (p => p.Name == "Name") : default;
string? name = null;
if (ap.Name == null) {
var ca = attr.ConstructorArguments.FirstOrDefault ();
if (ca.Type == null || ca.Type.FullName != "System.String")
return null;
continue;
name = (string) ca.Value;
} else
name = (string) ap.Argument.Value;
if (!string.IsNullOrEmpty (name))
return name.Replace ('.', '/');
else
return null;
})
.FirstOrDefault (s => s != null);
#endregion
}
return null;
}

static readonly HashSet<string> KnownIJniNameProviders = new HashSet<string> (StringComparer.Ordinal) {
"Android.App.ActivityAttribute",
"Android.App.ApplicationAttribute",
"Android.App.InstrumentationAttribute",
"Android.App.ServiceAttribute",
"Android.Content.BroadcastReceiverAttribute",
"Android.Content.ContentProviderAttribute",
"Android.Runtime.RegisterAttribute",
};

static bool IsIJniNameProviderAttribute (CustomAttribute attr, IMetadataResolver? resolver)
{
// Fast path for a list of known IJniNameProviderAttribute implementations
if (KnownIJniNameProviders.Contains (attr.AttributeType.FullName))
return true;

// Slow path resolves the type, looking for IJniNameProviderAttribute
var attributeType = Resolve (resolver, attr.AttributeType);
if (!attributeType.HasInterfaces)
return false;
return attributeType.Interfaces.Any (it => it.InterfaceType.FullName == typeof (IJniNameProviderAttribute).FullName);
}

static TypeDefinition Resolve (IMetadataResolver? resolver, TypeReference typeReference) =>
Expand Down

0 comments on commit 8ab9d33

Please sign in to comment.