Skip to content

Commit d8bcd19

Browse files
committed
[XABT] Move marshal method generation to a "linker step".
1 parent a89dfa4 commit d8bcd19

10 files changed

+206
-50
lines changed
Lines changed: 115 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,115 @@
1+
#nullable enable
2+
using System;
3+
using System.Collections.Generic;
4+
using Microsoft.Android.Build.Tasks;
5+
using Microsoft.Build.Utilities;
6+
using Mono.Cecil;
7+
using Mono.Linker;
8+
using Mono.Linker.Steps;
9+
using Xamarin.Android.Tasks;
10+
using Xamarin.Android.Tools;
11+
12+
namespace MonoDroid.Tuner;
13+
14+
/// <summary>
15+
/// Scans an assembly for JLOs that need JCWs generated and writes them to an XML file.
16+
/// </summary>
17+
public class RewriteMarshalMethodsStep : BaseStep, IAssemblyModifierPipelineStep
18+
{
19+
public TaskLoggingHelper Log { get; set; }
20+
21+
bool? brokenExceptionTransitionsEnabled;
22+
23+
public RewriteMarshalMethodsStep (TaskLoggingHelper log)
24+
{
25+
Log = log;
26+
}
27+
28+
public bool ProcessAssembly (AssemblyDefinition assembly, StepContext context)
29+
{
30+
if (!context.IsAndroidAssembly)
31+
return false;
32+
33+
var action = Annotations.HasAction (assembly) ? Annotations.GetAction (assembly) : AssemblyAction.Skip;
34+
35+
if (action == AssemblyAction.Delete)
36+
return false;
37+
38+
// We need to parse the environment files supplied by the user to see if they want to use broken exception transitions. This information is needed
39+
// in order to properly generate wrapper methods in the marshal methods assembly rewriter.
40+
// We don't care about those generated by us, since they won't contain the `XA_BROKEN_EXCEPTION_TRANSITIONS` variable we look for.
41+
if (!brokenExceptionTransitionsEnabled.HasValue) {
42+
var environmentParser = new EnvironmentFilesParser ();
43+
brokenExceptionTransitionsEnabled = environmentParser.AreBrokenExceptionTransitionsEnabled (context.Environments);
44+
}
45+
46+
var allJavaTypes = ScanForJavaTypes (assembly, context.Architecture);
47+
48+
var jcwContext = new JCWGeneratorContext (context.Architecture, Context.Resolver, context.ResolvedAssemblies, allJavaTypes, Context, true);
49+
var jcwGenerator = new JCWGenerator (Log, jcwContext) {
50+
CodeGenerationTarget = context.CodeGenerationTarget,
51+
};
52+
53+
jcwGenerator.Classify (context.AndroidSdkPlatform);
54+
55+
var state = new NativeCodeGenState (context.Architecture, Context, Context.Resolver, allJavaTypes, allJavaTypes, jcwGenerator.Classifier);
56+
Run (state, context);
57+
58+
// TODO: Only return true if we actually modified the assembly
59+
return true;
60+
}
61+
62+
List<TypeDefinition> ScanForJavaTypes (AssemblyDefinition assembly, AndroidTargetArch arch)
63+
{
64+
var types = new List<TypeDefinition> ();
65+
66+
var scanner = new XAJavaTypeScanner (arch, Log, Context);
67+
68+
foreach (ModuleDefinition md in assembly.Modules) {
69+
foreach (TypeDefinition td in md.Types) {
70+
scanner.AddJavaType (td, types);
71+
}
72+
}
73+
74+
return types;
75+
}
76+
77+
void Run (NativeCodeGenState state, StepContext context)
78+
{
79+
if (state.Classifier is null) {
80+
Log.LogError ("state.Classifier cannot be null if marshal methods are enabled");
81+
return;
82+
}
83+
84+
if (!context.EnableManagedMarshalMethodsLookup) {
85+
RewriteMethods (state, brokenExceptionTransitionsEnabled.GetValueOrDefault ());
86+
state.Classifier.AddSpecialCaseMethods ();
87+
} else {
88+
// We need to run `AddSpecialCaseMethods` before `RewriteMarshalMethods` so that we can see the special case
89+
// methods (such as TypeManager.n_Activate_mm) when generating the managed lookup tables.
90+
state.Classifier.AddSpecialCaseMethods ();
91+
state.ManagedMarshalMethodsLookupInfo = new ManagedMarshalMethodsLookupInfo (Log);
92+
RewriteMethods (state, brokenExceptionTransitionsEnabled.GetValueOrDefault ());
93+
}
94+
95+
Log.LogDebugMessage ($"[{state.TargetArch}] Number of generated marshal methods: {state.Classifier.MarshalMethods.Count}");
96+
if (state.Classifier.RejectedMethodCount > 0) {
97+
Log.LogWarning ($"[{state.TargetArch}] Number of methods in the project that will be registered dynamically: {state.Classifier.RejectedMethodCount}");
98+
}
99+
100+
if (state.Classifier.WrappedMethodCount > 0) {
101+
// TODO: change to LogWarning once the generator can output code which requires no non-blittable wrappers
102+
Log.LogDebugMessage ($"[{state.TargetArch}] Number of methods in the project that need marshal method wrappers: {state.Classifier.WrappedMethodCount}");
103+
}
104+
}
105+
106+
void RewriteMethods (NativeCodeGenState state, bool brokenExceptionTransitionsEnabled)
107+
{
108+
if (state.Classifier == null) {
109+
return;
110+
}
111+
112+
var rewriter = new MarshalMethodsAssemblyRewriter (Log, state.TargetArch, state.Classifier, state.Resolver, state.ManagedMarshalMethodsLookupInfo);
113+
rewriter.Rewrite (brokenExceptionTransitionsEnabled);
114+
}
115+
}

