Skip to content

Commit 70bd636

Browse files
[NativeAOT] introduce Microsoft.Android.Runtime.NativeAOT.dll (#9760)
This moves: * C# code from `samples/NativeAOT` to a new assembly, `Microsoft.Android.Runtime.NativeAOT.dll`. * Java code from `samples/NativeAOT` to `Xamarin.Android.Build.Tasks`, to be generated at build time. * C# code from `Java.Interop.Environment.csproj` has been trimmed down, copied into `Microsoft.Android.Runtime.NativeAOT.dll`. * Minimum Viable Product™ "typemap" implementation. * Change namespace to `Microsoft.Android.Runtime` ## Minimum Viable Product™ typemaps A quick-and-dirty -- and totally unoptimized -- typemap implementation has been added as a new `TypeMappingStep` into `Microsoft.Android.Sdk.ILLink`. Given this initial starting point within `NativeAotTypeManager`: partial class NativeAotTypeManager { IDictionary<string, Type> TypeMappings = new Dictionary<string, Type>(StringComparer.Ordinal); NativeAotTypeManager () { InitializeTypeMappings (); } void InitializeTypeMappings () { throw new InvalidOperationException ("Should be replaced at build time!"); } } then at build time `TypeMappingStep` will *replace* the body of `NativeAotTypeManager.InitializeTypeMappings()` with repeated `IDictionary<string, Type>.Add()` calls for each type which survived linking: // Cecil-generated code, C# equivalent void InitializeTypeMappings () { TypeMappings.Add ("java/lang/Object", typeof (Java.Lang.Object)); TypeMappings.Add ("android/app/Application", typeof (Android.App.Application)); // … }
1 parent 7acf328 commit 70bd636

23 files changed

+593
-83
lines changed

Xamarin.Android.sln

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -125,6 +125,8 @@ Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Microsoft.Android.Sdk.Analy
125125
EndProject
126126
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "proguard-android", "src\proguard-android\proguard-android.csproj", "{5FD0133B-69E5-4474-9B67-9FD1D0150C70}"
127127
EndProject
128+
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Microsoft.Android.Runtime.NativeAOT", "src\Microsoft.Android.Runtime.NativeAOT\Microsoft.Android.Runtime.NativeAOT.csproj", "{E8831F32-11D7-D42C-E43C-711998BC357A}"
129+
EndProject
128130
Global
129131
GlobalSection(SolutionConfigurationPlatforms) = preSolution
130132
Debug|AnyCPU = Debug|AnyCPU
@@ -347,6 +349,10 @@ Global
347349
{5FD0133B-69E5-4474-9B67-9FD1D0150C70}.Debug|AnyCPU.Build.0 = Debug|Any CPU
348350
{5FD0133B-69E5-4474-9B67-9FD1D0150C70}.Release|AnyCPU.ActiveCfg = Release|Any CPU
349351
{5FD0133B-69E5-4474-9B67-9FD1D0150C70}.Release|AnyCPU.Build.0 = Release|Any CPU
352+
{E8831F32-11D7-D42C-E43C-711998BC357A}.Debug|AnyCPU.ActiveCfg = Debug|Any CPU
353+
{E8831F32-11D7-D42C-E43C-711998BC357A}.Debug|AnyCPU.Build.0 = Debug|Any CPU
354+
{E8831F32-11D7-D42C-E43C-711998BC357A}.Release|AnyCPU.ActiveCfg = Release|Any CPU
355+
{E8831F32-11D7-D42C-E43C-711998BC357A}.Release|AnyCPU.Build.0 = Release|Any CPU
350356
EndGlobalSection
351357
GlobalSection(SolutionProperties) = preSolution
352358
HideSolutionNode = FALSE
@@ -406,6 +412,7 @@ Global
406412
{A39B6D7C-6616-40D6-8AE4-C6CEE93D2708} = {CAB438D8-B0F5-4AF0-BEBD-9E2ADBD7B483}
407413
{5E806C9F-1B67-4B6B-A6AB-258834250DBB} = {FFCF518F-2A4A-40A2-9174-2EE13B76C723}
408414
{5FD0133B-69E5-4474-9B67-9FD1D0150C70} = {FFCF518F-2A4A-40A2-9174-2EE13B76C723}
415+
{E8831F32-11D7-D42C-E43C-711998BC357A} = {04E3E11E-B47D-4599-8AFC-50515A95E715}
409416
EndGlobalSection
410417
GlobalSection(ExtensibilityGlobals) = postSolution
411418
SolutionGuid = {53A1F287-EFB2-4D97-A4BB-4A5E145613F6}

build-tools/create-packs/Microsoft.Android.Runtime.proj

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -37,7 +37,11 @@ projects that use the Microsoft.Android framework in .NET 6+.
3737
<_AndroidRuntimePackAssemblies Include="$(_MonoAndroidNETDefaultOutDir)Java.Interop.dll" />
3838
<_AndroidRuntimePackAssemblies Include="$(_MonoAndroidNETDefaultOutDir)Mono.Android.dll" />
3939
<_AndroidRuntimePackAssemblies Include="$(_MonoAndroidNETDefaultOutDir)Mono.Android.Runtime.dll" />
40-
<!-- Always include stable Mono.Android.Export.dll -->
40+
<!-- Always include stable versions of the following assemblies -->
41+
<_AndroidRuntimePackAssemblies
42+
Include="$(_MonoAndroidNETOutputRoot)$(AndroidLatestStableApiLevel)\Microsoft.Android.Runtime.NativeAOT.dll"
43+
Condition=" '$(AndroidRuntime)' == 'NativeAOT' "
44+
/>
4145
<_AndroidRuntimePackAssemblies Include="$(_MonoAndroidNETOutputRoot)$(AndroidLatestStableApiLevel)\Mono.Android.Export.dll" />
4246
</ItemGroup>
4347

samples/NativeAOT/AndroidManifest.xml

Lines changed: 0 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -1,13 +1,6 @@
11
<?xml version="1.0" encoding="utf-8"?>
22
<manifest xmlns:android="http://schemas.android.com/apk/res/android">
33
<application android:allowBackup="true" android:icon="@mipmap/appicon" android:label="@string/app_name" android:roundIcon="@mipmap/appicon_round" android:supportsRtl="true">
4-
<!-- Temporary, to eventually be included in .NET Android infrastructure -->
5-
<provider
6-
android:name="net.dot.jni.nativeaot.NativeAotRuntimeProvider"
7-
android:exported="false"
8-
android:initOrder="1999999999"
9-
android:authorities="net.dot.jni.nativeaot.NativeAotRuntimeProvider.__init__"
10-
/>
114
</application>
125
<uses-permission android:name="android.permission.INTERNET" />
136
</manifest>

samples/NativeAOT/NativeAOT.csproj

Lines changed: 0 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -9,10 +9,6 @@
99
<ApplicationVersion>1</ApplicationVersion>
1010
<ApplicationDisplayVersion>1.0</ApplicationDisplayVersion>
1111
<AndroidPackageFormat>apk</AndroidPackageFormat>
12-
<AllowUnsafeBlocks>true</AllowUnsafeBlocks>
13-
<!-- Temporary for InternalsVisibleTo -->
14-
<SignAssembly>true</SignAssembly>
15-
<AssemblyOriginatorKeyFile>..\..\product.snk</AssemblyOriginatorKeyFile>
1612
<!-- Default to arm64 device -->
1713
<RuntimeIdentifier>android-arm64</RuntimeIdentifier>
1814
<!-- Current required properties for NativeAOT -->
@@ -29,8 +25,4 @@
2925
<_FastDeploymentDiagnosticLogging>true</_FastDeploymentDiagnosticLogging>
3026
</PropertyGroup>
3127

32-
<ItemGroup>
33-
<AndroidJavaSource Update="*.java" Bind="false" />
34-
<ProjectReference Include="..\..\external\Java.Interop\src\Java.Runtime.Environment\Java.Runtime.Environment.csproj" />
35-
</ItemGroup>
3628
</Project>

samples/NativeAOT/JavaInteropRuntime.cs renamed to src/Microsoft.Android.Runtime.NativeAOT/Android.Runtime.NativeAOT/JavaInteropRuntime.cs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@
22
using Java.Interop;
33
using System.Runtime.InteropServices;
44

5-
namespace NativeAOT;
5+
namespace Microsoft.Android.Runtime;
66

77
static class JavaInteropRuntime
88
{
@@ -35,7 +35,7 @@ static void init (IntPtr jnienv, IntPtr klass)
3535
{
3636
try {
3737
var typeManager = new NativeAotTypeManager ();
38-
var options = new JreRuntimeOptions {
38+
var options = new NativeAotRuntimeOptions {
3939
EnvironmentPointer = jnienv,
4040
TypeManager = typeManager,
4141
ValueManager = new NativeAotValueManager (typeManager),

samples/NativeAOT/Logging.cs renamed to src/Microsoft.Android.Runtime.NativeAOT/Android.Runtime.NativeAOT/Logging.cs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,7 @@
66
using System.Runtime.InteropServices;
77
using System.Text;
88

9-
namespace NativeAOT;
9+
namespace Microsoft.Android.Runtime;
1010

1111
internal sealed class LogcatTextWriter : TextWriter {
1212

samples/NativeAOT/NativeAotTypeManager.cs renamed to src/Microsoft.Android.Runtime.NativeAOT/Android.Runtime.NativeAOT/NativeAotTypeManager.cs

Lines changed: 23 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -3,33 +3,35 @@
33
using Java.Interop;
44
using Java.Interop.Tools.TypeNameMappings;
55

6-
namespace NativeAOT;
6+
namespace Microsoft.Android.Runtime;
77

88
partial class NativeAotTypeManager : JniRuntime.JniTypeManager {
99

1010
const DynamicallyAccessedMemberTypes Constructors = DynamicallyAccessedMemberTypes.PublicConstructors | DynamicallyAccessedMemberTypes.NonPublicConstructors;
1111
internal const DynamicallyAccessedMemberTypes Methods = DynamicallyAccessedMemberTypes.PublicMethods | DynamicallyAccessedMemberTypes.NonPublicMethods;
1212
internal const DynamicallyAccessedMemberTypes MethodsAndPrivateNested = Methods | DynamicallyAccessedMemberTypes.NonPublicNestedTypes;
1313

14-
// TODO: list of types specific to this application
15-
Dictionary<string, Type> typeMappings = new () {
16-
["android/app/Activity"] = typeof (Android.App.Activity),
17-
["android/app/Application"] = typeof (Android.App.Application),
18-
["android/content/Context"] = typeof (Android.Content.Context),
19-
["android/content/ContextWrapper"] = typeof (Android.Content.ContextWrapper),
20-
["android/os/BaseBundle"] = typeof (Android.OS.BaseBundle),
21-
["android/os/Bundle"] = typeof (Android.OS.Bundle),
22-
["android/view/ContextThemeWrapper"] = typeof (Android.Views.ContextThemeWrapper),
23-
["my/MainActivity"] = typeof (MainActivity),
24-
["my/MainApplication"] = typeof (MainApplication),
25-
};
14+
readonly IDictionary<string, Type> TypeMappings = new Dictionary<string, Type> (StringComparer.Ordinal);
2615

2716
public NativeAotTypeManager ()
2817
{
29-
AndroidLog.Print (AndroidLogLevel.Info, "NativeAotTypeManager", $"# jonp: NativeAotTypeManager()");
18+
AndroidLog.Print (AndroidLogLevel.Info, "NativeAotTypeManager", $"NativeAotTypeManager()");
19+
var startTicks = global::System.Environment.TickCount;
20+
InitializeTypeMappings ();
21+
var endTicks = global::System.Environment.TickCount;
22+
AndroidLog.Print (AndroidLogLevel.Info, "NativeAotTypeManager", $"InitializeTypeMappings() took {endTicks - startTicks}ms");
3023
}
3124

32-
protected override Type? GetInvokerTypeCore (Type type)
25+
void InitializeTypeMappings ()
26+
{
27+
// Should be replaced by src/Microsoft.Android.Sdk.ILLink/TypeMappingStep.cs
28+
throw new InvalidOperationException ("TypeMappings should be replaced during trimming!");
29+
}
30+
31+
[return: DynamicallyAccessedMembers (Constructors)]
32+
protected override Type? GetInvokerTypeCore (
33+
[DynamicallyAccessedMembers (Constructors)]
34+
Type type)
3335
{
3436
const string suffix = "Invoker";
3537

@@ -68,6 +70,10 @@ static Type MakeGenericType (
6870
return MakeGenericType (suffixDefinition, arguments);
6971
}
7072

73+
// NOTE: suppressions below also in `src/Mono.Android/Android.Runtime/AndroidRuntime.cs`
74+
[UnconditionalSuppressMessage ("Trimming", "IL2057", Justification = "Type.GetType() can never statically know the string value parsed from parameter 'methods'.")]
75+
[UnconditionalSuppressMessage ("Trimming", "IL2067", Justification = "Delegate.CreateDelegate() can never statically know the string value parsed from parameter 'methods'.")]
76+
[UnconditionalSuppressMessage ("Trimming", "IL2072", Justification = "Delegate.CreateDelegate() can never statically know the string value parsed from parameter 'methods'.")]
7177
public override void RegisterNativeMembers (
7278
JniType nativeClass,
7379
[DynamicallyAccessedMembers (MethodsAndPrivateNested)]
@@ -143,7 +149,7 @@ public override void RegisterNativeMembers (
143149
protected override IEnumerable<Type> GetTypesForSimpleReference (string jniSimpleReference)
144150
{
145151
AndroidLog.Print (AndroidLogLevel.Info, "NativeAotTypeManager", $"# jonp: GetTypesForSimpleReference: jniSimpleReference=`{jniSimpleReference}`");
146-
if (typeMappings.TryGetValue (jniSimpleReference, out var target)) {
152+
if (TypeMappings.TryGetValue (jniSimpleReference, out var target)) {
147153
Console.WriteLine ($"# jonp: GetTypesForSimpleReference: jniSimpleReference=`{jniSimpleReference}` -> `{target}`");
148154
yield return target;
149155
}
@@ -161,9 +167,7 @@ protected override IEnumerable<string> GetSimpleReferences (Type type)
161167

162168
IEnumerable<string> CreateSimpleReferencesEnumerator (Type type)
163169
{
164-
if (typeMappings == null)
165-
yield break;
166-
foreach (var e in typeMappings) {
170+
foreach (var e in TypeMappings) {
167171
if (e.Value == type)
168172
yield return e.Key;
169173
}

samples/NativeAOT/NativeAotValueManager.cs renamed to src/Microsoft.Android.Runtime.NativeAOT/Android.Runtime.NativeAOT/NativeAotValueManager.cs

Lines changed: 19 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@
44
using System.Collections.ObjectModel;
55
using System.Diagnostics;
66
using System.Diagnostics.CodeAnalysis;
7+
using System.Globalization;
78
using System.IO;
89
using System.Linq;
910
using System.Reflection;
@@ -13,9 +14,9 @@
1314
using Android.Runtime;
1415
using Java.Interop;
1516

16-
namespace NativeAOT;
17+
namespace Microsoft.Android.Runtime;
1718

18-
internal class NativeAotValueManager : JniRuntime.JniValueManager
19+
class NativeAotValueManager : JniRuntime.JniValueManager
1920
{
2021
const DynamicallyAccessedMemberTypes Constructors = DynamicallyAccessedMemberTypes.PublicConstructors | DynamicallyAccessedMemberTypes.NonPublicConstructors;
2122

@@ -113,12 +114,12 @@ void WarnNotReplacing (int key, IJavaPeerable ignoreValue, IJavaPeerable keepVal
113114
"Warning: Not registering PeerReference={0} IdentityHashCode=0x{1} Instance={2} Instance.Type={3} Java.Type={4}; " +
114115
"keeping previously registered PeerReference={5} Instance={6} Instance.Type={7} Java.Type={8}.",
115116
ignoreValue.PeerReference.ToString (),
116-
key.ToString ("x"),
117-
RuntimeHelpers.GetHashCode (ignoreValue).ToString ("x"),
117+
key.ToString ("x", CultureInfo.InvariantCulture),
118+
RuntimeHelpers.GetHashCode (ignoreValue).ToString ("x", CultureInfo.InvariantCulture),
118119
ignoreValue.GetType ().FullName,
119120
JniEnvironment.Types.GetJniTypeNameFromInstance (ignoreValue.PeerReference),
120121
keepValue.PeerReference.ToString (),
121-
RuntimeHelpers.GetHashCode (keepValue).ToString ("x"),
122+
RuntimeHelpers.GetHashCode (keepValue).ToString ("x", CultureInfo.InvariantCulture),
122123
keepValue.GetType ().FullName,
123124
JniEnvironment.Types.GetJniTypeNameFromInstance (keepValue.PeerReference));
124125
}
@@ -186,8 +187,8 @@ public override void FinalizePeer (IJavaPeerable value)
186187
if (o.LogGlobalReferenceMessages) {
187188
o.WriteGlobalReferenceLine ("Finalizing PeerReference={0} IdentityHashCode=0x{1} Instance=0x{2} Instance.Type={3}",
188189
h.ToString (),
189-
value.JniIdentityHashCode.ToString ("x"),
190-
RuntimeHelpers.GetHashCode (value).ToString ("x"),
190+
value.JniIdentityHashCode.ToString ("x", CultureInfo.InvariantCulture),
191+
RuntimeHelpers.GetHashCode (value).ToString ("x", CultureInfo.InvariantCulture),
191192
value.GetType ().ToString ());
192193
}
193194
RemovePeer (value);
@@ -200,8 +201,8 @@ public override void FinalizePeer (IJavaPeerable value)
200201
if (o.LogGlobalReferenceMessages) {
201202
o.WriteGlobalReferenceLine ("Finalizing PeerReference={0} IdentityHashCode=0x{1} Instance=0x{2} Instance.Type={3}",
202203
h.ToString (),
203-
value.JniIdentityHashCode.ToString ("x"),
204-
RuntimeHelpers.GetHashCode (value).ToString ("x"),
204+
value.JniIdentityHashCode.ToString ("x", CultureInfo.InvariantCulture),
205+
RuntimeHelpers.GetHashCode (value).ToString ("x", CultureInfo.InvariantCulture),
205206
value.GetType ().ToString ());
206207
}
207208
value.SetPeerReference (new JniObjectReference ());
@@ -214,9 +215,11 @@ public override void ActivatePeer (IJavaPeerable? self, JniObjectReference refer
214215
try {
215216
ActivateViaReflection (reference, cinfo, argumentValues);
216217
} catch (Exception e) {
217-
var m = string.Format ("Could not activate {{ PeerReference={0} IdentityHashCode=0x{1} Java.Type={2} }} for managed type '{3}'.",
218+
var m = string.Format (
219+
CultureInfo.InvariantCulture,
220+
"Could not activate {{ PeerReference={0} IdentityHashCode=0x{1} Java.Type={2} }} for managed type '{3}'.",
218221
reference,
219-
GetJniIdentityHashCode (reference).ToString ("x"),
222+
GetJniIdentityHashCode (reference).ToString ("x", CultureInfo.InvariantCulture),
220223
JniEnvironment.Types.GetJniTypeNameFromInstance (reference),
221224
cinfo.DeclaringType?.FullName);
222225
Debug.WriteLine (m);
@@ -257,7 +260,11 @@ public override List<JniSurfacedPeerInfo> GetSurfacedPeers ()
257260

258261
static readonly Type[] XAConstructorSignature = new Type [] { typeof (IntPtr), typeof (JniHandleOwnership) };
259262

260-
protected override IJavaPeerable? TryCreatePeer (ref JniObjectReference reference, JniObjectReferenceOptions options, Type type)
263+
protected override IJavaPeerable? TryCreatePeer (
264+
ref JniObjectReference reference,
265+
JniObjectReferenceOptions options,
266+
[DynamicallyAccessedMembers (Constructors)]
267+
Type type)
261268
{
262269
var c = type.GetConstructor (ActivationConstructorBindingFlags, null, XAConstructorSignature, null);
263270
if (c != null) {
Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
using System.Runtime.Versioning;
2+
3+
// NOTE: silences the CA1416 analyzer about supported Android APIs
4+
[assembly: TargetPlatformAttribute("Android35.0")]
5+
[assembly: SupportedOSPlatformAttribute("Android21.0")]
Lines changed: 98 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,98 @@
1+
// Originally from: https://github.com/dotnet/java-interop/blob/dd3c1d0514addfe379f050627b3e97493e985da6/src/Java.Runtime.Environment/Java.Interop/JreRuntime.cs
2+
using System;
3+
using System.Collections.Generic;
4+
using System.Collections.ObjectModel;
5+
using System.Diagnostics;
6+
using System.Diagnostics.CodeAnalysis;
7+
using System.Globalization;
8+
using System.IO;
9+
using System.Linq;
10+
using System.Reflection;
11+
using System.Runtime.InteropServices;
12+
using System.Threading;
13+
using Microsoft.Android.Runtime;
14+
15+
namespace Java.Interop {
16+
17+
struct JavaVMInitArgs {
18+
#pragma warning disable CS0649 // Field is never assigned to;
19+
public JniVersion version; /* use JNI_VERSION_1_2 or later */
20+
21+
public int nOptions;
22+
public IntPtr /* JavaVMOption[] */ options;
23+
public byte ignoreUnrecognized;
24+
#pragma warning restore CS0649
25+
}
26+
27+
class NativeAotRuntimeOptions : JniRuntime.CreationOptions {
28+
29+
public bool IgnoreUnrecognizedOptions {get; set;}
30+
31+
public TextWriter? JniGlobalReferenceLogWriter {get; set;}
32+
public TextWriter? JniLocalReferenceLogWriter {get; set;}
33+
34+
public NativeAotRuntimeOptions ()
35+
{
36+
JniVersion = JniVersion.v1_2;
37+
}
38+
39+
public JreRuntime CreateJreVM ()
40+
{
41+
return new JreRuntime (this);
42+
}
43+
}
44+
45+
class JreRuntime : JniRuntime
46+
{
47+
static JreRuntime ()
48+
{
49+
}
50+
51+
static NativeAotRuntimeOptions CreateJreVM (NativeAotRuntimeOptions builder)
52+
{
53+
if (builder == null)
54+
throw new ArgumentNullException ("builder");
55+
if (builder.InvocationPointer == IntPtr.Zero &&
56+
builder.EnvironmentPointer == IntPtr.Zero &&
57+
string.IsNullOrEmpty (builder.JvmLibraryPath))
58+
throw new InvalidOperationException ($"Member `{nameof (NativeAotRuntimeOptions)}.{nameof (NativeAotRuntimeOptions.JvmLibraryPath)}` must be set.");
59+
60+
#if NET
61+
builder.TypeManager ??= new NativeAotTypeManager ();
62+
#endif // NET
63+
64+
builder.ValueManager ??= new NativeAotValueManager (builder.TypeManager);
65+
builder.ObjectReferenceManager ??= new ManagedObjectReferenceManager (builder.JniGlobalReferenceLogWriter, builder.JniLocalReferenceLogWriter);
66+
67+
if (builder.InvocationPointer != IntPtr.Zero || builder.EnvironmentPointer != IntPtr.Zero)
68+
return builder;
69+
70+
throw new NotImplementedException ();
71+
}
72+
73+
[UnconditionalSuppressMessage ("Trimming", "IL3000", Justification = "We check for a null Assembly.Location value!")]
74+
internal static string? GetAssemblyLocation (Assembly assembly)
75+
{
76+
var location = assembly.Location;
77+
if (!string.IsNullOrEmpty (location))
78+
return location;
79+
return null;
80+
}
81+
82+
internal protected JreRuntime (NativeAotRuntimeOptions builder)
83+
: base (CreateJreVM (builder))
84+
{
85+
}
86+
87+
public override string? GetCurrentManagedThreadName ()
88+
{
89+
return Thread.CurrentThread.Name;
90+
}
91+
92+
public override string GetCurrentManagedThreadStackTrace (int skipFrames, bool fNeedFileInfo)
93+
{
94+
return new StackTrace (skipFrames, fNeedFileInfo)
95+
.ToString ();
96+
}
97+
}
98+
}

0 commit comments

Comments
 (0)