Skip to content
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

[dotnet] Integrate class handle rewriting into static registrar process. #18456

Merged
merged 9 commits into from
Jun 23, 2023
Merged
1 change: 1 addition & 0 deletions dotnet/targets/Xamarin.Shared.Sdk.targets
Original file line number Diff line number Diff line change
Expand Up @@ -504,6 +504,7 @@
@(_MonoLibrary -> 'MonoLibrary=%(Identity)')
MtouchFloat32=$(MtouchFloat32)
Optimize=$(_BundlerOptimize)
OptimizeClassHandles=$(OptimizeClassHandles)
Copy link
Member

Choose a reason for hiding this comment

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

You already added an optimization option here:

62634bd

can't that one be used instead of adding another one?

PartialStaticRegistrarLibrary=$(_LibPartialStaticRegistrar)
Platform=$(_PlatformName)
PlatformAssembly=$(_PlatformAssemblyName).dll
Expand Down
134 changes: 0 additions & 134 deletions msbuild/Xamarin.MacDev.Tasks/Tasks/ClassRedirectorTask.cs

This file was deleted.

3 changes: 3 additions & 0 deletions msbuild/Xamarin.MacDev.Tasks/Xamarin.MacDev.Tasks.csproj
Original file line number Diff line number Diff line change
Expand Up @@ -78,6 +78,9 @@
<Compile Include="..\..\tools\common\ObjCNameIndex.cs">
<Link>external\ObjCNameIndex.cs</Link>
</Compile>
<Compile Include="..\..\tools\common\Rewriter.cs">
<Link>external\ObjCNameIndex.cs</Link>
</Compile>
Copy link
Member

Choose a reason for hiding this comment

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

Not sure why you need this if it's not being implemented as an MSBuild task anymore?

</ItemGroup>

<ItemGroup>
Expand Down
1 change: 1 addition & 0 deletions msbuild/Xamarin.Shared/Xamarin.Shared.targets
Original file line number Diff line number Diff line change
Expand Up @@ -1838,6 +1838,7 @@ Copyright (C) 2018 Microsoft. All rights reserved.
NoDSymUtil="$(NoDSymUtil)"
PackageDebugSymbols="$(PackageDebugSymbols)"
Registrar="$(Registrar)"
OptimizeClassHandles="$(OptimizeClassHandles)"
Verbosity="$(_BundlerVerbosity)"
>
<Output TaskParameter="Aot" ItemName="_AotArguments" />
Expand Down
25 changes: 20 additions & 5 deletions tools/common/Application.cs
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,8 @@

using ObjCRuntime;

using ClassRedirector;

#if MONOTOUCH
using PlatformResolver = MonoTouch.Tuner.MonoTouchResolver;
#elif MMP
Expand Down Expand Up @@ -167,7 +169,7 @@ public bool IsDefaultMarshalManagedExceptionMode {
public bool EnableBitCode { get { return BitCodeMode != BitCodeMode.None; } }

public bool SkipMarkingNSObjectsInUserAssemblies { get; set; }
public string ClassMapPath = "";
public bool OptimizeClassHandles { get; set; } = false;

// assembly_build_targets describes what kind of native code each assembly should be compiled into for mobile targets (iOS, tvOS, watchOS).
// An assembly can be compiled into: static object (.o), dynamic library (.dylib) or a framework (.framework).
Expand Down Expand Up @@ -1029,10 +1031,23 @@ public void RunRegistrar ()
}
#endif
var registrar = new Registrar.StaticRegistrar (this);
if (RootAssemblies.Count == 1)
registrar.GenerateSingleAssembly (resolver, resolvedAssemblies.Values, Path.ChangeExtension (registrar_m, "h"), registrar_m, Path.GetFileNameWithoutExtension (RootAssembly), out var _, ClassMapPath);
else
registrar.Generate (resolver, resolvedAssemblies.Values, Path.ChangeExtension (registrar_m, "h"), registrar_m, out var _, ClassMapPath);
if (RootAssemblies.Count == 1) {
registrar.GenerateSingleAssembly (resolver, resolvedAssemblies.Values, Path.ChangeExtension (registrar_m, "h"), registrar_m, Path.GetFileNameWithoutExtension (RootAssembly), out var _, out var oneAssemblyMap);
if (OptimizeClassHandles) {
RewriteClassHandles (oneAssemblyMap, resolvedAssemblies.Values);
}
} else {
registrar.Generate (resolver, resolvedAssemblies.Values, Path.ChangeExtension (registrar_m, "h"), registrar_m, out var _, out var typeMap);
if (OptimizeClassHandles) {
RewriteClassHandles (typeMap, resolvedAssemblies.Values);
}
}
}

void RewriteClassHandles (CSToObjCMap map, IEnumerable<AssemblyDefinition> assemblies)
{
var rewriter = new Rewriter (map, assemblies);
rewriter.Process ();
}

