Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions src/Mono.Android.Runtime/Properties/AssemblyInfo.cs.in
Original file line number Diff line number Diff line change
Expand Up @@ -19,3 +19,5 @@ using System.Runtime.Versioning;
[assembly: SupportedOSPlatform("Android@MIN_API_LEVEL@.0")]

[assembly: InternalsVisibleTo("Mono.Android, PublicKey=0024000004800000940000000602000000240000525341310004000011000000438ac2a5acfbf16cbd2b2b47a62762f273df9cb2795ceccdf77d10bf508e69e7a362ea7a45455bbf3ac955e1f2e2814f144e5d817efc4c6502cc012df310783348304e3ae38573c6d658c234025821fda87a0be8a0d504df564e2c93b2b878925f42503e9d54dfef9f9586d9e6f38a305769587b1de01f6c0410328b2c9733db")]
[assembly: InternalsVisibleTo("Microsoft.Android.Runtime.NativeAOT, PublicKey=0024000004800000940000000602000000240000525341310004000011000000438ac2a5acfbf16cbd2b2b47a62762f273df9cb2795ceccdf77d10bf508e69e7a362ea7a45455bbf3ac955e1f2e2814f144e5d817efc4c6502cc012df310783348304e3ae38573c6d658c234025821fda87a0be8a0d504df564e2c93b2b878925f42503e9d54dfef9f9586d9e6f38a305769587b1de01f6c0410328b2c9733db")]
[assembly: InternalsVisibleTo("Mono.Android.NET-Tests, PublicKey=0024000004800000940000000602000000240000525341310004000011000000438ac2a5acfbf16cbd2b2b47a62762f273df9cb2795ceccdf77d10bf508e69e7a362ea7a45455bbf3ac955e1f2e2814f144e5d817efc4c6502cc012df310783348304e3ae38573c6d658c234025821fda87a0be8a0d504df564e2c93b2b878925f42503e9d54dfef9f9586d9e6f38a305769587b1de01f6c0410328b2c9733db")]
1 change: 1 addition & 0 deletions src/Mono.Android/Android.Runtime/AndroidRuntime.cs
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@
using Java.Interop.Tools.TypeNameMappings;
using Microsoft.Android.Runtime;
using System.Diagnostics.CodeAnalysis;
using RuntimeFeature = Microsoft.Android.Runtime.RuntimeFeature;

