From 71887ee87cc02f26bce8c756e7c58f33ff841001 Mon Sep 17 00:00:00 2001 From: Rolf Bjarne Kvinge Date: Thu, 8 Jan 2026 18:42:36 +0100 Subject: [PATCH 1/4] [dotnet] Fix support for startup hooks. * Enable support by default, except when Optimize=true (aka Release builds). * Set the STARTUP_HOOKS runtime property if the DOTNET_STARTUP_HOOKS environment variable is set - this is typically done at startup by CoreCLR or MonoVM, but we're using both in an embedding mode, so we have to do it ourselves. * Add a test. Refs: * https://github.com/dotnet/android/pull/10670 * https://github.com/dotnet/runtime/blob/242f7b23752599f22157268de41fee91cb97ef6c/docs/design/features/host-startup-hook.md --- dotnet/targets/Xamarin.Shared.Sdk.targets | 6 +- runtime/runtime.m | 6 ++ tests/dotnet/StartupHookTest/AppDelegate.cs | 29 +++++++ .../StartupHookTest/MacCatalyst/Makefile | 1 + .../MacCatalyst/StartupHookTest.csproj | 7 ++ tests/dotnet/StartupHookTest/Makefile | 2 + tests/dotnet/StartupHookTest/iOS/Makefile | 1 + .../iOS/StartupHookTest.csproj | 7 ++ tests/dotnet/StartupHookTest/macOS/Makefile | 1 + .../macOS/StartupHookTest.csproj | 7 ++ tests/dotnet/StartupHookTest/shared.csproj | 15 ++++ tests/dotnet/StartupHookTest/shared.mk | 3 + tests/dotnet/StartupHookTest/tvOS/Makefile | 1 + .../tvOS/StartupHookTest.csproj | 7 ++ tests/dotnet/UnitTests/StartupHookTest.cs | 87 +++++++++++++++++++ tests/dotnet/UnitTests/TestBaseClass.cs | 6 +- 16 files changed, 182 insertions(+), 4 deletions(-) create mode 100644 tests/dotnet/StartupHookTest/AppDelegate.cs create mode 100644 tests/dotnet/StartupHookTest/MacCatalyst/Makefile create mode 100644 tests/dotnet/StartupHookTest/MacCatalyst/StartupHookTest.csproj create mode 100644 tests/dotnet/StartupHookTest/Makefile create mode 100644 tests/dotnet/StartupHookTest/iOS/Makefile create mode 100644 tests/dotnet/StartupHookTest/iOS/StartupHookTest.csproj create mode 100644 tests/dotnet/StartupHookTest/macOS/Makefile create mode 100644 tests/dotnet/StartupHookTest/macOS/StartupHookTest.csproj create mode 100644 tests/dotnet/StartupHookTest/shared.csproj create mode 100644 tests/dotnet/StartupHookTest/shared.mk create mode 100644 tests/dotnet/StartupHookTest/tvOS/Makefile create mode 100644 tests/dotnet/StartupHookTest/tvOS/StartupHookTest.csproj create mode 100644 tests/dotnet/UnitTests/StartupHookTest.cs diff --git a/dotnet/targets/Xamarin.Shared.Sdk.targets b/dotnet/targets/Xamarin.Shared.Sdk.targets index e5e5b4d5f571..c46d8fff53ad 100644 --- a/dotnet/targets/Xamarin.Shared.Sdk.targets +++ b/dotnet/targets/Xamarin.Shared.Sdk.targets @@ -124,7 +124,6 @@ false true - false true true false @@ -142,6 +141,10 @@ false <_ComObjectDescriptorSupport Condition="'$(_ComObjectDescriptorSupport)' == ''">false + + true + false + false @@ -1266,6 +1269,7 @@ <_RuntimeConfigReservedProperties Include="NATIVE_DLL_SEARCH_DIRECTORIES" /> <_RuntimeConfigReservedProperties Include="RUNTIME_IDENTIFIER" /> <_RuntimeConfigReservedProperties Include="APP_CONTEXT_BASE_DIRECTORY" /> + <_RuntimeConfigReservedProperties Include="STARTUP_HOOKS" /> + + + net$(BundledNETCoreAppTargetFrameworkVersion)-maccatalyst + + + diff --git a/tests/dotnet/StartupHookTest/Makefile b/tests/dotnet/StartupHookTest/Makefile new file mode 100644 index 000000000000..6affa45ff122 --- /dev/null +++ b/tests/dotnet/StartupHookTest/Makefile @@ -0,0 +1,2 @@ +TOP=../../.. +include $(TOP)/tests/common/shared-dotnet-test.mk diff --git a/tests/dotnet/StartupHookTest/iOS/Makefile b/tests/dotnet/StartupHookTest/iOS/Makefile new file mode 100644 index 000000000000..110d078f4577 --- /dev/null +++ b/tests/dotnet/StartupHookTest/iOS/Makefile @@ -0,0 +1 @@ +include ../shared.mk diff --git a/tests/dotnet/StartupHookTest/iOS/StartupHookTest.csproj b/tests/dotnet/StartupHookTest/iOS/StartupHookTest.csproj new file mode 100644 index 000000000000..86d408734aa8 --- /dev/null +++ b/tests/dotnet/StartupHookTest/iOS/StartupHookTest.csproj @@ -0,0 +1,7 @@ + + + + net$(BundledNETCoreAppTargetFrameworkVersion)-ios + + + diff --git a/tests/dotnet/StartupHookTest/macOS/Makefile b/tests/dotnet/StartupHookTest/macOS/Makefile new file mode 100644 index 000000000000..110d078f4577 --- /dev/null +++ b/tests/dotnet/StartupHookTest/macOS/Makefile @@ -0,0 +1 @@ +include ../shared.mk diff --git a/tests/dotnet/StartupHookTest/macOS/StartupHookTest.csproj b/tests/dotnet/StartupHookTest/macOS/StartupHookTest.csproj new file mode 100644 index 000000000000..a77287b9ba00 --- /dev/null +++ b/tests/dotnet/StartupHookTest/macOS/StartupHookTest.csproj @@ -0,0 +1,7 @@ + + + + net$(BundledNETCoreAppTargetFrameworkVersion)-macos + + + diff --git a/tests/dotnet/StartupHookTest/shared.csproj b/tests/dotnet/StartupHookTest/shared.csproj new file mode 100644 index 000000000000..5ef705ff08ef --- /dev/null +++ b/tests/dotnet/StartupHookTest/shared.csproj @@ -0,0 +1,15 @@ + + + + Exe + + StartupHookTest + com.xamarin.startuphooktest + + + + + + + + diff --git a/tests/dotnet/StartupHookTest/shared.mk b/tests/dotnet/StartupHookTest/shared.mk new file mode 100644 index 000000000000..7224dd07d815 --- /dev/null +++ b/tests/dotnet/StartupHookTest/shared.mk @@ -0,0 +1,3 @@ +TOP=../../../.. +TESTNAME=StartupHookTest +include $(TOP)/tests/common/shared-dotnet.mk diff --git a/tests/dotnet/StartupHookTest/tvOS/Makefile b/tests/dotnet/StartupHookTest/tvOS/Makefile new file mode 100644 index 000000000000..110d078f4577 --- /dev/null +++ b/tests/dotnet/StartupHookTest/tvOS/Makefile @@ -0,0 +1 @@ +include ../shared.mk diff --git a/tests/dotnet/StartupHookTest/tvOS/StartupHookTest.csproj b/tests/dotnet/StartupHookTest/tvOS/StartupHookTest.csproj new file mode 100644 index 000000000000..bd487ddcd88d --- /dev/null +++ b/tests/dotnet/StartupHookTest/tvOS/StartupHookTest.csproj @@ -0,0 +1,7 @@ + + + + net$(BundledNETCoreAppTargetFrameworkVersion)-tvos + + + diff --git a/tests/dotnet/UnitTests/StartupHookTest.cs b/tests/dotnet/UnitTests/StartupHookTest.cs new file mode 100644 index 000000000000..5d9d736eff56 --- /dev/null +++ b/tests/dotnet/UnitTests/StartupHookTest.cs @@ -0,0 +1,87 @@ +#nullable enable + +namespace Xamarin.Tests { + [TestFixture] + public class StartupHookTest : TestBaseClass { + const string project = "StartupHookTest"; + + [TestCase (ApplePlatform.MacOSX, "osx-arm64")] + [TestCase (ApplePlatform.MacCatalyst, "maccatalyst-x64")] + public void EnabledForDebug (ApplePlatform platform, string runtimeIdentifiers) + { + Configuration.IgnoreIfIgnoredPlatform (platform); + + var configuration = "Debug"; + var project_path = GetProjectPath (project, runtimeIdentifiers: runtimeIdentifiers, platform: platform, out var appPath, configuration: configuration); + Clean (project_path); + + var properties = GetDefaultProperties (runtimeIdentifiers); + properties ["Configuration"] = configuration; + DotNet.AssertBuild (project_path, properties); + + if (CanExecute (platform, properties)) { + var appExecutable = GetNativeExecutable (platform, appPath); + var env = new Dictionary { + { "DOTNET_STARTUP_HOOKS", "StartupHookTest" }, + }; + ExecuteWithMagicWordAndAssert (appExecutable, env); + + env = new Dictionary (); + ExecuteWithMagicWordAndAssert (appExecutable, env, expectedExitCode: 1); // this should fail + } + } + + [TestCase (ApplePlatform.MacOSX, "osx-arm64")] + [TestCase (ApplePlatform.MacCatalyst, "maccatalyst-x64")] + public void DisabledForRelease (ApplePlatform platform, string runtimeIdentifiers) + { + Configuration.IgnoreIfIgnoredPlatform (platform); + + var configuration = "Release"; + var project_path = GetProjectPath (project, runtimeIdentifiers: runtimeIdentifiers, platform: platform, out var appPath, configuration: configuration); + Clean (project_path); + + var properties = GetDefaultProperties (runtimeIdentifiers); + properties ["Configuration"] = configuration; + DotNet.AssertBuild (project_path, properties); + + if (CanExecute (platform, properties)) { + var appExecutable = GetNativeExecutable (platform, appPath); + var env = new Dictionary { + { "DOTNET_STARTUP_HOOKS", "StartupHookTest" }, + }; + ExecuteWithMagicWordAndAssert (appExecutable, env, expectedExitCode: 1); // this should fail + + env = new Dictionary (); + ExecuteWithMagicWordAndAssert (appExecutable, env, expectedExitCode: 1); // this should fail + } + } + + [TestCase (ApplePlatform.MacOSX, "osx-arm64")] + [TestCase (ApplePlatform.MacCatalyst, "maccatalyst-x64")] + public void Enableable (ApplePlatform platform, string runtimeIdentifiers) + { + Configuration.IgnoreIfIgnoredPlatform (platform); + + var configuration = "Release"; + var project_path = GetProjectPath (project, runtimeIdentifiers: runtimeIdentifiers, platform: platform, out var appPath, configuration: configuration); + Clean (project_path); + + var properties = GetDefaultProperties (runtimeIdentifiers); + properties ["Configuration"] = configuration; + properties ["StartupHookSupport"] = "true"; + DotNet.AssertBuild (project_path, properties); + + if (CanExecute (platform, properties)) { + var appExecutable = GetNativeExecutable (platform, appPath); + var env = new Dictionary { + { "DOTNET_STARTUP_HOOKS", "StartupHookTest" }, + }; + ExecuteWithMagicWordAndAssert (appExecutable, env); // this should work + + env = new Dictionary (); + ExecuteWithMagicWordAndAssert (appExecutable, env, expectedExitCode: 1); // this should fail + } + } + } +} diff --git a/tests/dotnet/UnitTests/TestBaseClass.cs b/tests/dotnet/UnitTests/TestBaseClass.cs index fed8415f5fc5..6b35686da384 100644 --- a/tests/dotnet/UnitTests/TestBaseClass.cs +++ b/tests/dotnet/UnitTests/TestBaseClass.cs @@ -386,7 +386,7 @@ protected string ExecuteWithMagicWordAndAssert (ApplePlatform platform, string r return ExecuteWithMagicWordAndAssert (executable, environment); } - protected string ExecuteWithMagicWordAndAssert (string executable, Dictionary? environment = null) + protected string ExecuteWithMagicWordAndAssert (string executable, Dictionary? environment = null, int expectedExitCode = 0) { if (Environment.OSVersion.Platform == PlatformID.Win32NT) { Console.WriteLine ($"Not executing '{executable}' because we're on Windows."); @@ -395,8 +395,8 @@ protected string ExecuteWithMagicWordAndAssert (string executable, Dictionary Date: Tue, 10 Feb 2026 16:20:52 +0000 Subject: [PATCH 2/4] Auto-format source code --- tests/dotnet/StartupHookTest/AppDelegate.cs | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/tests/dotnet/StartupHookTest/AppDelegate.cs b/tests/dotnet/StartupHookTest/AppDelegate.cs index cc032c738dd8..ce2636397bf4 100644 --- a/tests/dotnet/StartupHookTest/AppDelegate.cs +++ b/tests/dotnet/StartupHookTest/AppDelegate.cs @@ -17,10 +17,9 @@ static int Main (string [] args) } } -class StartupHook -{ +class StartupHook { public static bool Initialized { get; private set; } - public static void Initialize() + public static void Initialize () { Console.WriteLine ("STARTUP"); From 883f1d76c73fecf26aebbf6a8f346a5c9acb31e1 Mon Sep 17 00:00:00 2001 From: Rolf Bjarne Kvinge Date: Wed, 11 Feb 2026 16:31:51 +0100 Subject: [PATCH 3/4] Add library. --- .../MacCatalyst/StartupHookLibrary.csproj | 8 ++++ .../dotnet/StartupHookLibrary/StartupHook.cs | 17 ++++++++ .../iOS/StartupHookLibrary.csproj | 8 ++++ .../macOS/StartupHookLibrary.csproj | 8 ++++ tests/dotnet/StartupHookLibrary/shared.csproj | 6 +++ .../tvOS/StartupHookLibrary.csproj | 8 ++++ tests/dotnet/StartupHookTest/AppDelegate.cs | 15 +++++-- tests/dotnet/StartupHookTest/shared.csproj | 2 + tests/dotnet/UnitTests/StartupHookTest.cs | 39 ++++++++++++++++--- 9 files changed, 101 insertions(+), 10 deletions(-) create mode 100644 tests/dotnet/StartupHookLibrary/MacCatalyst/StartupHookLibrary.csproj create mode 100644 tests/dotnet/StartupHookLibrary/StartupHook.cs create mode 100644 tests/dotnet/StartupHookLibrary/iOS/StartupHookLibrary.csproj create mode 100644 tests/dotnet/StartupHookLibrary/macOS/StartupHookLibrary.csproj create mode 100644 tests/dotnet/StartupHookLibrary/shared.csproj create mode 100644 tests/dotnet/StartupHookLibrary/tvOS/StartupHookLibrary.csproj diff --git a/tests/dotnet/StartupHookLibrary/MacCatalyst/StartupHookLibrary.csproj b/tests/dotnet/StartupHookLibrary/MacCatalyst/StartupHookLibrary.csproj new file mode 100644 index 000000000000..02fabd2f8260 --- /dev/null +++ b/tests/dotnet/StartupHookLibrary/MacCatalyst/StartupHookLibrary.csproj @@ -0,0 +1,8 @@ + + + + net$(BundledNETCoreAppTargetFrameworkVersion)-maccatalyst + + + + diff --git a/tests/dotnet/StartupHookLibrary/StartupHook.cs b/tests/dotnet/StartupHookLibrary/StartupHook.cs new file mode 100644 index 000000000000..c8a6d94e4b68 --- /dev/null +++ b/tests/dotnet/StartupHookLibrary/StartupHook.cs @@ -0,0 +1,17 @@ +using System; + +using Foundation; + + +class StartupHook { + public static void Initialize () + { + Console.WriteLine ("STARTUP"); + + StartupStatus.Initialized = true; + } +} + +public static class StartupStatus { + public static bool Initialized { get; internal set; } +} diff --git a/tests/dotnet/StartupHookLibrary/iOS/StartupHookLibrary.csproj b/tests/dotnet/StartupHookLibrary/iOS/StartupHookLibrary.csproj new file mode 100644 index 000000000000..bb9259517c64 --- /dev/null +++ b/tests/dotnet/StartupHookLibrary/iOS/StartupHookLibrary.csproj @@ -0,0 +1,8 @@ + + + + net$(BundledNETCoreAppTargetFrameworkVersion)-ios + + + + diff --git a/tests/dotnet/StartupHookLibrary/macOS/StartupHookLibrary.csproj b/tests/dotnet/StartupHookLibrary/macOS/StartupHookLibrary.csproj new file mode 100644 index 000000000000..71b28ba48c7c --- /dev/null +++ b/tests/dotnet/StartupHookLibrary/macOS/StartupHookLibrary.csproj @@ -0,0 +1,8 @@ + + + + net$(BundledNETCoreAppTargetFrameworkVersion)-macos + + + + diff --git a/tests/dotnet/StartupHookLibrary/shared.csproj b/tests/dotnet/StartupHookLibrary/shared.csproj new file mode 100644 index 000000000000..753c8fe96d98 --- /dev/null +++ b/tests/dotnet/StartupHookLibrary/shared.csproj @@ -0,0 +1,6 @@ + + + + + + diff --git a/tests/dotnet/StartupHookLibrary/tvOS/StartupHookLibrary.csproj b/tests/dotnet/StartupHookLibrary/tvOS/StartupHookLibrary.csproj new file mode 100644 index 000000000000..388e767c58ed --- /dev/null +++ b/tests/dotnet/StartupHookLibrary/tvOS/StartupHookLibrary.csproj @@ -0,0 +1,8 @@ + + + + net$(BundledNETCoreAppTargetFrameworkVersion)-tvos + + + + diff --git a/tests/dotnet/StartupHookTest/AppDelegate.cs b/tests/dotnet/StartupHookTest/AppDelegate.cs index ce2636397bf4..22b018a22d00 100644 --- a/tests/dotnet/StartupHookTest/AppDelegate.cs +++ b/tests/dotnet/StartupHookTest/AppDelegate.cs @@ -10,9 +10,18 @@ static int Main (string [] args) GC.KeepAlive (typeof (NSObject)); // prevent linking away the platform assembly Console.WriteLine (Environment.GetEnvironmentVariable ("MAGIC_WORD")); - Console.WriteLine ($"Startup: {StartupHook.Initialized}"); + Console.WriteLine ($"Startup.Initialized: {StartupHook.Initialized}"); + Console.WriteLine ($"StartupStatus.Initialized: {StartupStatus.Initialized}"); - return StartupHook.Initialized ? 0 : 1; + var rv = 0; + + if (!StartupHook.Initialized) + rv += 1; + + if (!StartupStatus.Initialized) + rv += 2; + + return rv; } } } @@ -21,8 +30,6 @@ class StartupHook { public static bool Initialized { get; private set; } public static void Initialize () { - Console.WriteLine ("STARTUP"); - Initialized = true; } } diff --git a/tests/dotnet/StartupHookTest/shared.csproj b/tests/dotnet/StartupHookTest/shared.csproj index 5ef705ff08ef..193d25df8cee 100644 --- a/tests/dotnet/StartupHookTest/shared.csproj +++ b/tests/dotnet/StartupHookTest/shared.csproj @@ -11,5 +11,7 @@ + + diff --git a/tests/dotnet/UnitTests/StartupHookTest.cs b/tests/dotnet/UnitTests/StartupHookTest.cs index 5d9d736eff56..e8b22e33d0af 100644 --- a/tests/dotnet/UnitTests/StartupHookTest.cs +++ b/tests/dotnet/UnitTests/StartupHookTest.cs @@ -22,12 +22,19 @@ public void EnabledForDebug (ApplePlatform platform, string runtimeIdentifiers) if (CanExecute (platform, properties)) { var appExecutable = GetNativeExecutable (platform, appPath); var env = new Dictionary { - { "DOTNET_STARTUP_HOOKS", "StartupHookTest" }, + { "DOTNET_STARTUP_HOOKS", "StartupHookTest:StartupHookLibrary" }, }; ExecuteWithMagicWordAndAssert (appExecutable, env); - env = new Dictionary (); + env = new Dictionary { + { "DOTNET_STARTUP_HOOKS", "StartupHookLibrary" }, + }; ExecuteWithMagicWordAndAssert (appExecutable, env, expectedExitCode: 1); // this should fail + + env = new Dictionary { + { "DOTNET_STARTUP_HOOKS", "StartupHookTest" }, + }; + ExecuteWithMagicWordAndAssert (appExecutable, env, expectedExitCode: 2); // this should fail } } @@ -48,12 +55,22 @@ public void DisabledForRelease (ApplePlatform platform, string runtimeIdentifier if (CanExecute (platform, properties)) { var appExecutable = GetNativeExecutable (platform, appPath); var env = new Dictionary { - { "DOTNET_STARTUP_HOOKS", "StartupHookTest" }, + { "DOTNET_STARTUP_HOOKS", "StartupHookTest:StartupHookLibrary" }, }; - ExecuteWithMagicWordAndAssert (appExecutable, env, expectedExitCode: 1); // this should fail + ExecuteWithMagicWordAndAssert (appExecutable, env, expectedExitCode: 3); // this should fail env = new Dictionary (); - ExecuteWithMagicWordAndAssert (appExecutable, env, expectedExitCode: 1); // this should fail + ExecuteWithMagicWordAndAssert (appExecutable, env, expectedExitCode: 3); // this should fail + + env = new Dictionary { + { "DOTNET_STARTUP_HOOKS", "StartupHookLibrary" }, + }; + ExecuteWithMagicWordAndAssert (appExecutable, env, expectedExitCode: 3); // this should fail + + env = new Dictionary { + { "DOTNET_STARTUP_HOOKS", "StartupHookTest" }, + }; + ExecuteWithMagicWordAndAssert (appExecutable, env, expectedExitCode: 3); // this should fail } } @@ -75,12 +92,22 @@ public void Enableable (ApplePlatform platform, string runtimeIdentifiers) if (CanExecute (platform, properties)) { var appExecutable = GetNativeExecutable (platform, appPath); var env = new Dictionary { - { "DOTNET_STARTUP_HOOKS", "StartupHookTest" }, + { "DOTNET_STARTUP_HOOKS", "StartupHookTest:StartupHookLibrary" }, }; ExecuteWithMagicWordAndAssert (appExecutable, env); // this should work env = new Dictionary (); + ExecuteWithMagicWordAndAssert (appExecutable, env, expectedExitCode: 3); // this should fail + + env = new Dictionary { + { "DOTNET_STARTUP_HOOKS", "StartupHookLibrary" }, + }; ExecuteWithMagicWordAndAssert (appExecutable, env, expectedExitCode: 1); // this should fail + + env = new Dictionary { + { "DOTNET_STARTUP_HOOKS", "StartupHookTest" }, + }; + ExecuteWithMagicWordAndAssert (appExecutable, env, expectedExitCode: 2); // this should fail } } } From 05bb49024d21cfcbc9e715ee990c7bd90a77159f Mon Sep 17 00:00:00 2001 From: Rolf Bjarne Kvinge Date: Wed, 11 Feb 2026 19:11:19 +0100 Subject: [PATCH 4/4] Suppress StartupHookProvider IL2026 trimmer warning when we set StartupHookSupport=true When we automatically enable StartupHookSupport for Hot Reload (because the developer didn't set it explicitly), suppress the resulting IL2026 trimmer warning from System.StartupHookProvider.ProcessStartupHooks. This is done by setting a feature switch (ObjCRuntime.SuppressStartupHookTrimWarning) that conditionally adds [UnconditionalSuppressMessage] to the method via the linker's link-attributes XML. --- dotnet/targets/Xamarin.Shared.Sdk.targets | 3 +++ src/TrimAttributes.LinkDescription.xml | 11 +++++++++++ 2 files changed, 14 insertions(+) diff --git a/dotnet/targets/Xamarin.Shared.Sdk.targets b/dotnet/targets/Xamarin.Shared.Sdk.targets index c46d8fff53ad..186244756bc7 100644 --- a/dotnet/targets/Xamarin.Shared.Sdk.targets +++ b/dotnet/targets/Xamarin.Shared.Sdk.targets @@ -142,6 +142,8 @@ <_ComObjectDescriptorSupport Condition="'$(_ComObjectDescriptorSupport)' == ''">false + + <_SuppressStartupHookSupportTrimWarning Condition="'$(StartupHookSupport)' == '' And '$(Optimize)' != 'true'">true true false @@ -697,6 +699,7 @@ + diff --git a/src/TrimAttributes.LinkDescription.xml b/src/TrimAttributes.LinkDescription.xml index 6680f87b5649..bfe5a67b8964 100644 --- a/src/TrimAttributes.LinkDescription.xml +++ b/src/TrimAttributes.LinkDescription.xml @@ -290,4 +290,15 @@ + + + + + + ILLink + IL2026 + + + +