src/Xamarin.Android.Build.Tasks/Tasks/AssemblyModifierPipeline.cs

Lines changed: 25 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -20,10 +20,12 @@ namespace Xamarin.Android.Tasks;
2020
/// are run *after* the linker has run. Additionally, this task is run by
2121
/// LinkAssembliesNoShrink to modify assemblies when ILLink is not used.
2222
/// </summary>
23-
public partial class AssemblyModifierPipeline : AndroidTask
23+
public class AssemblyModifierPipeline : AndroidTask
2424
{
2525
public override string TaskPrefix => "AMP";
2626

27+
public string AndroidSdkPlatform { get; set; } = "";
28+
2729
public string ApplicationJavaClass { get; set; } = "";
2830

2931
public string CodeGenerationTarget { get; set; } = "";
@@ -37,8 +39,15 @@ public partial class AssemblyModifierPipeline : AndroidTask
3739

3840
public bool EnableMarshalMethods { get; set; }
3941

42+
public bool EnableManagedMarshalMethodsLookup { get; set; }
43+
44+
public ITaskItem [] Environments { get; set; } = [];
45+
4046
public bool ErrorOnCustomJavaObject { get; set; }
4147

48+
// If we're using ILLink, this process modifies the linked assemblies in place
49+
protected virtual bool ModifiesAssembliesInPlace => true;
50+
4251
public string? PackageNamingPolicy { get; set; }
4352

4453
/// <summary>
@@ -64,7 +73,7 @@ public partial class AssemblyModifierPipeline : AndroidTask
6473
[Required]
6574
public string TargetName { get; set; } = "";
6675

67-
protected JavaPeerStyle codeGenerationTarget;
76+
JavaPeerStyle codeGenerationTarget;
6877

6978
public override bool RunTask ()
7079
{
@@ -76,6 +85,8 @@ public override bool RunTask ()
7685

7786
var readerParameters = new ReaderParameters {
7887
ReadSymbols = ReadSymbols,
88+
ReadWrite = ModifiesAssembliesInPlace,
89+
InMemory = ModifiesAssembliesInPlace,
7990
};
8091

8192
var writerParameters = new WriterParameters {
@@ -122,7 +133,7 @@ public override bool RunTask ()
122133

123134
Directory.CreateDirectory (Path.GetDirectoryName (destination.ItemSpec));
124135

125-
RunPipeline (pipeline!, source, destination, writerParameters);
136+
RunPipeline (pipeline!, source, destination, perArchAssemblies [sourceArch].Values.ToArray (), writerParameters);
126137
}
127138

128139
pipeline?.Dispose ();
@@ -141,15 +152,24 @@ protected virtual void BuildPipeline (AssemblyPipeline pipeline, MSBuildLinkCont
141152

142153
findJavaObjectsStep.Initialize (context);
143154
pipeline.Steps.Add (findJavaObjectsStep);
155+
156+
// RewriteMarshalMethodsStep
157+
if (EnableMarshalMethods && !Debug) {
158+
var rewriteMarshalMethodsStep = new RewriteMarshalMethodsStep (Log);
159+
rewriteMarshalMethodsStep.Initialize (context);
160+
pipeline.Steps.Add (rewriteMarshalMethodsStep);
161+
}
144162
}
145163

146-
void RunPipeline (AssemblyPipeline pipeline, ITaskItem source, ITaskItem destination, WriterParameters writerParameters)
164+
void RunPipeline (AssemblyPipeline pipeline, ITaskItem source, ITaskItem destination, ITaskItem [] archAssemblies, WriterParameters writerParameters)
147165
{
148166
var assembly = pipeline.Resolver.GetAssembly (source.ItemSpec);
149167

150-
var context = new StepContext (source, destination) {
168+
var context = new StepContext (source, destination, AndroidSdkPlatform, Environments, archAssemblies) {
169+
Architecture = MonoAndroidHelper.GetRequiredValidArchitecture (source),
151170
CodeGenerationTarget = codeGenerationTarget,
152171
EnableMarshalMethods = EnableMarshalMethods,
172+
EnableManagedMarshalMethodsLookup = EnableManagedMarshalMethodsLookup,
153173
IsAndroidAssembly = MonoAndroidHelper.IsAndroidAssembly (source),
154174
IsDebug = Debug,
155175
IsFrameworkAssembly = MonoAndroidHelper.IsFrameworkAssembly (source),

src/Xamarin.Android.Build.Tasks/Tasks/GenerateJavaStubs.cs

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -222,7 +222,8 @@ internal static Dictionary<string, ITaskItem> MaybeGetArchAssemblies (Dictionary
222222

223223
generatedJavaFiles = jcwGenerator.GeneratedJavaFiles;
224224
} else {
225-
success = jcwGenerator.Classify (AndroidSdkPlatform);
225+
success = true;
226+
//success = jcwGenerator.Classify (AndroidSdkPlatform);
226227
}
227228

228229
if (!success) {

src/Xamarin.Android.Build.Tasks/Tasks/LinkAssembliesNoShrink.cs

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,9 @@ public class LinkAssembliesNoShrink : AssemblyModifierPipeline
1515

1616
public bool AddKeepAlives { get; set; }
1717

18+
// If we're running instead of ILLink, this process writes copies of the assemblies to a new location
19+
protected override bool ModifiesAssembliesInPlace => false;
20+
1821
public bool UseDesignerAssembly { get; set; }
1922

2023
protected override void BuildPipeline (AssemblyPipeline pipeline, MSBuildLinkContext context)

src/Xamarin.Android.Build.Tasks/Utilities/AssemblyPipeline.cs

Lines changed: 12 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@
55
using Java.Interop.Tools.JavaCallableWrappers;
66
using Microsoft.Build.Framework;
77
using Mono.Cecil;
8+
using Xamarin.Android.Tools;
89

910
namespace Xamarin.Android.Tasks;
1011

@@ -56,19 +57,28 @@ public interface IAssemblyModifierPipelineStep
5657

5758
public class StepContext
5859
{
60+
public AndroidTargetArch Architecture { get; set; }
61+
public string AndroidSdkPlatform { get; set; }
5962
public JavaPeerStyle CodeGenerationTarget { get; set; }
6063
public ITaskItem Destination { get; }
6164
public bool EnableMarshalMethods { get; set; }
65+
public bool EnableManagedMarshalMethodsLookup { get; set; }
66+
public ITaskItem [] Environments { get; set; }
6267
public bool IsAndroidAssembly { get; set; }
6368
public bool IsDebug { get; set; }
6469
public bool IsFrameworkAssembly { get; set; }
6570
public bool IsMainAssembly { get; set; }
6671
public bool IsUserAssembly { get; set; }
72+
// This only contains the resolved assemblies for *this* architecture
73+
public ITaskItem [] ResolvedAssemblies { get; set; }
6774
public ITaskItem Source { get; }
6875

69-
public StepContext (ITaskItem source, ITaskItem destination)
76+
public StepContext (ITaskItem source, ITaskItem destination, string androidSdkPlatform, ITaskItem [] environments, ITaskItem [] resolvedAssemblies)
7077
{
71-
Source = source;
78+
AndroidSdkPlatform = androidSdkPlatform;
7279
Destination = destination;
80+
Environments = environments;
81+
ResolvedAssemblies = resolvedAssemblies;
82+
Source = source;
7383
}
7484
}

src/Xamarin.Android.Build.Tasks/Utilities/JCWGenerator.cs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -24,11 +24,11 @@ class JCWGeneratorContext
2424
public bool UseMarshalMethods { get; }
2525
public AndroidTargetArch Arch { get; }
2626
public TypeDefinitionCache TypeDefinitionCache { get; }
27-
public XAAssemblyResolver Resolver { get; }
27+
public IAssemblyResolver Resolver { get; }
2828
public IList<TypeDefinition> JavaTypes { get; }
2929
public ICollection<ITaskItem> ResolvedAssemblies { get; }
3030

31-
public JCWGeneratorContext (AndroidTargetArch arch, XAAssemblyResolver res, ICollection<ITaskItem> resolvedAssemblies, List<TypeDefinition> javaTypesForJCW, TypeDefinitionCache tdCache, bool useMarshalMethods)
31+
public JCWGeneratorContext (AndroidTargetArch arch, IAssemblyResolver res, ICollection<ITaskItem> resolvedAssemblies, List<TypeDefinition> javaTypesForJCW, TypeDefinitionCache tdCache, bool useMarshalMethods)
3232
{
3333
Arch = arch;
3434
Resolver = res;

src/Xamarin.Android.Build.Tasks/Utilities/MarshalMethodsAssemblyRewriter.cs

Lines changed: 36 additions & 36 deletions
Original file line numberDiff line numberDiff line change
@@ -24,11 +24,11 @@ sealed class AssemblyImports
2424

2525
readonly TaskLoggingHelper log;
2626
readonly MarshalMethodsClassifier classifier;
27-
readonly XAAssemblyResolver resolver;
27+
readonly IAssemblyResolver resolver;
2828
readonly AndroidTargetArch targetArch;
2929
readonly ManagedMarshalMethodsLookupInfo? managedMarshalMethodsLookupInfo;
3030

31-
public MarshalMethodsAssemblyRewriter (TaskLoggingHelper log, AndroidTargetArch targetArch, MarshalMethodsClassifier classifier, XAAssemblyResolver resolver, ManagedMarshalMethodsLookupInfo? managedMarshalMethodsLookupInfo)
31+
public MarshalMethodsAssemblyRewriter (TaskLoggingHelper log, AndroidTargetArch targetArch, MarshalMethodsClassifier classifier, IAssemblyResolver resolver, ManagedMarshalMethodsLookupInfo? managedMarshalMethodsLookupInfo)
3232
{
3333
this.log = log ?? throw new ArgumentNullException (nameof (log));
3434
this.targetArch = targetArch;
@@ -121,40 +121,40 @@ public void Rewrite (bool brokenExceptionTransitions)
121121
managedMarshalMethodLookupGenerator.Generate (classifier.MarshalMethods.Values);
122122
}
123123

124-
foreach (AssemblyDefinition asm in classifier.Assemblies) {
125-
string? path = asm.MainModule.FileName;
126-
if (String.IsNullOrEmpty (path)) {
127-
throw new InvalidOperationException ($"[{targetArch}] Internal error: assembly '{asm}' does not specify path to its file");
128-
}
129-
130-
string pathPdb = Path.ChangeExtension (path, ".pdb");
131-
bool havePdb = File.Exists (pathPdb);
132-
133-
var writerParams = new WriterParameters {
134-
WriteSymbols = havePdb,
135-
};
136-
137-
string directory = Path.Combine (Path.GetDirectoryName (path), "new");
138-
Directory.CreateDirectory (directory);
139-
string output = Path.Combine (directory, Path.GetFileName (path));
140-
log.LogDebugMessage ($"[{targetArch}] Writing new version of '{path}' assembly: {output}");
141-
142-
// TODO: this should be used eventually, but it requires that all the types are reloaded from the assemblies before typemaps are generated
143-
// since Cecil doesn't update the MVID in the already loaded types
144-
//asm.MainModule.Mvid = Guid.NewGuid ();
145-
asm.Write (output, writerParams);
146-
147-
CopyFile (output, path);
148-
RemoveFile (output);
149-
150-
if (havePdb) {
151-
string outputPdb = Path.ChangeExtension (output, ".pdb");
152-
if (File.Exists (outputPdb)) {
153-
CopyFile (outputPdb, pathPdb);
154-
}
155-
RemoveFile (outputPdb);
156-
}
157-
}
124+
//foreach (AssemblyDefinition asm in classifier.Assemblies) {
125+
// string? path = asm.MainModule.FileName;
126+
// if (String.IsNullOrEmpty (path)) {
127+
// throw new InvalidOperationException ($"[{targetArch}] Internal error: assembly '{asm}' does not specify path to its file");
128+
// }
129+
130+
// string pathPdb = Path.ChangeExtension (path, ".pdb");
131+
// bool havePdb = File.Exists (pathPdb);
132+
133+
// var writerParams = new WriterParameters {
134+
// WriteSymbols = havePdb,
135+
// };
136+
137+
// string directory = Path.Combine (Path.GetDirectoryName (path), "new");
138+
// Directory.CreateDirectory (directory);
139+
// string output = Path.Combine (directory, Path.GetFileName (path));
140+
// log.LogDebugMessage ($"[{targetArch}] Writing new version of '{path}' assembly: {output}");
141+
142+
// // TODO: this should be used eventually, but it requires that all the types are reloaded from the assemblies before typemaps are generated
143+
// // since Cecil doesn't update the MVID in the already loaded types
144+
// //asm.MainModule.Mvid = Guid.NewGuid ();
145+
// asm.Write (output, writerParams);
146+
147+
// CopyFile (output, path);
148+
// RemoveFile (output);
149+
150+
// if (havePdb) {
151+
// string outputPdb = Path.ChangeExtension (output, ".pdb");
152+
// if (File.Exists (outputPdb)) {
153+
// CopyFile (outputPdb, pathPdb);
154+
// }
155+
// RemoveFile (outputPdb);
156+
// }
157+
//}
158158

159159
void CopyFile (string source, string target)
160160
{

src/Xamarin.Android.Build.Tasks/Utilities/NativeCodeGenState.cs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -31,13 +31,13 @@ class NativeCodeGenState
3131
public List<TypeDefinition> AllJavaTypes { get; }
3232

3333
public List<TypeDefinition> JavaTypesForJCW { get; }
34-
public XAAssemblyResolver Resolver { get; }
34+
public IAssemblyResolver Resolver { get; }
3535
public TypeDefinitionCache TypeCache { get; }
3636
public bool JniAddNativeMethodRegistrationAttributePresent { get; set; }
3737

3838
public ManagedMarshalMethodsLookupInfo? ManagedMarshalMethodsLookupInfo { get; set; }
3939

40-
public NativeCodeGenState (AndroidTargetArch arch, TypeDefinitionCache tdCache, XAAssemblyResolver resolver, List<TypeDefinition> allJavaTypes, List<TypeDefinition> javaTypesForJCW, MarshalMethodsClassifier? classifier)
40+
public NativeCodeGenState (AndroidTargetArch arch, TypeDefinitionCache tdCache, IAssemblyResolver resolver, List<TypeDefinition> allJavaTypes, List<TypeDefinition> javaTypesForJCW, MarshalMethodsClassifier? classifier)
4141
{
4242
TargetArch = arch;
4343
TypeCache = tdCache;

src/Xamarin.Android.Build.Tasks/Xamarin.Android.Build.Tasks.csproj

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -47,6 +47,7 @@
4747

4848
<!-- Include only the linker sources required for LinkAssembliesNoShrink - AddKeepAlivesStep, FixAbstractMethodsStep, FixLegacyResourceDesignerStep -->
4949
<Compile Remove="Linker\**" />
50+
<Compile Include="Linker\MonoDroid.Tuner\RewriteMarshalMethodsStep.cs" />
5051
<Compile Include="Linker\MonoDroid.Tuner\FindJavaObjectsStep.cs" />
5152
<Compile Include="Linker\MonoDroid.Tuner\AddKeepAlivesStep.cs" />
5253
<Compile Include="Linker\MonoDroid.Tuner\AndroidLinkConfiguration.cs" />

0 commit comments

Comments
 (0)