#if JAVA_INTEROP
namespace Android.Runtime {
Expand Down
1 change: 1 addition & 0 deletions src/Mono.Android/Android.Runtime/JNIEnv.cs
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@
using Java.Interop;
using Java.Interop.Tools.TypeNameMappings;
using Microsoft.Android.Runtime;
using RuntimeFeature = Microsoft.Android.Runtime.RuntimeFeature;

namespace Android.Runtime {
public static partial class JNIEnv {
Expand Down
1 change: 1 addition & 0 deletions src/Mono.Android/Java.Interop/TypeManager.cs
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@

using Android.Runtime;
using Microsoft.Android.Runtime;
using RuntimeFeature = Microsoft.Android.Runtime.RuntimeFeature;

namespace Java.Interop {

Expand Down
1 change: 0 additions & 1 deletion src/Mono.Android/Mono.Android.csproj
Original file line number Diff line number Diff line change
Expand Up @@ -368,7 +368,6 @@
<Compile Include="Microsoft.Android.Runtime\JavaMarshalRegisteredPeers.cs" />
<Compile Include="Microsoft.Android.Runtime\JavaMarshalValueManager.cs" />
<Compile Include="Microsoft.Android.Runtime\PrimitiveArrayInfo.cs" />
<Compile Include="Microsoft.Android.Runtime\RuntimeFeature.cs" />
<Compile Include="Microsoft.Android.Runtime\SingleUniverseTypeMap.cs" />
<Compile Include="Microsoft.Android.Runtime\TrimmableTypeMap.cs" />
<Compile Include="Microsoft.Android.Runtime\TrimmableTypeMapTypeManager.cs" />
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -532,15 +532,15 @@ static IEnumerable<object[]> Get_BuildHasTrimmerWarningsData ()
} else {
AddTestData (runtime, "", new string [0], true);
}
AddTestData (runtime, "SuppressTrimAnalysisWarnings=false", new string [] { "IL2055" }, true, 2);
AddTestData (runtime, "SuppressTrimAnalysisWarnings=false", new string [] { "IL2055" }, true, runtime == AndroidRuntime.NativeAOT ? 2 : 3);
AddTestData (runtime, "TrimMode=full", new string [] { "IL2055" }, false, 1);
AddTestData (runtime, "TrimMode=full", new string [] { "IL2055" }, true, 2);
AddTestData (runtime, "TrimMode=full", new string [] { "IL2055" }, true, runtime == AndroidRuntime.NativeAOT ? 2 : 2);
AddTestData (runtime, "IsAotCompatible=true", new string [] { "IL2055", "IL3050" }, false);

if (runtime == AndroidRuntime.NativeAOT) {
AddTestData (runtime, "IsAotCompatible=true", new string [] { "IL2055", "IL3050" }, true, 2);
} else {
AddTestData (runtime, "IsAotCompatible=true", new string [] { "IL2055", "IL3050" }, true, 3);
AddTestData (runtime, "IsAotCompatible=true", new string [] { "IL2055", "IL3050" }, true, 4);
}
}

Expand All @@ -562,6 +562,8 @@ void AddTestData (AndroidRuntime runtime, string properties, string [] codes, bo
[TestCaseSource (nameof (Get_BuildHasTrimmerWarningsData))]
public void BuildHasTrimmerWarnings (AndroidRuntime runtime, string properties, string [] codes, bool isRelease, int? totalWarnings = null)
{
const int maxWarningLinesToShow = 25;

if (IgnoreUnsupportedConfiguration (runtime, release: isRelease)) {
return;
}
Expand Down Expand Up @@ -601,10 +603,38 @@ public void BuildHasTrimmerWarnings (AndroidRuntime runtime, string properties,
b.AssertHasNoWarnings ();
} else {
totalWarnings ??= codes.Length;
Assert.True (StringAssertEx.ContainsText (b.LastBuildOutput, $"{totalWarnings} Warning(s)"), $"Should receive {totalWarnings} warnings");

string warningSummaryLine = b.LastBuildOutput.LastOrDefault (line => line.Contains ("Warning(s)", StringComparison.Ordinal)) ?? "";
var actualWarnings = GetWarningCount (warningSummaryLine);

var allWarningLines = b.LastBuildOutput
.Where (line => line.Contains (": warning ", StringComparison.OrdinalIgnoreCase))
.Take (maxWarningLinesToShow)
.ToArray ();
Assert.AreEqual (
totalWarnings.Value,
actualWarnings,
$"{b.BuildLogFile} should have {totalWarnings} warnings. Summary line: '{warningSummaryLine}'. " +
$"Warnings found ({allWarningLines.Length} shown):{Environment.NewLine}{string.Join (Environment.NewLine, allWarningLines)}"
);
foreach (var code in codes) {
Assert.True (StringAssertEx.ContainsText (b.LastBuildOutput, code), $"Should receive {code} warning");
Assert.True (
StringAssertEx.ContainsText (b.LastBuildOutput, code),
$"{b.BuildLogFile} should contain warning {code}. Summary line: '{warningSummaryLine}'. " +
$"Warnings found ({allWarningLines.Length} shown):{Environment.NewLine}{string.Join (Environment.NewLine, allWarningLines)}"
);
}
}

static int GetWarningCount (string warningSummaryLine)
{
string [] tokens = warningSummaryLine.Split (new [] { ' ', '\t' }, StringSplitOptions.RemoveEmptyEntries);
for (int i = 1; i < tokens.Length; i++) {
if (tokens [i] == "Warning(s)" && int.TryParse (tokens [i - 1], out var warningCount)) {
return warningCount;
}
}
return -1;
}
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@
using System.IO;
using Microsoft.Build.Utilities;
using NUnit.Framework;
using Xamarin.Android.Build.Tasks;
using Xamarin.Android.Tasks.LLVMIR;
using Xamarin.Android.Tools;

Expand Down Expand Up @@ -79,5 +80,32 @@ public void GeneratedIR_FunctionWithWhitespaceParameterName_ProducesValidOutput
Assert.That (output, Does.Not.Contain ("%\t)"), "Generated LLVM IR should not contain 'ptr noundef %\\t)' pattern");
Assert.That (output, Does.Contain ("@test_function"), "Generated LLVM IR should contain the function name");
}

/// <summary>
/// Regression test for the NativeAOT link failure in https://github.com/dotnet/android/pull/11669.
///
/// `Android.Runtime.JNIEnvInit.Initialize` (used only by MonoVM/CoreCLR) has a `[LibraryImport]`
/// p/invoke to the native `xamarin_app_init` function, which becomes a direct native reference
/// under NativeAOT (`xa-internal-api` is a `DirectPInvoke`). Since `Initialize` is force-preserved
/// by the ILLink descriptor and is never trimmed, the reference reaches the linker even though
/// NativeAOT never generates the marshal methods native code that defines `xamarin_app_init`.
/// The NativeAOT jni-init assembler source must therefore emit a no-op definition so the linker
/// can resolve the symbol.
/// </summary>
[Test]
public void NativeAotJniInit_EmitsNoOpXamarinAppInit ()
{
var log = new TaskLoggingHelper (new MockBuildEngine (TestContext.Out, [], [], []), "test");
var composer = new NativeAotJniInitNativeAssemblyGenerator (log, runtimeComponentsJniOnLoadHandlers: null, customJniOnLoadHandlers: null);
LlvmIrModule module = composer.Construct ();

var generator = LlvmIrGenerator.Create (AndroidTargetArch.Arm64, "jniinit.ll");
using var writer = new StringWriter ();
generator.Generate (writer, module);

string output = writer.ToString ();
Assert.That (output, Does.Contain ("define"), "Generated LLVM IR should contain a function definition");
Assert.That (output, Does.Contain ("@xamarin_app_init"), "Generated LLVM IR should define xamarin_app_init so the NativeAOT linker can resolve JNIEnvInit.Initialize's DirectPInvoke");
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,8 @@ protected override void Construct (LlvmIrModule module)
CollectHandlers (customJniOnLoadHandlers);
JniOnLoadNativeAssemblerHelper.GenerateJniOnLoadHandlerCode (jniOnLoadNames, module);

DefineNoOpXamarinAppInit (module);

void CollectHandlers (List<string>? handlers)
{
if (handlers == null || handlers.Count == 0) {
Expand All @@ -44,4 +46,24 @@ void CollectHandlers (List<string>? handlers)
}
}
}

// The managed method `Android.Runtime.JNIEnvInit.Initialize` (used only by MonoVM and CoreCLR) has a
// `[LibraryImport]` p/invoke to the native `xamarin_app_init` function, which becomes a direct native
// reference because the `xa-internal-api` "library" is registered as a `DirectPInvoke` for NativeAOT.
// That symbol is only ever defined by the MonoVM/CoreCLR marshal methods native code, which is never
// generated for NativeAOT. `Initialize` is unreachable under NativeAOT (which uses
// `InitializeNativeAotRuntime` instead), but it is force-preserved by the ILLink descriptor
// (`PreserveLists/Mono.Android.xml`), so the unresolved reference reaches the linker and fails with
// XA3007 "undefined symbol: xamarin_app_init". Emit an empty definition to satisfy the linker; it is
// never actually called under NativeAOT.
static void DefineNoOpXamarinAppInit (LlvmIrModule module)
{
var parameters = new List<LlvmIrFunctionParameter> {
new LlvmIrFunctionParameter (typeof (IntPtr), "env"),
new LlvmIrFunctionParameter (typeof (IntPtr), "fn"),
};
var func = new LlvmIrFunction ("xamarin_app_init", typeof (void), parameters);
func.Body.Ret (typeof (void));
module.Add (func);
}
}
Loading