Skip to content

Commit 403e4c4

Browse files
committed
First steps towards p/invoke usage scanner
1 parent 9ad621a commit 403e4c4

File tree

5 files changed

+170
-1
lines changed

5 files changed

+170
-1
lines changed

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

Lines changed: 33 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -91,6 +91,8 @@ public class GenerateJavaStubs : AndroidTask
9191

9292
public ITaskItem[] Environments { get; set; }
9393

94+
public bool EnableNativeRuntimeLinking { get; set; }
95+
9496
[Output]
9597
public ITaskItem[] GeneratedBinaryTypeMaps { get; set; }
9698

@@ -187,6 +189,7 @@ void Run (bool useMarshalMethods)
187189
// Now that "never" never happened, we can proceed knowing that at least the assembly sets are the same for each architecture
188190
var nativeCodeGenStates = new ConcurrentDictionary<AndroidTargetArch, NativeCodeGenState> ();
189191
NativeCodeGenState? templateCodeGenState = null;
192+
var scanner = new PinvokeScanner (Log);
190193

191194
var firstArch = allAssembliesPerArch.First ().Key;
192195
var generateSucceeded = true;
@@ -206,6 +209,11 @@ void Run (bool useMarshalMethods)
206209
generateSucceeded = false;
207210
}
208211

