Skip to content

Commit 482159f

Browse files
committed
First steps towards p/invoke usage scanner
1 parent b35028e commit 482159f

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
@@ -89,6 +89,8 @@ public class GenerateJavaStubs : AndroidTask
8989

9090
public ITaskItem[] Environments { get; set; }
9191

92+
public bool EnableNativeRuntimeLinking { get; set; }
93+
9294
[Output]
9395
public ITaskItem[] GeneratedBinaryTypeMaps { get; set; }
9496

@@ -186,6 +188,7 @@ void Run (bool useMarshalMethods)
186188
var nativeCodeGenStates = new Dictionary<AndroidTargetArch, NativeCodeGenState> ();
187189
bool generateJavaCode = true;
188190
NativeCodeGenState? templateCodeGenState = null;
191+
var scanner = new PinvokeScanner (Log);
189192

190193
foreach (var kvp in allAssembliesPerArch) {
191194
AndroidTargetArch arch = kvp.Key;
@@ -196,6 +199,11 @@ void Run (bool useMarshalMethods)
196199
return;
197200
}
198201

202+
(success, List<PinvokeScanner.PinvokeEntryInfo> pinfos) = ScanForUsedPinvokes (scanner, arch, state.Resolver);
203+
if (!success) {
204+
return;
205+
}
206+
199207
if (generateJavaCode) {
200208
templateCodeGenState = state;
201209
generateJavaCode = false;
@@ -349,6 +357,31 @@ IList<string> MergeManifest (NativeCodeGenState codeGenState, Dictionary<string,
349357
return additionalProviders;
350358
}
351359

360+
(bool success, List<PinvokeScanner.PinvokeEntryInfo> pinfos) ScanForUsedPinvokes (PinvokeScanner scanner, AndroidTargetArch arch, XAAssemblyResolver resolver)
361+
{
362+
if (!EnableNativeRuntimeLinking) {
363+
return;
364+
}
365+
366+
var frameworkAssemblies = new List<ITaskItem> ();
367+
368+
foreach (ITaskItem asm in ResolvedAssemblies) {
369+
string? metadata = asm.GetMetadata ("FrameworkAssembly");
370+
if (String.IsNullOrEmpty (metadata)) {
371+
continue;
372+
}
373+
374+
if (!Boolean.TryParse (metadata, out bool isFrameworkAssembly) || !isFrameworkAssembly) {
375+
continue;
376+
}
377+
378+
frameworkAssemblies.Add (asm);
379+
}
380+
381+
var pinfos = scanner.Scan (arch, resolver, frameworkAssemblies);
382+
return (true, pinfos);
383+
}
384+
352385
(bool success, NativeCodeGenState? stubsState) GenerateJavaSourcesAndMaybeClassifyMarshalMethods (AndroidTargetArch arch, Dictionary<string, ITaskItem> assemblies, Dictionary<string, ITaskItem> userAssemblies, bool useMarshalMethods, bool generateJavaCode)
353386
{
354387
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
@@ -81,6 +81,7 @@ public class GeneratePackageManagerJava : AndroidTask
8181
public bool EnableSGenConcurrent { get; set; }
8282
public string? CustomBundleConfigFile { get; set; }
8383
public int ZipAlignmentPages { get; set; } = AndroidZipAlign.DefaultZipAlignment;
84+
public bool EnableNativeRuntimeLinking { get; set; }
8485

8586
[Output]
8687
public string BuildId { get; set; }
@@ -400,6 +401,13 @@ void AddEnvironment ()
400401
);
401402
}
402403

404+
if (EnableNativeRuntimeLinking) {
405+
// var pinfoGen = new PreservePinvokesNativeAssemblyGenerator (
406+
// Log,
407+
// targetArch,
408+
409+
}
410+
403411
LLVMIR.LlvmIrModule marshalMethodsModule = marshalMethodsAsmGen.Construct ();
404412
using var marshalMethodsWriter = MemoryStreamPool.Shared.CreateStreamWriter ();
405413
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
@@ -1494,7 +1494,8 @@ because xbuild doesn't support framework reference assemblies.
14941494
LinkingEnabled="$(_LinkingEnabled)"
14951495
HaveMultipleRIDs="$(_HaveMultipleRIDs)"
14961496
IntermediateOutputDirectory="$(IntermediateOutputPath)"
1497-
Environments="@(AndroidEnvironment);@(LibraryEnvironments)">
1497+
Environments="@(AndroidEnvironment);@(LibraryEnvironments)"
1498+
EnableNativeRuntimeLinking="$(_AndroidEnableNativeRuntimeLinking)">
14981499
<Output TaskParameter="GeneratedBinaryTypeMaps" ItemName="_AndroidTypeMapping" Condition=" '$(_InstantRunEnabled)' == 'True' " />
14991500
</GenerateJavaStubs>
15001501

@@ -1725,6 +1726,7 @@ because xbuild doesn't support framework reference assemblies.
17251726
EnableMarshalMethods="$(_AndroidUseMarshalMethods)"
17261727
CustomBundleConfigFile="$(AndroidBundleConfigurationFile)"
17271728
ZipAlignmentPages="$(_AndroidZipAlignment)"
1729+
EnableNativeRuntimeLinking="$(_AndroidEnableNativeRuntimeLinking)"
17281730
>
17291731
<Output TaskParameter="BuildId" PropertyName="_XamarinBuildId" />
17301732
</GeneratePackageManagerJava>

0 commit comments

Comments
 (0)