Skip to content

[XABT] Separate marshal method storage from MarshalMethodClassifier to MarshalMethodsCollection. #10077

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 1 commit into from
May 1, 2025

Conversation

jpobst
Copy link
Contributor

@jpobst jpobst commented Apr 24, 2025

Today the marshal method scanning process is tightly coupled to scanning for JavaCallableWrappers. That is, when building CallableWrapperType objects, the MarshalMethodsClassifier is used to classify methods as marshal methods by calling ShouldBeDynamicallyRegistered (...).

However ShouldBeDynamicallyRegistered doesn't just return a simple bool, it has the side effect of building a list of MarshalMethodEntry objects that are used extensively throughout the build process.

We are likely going to need to build this list in places other than JavaCallableWrappers scanning: assembly rewriting will likely be before JCWs, and GenerateNativeMarshalMethodSources will likely be after JCWs.

As such:

  • Rework MarshalMethodsClassifier to simply classify methods as LLVM marshal method eligible or not (dynamically registered).
  • Move storage to a new MarshalMethodsCollection.

Additionally, rework MarshalMethodsClassifier to also be able to detect marshal methods that have already been rewritten and provide the information later steps (GenerateJavaStubs, GenerateNativeMarshalMethodSources) need to function properly. This isn't in use yet, but will be needed in the future.

@jpobst jpobst changed the title [XABT] Separate marshal method storage from MarshalMethodClassifier to MarshalMethodCollection. [XABT] Separate marshal method storage from MarshalMethodClassifier to MarshalMethodsCollection. Apr 24, 2025
@jpobst jpobst force-pushed the dev/jpobst/marshal-method-collection branch 6 times, most recently from 673e9c5 to 4fb09ef Compare April 29, 2025 19:31
@jpobst jpobst force-pushed the dev/jpobst/marshal-method-collection branch from 4fb09ef to 83a549f Compare April 29, 2025 21:21
@jpobst jpobst marked this pull request as ready for review April 29, 2025 23:10
Comment on lines +18 to +24
class MarshalMethodsCollection : JavaCallableMethodClassifier
{
/// <summary>
/// Assemblies that contain convertible marshal methods. These assemblies need to
/// have the proper assembly references added to them.
/// </summary>
public HashSet<AssemblyDefinition> AssembliesWithMarshalMethods { get; } = [];
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We don't put this object in RegisterTaskObject() since #10058?

So, should be ok to store an AssemblyDefinition here.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

These stored AssemblyDefinition objects were simply moved from MarshalMethodsClassifier, so it should be functionally the same as what we have today. I suspect when the assembly resolver gets disposed it will close all files held by AssemblyDefinition. Attempting to use this object after that would probably result in ObjectDisposedException but I guess we have structured our code so we don't use these after the resolver has been disposed.

Comment on lines +99 to +106
public static IEnumerable<TAttribute> GetAttributes<TAttribute> (Mono.Cecil.ICustomAttributeProvider p, string attributeName, Func<CustomAttribute, TAttribute?> selector)
where TAttribute : class
{
return p.GetCustomAttributes (attributeName)
.Select (selector)
.Where (v => v != null)
.Select (v => v!);
}
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I would usually hesitate to add an overload using this much System.Linq, when I look at the caller below...

Comment on lines +110 to +119
foreach (var a in CecilExtensions.GetAttributes<RegisterAttribute> (p, a => CecilExtensions.ToRegisterAttribute (a))) {
yield return a;
}
foreach (var c in p.GetCustomAttributes ("Java.Interop.JniTypeSignatureAttribute")) {
var r = RegisterFromJniTypeSignatureAttribute (c);
if (r == null) {
continue;
}
yield return r;
}
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Does it matter the ordering these are yield returned?

It seems like a single foreach loop would allocate a lot less and look for the two attributes by name. But I don't know if the ordering matters.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

All of these functions were copy/pasted from the internal CecilExtensions.cs in Java.Interop.Tools.JavaCallableWrappers because we need them outside the JCW process. Once we figure out where everything ends up, we will probably need to determine where functions like this need to live if they are still needed by multiple processes.

Comment on lines +158 to +159
var v = attr.Properties.FirstOrDefault (p => p.Name == "DoNotGenerateAcw");
r.DoNotGenerateAcw = v.Name == null ? false : (bool) v.Argument.Value;
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Does something need to check if v is null? Maybe we should add #nullable enable to the top of this file.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Good thought, I added #nullable enable locally and there are no warnings, so I will commit it with a future PR.

Here, var is the struct CustomAttributeNamedArgument so the "Default" will be an empty struct rather than null.

Copy link
Member

@jonathanpeppers jonathanpeppers left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Otherwise, this looks good to me, didn't have any other comments.

@@ -0,0 +1,199 @@
#nullable enable
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

#10093 was merged should we rebase/remove this line?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This file is in the Xamarin.Android.Build.Tests project which wasn't touched, so I think it's still needed.

@jpobst jpobst merged commit 6a27915 into main May 1, 2025
59 checks passed
@jpobst jpobst deleted the dev/jpobst/marshal-method-collection branch May 1, 2025 17:45
@github-actions github-actions bot locked and limited conversation to collaborators Jun 1, 2025
Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

2 participants