You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
[Java.Interop] address some "easy" trimmer warnings (#1184)
Context: #1157
We want to enable `$(IsAotCompatible)`=true, so we can identify and
fix trimmer warnings within `Java.Interop.dll`. (Then later work our
way "up the stack", fixing trimmer warnings within `Mono.Android.dll`
and `Microsoft.Maui.dll` and…)
On the path to enabling `$(IsAotCompatible)`=true, we can enable some
settings to get started:
<IsTrimmable>true</IsTrimmable>
<EnableSingleFileAnalyzer>true</EnableSingleFileAnalyzer>
<EnableAotAnalyzer>true</EnableAotAnalyzer>
This opts into the analyzers without declaring that the assembly is
fully AOT-compatible.
Starting out, I got 33 warnings: this is an attempt to address the
ones that don't require too much thinking. Unfortunately, solving
one warning likely will create dozens more -- as you have to update
all callers.
This results in 24 warnings remaining. Since `Release` builds have
`$(TreatWarningsAsErrors)`, I will wait to enable the analyzers until
all warnings are addressed.
~~ Example Warnings ~~
**`System.Linq.Expression` usage:**
`JniRuntime.JniValueManager.CreateParameterFromManagedExpression()`
/* 638 */ partial class JavaPeerableValueMarshaler {
/* 667 */ public override Expression CreateParameterFromManagedExpression (JniValueMarshalerContext context, ParameterExpression sourceValue, ParameterAttributes synchronize)
/* 668 */ {
/* 669 */ var r = CreateIntermediaryExpressionFromManagedExpression (context, sourceValue);
/* 670 */ var h = Expression.Variable (typeof (IntPtr), sourceValue.Name + "_handle");
/* 671 */ context.LocalVariables.Add (h);
/* 672 */ context.CreationStatements.Add (Expression.Assign (h, Expression.Property (r, "Handle")));
/* 674 */ return h;
/* 675 */ }
/* 710 */ }
emits an IL2026:
src\Java.Interop\Java.Interop\JniRuntime.JniValueManager.cs(672,58):
warning IL2026: Using member 'System.Linq.Expressions.Expression.Property(Expression, String)'
which has 'RequiresUnreferencedCodeAttribute' can break functionality when trimming application code.
Creating Expressions requires unreferenced code because the members being referenced by the Expression may be trimmed.
I updated this with:
partial class JniValueMarshaler {
internal const string ExpressionRequiresUnreferencedCode = "System.Linq.Expression usage may trim away required code.";
[RequiresUnreferencedCode (ExpressionRequiresUnreferencedCode)]
public virtual Expression CreateParameterFromManagedExpression (JniValueMarshalerContext context, ParameterExpression sourceValue, ParameterAttributes synchronize) => …
}
partial class JavaPeerableValueMarshaler /* indirectly inherits JniValueMarshaler */ {
[RequiresUnreferencedCode (ExpressionRequiresUnreferencedCode)]
public override Expression CreateParameterFromManagedExpression (JniValueMarshalerContext context, ParameterExpression sourceValue, ParameterAttributes synchronize) => …
}
**`Type.GetNestedType()` usage:**
`JniRuntime.JniTypeManager.TryLoadJniMarshalMethods()`:
/* 82 */ partial class JniRuntime.JniTypeManager {
/* 445 */ bool TryLoadJniMarshalMethods (JniType nativeClass, Type type, string? methods)
/* 446 */ {
/* 447 */ var marshalType = type?.GetNestedType ("__<$>_jni_marshal_methods", BindingFlags.NonPublic);
emits an IL2070 warning:
src\Java.Interop\Java.Interop\JniRuntime.JniTypeManager.cs(447,28):
warning IL2070: 'this' argument does not satisfy 'DynamicallyAccessedMemberTypes.NonPublicNestedTypes' in call to
'System.Type.GetNestedType(String, BindingFlags)'.
The parameter 'type' of method 'Java.Interop.JniRuntime.JniTypeManager.TryLoadJniMarshalMethods(JniType, Type, String)'
does not have matching annotations.
The source value must declare at least the same requirements as those declared on the target location it is assigned to.
I updated this with:
partial class JniRuntime.JniTypeManager {
bool TryLoadJniMarshalMethods (
JniType nativeClass,
[DynamicallyAccessedMembers (DynamicallyAccessedMemberTypes.NonPublicNestedTypes)]
Type type,
string? methods) => …
**`Activator.CreateInstance()` usage:**
`JniRuntime.JniValueManager.GetValueMarshaler()`:
/* 50 */ partial class JniRuntime.JniValueManager {
/* 530 */ public JniValueMarshaler GetValueMarshaler (Type type)
/* 531 */ {
/* 541 */ if (marshalerAttr != null)
/* 542 */ return (JniValueMarshaler) Activator.CreateInstance (marshalerAttr.MarshalerType)!;
/* 591 */ }
/* 612 */ }
emits an IL2072 warning:
src\Java.Interop\Java.Interop\JniRuntime.JniValueManager.cs(542,33): warning IL2072:
'type' argument does not satisfy 'DynamicallyAccessedMemberTypes.PublicParameterlessConstructor'
in call to 'System.Activator.CreateInstance(Type)'.
The return value of method 'Java.Interop.JniValueMarshalerAttribute.MarshalerType.get' does not have matching annotations.
The source value must declare at least the same requirements as those declared on the target location it is assigned to.
I updated this with:
partial class JniRuntime.JniValueManager {
public JniValueMarshaler GetValueMarshaler (
[DynamicallyAccessedMembers (DynamicallyAccessedMemberTypes.Interfaces)]
Type type)
=> …
}
partial class JniValueMarshalerAttribute {
public Type MarshalerType {
[return: DynamicallyAccessedMembers (DynamicallyAccessedMemberTypes.PublicParameterlessConstructor)]
get;
}
}
~~ Code that Actually Changed ~~
As I added more attributes, these snowballed into more and more
warnings! I eventually had to make
`JniRuntime.JniValueManager.GetPeerType()` look like:
partial class JniRuntime.JniValueManager {
internal const DynamicallyAccessedMemberTypes Constructors = DynamicallyAccessedMemberTypes.PublicConstructors | DynamicallyAccessedMemberTypes.NonPublicConstructors;
[return: DynamicallyAccessedMembers (Constructors)]
static Type GetPeerType ([DynamicallyAccessedMembers Constructors)] Type type) => …
}
The analyzer was not able to understand code like:
partial class JniRuntime.JniValueManager {
static readonly KeyValuePair<Type, Type>[] PeerTypeMappings = new []{
new KeyValuePair<Type, Type>(typeof (object), typeof (JavaObject)),
new KeyValuePair<Type, Type>(typeof (IJavaPeerable), typeof (JavaObject)),
new KeyValuePair<Type, Type>(typeof (Exception), typeof (JavaException)),
};
static Type GetPeerType (Type type)
{
foreach (var m in PeerTypeMappings) {
if (m.Key == type)
return m.Value;
}
}
}
Simply removing the `PeerTypeMappings` array and using `if` statements
solved the warnings. This may be the only real code change if any.
0 commit comments