Skip to content

Commit 33cc446

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

File tree

10 files changed

+210
-52
lines changed

10 files changed

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

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/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: 38 additions & 38 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;
@@ -93,7 +93,7 @@ public void Rewrite (bool brokenExceptionTransitions)
9393
if (method.Connector != null) {
9494
if (method.Connector.IsStatic && method.Connector.IsPrivate) {
9595
log.LogDebugMessage ($"[{targetArch}] Removing connector method {method.Connector.FullName}");
96-
method.Connector.DeclaringType?.Methods?.Remove (method.Connector);
96+
//method.Connector.DeclaringType?.Methods?.Remove (method.Connector);
9797
} else {
9898
log.LogWarning ($"[{targetArch}] NOT removing connector method {method.Connector.FullName} because it's either not static or not private");
9999
}
@@ -102,7 +102,7 @@ public void Rewrite (bool brokenExceptionTransitions)
102102
if (method.CallbackField != null) {
103103
if (method.CallbackField.IsStatic && method.CallbackField.IsPrivate) {
104104
log.LogDebugMessage ($"[{targetArch}] Removing callback delegate backing field {method.CallbackField.FullName}");
105-
method.CallbackField.DeclaringType?.Fields?.Remove (method.CallbackField);
105+
//method.CallbackField.DeclaringType?.Fields?.Remove (method.CallbackField);
106106
} else {
107107
log.LogWarning ($"[{targetArch}] NOT removing callback field {method.CallbackField.FullName} because it's either not static or not private");
108108
}
@@ -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)
pFad - Phonifier reborn

Pfad - The Proxy pFad of © 2024 Garber Painting. All rights reserved.

Note: This service is not intended for secure transactions such as banking, social media, email, or purchasing. Use at your own risk. We assume no liability whatsoever for broken pages.


Alternative Proxies:

Alternative Proxy

pFad Proxy

pFad v3 Proxy

pFad v4 Proxy