212+
(success, List<PinvokeScanner.PinvokeEntryInfo> pinfos) = ScanForUsedPinvokes (scanner, arch, state.Resolver);
213+
if (!success) {
214+
return;
215+
}
216+
209217
// If this is the first architecture, we need to store the state for later use
210218
if (generateJavaCode) {
211219
templateCodeGenState = state;
@@ -363,6 +371,31 @@ IList<string> MergeManifest (NativeCodeGenState codeGenState, Dictionary<string,
363371
return additionalProviders;
364372
}
365373

374+
(bool success, List<PinvokeScanner.PinvokeEntryInfo> pinfos) ScanForUsedPinvokes (PinvokeScanner scanner, AndroidTargetArch arch, XAAssemblyResolver resolver)
375+
{
376+
if (!EnableNativeRuntimeLinking) {
377+
return;
378+
}
379+
380+
var frameworkAssemblies = new List<ITaskItem> ();
381+
382+
foreach (ITaskItem asm in ResolvedAssemblies) {
383+
string? metadata = asm.GetMetadata ("FrameworkAssembly");
384+
if (String.IsNullOrEmpty (metadata)) {
385+
continue;
386+
}
387+
388+
if (!Boolean.TryParse (metadata, out bool isFrameworkAssembly) || !isFrameworkAssembly) {
389+
continue;
390+
}
391+
392+
frameworkAssemblies.Add (asm);
393+
}
394+
395+
var pinfos = scanner.Scan (arch, resolver, frameworkAssemblies);
396+
return (true, pinfos);
397+
}
398+
366399
(bool success, NativeCodeGenState? stubsState) GenerateJavaSourcesAndMaybeClassifyMarshalMethods (AndroidTargetArch arch, Dictionary<string, ITaskItem> assemblies, Dictionary<string, ITaskItem> userAssemblies, bool useMarshalMethods, bool generateJavaCode)
367400
{
368401
XAAssemblyResolver resolver = MakeResolver (useMarshalMethods, arch, assemblies);

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

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -79,6 +79,7 @@ public class GeneratePackageManagerJava : AndroidTask
7979
public string AndroidSequencePointsMode { get; set; }
8080
public bool EnableSGenConcurrent { get; set; }
8181
public string? CustomBundleConfigFile { get; set; }
82+
public bool EnableNativeRuntimeLinking { get; set; }
8283

8384
bool _Debug {
8485
get {
@@ -387,6 +388,13 @@ void AddEnvironment ()
387388
);
388389
}
389390

391+
if (EnableNativeRuntimeLinking) {
392+
// var pinfoGen = new PreservePinvokesNativeAssemblyGenerator (
393+
// Log,
394+
// targetArch,
395+
396+
}
397+
390398
LLVMIR.LlvmIrModule marshalMethodsModule = marshalMethodsAsmGen.Construct ();
391399
using var marshalMethodsWriter = MemoryStreamPool.Shared.CreateStreamWriter ();
392400
try {
Lines changed: 91 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,91 @@
1+
using System;
2+
using System.Collections.Generic;
3+
using System.IO;
4+
5+
using Microsoft.Build.Framework;
6+
using Microsoft.Build.Utilities;
7+
using Microsoft.Android.Build.Tasks;
8+
9+
using Mono.Cecil;
10+
11+
using Xamarin.Android.Tools;
12+
13+
namespace Xamarin.Android.Tasks;
14+
15+
class PinvokeScanner
16+
{
17+
public sealed class PinvokeEntryInfo
18+
{
19+
public readonly string LibraryName;
20+
public readonly string EntryName;
21+
22+
public PinvokeEntryInfo (MethodDefinition method)
23+
{
24+
LibraryName = method.PInvokeInfo.Module.Name;
25+
EntryName = method.PInvokeInfo.EntryPoint;
26+
}
27+
}
28+
29+
readonly TaskLoggingHelper log;
30+
31+
public PinvokeScanner (TaskLoggingHelper log)
32+
{
33+
this.log = log;
34+
}
35+
36+
public List<PinvokeEntryInfo> Scan (AndroidTargetArch targetArch, XAAssemblyResolver resolver, ICollection<ITaskItem> frameworkAssemblies)
37+
{
38+
var pinvokes = new List<PinvokeEntryInfo> ();
39+
var pinvokeCache = new HashSet<string> (StringComparer.Ordinal);
40+
41+
foreach (ITaskItem fasm in frameworkAssemblies) {
42+
string asmName = Path.GetFileNameWithoutExtension (fasm.ItemSpec);
43+
AssemblyDefinition? asmdef = resolver.Resolve (asmName);
44+
if (asmdef == null) {
45+
log.LogWarning ($"Failed to resolve assembly '{fasm.ItemSpec}' for target architecture {targetArch}");
46+
continue;
47+
}
48+
Scan (asmdef, pinvokeCache, pinvokes);
49+
}
50+
51+
return pinvokes;
52+
}
53+
54+
void Scan (AssemblyDefinition assembly, HashSet<string> pinvokeCache, List<PinvokeEntryInfo> pinvokes)
55+
{
56+
log.LogDebugMessage ($"Scanning assembly {assembly}");
57+
foreach (ModuleDefinition module in assembly.Modules) {
58+
if (!module.HasTypes) {
59+
continue;
60+
}
61+
62+
foreach (TypeDefinition type in module.Types) {
63+
Scan (type, pinvokeCache, pinvokes);
64+
}
65+
}
66+
}
67+
68+
void Scan (TypeDefinition type, HashSet<string> pinvokeCache, List<PinvokeEntryInfo> pinvokes)
69+
{
70+
if (!type.HasMethods) {
71+
return;
72+
}
73+
74+
log.LogDebugMessage ($"Scanning type '{type}'");
75+
foreach (MethodDefinition method in type.Methods) {
76+
if (!method.HasPInvokeInfo) {
77+
continue;
78+
}
79+
80+
var pinfo = new PinvokeEntryInfo (method);
81+
string key = $"{pinfo.LibraryName}/{pinfo.EntryName}";
82+
if (pinvokeCache.Contains (key)) {
83+
continue;
84+
}
85+
86+
log.LogDebugMessage ($" p/invoke method: {pinfo.LibraryName}/{pinfo.EntryName}");
87+
pinvokeCache.Add (key);
88+
pinvokes.Add (pinfo);
89+
}
90+
}
91+
}
Lines changed: 35 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,35 @@
1+
using System;
2+
using System.Collections.Generic;
3+
using System.IO;
4+
using System.Linq;
5+
using System.Text;
6+
7+
using Microsoft.Android.Build.Tasks;
8+
using Microsoft.Build.Utilities;
9+
10+
using Xamarin.Android.Tasks.LLVMIR;
11+
using Xamarin.Android.Tools;
12+
13+
using CecilMethodDefinition = global::Mono.Cecil.MethodDefinition;
14+
using CecilParameterDefinition = global::Mono.Cecil.ParameterDefinition;
15+
16+
namespace Xamarin.Android.Tasks;
17+
18+
class PreservePinvokesNativeAssemblyGenerator : LlvmIrComposer
19+
{
20+
readonly TaskLoggingHelper log;
21+
readonly AndroidTargetArch targetArch;
22+
readonly ICollection<PinvokeScanner.PinvokeEntryInfo> pinfos;
23+
24+
public PreservePinvokesNativeAssemblyGenerator (TaskLoggingHelper log, AndroidTargetArch targetArch, ICollection<PinvokeScanner.PinvokeEntryInfo> pinfos)
25+
: base (log)
26+
{
27+
this.log = log;
28+
this.targetArch = targetArch;
29+
this.pinfos = pinfos;
30+
}
31+
32+
protected override void Construct (LlvmIrModule module)
33+
{
34+
}
35+
}

src/Xamarin.Android.Build.Tasks/Xamarin.Android.Common.targets

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1502,7 +1502,8 @@ because xbuild doesn't support framework reference assemblies.
15021502
LinkingEnabled="$(_LinkingEnabled)"
15031503
HaveMultipleRIDs="$(_HaveMultipleRIDs)"
15041504
IntermediateOutputDirectory="$(IntermediateOutputPath)"
1505-
Environments="@(AndroidEnvironment);@(LibraryEnvironments)">
1505+
Environments="@(AndroidEnvironment);@(LibraryEnvironments)"
1506+
EnableNativeRuntimeLinking="$(_AndroidEnableNativeRuntimeLinking)">
15061507
<Output TaskParameter="GeneratedBinaryTypeMaps" ItemName="_AndroidTypeMapping" Condition=" '$(_InstantRunEnabled)' == 'True' " />
15071508
</GenerateJavaStubs>
15081509

@@ -1732,6 +1733,7 @@ because xbuild doesn't support framework reference assemblies.
17321733
UseAssemblyStore="$(AndroidUseAssemblyStore)"
17331734
EnableMarshalMethods="$(_AndroidUseMarshalMethods)"
17341735
CustomBundleConfigFile="$(AndroidBundleConfigurationFile)"
1736+
EnableNativeRuntimeLinking="$(_AndroidEnableNativeRuntimeLinking)"
17351737
>
17361738
</GeneratePackageManagerJava>
17371739
<Touch Files="$(_AndroidStampDirectory)_GeneratePackageManagerJava.stamp" AlwaysCreate="True" />

0 commit comments

Comments
 (0)