public IEnumerable<Abi> Abis {
Expand Down
2 changes: 1 addition & 1 deletion tools/common/Driver.cs
Original file line number Diff line number Diff line change
Expand Up @@ -257,7 +257,7 @@ static bool ParseOptions (Application app, Mono.Options.OptionSet options, strin
options.Add ("skip-marking-nsobjects-in-user-assemblies:", "Don't mark NSObject (and any subclass of NSObject) in user assemblies in the linker. This may break your app, use at own risk.", v => {
app.SkipMarkingNSObjectsInUserAssemblies = ParseBool (v, "--skip-marking-nsobjects-in-user-assemblies");
});
options.Add ("class-map-path=", "Sets the path for an output path to generate a class map XML file used to optimize class handle access when the static registrar has been used.", v => { app.ClassMapPath = v; });
options.Add ("optimitize-class-handles", "Optimizes usage for class handles to static references instead of pinvokes.", v => { app.OptimizeClassHandles = ParseBool (v, "--optimitize-class-handles"); });


// Keep the ResponseFileSource option at the end.
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@

#nullable enable

namespace Xamarin.MacDev.Tasks {
namespace ClassRedirector {
public class Rewriter {
const string runtimeName = "ObjCRuntime.Runtime";
const string classHandleName = "ObjCRuntime.Runtime/ClassHandles";
Expand All @@ -18,18 +18,23 @@ public class Rewriter {
const string classPtrName = "class_ptr";
CSToObjCMap map;
string pathToXamarinAssembly;
string [] assembliesToPatch;
string outputDirectory;
SimpleAssemblyResolver resolver;
string? outputDirectory = null;
Dictionary<string, FieldDefinition> csTypeToFieldDef = new Dictionary<string, FieldDefinition> ();
IEnumerable<AssemblyDefinition> assemblies;
AssemblyDefinition xamarinAssembly;

public Rewriter (CSToObjCMap map, string pathToXamarinAssembly, string [] assembliesToPatch, string outputDirectory)
public Rewriter (CSToObjCMap map, IEnumerable<AssemblyDefinition> assembliesToPatch)
{
this.map = map;
this.pathToXamarinAssembly = pathToXamarinAssembly;
this.assembliesToPatch = assembliesToPatch;
this.outputDirectory = outputDirectory;
resolver = new SimpleAssemblyResolver (assembliesToPatch);
this.assemblies = assembliesToPatch;
var xasm = assembliesToPatch.Select (assem => assem.MainModule).FirstOrDefault (ContainsNativeHandle)?.Assembly;
if (xasm is null) {
throw new Exception ("Unable to find Xamarin assembly.");
} else {
xamarinAssembly = xasm;
pathToXamarinAssembly = xamarinAssembly.MainModule.FileName;
}

}

public void Process ()
Expand All @@ -41,8 +46,7 @@ public void Process ()
Dictionary<string, FieldDefinition> CreateClassHandles ()
{
var classMap = new Dictionary<string, FieldDefinition> ();
using var assemblyStm = new FileStream (pathToXamarinAssembly, FileMode.Open, FileAccess.ReadWrite, FileShare.ReadWrite);
using var module = ModuleDefinition.ReadModule (assemblyStm);
var module = xamarinAssembly.MainModule;

var classHandles = LocateClassHandles (module);
if (classHandles is null)
Expand All @@ -58,7 +62,7 @@ Dictionary<string, FieldDefinition> CreateClassHandles ()
if (mtClassMapDef is null)
throw new Exception ($"Unable to find {mtClassMapName} in {pathToXamarinAssembly}");

var nativeHandle = module.Types.FirstOrDefault (t => t.FullName == nativeHandleName);
var nativeHandle = LocateNativeHandle (module);
if (nativeHandle is null)
throw new Exception ($"Unable to find {nativeHandleName} in {pathToXamarinAssembly}");

Expand All @@ -74,7 +78,7 @@ Dictionary<string, FieldDefinition> CreateClassHandles ()
classMap [csName] = fieldDef;
}

module.Write (ToOutputFileName (pathToXamarinAssembly));
module.Write ();
return classMap;
}

Expand Down Expand Up @@ -124,6 +128,16 @@ FieldDefinition AddPublicStaticField (TypeDefinition inType, string fieldName, T
return fieldDef;
}

bool ContainsNativeHandle (ModuleDefinition module)
{
return LocateNativeHandle (module) is not null;
}

TypeDefinition? LocateNativeHandle (ModuleDefinition module)
{
return AllTypes (module).FirstOrDefault (t => t.FullName == nativeHandleName);
}

TypeDefinition? LocateClassHandles (ModuleDefinition module)
{
return AllTypes (module).FirstOrDefault (t => t.FullName == classHandleName);
Expand All @@ -136,11 +150,10 @@ FieldDefinition AddPublicStaticField (TypeDefinition inType, string fieldName, T

void PatchClassPtrUsage (Dictionary<string, FieldDefinition> classMap)
{
foreach (var path in assembliesToPatch) {
using var stm = new FileStream (path, FileMode.Open, FileAccess.ReadWrite, FileShare.ReadWrite);
using var module = ModuleDefinition.ReadModule (stm);
foreach (var assem in assemblies) {
var module = assem.MainModule;
PatchClassPtrUsage (classMap, module);
module.Write (ToOutputFileName (path));
module.Write ();
Copy link
Member

Choose a reason for hiding this comment

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

Don't write out the assembly, the linker will do that later.

However, it's important to tell the linker that the assembly was changed, and that's done by changing the action:

var annotations = configuration.Context.Annotations;
var action = annotations.GetAction (assembly);
if (action == AssemblyAction.Copy) {

annotations.SetAction (assembly, AssemblyAction.Save);

Note that you should only save the assembly if you actually modified it.

}
}

Expand Down
23 changes: 10 additions & 13 deletions tools/common/StaticRegistrar.cs
Original file line number Diff line number Diff line change
Expand Up @@ -2843,7 +2843,7 @@ public string GetInitializationMethodName (string single_assembly)
}
}

void Specialize (AutoIndentStringBuilder sb, out string initialization_method, string type_map_path)
void Specialize (AutoIndentStringBuilder sb, out string initialization_method, out CSToObjCMap typeMap)
{
List<Exception> exceptions = new List<Exception> ();
List<ObjCMember> skip = new List<ObjCMember> ();
Expand Down Expand Up @@ -3296,10 +3296,7 @@ void Specialize (AutoIndentStringBuilder sb, out string initialization_method, s
sb.WriteLine (map.ToString ());
sb.WriteLine (map_init.ToString ());

if (!string.IsNullOrEmpty (type_map_path)) {
var doc = CSToObjCMap.ToXDocument (map_dict);
doc.Save (type_map_path);
}
typeMap = map_dict;

ErrorHelper.ThrowIfErrors (exceptions);
}
Expand Down Expand Up @@ -5504,24 +5501,24 @@ public void FilterTrimmedApi (AnnotationStore annotations)
}
}

public void GenerateSingleAssembly (PlatformResolver resolver, IEnumerable<AssemblyDefinition> assemblies, string header_path, string source_path, string assembly, out string initialization_method, string type_map_path)
public void GenerateSingleAssembly (PlatformResolver resolver, IEnumerable<AssemblyDefinition> assemblies, string header_path, string source_path, string assembly, out string initialization_method, out CSToObjCMap typeMap)
{
single_assembly = assembly;
Generate (resolver, assemblies, header_path, source_path, out initialization_method, type_map_path);
Generate (resolver, assemblies, header_path, source_path, out initialization_method, out typeMap);
}

public void Generate (IEnumerable<AssemblyDefinition> assemblies, string header_path, string source_path, out string initialization_method, string type_map_path)
public void Generate (IEnumerable<AssemblyDefinition> assemblies, string header_path, string source_path, out string initialization_method, out CSToObjCMap typeMap)
{
Generate (null, assemblies, header_path, source_path, out initialization_method, type_map_path);
Generate (null, assemblies, header_path, source_path, out initialization_method, out typeMap);
}

public void Generate (PlatformResolver resolver, IEnumerable<AssemblyDefinition> assemblies, string header_path, string source_path, out string initialization_method, string type_map_path)
public void Generate (PlatformResolver resolver, IEnumerable<AssemblyDefinition> assemblies, string header_path, string source_path, out string initialization_method, out CSToObjCMap typeMap)
{
Register (resolver, assemblies);
Generate (header_path, source_path, out initialization_method, type_map_path);
Generate (header_path, source_path, out initialization_method, out typeMap);
}

public void Generate (string header_path, string source_path, out string initialization_method, string type_map_path)
public void Generate (string header_path, string source_path, out string initialization_method)
{
var sb = new AutoIndentStringBuilder ();
header = new AutoIndentStringBuilder ();
Expand Down Expand Up @@ -5556,7 +5553,7 @@ public void Generate (string header_path, string source_path, out string initial
if (App.Embeddinator)
methods.WriteLine ("void xamarin_embeddinator_initialize ();");

Specialize (sb, out initialization_method, type_map_path);
Specialize (sb, out initialization_method, out var typeMap);

methods.WriteLine ();
methods.AppendLine ();
Expand Down
3 changes: 3 additions & 0 deletions tools/dotnet-linker/LinkerConfiguration.cs
Original file line number Diff line number Diff line change
Expand Up @@ -224,6 +224,9 @@ public static LinkerConfiguration GetInstance (LinkContext context)
case "Optimize":
user_optimize_flags = value;
break;
case "OptimizeClassHandles":
Application.OptimizeClassHandles = true;
break;
case "PartialStaticRegistrarLibrary":
PartialStaticRegistrarLibrary = value;
break;
Expand Down
2 changes: 1 addition & 1 deletion tools/dotnet-linker/Steps/RegistrarStep.cs
Original file line number Diff line number Diff line change
Expand Up @@ -42,7 +42,7 @@ protected override void TryEndProcess ()
// so we need to remove those that were later trimmed away by the trimmer.
Configuration.Target.StaticRegistrar.FilterTrimmedApi (Annotations);
}
Configuration.Target.StaticRegistrar.Generate (header, code, out var initialization_method, app.ClassMapPath);
Configuration.Target.StaticRegistrar.Generate (header, code, out var initialization_method);

var items = new List<MSBuildItem> ();
foreach (var abi in Configuration.Abis) {
Expand Down
Loading