Fixes for .NET 6 linker (#11739)
* Pass custom steps separately from msbuild * Remove reflection over linker pipeline * Fix ListExportedSymbols ctor * Add CoreTypeMapStep * PR feedback - Avoid unnecessary tracking of loaded assemblies (Use GetLoadedAssembly instead) - Create extension method on LinkContext to avoid conditional code - Rename dispatchers to reflect when they run * Fix PreMarkDispatcher * Fix DoneStep ordering * Fix other order-dependent steps, test asserts * Handle cyclic assembly references * Simplify reference search By using the already-loaded Assembly closure * Fix warning number Co-authored-by: Rolf Bjarne Kvinge <rolf@xamarin.com> * PR feedback - Undo whitespace changes - Move comment to a more appropriate place Co-authored-by: Rolf Bjarne Kvinge <rolf@xamarin.com>
This commit is contained in:
Родитель
2837a9c6bb
Коммит
045ccaf3a6
|
@ -317,11 +317,55 @@
|
||||||
<!-- Mark our entry assembly as a root assembly. -->
|
<!-- Mark our entry assembly as a root assembly. -->
|
||||||
<TrimmerRootAssembly Include="@(ResolvedFileToPublish)" Condition="'%(ResolvedFileToPublish.Filename)' == '$(AssemblyName)' And '%(ResolvedFileToPublish.Extension)' == '.dll'" />
|
<TrimmerRootAssembly Include="@(ResolvedFileToPublish)" Condition="'%(ResolvedFileToPublish.Filename)' == '$(AssemblyName)' And '%(ResolvedFileToPublish.Extension)' == '.dll'" />
|
||||||
|
|
||||||
<!-- add a custom step which inserts any other steps we need -->
|
<!--
|
||||||
<_TrimmerCustomSteps Include="$(_AdditionalTaskAssembly)">
|
pre-mark custom steps
|
||||||
<BeforeStep>MarkStep</BeforeStep>
|
-->
|
||||||
<Type>Xamarin.SetupStep</Type>
|
<!-- TODO: reverse the pre-mark steps once we get the fix from https://github.com/mono/linker/pull/2082 -->
|
||||||
</_TrimmerCustomSteps>
|
<!-- TODO: these steps should probably run after mark. -->
|
||||||
|
<_TrimmerCustomSteps Include="$(_AdditionalTaskAssembly)" BeforeStep="MarkStep" Condition="'$(_LinkMode)' != 'None'" Type="Xamarin.Linker.Steps.PreMarkDispatcher" />
|
||||||
|
<!-- The final decision to remove/keep the dynamic registrar must be done before the linking step -->
|
||||||
|
<_TrimmerCustomSteps Include="$(_AdditionalTaskAssembly)" BeforeStep="MarkStep" Type="MonoTouch.Tuner.RegistrarRemovalTrackingStep" />
|
||||||
|
<_TrimmerCustomSteps Include="$(_AdditionalTaskAssembly)" BeforeStep="MarkStep" Type="MonoTouch.Tuner.CoreTypeMapStep" />
|
||||||
|
<!-- Load the list of assemblies loaded by the linker. -->
|
||||||
|
<!-- This would not be needed if LinkContext.GetAssemblies () was exposed to us. -->
|
||||||
|
<_TrimmerCustomSteps Include="$(_AdditionalTaskAssembly)" BeforeStep="MarkStep" Type="Xamarin.Linker.CollectAssembliesStep" />
|
||||||
|
<_TrimmerCustomSteps Include="$(_AdditionalTaskAssembly)" BeforeStep="MarkStep" Type="Xamarin.SetupStep" />
|
||||||
|
|
||||||
|
<!--
|
||||||
|
IMarkHandlers which run during Mark
|
||||||
|
-->
|
||||||
|
<_TrimmerCustomSteps Include="$(_AdditionalTaskAssembly)" Condition="'$(_LinkMode)' != 'None'" Type="Xamarin.Linker.Steps.PreserveBlockCodeHandler" />
|
||||||
|
<_TrimmerCustomSteps Include="$(_AdditionalTaskAssembly)" Condition="'$(_LinkMode)' != 'None'" Type="Xamarin.Linker.OptimizeGeneratedCodeHandler" />
|
||||||
|
<!-- MarkDispatcher substeps will run for all marked assemblies. -->
|
||||||
|
<_TrimmerCustomSteps Include="$(_AdditionalTaskAssembly)" Condition="'$(_LinkMode)' != 'None'" Type="Xamarin.Linker.Steps.MarkDispatcher" />
|
||||||
|
<_TrimmerCustomSteps Include="$(_AdditionalTaskAssembly)" Condition="'$(_LinkMode)' != 'None'" Type="Xamarin.Linker.Steps.PreserveSmartEnumConversionsHandler" />
|
||||||
|
|
||||||
|
<!--
|
||||||
|
post-sweep custom steps
|
||||||
|
-->
|
||||||
|
<_TrimmerCustomSteps Include="$(_AdditionalTaskAssembly)" AfterStep="SweepStep" Condition="'$(_LinkMode)' != 'None'" Type="Xamarin.Linker.Steps.PostSweepDispatcher" />
|
||||||
|
|
||||||
|
<!--
|
||||||
|
pre-output custom steps
|
||||||
|
-->
|
||||||
|
<!-- TODO: reverse the pre-output steps once we get the fix from https://github.com/mono/linker/pull/2082 -->
|
||||||
|
<_TrimmerCustomSteps Include="$(_AdditionalTaskAssembly)" BeforeStep="OutputStep" Type="Xamarin.Linker.Steps.PreOutputDispatcher" />
|
||||||
|
<_TrimmerCustomSteps Include="$(_AdditionalTaskAssembly)" BeforeStep="OutputStep" Type="Xamarin.Linker.ExtractBindingLibrariesStep" />
|
||||||
|
<_TrimmerCustomSteps Include="$(_AdditionalTaskAssembly)" BeforeStep="OutputStep" Type="Xamarin.Linker.LoadNonSkippedAssembliesStep" />
|
||||||
|
<_TrimmerCustomSteps Include="$(_AdditionalTaskAssembly)" BeforeStep="OutputStep" Type="Xamarin.Linker.Steps.ListExportedSymbols" />
|
||||||
|
|
||||||
|
<!--
|
||||||
|
post-output steps
|
||||||
|
-->
|
||||||
|
<!-- TODO: remove AfterStep="OutputStep" from the post-output steps once we get the fix from https://github.com/mono/linker/pull/2082 -->
|
||||||
|
<_TrimmerCustomSteps Include="$(_AdditionalTaskAssembly)" AfterStep="OutputStep" Type="Xamarin.Linker.RegistrarStep" />
|
||||||
|
<_TrimmerCustomSteps Include="$(_AdditionalTaskAssembly)" AfterStep="OutputStep" Type="Xamarin.GenerateMainStep" />
|
||||||
|
<_TrimmerCustomSteps Include="$(_AdditionalTaskAssembly)" AfterStep="OutputStep" Type="Xamarin.GenerateReferencesStep" />
|
||||||
|
<_TrimmerCustomSteps Include="$(_AdditionalTaskAssembly)" AfterStep="OutputStep" Type="Xamarin.GatherFrameworksStep" />
|
||||||
|
<_TrimmerCustomSteps Include="$(_AdditionalTaskAssembly)" AfterStep="OutputStep" Type="Xamarin.Linker.ComputeNativeBuildFlagsStep" />
|
||||||
|
<_TrimmerCustomSteps Include="$(_AdditionalTaskAssembly)" AfterStep="OutputStep" Type="Xamarin.Linker.ComputeAOTArguments" />
|
||||||
|
<!-- Must be the last step. -->
|
||||||
|
<_TrimmerCustomSteps Include="$(_AdditionalTaskAssembly)" AfterStep="OutputStep" Type="Xamarin.Linker.DoneStep" />
|
||||||
|
|
||||||
<!-- _BundlerXmlDefinitions comes from any -xml arguments to mtouch/mmp -->
|
<!-- _BundlerXmlDefinitions comes from any -xml arguments to mtouch/mmp -->
|
||||||
<TrimmerRootDescriptor Include="@(_BundlerXmlDefinitions)" />
|
<TrimmerRootDescriptor Include="@(_BundlerXmlDefinitions)" />
|
||||||
|
|
|
@ -443,14 +443,14 @@ namespace Xamarin.Tests {
|
||||||
{
|
{
|
||||||
var output = BinLog.PrintToString (result.BinLogPath);
|
var output = BinLog.PrintToString (result.BinLogPath);
|
||||||
Assert.That (output, Does.Contain ("Building target \"_RunILLink\" completely."), "Linker did not executed as expected.");
|
Assert.That (output, Does.Contain ("Building target \"_RunILLink\" completely."), "Linker did not executed as expected.");
|
||||||
Assert.That (output, Does.Contain ("Pipeline Steps:"), "Custom steps did not run as expected.");
|
Assert.That (output, Does.Contain ("LinkerConfiguration:"), "Custom steps did not run as expected.");
|
||||||
}
|
}
|
||||||
|
|
||||||
void AssertThatLinkerDidNotExecute (ExecutionResult result)
|
void AssertThatLinkerDidNotExecute (ExecutionResult result)
|
||||||
{
|
{
|
||||||
var output = BinLog.PrintToString (result.BinLogPath);
|
var output = BinLog.PrintToString (result.BinLogPath);
|
||||||
Assert.That (output, Does.Not.Contain ("Building target \"_RunILLink\" completely."), "Linker did not executed as expected.");
|
Assert.That (output, Does.Not.Contain ("Building target \"_RunILLink\" completely."), "Linker did not executed as expected.");
|
||||||
Assert.That (output, Does.Not.Contain ("Pipeline Steps:"), "Custom steps did not run as expected.");
|
Assert.That (output, Does.Not.Contain ("LinkerConfiguration:"), "Custom steps did not run as expected.");
|
||||||
}
|
}
|
||||||
|
|
||||||
void AssertAppContents (ApplePlatform platform, string app_directory)
|
void AssertAppContents (ApplePlatform platform, string app_directory)
|
||||||
|
|
|
@ -0,0 +1,32 @@
|
||||||
|
using Mono.Cecil;
|
||||||
|
using Mono.Linker;
|
||||||
|
using Mono.Tuner;
|
||||||
|
using System;
|
||||||
|
|
||||||
|
namespace Xamarin.Linker {
|
||||||
|
public static class Extensions {
|
||||||
|
public static bool Inherits (this TypeReference self, string @namespace, string name, IMetadataResolver resolver)
|
||||||
|
{
|
||||||
|
if (@namespace == null)
|
||||||
|
throw new ArgumentNullException ("namespace");
|
||||||
|
if (name == null)
|
||||||
|
throw new ArgumentNullException ("name");
|
||||||
|
if (self == null)
|
||||||
|
return false;
|
||||||
|
|
||||||
|
TypeReference current = resolver.Resolve (self);
|
||||||
|
while (current != null) {
|
||||||
|
if (current.Is (@namespace, name))
|
||||||
|
return true;
|
||||||
|
if (current.Is ("System", "Object"))
|
||||||
|
return false;
|
||||||
|
|
||||||
|
TypeDefinition td = resolver.Resolve (current);
|
||||||
|
if (td == null)
|
||||||
|
return false; // could not resolve type
|
||||||
|
current = td.BaseType;
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -17,129 +17,12 @@ namespace Xamarin {
|
||||||
protected override string Name { get; } = "Setup";
|
protected override string Name { get; } = "Setup";
|
||||||
protected override int ErrorCode { get; } = 2300;
|
protected override int ErrorCode { get; } = 2300;
|
||||||
|
|
||||||
List<IStep> _steps;
|
|
||||||
public List<IStep> Steps {
|
|
||||||
get {
|
|
||||||
if (_steps == null) {
|
|
||||||
var pipeline = typeof (LinkContext).GetProperty ("Pipeline").GetGetMethod ().Invoke (Context, null);
|
|
||||||
_steps = (List<IStep>) pipeline.GetType ().GetField ("_steps", BindingFlags.Instance | BindingFlags.NonPublic).GetValue (pipeline);
|
|
||||||
}
|
|
||||||
return _steps;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
List<IMarkHandler> _markHandlers;
|
|
||||||
List<IMarkHandler> MarkHandlers {
|
|
||||||
get {
|
|
||||||
if (_markHandlers == null) {
|
|
||||||
var pipeline = typeof (LinkContext).GetProperty ("Pipeline").GetGetMethod ().Invoke (Context, null);
|
|
||||||
_markHandlers = (List<IMarkHandler>) pipeline.GetType ().GetProperty ("MarkHandlers").GetValue (pipeline);
|
|
||||||
}
|
|
||||||
return _markHandlers;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void InsertBefore (IStep step, string stepName)
|
|
||||||
{
|
|
||||||
for (int i = 0; i < Steps.Count; i++) {
|
|
||||||
if (Steps [i].GetType ().Name == stepName) {
|
|
||||||
Steps.Insert (i, step);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
DumpSteps ();
|
|
||||||
throw new InvalidOperationException ($"Could not insert {step} before {stepName} because {stepName} wasn't found.");
|
|
||||||
}
|
|
||||||
|
|
||||||
void InsertAfter (IStep step, string stepName)
|
|
||||||
{
|
|
||||||
for (int i = 0; i < Steps.Count;) {
|
|
||||||
if (Steps [i++].GetType ().Name == stepName) {
|
|
||||||
Steps.Insert (i, step);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
DumpSteps ();
|
|
||||||
throw new InvalidOperationException ($"Could not insert {step} after {stepName} because {stepName} wasn't found.");
|
|
||||||
}
|
|
||||||
|
|
||||||
protected override void TryProcess ()
|
protected override void TryProcess ()
|
||||||
{
|
{
|
||||||
// Don't use --custom-step to load each step, because this assembly
|
|
||||||
// is loaded into the current process once per --custom-step,
|
|
||||||
// which makes it very difficult to share state between steps.
|
|
||||||
|
|
||||||
// Load the list of assemblies loaded by the linker.
|
|
||||||
// This would not be needed of LinkContext.GetAssemblies () was exposed to us.
|
|
||||||
InsertBefore (new CollectAssembliesStep (), "MarkStep");
|
|
||||||
|
|
||||||
// the final decision to remove/keep the dynamic registrar must be done before the linking step
|
|
||||||
InsertBefore (new RegistrarRemovalTrackingStep (), "MarkStep");
|
|
||||||
|
|
||||||
var pre_mark_substeps = new DotNetSubStepDispatcher ();
|
|
||||||
InsertBefore (pre_mark_substeps, "MarkStep");
|
|
||||||
|
|
||||||
var post_sweep_substeps = new DotNetSubStepDispatcher ();
|
|
||||||
InsertAfter (post_sweep_substeps, "SweepStep");
|
|
||||||
|
|
||||||
if (Configuration.LinkMode != LinkMode.None) {
|
|
||||||
MarkHandlers.Add (new PreserveBlockCodeHandler ());
|
|
||||||
|
|
||||||
// We need to run the ApplyPreserveAttribute step even we're only linking sdk assemblies, because even
|
|
||||||
// though we know that sdk assemblies will never have Preserve attributes, user assemblies may have
|
|
||||||
// [assembly: LinkSafe] attributes, which means we treat them as sdk assemblies and those may have
|
|
||||||
// Preserve attributes.
|
|
||||||
MarkHandlers.Add (new DotNetMarkAssemblySubStepDispatcher (new ApplyPreserveAttribute ()));
|
|
||||||
MarkHandlers.Add (new OptimizeGeneratedCodeHandler ());
|
|
||||||
MarkHandlers.Add (new DotNetMarkAssemblySubStepDispatcher (new MarkNSObjects ()));
|
|
||||||
MarkHandlers.Add (new PreserveSmartEnumConversionsHandler ());
|
|
||||||
|
|
||||||
// This step could be run after Mark to avoid tracking all members:
|
|
||||||
// https://github.com/xamarin/xamarin-macios/issues/11447
|
|
||||||
pre_mark_substeps.Add (new CollectUnmarkedMembersSubStep ());
|
|
||||||
pre_mark_substeps.Add (new StoreAttributesStep ());
|
|
||||||
|
|
||||||
post_sweep_substeps.Add (new RemoveAttributesStep ());
|
|
||||||
}
|
|
||||||
|
|
||||||
InsertBefore (new ListExportedSymbols (null), "OutputStep");
|
|
||||||
InsertBefore (new LoadNonSkippedAssembliesStep (), "OutputStep");
|
|
||||||
InsertBefore (new ExtractBindingLibrariesStep (), "OutputStep");
|
|
||||||
InsertBefore (new DotNetSubStepDispatcher (new RemoveUserResourcesSubStep ()), "OutputStep");
|
|
||||||
Steps.Add (new RegistrarStep ());
|
|
||||||
Steps.Add (new GenerateMainStep ());
|
|
||||||
Steps.Add (new GenerateReferencesStep ());
|
|
||||||
Steps.Add (new GatherFrameworksStep ());
|
|
||||||
Steps.Add (new ComputeNativeBuildFlagsStep ());
|
|
||||||
Steps.Add (new ComputeAOTArguments ());
|
|
||||||
Steps.Add (new DoneStep ()); // Must be the last step.
|
|
||||||
|
|
||||||
Configuration.Write ();
|
Configuration.Write ();
|
||||||
|
|
||||||
if (Configuration.Verbosity > 0) {
|
|
||||||
DumpSteps ();
|
|
||||||
}
|
|
||||||
|
|
||||||
ErrorHelper.Platform = Configuration.Platform;
|
ErrorHelper.Platform = Configuration.Platform;
|
||||||
Directory.CreateDirectory (Configuration.ItemsDirectory);
|
Directory.CreateDirectory (Configuration.ItemsDirectory);
|
||||||
Directory.CreateDirectory (Configuration.CacheDirectory);
|
Directory.CreateDirectory (Configuration.CacheDirectory);
|
||||||
}
|
}
|
||||||
|
|
||||||
void DumpSteps ()
|
|
||||||
{
|
|
||||||
Console.WriteLine ();
|
|
||||||
Console.WriteLine ("Pipeline Steps:");
|
|
||||||
foreach (var step in Steps) {
|
|
||||||
Console.WriteLine ($" {step}");
|
|
||||||
if (step is SubStepsDispatcher) {
|
|
||||||
var substeps = typeof (SubStepsDispatcher).GetField ("substeps", BindingFlags.Instance | BindingFlags.NonPublic)?.GetValue (step) as IEnumerable<ISubStep>;
|
|
||||||
if (substeps != null) {
|
|
||||||
foreach (var substep in substeps) {
|
|
||||||
Console.WriteLine ($" {substep}");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,11 +0,0 @@
|
||||||
using Mono.Linker.Steps;
|
|
||||||
|
|
||||||
namespace Xamarin.Linker.Steps {
|
|
||||||
// MarkSubStepsDispatcher is abstract, so create a subclass we can instantiate.
|
|
||||||
// Can be removed when we update to the preview4 linker, which makes MarkSubStepsDispatcher non-abstract.
|
|
||||||
class DotNetMarkAssemblySubStepDispatcher : MarkSubStepsDispatcher {
|
|
||||||
public DotNetMarkAssemblySubStepDispatcher (params BaseSubStep[] subSteps) : base (subSteps)
|
|
||||||
{
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -0,0 +1,14 @@
|
||||||
|
using Mono.Linker.Steps;
|
||||||
|
using Xamarin.Linker;
|
||||||
|
|
||||||
|
namespace Xamarin.Linker.Steps {
|
||||||
|
class MarkDispatcher : MarkSubStepsDispatcher {
|
||||||
|
public MarkDispatcher ()
|
||||||
|
: base (new BaseSubStep[] {
|
||||||
|
new ApplyPreserveAttribute (),
|
||||||
|
new MarkNSObjects ()
|
||||||
|
})
|
||||||
|
{
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,11 @@
|
||||||
|
using Mono.Linker.Steps;
|
||||||
|
using Xamarin.Linker;
|
||||||
|
|
||||||
|
namespace Xamarin.Linker.Steps {
|
||||||
|
class PostSweepDispatcher : SubStepsDispatcher {
|
||||||
|
public PostSweepDispatcher ()
|
||||||
|
: base (new [] { new RemoveAttributesStep () })
|
||||||
|
{
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,14 @@
|
||||||
|
using Mono.Linker.Steps;
|
||||||
|
using Xamarin.Linker;
|
||||||
|
|
||||||
|
namespace Xamarin.Linker.Steps {
|
||||||
|
class PreMarkDispatcher : SubStepsDispatcher {
|
||||||
|
public PreMarkDispatcher ()
|
||||||
|
: base (new BaseSubStep [] {
|
||||||
|
new CollectUnmarkedMembersSubStep (),
|
||||||
|
new StoreAttributesStep ()
|
||||||
|
})
|
||||||
|
{
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,11 @@
|
||||||
|
using Mono.Linker.Steps;
|
||||||
|
using Xamarin.Linker;
|
||||||
|
|
||||||
|
namespace Xamarin.Linker.Steps {
|
||||||
|
class PreOutputDispatcher : SubStepsDispatcher {
|
||||||
|
public PreOutputDispatcher ()
|
||||||
|
: base (new [] { new RemoveUserResourcesSubStep () })
|
||||||
|
{
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -106,6 +106,9 @@
|
||||||
<Compile Include="..\linker\CoreOptimizeGeneratedCode.cs">
|
<Compile Include="..\linker\CoreOptimizeGeneratedCode.cs">
|
||||||
<Link>external\tools\linker\CoreOptimizeGeneratedCode.cs</Link>
|
<Link>external\tools\linker\CoreOptimizeGeneratedCode.cs</Link>
|
||||||
</Compile>
|
</Compile>
|
||||||
|
<Compile Include="..\linker\CoreTypeMapStep.cs">
|
||||||
|
<Link>external\tools\linker\CoreTypeMapStep.cs</Link>
|
||||||
|
</Compile>
|
||||||
<Compile Include="..\..\src\ObjCRuntime\Registrar.cs">
|
<Compile Include="..\..\src\ObjCRuntime\Registrar.cs">
|
||||||
<Link>external\src\ObjCRuntime\Registrar.cs</Link>
|
<Link>external\src\ObjCRuntime\Registrar.cs</Link>
|
||||||
</Compile>
|
</Compile>
|
||||||
|
|
|
@ -17,6 +17,10 @@ namespace Xamarin.Linker.Steps {
|
||||||
HashSet<TypeDefinition> preserve_synonyms;
|
HashSet<TypeDefinition> preserve_synonyms;
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
// We need to run the ApplyPreserveAttribute step even if we're only linking sdk assemblies, because even
|
||||||
|
// though we know that sdk assemblies will never have Preserve attributes, user assemblies may have
|
||||||
|
// [assembly: LinkSafe] attributes, which means we treat them as sdk assemblies and those may have
|
||||||
|
// Preserve attributes.
|
||||||
public override bool IsActiveFor (AssemblyDefinition assembly)
|
public override bool IsActiveFor (AssemblyDefinition assembly)
|
||||||
{
|
{
|
||||||
return Annotations.GetAction (assembly) == AssemblyAction.Link;
|
return Annotations.GetAction (assembly) == AssemblyAction.Link;
|
||||||
|
|
|
@ -9,6 +9,7 @@
|
||||||
|
|
||||||
using System;
|
using System;
|
||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
|
using System.Diagnostics;
|
||||||
|
|
||||||
using Mono.Cecil;
|
using Mono.Cecil;
|
||||||
using Mono.Linker.Steps;
|
using Mono.Linker.Steps;
|
||||||
|
@ -20,27 +21,152 @@ using Xamarin.Tuner;
|
||||||
namespace MonoTouch.Tuner {
|
namespace MonoTouch.Tuner {
|
||||||
|
|
||||||
// This class is shared between Xamarin.Mac and Xamarin.iOS
|
// This class is shared between Xamarin.Mac and Xamarin.iOS
|
||||||
public class CoreTypeMapStep : TypeMapStep {
|
public class CoreTypeMapStep :
|
||||||
HashSet<TypeDefinition> cached_isnsobject = new HashSet<TypeDefinition> ();
|
#if NET
|
||||||
Dictionary<TypeDefinition, bool?> isdirectbinding_value = new Dictionary<TypeDefinition, bool?> ();
|
ConfigurationAwareStep
|
||||||
|
#else
|
||||||
|
TypeMapStep
|
||||||
|
#endif
|
||||||
|
{
|
||||||
|
|
||||||
|
#if NET
|
||||||
|
protected override string Name { get; } = "CoreTypeMap";
|
||||||
|
protected override int ErrorCode { get; } = 2390;
|
||||||
|
|
||||||
|
Profile Profile => new Profile (Configuration);
|
||||||
|
|
||||||
|
// Get the reverse mapping from assemblies to assemblies which reference them directly.
|
||||||
|
Dictionary<AssemblyDefinition, HashSet<AssemblyDefinition>> _reversedReferences;
|
||||||
|
Dictionary<AssemblyDefinition, HashSet<AssemblyDefinition>> GetReversedReferences ()
|
||||||
|
{
|
||||||
|
if (_reversedReferences != null)
|
||||||
|
return _reversedReferences;
|
||||||
|
|
||||||
|
_reversedReferences = new Dictionary<AssemblyDefinition, HashSet<AssemblyDefinition>> ();
|
||||||
|
foreach (var assembly in Configuration.Assemblies) {
|
||||||
|
if (!_reversedReferences.ContainsKey (assembly))
|
||||||
|
_reversedReferences.Add (assembly, new HashSet<AssemblyDefinition> ());
|
||||||
|
|
||||||
|
foreach (var reference in assembly.MainModule.AssemblyReferences) {
|
||||||
|
var resolvedReference = Configuration.Context.GetLoadedAssembly (reference.Name);
|
||||||
|
if (resolvedReference == null)
|
||||||
|
continue;
|
||||||
|
|
||||||
|
if (!_reversedReferences.TryGetValue (resolvedReference, out var referrers)) {
|
||||||
|
referrers = new HashSet<AssemblyDefinition> ();
|
||||||
|
_reversedReferences.Add (resolvedReference, referrers);
|
||||||
|
}
|
||||||
|
|
||||||
|
referrers.Add (assembly);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return _reversedReferences;
|
||||||
|
}
|
||||||
|
|
||||||
|
Dictionary<AssemblyDefinition, bool> _transitivelyReferencesProduct;
|
||||||
|
bool TransitivelyReferencesProduct (AssemblyDefinition assembly)
|
||||||
|
{
|
||||||
|
if (_transitivelyReferencesProduct != null) {
|
||||||
|
Debug.Assert (_transitivelyReferencesProduct.ContainsKey (assembly));
|
||||||
|
return _transitivelyReferencesProduct.TryGetValue (assembly, out bool result) && result;
|
||||||
|
}
|
||||||
|
|
||||||
|
_transitivelyReferencesProduct = new Dictionary<AssemblyDefinition, bool> ();
|
||||||
|
|
||||||
|
// A depth-first search is insufficient because there are reference cycles, so we
|
||||||
|
// get the set of transitive references, and do a reverse BFS.
|
||||||
|
var reversedReferences = GetReversedReferences ();
|
||||||
|
Debug.Assert (reversedReferences.ContainsKey (assembly));
|
||||||
|
var referencesProductToProcess = new Queue<AssemblyDefinition> ();
|
||||||
|
|
||||||
|
// We start the BFS from the product assembly.
|
||||||
|
foreach (var reference in reversedReferences.Keys) {
|
||||||
|
if (Profile.IsProductAssembly (reference)) {
|
||||||
|
_transitivelyReferencesProduct.Add (reference, true);
|
||||||
|
referencesProductToProcess.Enqueue (reference);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Scan the reverse references to find out which referencing assemblies
|
||||||
|
// are reachable from the product assembly (that is, transitively reference the product).
|
||||||
|
while (referencesProductToProcess.TryDequeue (out var reference)) {
|
||||||
|
foreach (var referrer in reversedReferences[reference]) {
|
||||||
|
if (_transitivelyReferencesProduct.TryGetValue (referrer, out bool referencesProduct)) {
|
||||||
|
Debug.Assert (referencesProduct);
|
||||||
|
// Any which were already determined to reference the product assembly
|
||||||
|
// don't need to be scanned again.
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
_transitivelyReferencesProduct.Add (referrer, true);
|
||||||
|
referencesProductToProcess.Enqueue (referrer);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Any remaining references that we didn't discover during the search
|
||||||
|
// don't reference the product assembly.
|
||||||
|
foreach (var reference in reversedReferences.Keys)
|
||||||
|
_transitivelyReferencesProduct.TryAdd (reference, false);
|
||||||
|
|
||||||
|
return _transitivelyReferencesProduct[assembly];
|
||||||
|
}
|
||||||
|
|
||||||
|
protected override void TryProcessAssembly (AssemblyDefinition assembly)
|
||||||
|
{
|
||||||
|
// We are only interested in types transitively derived from NSObject,
|
||||||
|
// which lives in the product assembly.
|
||||||
|
if (!TransitivelyReferencesProduct (assembly))
|
||||||
|
return;
|
||||||
|
|
||||||
|
foreach (var type in assembly.MainModule.Types)
|
||||||
|
ProcessType (type);
|
||||||
|
}
|
||||||
|
|
||||||
|
void ProcessType (TypeDefinition type)
|
||||||
|
{
|
||||||
|
MapType (type);
|
||||||
|
|
||||||
|
if (!type.HasNestedTypes)
|
||||||
|
return;
|
||||||
|
|
||||||
|
foreach (var nestedType in type.NestedTypes)
|
||||||
|
ProcessType (nestedType);
|
||||||
|
}
|
||||||
|
|
||||||
|
DerivedLinkContext LinkContext => Configuration.DerivedLinkContext;
|
||||||
|
#else
|
||||||
DerivedLinkContext LinkContext {
|
DerivedLinkContext LinkContext {
|
||||||
get {
|
get {
|
||||||
return (DerivedLinkContext) base.Context;
|
return (DerivedLinkContext) base.Context;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
|
HashSet<TypeDefinition> cached_isnsobject = new HashSet<TypeDefinition> ();
|
||||||
|
Dictionary<TypeDefinition, bool?> isdirectbinding_value = new Dictionary<TypeDefinition, bool?> ();
|
||||||
|
|
||||||
|
#if NET
|
||||||
|
protected override void TryEndProcess ()
|
||||||
|
{
|
||||||
|
#else
|
||||||
protected override void EndProcess ()
|
protected override void EndProcess ()
|
||||||
{
|
{
|
||||||
base.EndProcess ();
|
base.EndProcess ();
|
||||||
|
#endif
|
||||||
|
|
||||||
LinkContext.CachedIsNSObject = cached_isnsobject;
|
LinkContext.CachedIsNSObject = cached_isnsobject;
|
||||||
LinkContext.IsDirectBindingValue = isdirectbinding_value;
|
LinkContext.IsDirectBindingValue = isdirectbinding_value;
|
||||||
}
|
}
|
||||||
|
|
||||||
protected override void MapType (TypeDefinition type)
|
protected
|
||||||
|
#if !NET
|
||||||
|
override
|
||||||
|
#endif
|
||||||
|
void MapType (TypeDefinition type)
|
||||||
{
|
{
|
||||||
|
#if !NET
|
||||||
base.MapType (type);
|
base.MapType (type);
|
||||||
|
#endif
|
||||||
|
|
||||||
// additional checks for NSObject to check if the type is a *generated* bindings
|
// additional checks for NSObject to check if the type is a *generated* bindings
|
||||||
// bonus: we cache, for every type, whether or not it inherits from NSObject (very useful later)
|
// bonus: we cache, for every type, whether or not it inherits from NSObject (very useful later)
|
||||||
|
@ -77,7 +203,7 @@ namespace MonoTouch.Tuner {
|
||||||
|
|
||||||
bool rv;
|
bool rv;
|
||||||
if (!ci_filter_types.TryGetValue (type, out rv)) {
|
if (!ci_filter_types.TryGetValue (type, out rv)) {
|
||||||
rv = type.Is (Namespaces.CoreImage, "CIFilter") || IsCIFilter (type.Resolve ().BaseType);
|
rv = type.Is (Namespaces.CoreImage, "CIFilter") || IsCIFilter (Context.Resolve (type).BaseType);
|
||||||
ci_filter_types [type] = rv;
|
ci_filter_types [type] = rv;
|
||||||
}
|
}
|
||||||
return rv;
|
return rv;
|
||||||
|
@ -97,10 +223,10 @@ namespace MonoTouch.Tuner {
|
||||||
// * https://bugzilla.xamarin.com/show_bug.cgi?id=15465
|
// * https://bugzilla.xamarin.com/show_bug.cgi?id=15465
|
||||||
if (IsCIFilter (type)) {
|
if (IsCIFilter (type)) {
|
||||||
isdirectbinding_value [type] = null;
|
isdirectbinding_value [type] = null;
|
||||||
var base_type = type.BaseType.Resolve ();
|
var base_type = Context.Resolve (type.BaseType);
|
||||||
while (base_type != null && IsNSObject (base_type)) {
|
while (base_type != null && IsNSObject (base_type)) {
|
||||||
isdirectbinding_value [base_type] = null;
|
isdirectbinding_value [base_type] = null;
|
||||||
base_type = base_type.BaseType.Resolve ();
|
base_type = Context.Resolve (base_type.BaseType);
|
||||||
}
|
}
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
@ -111,11 +237,11 @@ namespace MonoTouch.Tuner {
|
||||||
isdirectbinding_value [type] = false;
|
isdirectbinding_value [type] = false;
|
||||||
|
|
||||||
// We must clear IsDirectBinding for any wrapper superclasses.
|
// We must clear IsDirectBinding for any wrapper superclasses.
|
||||||
var base_type = type.BaseType.Resolve ();
|
var base_type = Context.Resolve (type.BaseType);
|
||||||
while (base_type != null && IsNSObject (base_type)) {
|
while (base_type != null && IsNSObject (base_type)) {
|
||||||
if (IsWrapperType (base_type))
|
if (IsWrapperType (base_type))
|
||||||
isdirectbinding_value [base_type] = null;
|
isdirectbinding_value [base_type] = null;
|
||||||
base_type = base_type.BaseType.Resolve ();
|
base_type = Context.Resolve (base_type.BaseType);
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
isdirectbinding_value [type] = true; // Let's try 'true' first, any derived non-wrapper classes will clear it if needed
|
isdirectbinding_value [type] = true; // Let's try 'true' first, any derived non-wrapper classes will clear it if needed
|
||||||
|
|
|
@ -85,7 +85,6 @@ namespace Xamarin.Linker.Steps {
|
||||||
continue;
|
continue;
|
||||||
|
|
||||||
// not optimal if "Link all" is used as the override might be removed later
|
// not optimal if "Link all" is used as the override might be removed later
|
||||||
// this may miss some overrides with the .NET6 linker (https://github.com/xamarin/xamarin-macios/issues/11449)
|
|
||||||
if (!IsOverridenInUserCode (method))
|
if (!IsOverridenInUserCode (method))
|
||||||
continue;
|
continue;
|
||||||
|
|
||||||
|
|
|
@ -1,6 +1,7 @@
|
||||||
using System;
|
using System;
|
||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
using Mono.Cecil;
|
using Mono.Cecil;
|
||||||
|
using Mono.Linker;
|
||||||
|
|
||||||
using Mono.Tuner;
|
using Mono.Tuner;
|
||||||
|
|
||||||
|
@ -21,5 +22,13 @@ namespace MonoTouch.Tuner {
|
||||||
|
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Extension method to avoid conditional code for files shared between
|
||||||
|
// .NET linker and Legacy (where LinkContext doesn't implement IMetadataResolver).
|
||||||
|
// This doesn't actually use the LinkContext.
|
||||||
|
public static TypeDefinition Resolve (this LinkContext context, TypeReference type)
|
||||||
|
{
|
||||||
|
return type.Resolve ();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -28,6 +28,10 @@ namespace Xamarin.Linker.Steps
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public ListExportedSymbols () : this (null)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
internal ListExportedSymbols (PInvokeWrapperGenerator state, bool skip_sdk_assemblies = false)
|
internal ListExportedSymbols (PInvokeWrapperGenerator state, bool skip_sdk_assemblies = false)
|
||||||
{
|
{
|
||||||
this.state = state;
|
this.state = state;
|
||||||
|
|
|
@ -84,7 +84,13 @@ namespace Xamarin.Linker {
|
||||||
const string INativeObject = Namespaces.ObjCRuntime + ".INativeObject";
|
const string INativeObject = Namespaces.ObjCRuntime + ".INativeObject";
|
||||||
public static bool IsNSObject (this TypeReference type, DerivedLinkContext link_context)
|
public static bool IsNSObject (this TypeReference type, DerivedLinkContext link_context)
|
||||||
{
|
{
|
||||||
return type.Resolve ().IsNSObject (link_context);
|
return
|
||||||
|
#if NET
|
||||||
|
link_context.LinkerConfiguration.Context.Resolve (type)
|
||||||
|
#else
|
||||||
|
type.Resolve ()
|
||||||
|
#endif
|
||||||
|
.IsNSObject (link_context);
|
||||||
}
|
}
|
||||||
|
|
||||||
// warning: *Is* means does 'type' inherits from MonoTouch.Foundation.NSObject ?
|
// warning: *Is* means does 'type' inherits from MonoTouch.Foundation.NSObject ?
|
||||||
|
@ -93,7 +99,11 @@ namespace Xamarin.Linker {
|
||||||
if (link_context?.CachedIsNSObject != null)
|
if (link_context?.CachedIsNSObject != null)
|
||||||
return link_context.CachedIsNSObject.Contains (type);
|
return link_context.CachedIsNSObject.Contains (type);
|
||||||
|
|
||||||
return type.Inherits (Namespaces.Foundation, "NSObject");
|
return type.Inherits (Namespaces.Foundation, "NSObject"
|
||||||
|
#if NET
|
||||||
|
, link_context.LinkerConfiguration.Context
|
||||||
|
#endif
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
public static bool IsNativeObject (this TypeDefinition type)
|
public static bool IsNativeObject (this TypeDefinition type)
|
||||||
|
|
Загрузка…
Ссылка в новой задаче