Skip to content

Commit

Permalink
[dotnet] Test integration (#18543)
Browse files Browse the repository at this point in the history
Fixed an issue where the C# class name was coming in with `,
AssemblyName` tacked on the end.
Fixed an issue where a class that had an entry in the map didn't have a
`class_ptr` which was causing an NRE.
Fixed a predicate for deciding when to mark the assembly for save.

Note - for an as yet undetermined reason, the linker is not picking up
that I'm marking the assembly for save, which is why the `true` test
cases are commented out.
This fixes issue 16671 #16671
  • Loading branch information
stephen-hawley authored Oct 17, 2023
1 parent 9132042 commit f4a0ea9
Show file tree
Hide file tree
Showing 20 changed files with 447 additions and 124 deletions.
1 change: 1 addition & 0 deletions dotnet/targets/Xamarin.Shared.Sdk.targets
Original file line number Diff line number Diff line change
Expand Up @@ -687,6 +687,7 @@
<!-- The ListExportedSymbols must run after ExtractBindingLibrariesStep, otherwise we won't properly list exported Objective-C classes from binding libraries -->
<_TrimmerCustomSteps Include="$(_AdditionalTaskAssembly)" BeforeStep="OutputStep" Type="Xamarin.Linker.Steps.ListExportedSymbols" />
<_TrimmerCustomSteps Include="$(_AdditionalTaskAssembly)" BeforeStep="OutputStep" Type="Xamarin.Linker.Steps.PreOutputDispatcher" />
<_TrimmerCustomSteps Include="$(_AdditionalTaskAssembly)" BeforeStep="OutputStep" Type="Xamarin.Linker.ClassHandleRewriterStep" />

<!--
post-output steps
Expand Down
1 change: 1 addition & 0 deletions runtime/xamarin/runtime.h
Original file line number Diff line number Diff line change
Expand Up @@ -129,6 +129,7 @@ struct MTRegistrationMap {
int skipped_map_count;
int protocol_wrapper_count;
int protocol_count;
void **classHandles;
};

typedef struct {
Expand Down
1 change: 1 addition & 0 deletions src/ObjCRuntime/Registrar.cs
Original file line number Diff line number Diff line change
Expand Up @@ -134,6 +134,7 @@ internal class ObjCType {
public IntPtr Handle;
#else
public TType ProtocolWrapperType;
public int ClassMapIndex;
#endif

public Dictionary<string, ObjCField> Fields;
Expand Down
28 changes: 25 additions & 3 deletions src/ObjCRuntime/Runtime.cs
Original file line number Diff line number Diff line change
Expand Up @@ -74,6 +74,7 @@ internal unsafe struct MTRegistrationMap {
public int skipped_map_count;
public int protocol_wrapper_count;
public int protocol_count;
public IntPtr* classHandles;
}
#pragma warning restore 649

Expand Down Expand Up @@ -210,8 +211,29 @@ public bool IsSimulator {
#if NET
public static class ClassHandles
{
internal static unsafe void InitializeClassHandles (MTClassMap* map)
static NativeHandle unused;
internal static unsafe void InitializeClassHandles (IntPtr* map)
{
// This code is necessary. What happens is that
// in the case of class handle rewriting, any
// IL generated by this gets deleted, but the
// variables created are left behind and
// Rewriter uses those.
// In the case of NO class handle rewriting,
// this is a no-op
fixed (NativeHandle *ptr = &unused) {
SetHandle (-1, ptr, map);
}
}

[Preserve]
internal static unsafe void SetHandle (int index, NativeHandle* handle, IntPtr* map)
{
if (index < 0)
return;

var nativeHandle = map [index];
*handle = (NativeHandle)nativeHandle;
}
}
#endif
Expand Down Expand Up @@ -327,8 +349,8 @@ unsafe static void Initialize (InitializationOptions* options)
}

#if NET
if (options->RegistrationMap is not null && options->RegistrationMap->map is not null) {
ClassHandles.InitializeClassHandles (options->RegistrationMap->map);
if (options->RegistrationMap is not null && options->RegistrationMap->classHandles is not null) {
ClassHandles.InitializeClassHandles (options->RegistrationMap->classHandles);
}
#endif

Expand Down
25 changes: 25 additions & 0 deletions tests/dotnet/MyClassRedirectApp/AppDelegate.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
using System;
using System.Runtime.InteropServices;
using Foundation;
using ObjCRuntime;

namespace MySimpleApp {
public class Program {
static int Main (string [] args)
{
var someObj = new SomeObj ();
var handle = someObj.ClassHandle;
return handle == NativeHandle.Zero ? 1 : 0;
}
}

public class SomeObj : NSObject {
static NativeHandle class_ptr = Class.GetHandle (typeof (SomeObj));
[Export ("whatever")]
public IntPtr Whatever ()
{
return new IntPtr (0xdeadf00d);
}
public override NativeHandle ClassHandle => class_ptr;
}
}
1 change: 1 addition & 0 deletions tests/dotnet/MyClassRedirectApp/MacCatalyst/Makefile
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
include ../shared.mk
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
<?xml version="1.0" encoding="utf-8"?>
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<TargetFramework>net$(BundledNETCoreAppTargetFrameworkVersion)-maccatalyst</TargetFramework>
</PropertyGroup>
<Import Project="..\shared.csproj" />
</Project>
2 changes: 2 additions & 0 deletions tests/dotnet/MyClassRedirectApp/Makefile
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
TOP=../../..
include $(TOP)/tests/common/shared-dotnet-test.mk
1 change: 1 addition & 0 deletions tests/dotnet/MyClassRedirectApp/iOS/Makefile
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
include ../shared.mk
7 changes: 7 additions & 0 deletions tests/dotnet/MyClassRedirectApp/iOS/MyClassRedirectApp.csproj
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
<?xml version="1.0" encoding="utf-8"?>
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<TargetFramework>net$(BundledNETCoreAppTargetFrameworkVersion)-ios</TargetFramework>
</PropertyGroup>
<Import Project="..\shared.csproj" />
</Project>
1 change: 1 addition & 0 deletions tests/dotnet/MyClassRedirectApp/macOS/Makefile
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
include ../shared.mk
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
<?xml version="1.0" encoding="utf-8"?>
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<TargetFramework>net$(BundledNETCoreAppTargetFrameworkVersion)-macos</TargetFramework>
</PropertyGroup>
<Import Project="..\shared.csproj" />
</Project>
20 changes: 20 additions & 0 deletions tests/dotnet/MyClassRedirectApp/shared.csproj
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
<?xml version="1.0" encoding="utf-8"?>
<Project>
<PropertyGroup>
<OutputType>Exe</OutputType>

<ApplicationTitle>MyClassRedirectApp</ApplicationTitle>
<ApplicationId>com.xamarin.myclassredirectapp</ApplicationId>

<ExcludeNUnitLiteReference>true</ExcludeNUnitLiteReference>
<ExcludeTouchUnitReference>true</ExcludeTouchUnitReference>

<DefineConstants>$(DefineConstants);$(AdditionalDefineConstants)</DefineConstants>
</PropertyGroup>

<Import Project="../../common/shared-dotnet.csproj" />

<ItemGroup>
<Compile Include="../*.cs" />
</ItemGroup>
</Project>
3 changes: 3 additions & 0 deletions tests/dotnet/MyClassRedirectApp/shared.mk
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
TOP=../../../..
TESTNAME=MyClassRedirectApp
include $(TOP)/tests/common/shared-dotnet.mk
1 change: 1 addition & 0 deletions tests/dotnet/MyClassRedirectApp/tvOS/Makefile
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
include ../shared.mk
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
<?xml version="1.0" encoding="utf-8"?>
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<TargetFramework>net$(BundledNETCoreAppTargetFrameworkVersion)-tvos</TargetFramework>
</PropertyGroup>
<Import Project="..\shared.csproj" />
</Project>
77 changes: 76 additions & 1 deletion tests/dotnet/UnitTests/RegistrarTest.cs
Original file line number Diff line number Diff line change
@@ -1,3 +1,5 @@
using Mono.Cecil;

namespace Xamarin.Tests {
[TestFixture]
public class RegistrarTest : TestBaseClass {
Expand Down Expand Up @@ -25,7 +27,6 @@ public void InvalidStaticRegistrarValidation (ApplePlatform platform, bool valid

var appDir = GetAppPath (projectPath, platform, runtimeIdentifiers, configuration);
var asmDir = Path.Combine (appDir, GetRelativeAssemblyDirectory (platform));

var appExecutable = Path.Combine (asmDir, project + ".dll");

// Save the first version of the main assembly in memory
Expand All @@ -50,6 +51,80 @@ public void InvalidStaticRegistrarValidation (ApplePlatform platform, bool valid
Environment.SetEnvironmentVariable ("XAMARIN_VALIDATE_STATIC_REGISTRAR_CODE", null);
}
}

#if NET
[TestCase (ApplePlatform.MacCatalyst, false)]
[TestCase (ApplePlatform.MacOSX, false)]
[TestCase (ApplePlatform.iOS, false)]
[TestCase (ApplePlatform.TVOS, false)]
[TestCase (ApplePlatform.MacCatalyst, true)]
[TestCase (ApplePlatform.iOS, true)]
[TestCase (ApplePlatform.TVOS, true)]
public void ClassRewriterTest (ApplePlatform platform, bool rewriteHandles)
{
var project = "MyClassRedirectApp";
var configuration = "Debug";
var runtimeIdentifiers = GetDefaultRuntimeIdentifier (platform);
Configuration.IgnoreIfIgnoredPlatform (platform);

var projectPath = GetProjectPath (project, platform: platform);
Clean (projectPath);
var properties = GetDefaultProperties ();
properties ["Registrar"] = "static";
// enable the linker (so that the main assembly is modified)
properties ["LinkMode"] = "full";
properties ["MtouchLink"] = "full";
if (rewriteHandles)
properties ["MtouchExtraArgs"] = "--optimize=redirect-class-handles";

DotNet.AssertBuild (projectPath, properties);

var appDir = GetAppPath (projectPath, platform, runtimeIdentifiers, configuration);
var asmDir = Path.Combine (appDir, GetRelativeAssemblyDirectory (platform));

var appExecutable = Path.Combine (asmDir, project + ".dll");
var platformDll = Path.Combine (asmDir, Configuration.GetBaseLibraryName (platform, true));
Assert.That (File.Exists (platformDll), "No platform dll.");
var module = ModuleDefinition.ReadModule (platformDll);
var classHandlesMaybe = AllTypes (module).FirstOrDefault (t => t.FullName == "ObjCRuntime.Runtime/ClassHandles");
Assert.NotNull (classHandlesMaybe, "Couldn't find ClassHandles type.");
var classHandles = classHandlesMaybe!;
if (!rewriteHandles) {
// NB: there is always at least one field named "unused"
var fields = classHandles.Fields.Where (f => f.Name != "unused").Select (f => f.Name).ToList ();
var sb = new StringBuilder ();
foreach (var f in fields) {
sb.Append (" ").Append (f);
}
Assert.That (fields.Count == 0, "There are fields in classHandles - rewriter was called when it should have done nothing." + sb);
} else {
// NB: there is always at least one field named "unused"
Assert.That (classHandles.HasFields && classHandles.Fields.Count () > 1, "There are no fields in ClassHandles - rewriter did nothing.");
var field = classHandles.Fields.FirstOrDefault (f => f.Name.Contains ("SomeObj"));
Assert.IsNotNull (field, "Didn't find a field for 'SomeObj'");
}
}

IEnumerable<TypeDefinition> AllTypes (ModuleDefinition module)
{
foreach (var type in module.Types) {
yield return type;
foreach (var t in InnerTypes (type))
yield return t;
}
}

IEnumerable<TypeDefinition> InnerTypes (TypeDefinition type)
{
if (type.HasNestedTypes) {
foreach (var t in type.NestedTypes) {
yield return t;
foreach (var nt in InnerTypes (t))
yield return nt;
}
}
}
#endif
}
}

Loading

6 comments on commit f4a0ea9

@vs-mobiletools-engineering-service2
Copy link
Collaborator

Choose a reason for hiding this comment

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

💻 [CI Build] Windows Integration Tests passed 💻

All Windows Integration Tests passed.

Pipeline on Agent
Hash: f4a0ea9ac28c5dc46f39cf351ba95d516fbdc89f [CI build]

@vs-mobiletools-engineering-service2
Copy link
Collaborator

Choose a reason for hiding this comment

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

💻 [CI Build] Tests on macOS M1 - Mac Ventura (13.0) passed 💻

All tests on macOS M1 - Mac Ventura (13.0) passed.

Pipeline on Agent
Hash: [CI build]

@vs-mobiletools-engineering-service2
Copy link
Collaborator

Choose a reason for hiding this comment

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

💻 [CI Build] Tests on macOS M1 - Mac Big Sur (11.5) passed 💻

All tests on macOS M1 - Mac Big Sur (11.5) passed.

Pipeline on Agent
Hash: [CI build]

@vs-mobiletools-engineering-service2
Copy link
Collaborator

Choose a reason for hiding this comment

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

✅ API diff for current PR / commit

Legacy Xamarin (No breaking changes)
  • iOS (no change detected)
  • tvOS (no change detected)
  • watchOS (no change detected)
  • macOS (no change detected)
NET (empty diffs)
  • iOS: (empty diff detected)
  • tvOS: (empty diff detected)
  • MacCatalyst: (empty diff detected)
  • macOS: (empty diff detected)

✅ API diff vs stable

Legacy Xamarin (No breaking changes)
.NET (No breaking changes)
Legacy Xamarin (stable) vs .NET

ℹ️ Generator diff

Generator Diff: vsdrops (html) vsdrops (raw diff) gist (raw diff) - Please review changes)

Pipeline on Agent
Hash: f4a0ea9ac28c5dc46f39cf351ba95d516fbdc89f [CI build]

@vs-mobiletools-engineering-service2
Copy link
Collaborator

Choose a reason for hiding this comment

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

📚 [CI Build] Artifacts 📚

Packages generated

View packages

Pipeline on Agent
Hash: [CI build]

@vs-mobiletools-engineering-service2
Copy link
Collaborator

Choose a reason for hiding this comment

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

🚀 [CI Build] Test results 🚀

Test results

✅ All tests passed on VSTS: simulator tests.

🎉 All 235 tests passed 🎉

Tests counts

✅ bcl: All 69 tests passed. Html Report (VSDrops) Download
✅ cecil: All 1 tests passed. Html Report (VSDrops) Download
✅ dotnettests: All 1 tests passed. Html Report (VSDrops) Download
✅ fsharp: All 7 tests passed. Html Report (VSDrops) Download
✅ framework: All 8 tests passed. Html Report (VSDrops) Download
✅ generator: All 2 tests passed. Html Report (VSDrops) Download
✅ interdependent_binding_projects: All 7 tests passed. Html Report (VSDrops) Download
✅ install_source: All 1 tests passed. Html Report (VSDrops) Download
✅ introspection: All 8 tests passed. Html Report (VSDrops) Download
✅ linker: All 65 tests passed. Html Report (VSDrops) Download
✅ mac_binding_project: All 1 tests passed. Html Report (VSDrops) Download
✅ mmp: All 2 tests passed. Html Report (VSDrops) Download
✅ mononative: All 6 tests passed. Html Report (VSDrops) Download
✅ monotouch: All 41 tests passed. Html Report (VSDrops) Download
✅ msbuild: All 2 tests passed. Html Report (VSDrops) Download
✅ mtouch: All 1 tests passed. Html Report (VSDrops) Download
✅ xammac: All 3 tests passed. Html Report (VSDrops) Download
✅ xcframework: All 8 tests passed. Html Report (VSDrops) Download
✅ xtro: All 2 tests passed. Html Report (VSDrops) Download

Pipeline on Agent
Hash: f4a0ea9ac28c5dc46f39cf351ba95d516fbdc89f [CI build]

Please sign in to comment.