diff --git a/Directory.Packages.props b/Directory.Packages.props
index fbca787c..a59812ef 100644
--- a/Directory.Packages.props
+++ b/Directory.Packages.props
@@ -38,10 +38,10 @@
Version="4.7.0" />
+ Version="2.0.34.1" />
+ Version="17.10.4" />
+ Version="5.3.8" />
@@ -95,7 +95,7 @@
Condition="'$(EnableSourceLink)' != 'false'" />
@@ -45,17 +45,6 @@
-MTGOSDK.API.Events;
-MTGOSDK.API.Interface.*;
-
diff --git a/MTGOSDK.Tests/packages.lock.json b/MTGOSDK.Tests/packages.lock.json
index 3e57774a..d9432a1a 100644
--- a/MTGOSDK.Tests/packages.lock.json
+++ b/MTGOSDK.Tests/packages.lock.json
@@ -10,15 +10,9 @@
},
"DotNet.ReproducibleBuilds": {
"type": "Direct",
- "requested": "[1.1.1, )",
- "resolved": "1.1.1",
- "contentHash": "+H2t/t34h6mhEoUvHi8yGXyuZ2GjSovcGYehJrS2MDm2XgmPfZL2Sdxg+uL2lKgZ4M6tTwKHIlxOob2bgh0NRQ==",
- "dependencies": {
- "Microsoft.SourceLink.AzureRepos.Git": "1.1.1",
- "Microsoft.SourceLink.Bitbucket.Git": "1.1.1",
- "Microsoft.SourceLink.GitHub": "1.1.1",
- "Microsoft.SourceLink.GitLab": "1.1.1"
- }
+ "requested": "[1.2.4, )",
+ "resolved": "1.2.4",
+ "contentHash": "Ch9U74tQA2fQH+U0hcYH7WyIFUfAq7jrjgSHVu2FAcYiMBtbrCMyq2nGA/ZZnB2jSaUeOOYiCjxeaDVB7Ssbdw=="
},
"Microsoft.Extensions.Logging": {
"type": "Direct",
@@ -53,9 +47,9 @@
},
"Nerdbank.GitVersioning": {
"type": "Direct",
- "requested": "[3.6.133, )",
- "resolved": "3.6.133",
- "contentHash": "VZWMd5YAeDxpjWjAP/X6bAxnRMiEf6tES/ITN0X5CHJgkWLLeHGmEALivmTAfYM6P+P/3Szy6VCITUAkqjcHVw=="
+ "requested": "[3.6.139, )",
+ "resolved": "3.6.139",
+ "contentHash": "rq0Ub/Jik7PtMtZtLn0tHuJ01Yt36RQ+eeBe+S7qnJ/EFOX6D4T9zuYD3vQPYKGI6Ro4t2iWgFm3fGDgjBrMfg=="
},
"NUnit": {
"type": "Direct",
@@ -77,9 +71,9 @@
},
"ReportGenerator": {
"type": "Direct",
- "requested": "[5.3.6, )",
- "resolved": "5.3.6",
- "contentHash": "cFmyBJGbD4eaE/qkwMclhcgqgrqktb0WmiZ8kt8i4ahIq5fr7SNVjVtAorbbh43vAXtvGPtD/ZUHZYqtAywM4A=="
+ "requested": "[5.3.8, )",
+ "resolved": "5.3.8",
+ "contentHash": "F+JLYA1c3uXMob8f8Wx6APRzZyvcsr5NVGsQOApBeOsyu1rZamWkfb68akm4aR5rfRXMytZsPGFou8xDJV+IcQ=="
},
"Dynamitey": {
"type": "Transitive",
@@ -159,38 +153,11 @@
"resolved": "1.1.0",
"contentHash": "aOZA3BWfz9RXjpzt0sRJJMjAscAUm3Hoa4UWAfceV9UTYxgwZ1lZt5nO2myFf+/jetYQo4uTP7zS8sJY67BBxg=="
},
- "Microsoft.SourceLink.AzureRepos.Git": {
- "type": "Transitive",
- "resolved": "1.1.1",
- "contentHash": "qB5urvw9LO2bG3eVAkuL+2ughxz2rR7aYgm2iyrB8Rlk9cp2ndvGRCvehk3rNIhRuNtQaeKwctOl1KvWiklv5w==",
- "dependencies": {
- "Microsoft.Build.Tasks.Git": "1.1.1",
- "Microsoft.SourceLink.Common": "1.1.1"
- }
- },
- "Microsoft.SourceLink.Bitbucket.Git": {
- "type": "Transitive",
- "resolved": "1.1.1",
- "contentHash": "cDzxXwlyWpLWaH0em4Idj0H3AmVo3L/6xRXKssYemx+7W52iNskj/SQ4FOmfCb8YQt39otTDNMveCZzYtMoucQ==",
- "dependencies": {
- "Microsoft.Build.Tasks.Git": "1.1.1",
- "Microsoft.SourceLink.Common": "1.1.1"
- }
- },
"Microsoft.SourceLink.Common": {
"type": "Transitive",
"resolved": "8.0.0",
"contentHash": "dk9JPxTCIevS75HyEQ0E4OVAFhB2N+V9ShCXf8Q6FkUQZDkgLI12y679Nym1YqsiSysuQskT7Z+6nUf3yab6Vw=="
},
- "Microsoft.SourceLink.GitLab": {
- "type": "Transitive",
- "resolved": "1.1.1",
- "contentHash": "tvsg47DDLqqedlPeYVE2lmiTpND8F0hkrealQ5hYltSmvruy/Gr5nHAKSsjyw5L3NeM/HLMI5ORv7on/M4qyZw==",
- "dependencies": {
- "Microsoft.Build.Tasks.Git": "1.1.1",
- "Microsoft.SourceLink.Common": "1.1.1"
- }
- },
"Microsoft.TestPlatform.ObjectModel": {
"type": "Transitive",
"resolved": "17.10.0",
diff --git a/MTGOSDK.Tests/src/Tests/Events.cs b/MTGOSDK.Tests/src/Tests/Events.cs
index 798266c1..cd8b0d12 100644
--- a/MTGOSDK.Tests/src/Tests/Events.cs
+++ b/MTGOSDK.Tests/src/Tests/Events.cs
@@ -155,7 +155,7 @@ public void ValidateLeague(League league)
Assert.That(league.MinMatches, Is.GreaterThanOrEqualTo(3));
Assert.That((bool?)league.IsPaused, Is.Not.Null);
- foreach(LeaderboardEntry entry in league.Leaderboard)
+ foreach(LeaderboardEntry entry in league.Leaderboard.Take(5))
{
Assert.That(entry.Name, Is.Not.Empty);
Assert.That(entry.TrophyCount, Is.GreaterThanOrEqualTo(0));
diff --git a/MTGOSDK.Win32/packages.lock.json b/MTGOSDK.Win32/packages.lock.json
index dd71cd18..184e19af 100644
--- a/MTGOSDK.Win32/packages.lock.json
+++ b/MTGOSDK.Win32/packages.lock.json
@@ -4,15 +4,9 @@
".NETStandard,Version=v2.0": {
"DotNet.ReproducibleBuilds": {
"type": "Direct",
- "requested": "[1.1.1, )",
- "resolved": "1.1.1",
- "contentHash": "+H2t/t34h6mhEoUvHi8yGXyuZ2GjSovcGYehJrS2MDm2XgmPfZL2Sdxg+uL2lKgZ4M6tTwKHIlxOob2bgh0NRQ==",
- "dependencies": {
- "Microsoft.SourceLink.AzureRepos.Git": "1.1.1",
- "Microsoft.SourceLink.Bitbucket.Git": "1.1.1",
- "Microsoft.SourceLink.GitHub": "1.1.1",
- "Microsoft.SourceLink.GitLab": "1.1.1"
- }
+ "requested": "[1.2.4, )",
+ "resolved": "1.2.4",
+ "contentHash": "Ch9U74tQA2fQH+U0hcYH7WyIFUfAq7jrjgSHVu2FAcYiMBtbrCMyq2nGA/ZZnB2jSaUeOOYiCjxeaDVB7Ssbdw=="
},
"Iced": {
"type": "Direct",
@@ -22,9 +16,9 @@
},
"Meziantou.Polyfill": {
"type": "Direct",
- "requested": "[1.0.37, )",
- "resolved": "1.0.37",
- "contentHash": "kFlH4Ye5lEK9AjViSWm32JrH3Ee42jd15FNnv4X4fK4JzdSlkMR7mqzsgRCBgZzszWLr0ioIA78dsMNxRoouSw=="
+ "requested": "[1.0.39, )",
+ "resolved": "1.0.39",
+ "contentHash": "hqeFk63pjJvovNTl1fnTDcIB7XTt7uiD1O9bNHRi94JIZ31ILkKsb/Gv32U0RGMScddQUp4KZGYkgSpw1zozog=="
},
"Microsoft.SourceLink.GitHub": {
"type": "Direct",
@@ -50,9 +44,9 @@
},
"Nerdbank.GitVersioning": {
"type": "Direct",
- "requested": "[3.6.133, )",
- "resolved": "3.6.133",
- "contentHash": "VZWMd5YAeDxpjWjAP/X6bAxnRMiEf6tES/ITN0X5CHJgkWLLeHGmEALivmTAfYM6P+P/3Szy6VCITUAkqjcHVw=="
+ "requested": "[3.6.139, )",
+ "resolved": "3.6.139",
+ "contentHash": "rq0Ub/Jik7PtMtZtLn0tHuJ01Yt36RQ+eeBe+S7qnJ/EFOX6D4T9zuYD3vQPYKGI6Ro4t2iWgFm3fGDgjBrMfg=="
},
"NETStandard.Library": {
"type": "Direct",
@@ -73,38 +67,11 @@
"resolved": "1.1.0",
"contentHash": "kz0PEW2lhqygehI/d6XsPCQzD7ff7gUJaVGPVETX611eadGsA3A877GdSlU0LRVMCTH/+P3o2iDTak+S08V2+A=="
},
- "Microsoft.SourceLink.AzureRepos.Git": {
- "type": "Transitive",
- "resolved": "1.1.1",
- "contentHash": "qB5urvw9LO2bG3eVAkuL+2ughxz2rR7aYgm2iyrB8Rlk9cp2ndvGRCvehk3rNIhRuNtQaeKwctOl1KvWiklv5w==",
- "dependencies": {
- "Microsoft.Build.Tasks.Git": "1.1.1",
- "Microsoft.SourceLink.Common": "1.1.1"
- }
- },
- "Microsoft.SourceLink.Bitbucket.Git": {
- "type": "Transitive",
- "resolved": "1.1.1",
- "contentHash": "cDzxXwlyWpLWaH0em4Idj0H3AmVo3L/6xRXKssYemx+7W52iNskj/SQ4FOmfCb8YQt39otTDNMveCZzYtMoucQ==",
- "dependencies": {
- "Microsoft.Build.Tasks.Git": "1.1.1",
- "Microsoft.SourceLink.Common": "1.1.1"
- }
- },
"Microsoft.SourceLink.Common": {
"type": "Transitive",
"resolved": "8.0.0",
"contentHash": "dk9JPxTCIevS75HyEQ0E4OVAFhB2N+V9ShCXf8Q6FkUQZDkgLI12y679Nym1YqsiSysuQskT7Z+6nUf3yab6Vw=="
},
- "Microsoft.SourceLink.GitLab": {
- "type": "Transitive",
- "resolved": "1.1.1",
- "contentHash": "tvsg47DDLqqedlPeYVE2lmiTpND8F0hkrealQ5hYltSmvruy/Gr5nHAKSsjyw5L3NeM/HLMI5ORv7on/M4qyZw==",
- "dependencies": {
- "Microsoft.Build.Tasks.Git": "1.1.1",
- "Microsoft.SourceLink.Common": "1.1.1"
- }
- },
"System.Buffers": {
"type": "Transitive",
"resolved": "4.5.1",
diff --git a/MTGOSDK/lib/ILLink/ILLink.props b/MTGOSDK/lib/ILLink/ILLink.props
index d669a88a..fc7f19c4 100644
--- a/MTGOSDK/lib/ILLink/ILLink.props
+++ b/MTGOSDK/lib/ILLink/ILLink.props
@@ -48,6 +48,8 @@
$(NoWarn);IL2092;IL2093;IL2094;IL2095
$(NoWarn);IL2097;IL2098;IL2099
+
+ $(NoWarn);IL2121
+
+ true
+
+
+
-
$(MSBuildThisFileDirectory)..\ILRepack\ILRepack.targets
Microsoft.Diagnostics.Runtime
$(SolutionDir)dist\$(Configuration)\$(ILRepackTarget).dll
- true
+
+
+
diff --git a/MTGOSDK/lib/ScubaDiver/packages.lock.json b/MTGOSDK/lib/ScubaDiver/packages.lock.json
index dc0ceb5a..a3a3ab95 100644
--- a/MTGOSDK/lib/ScubaDiver/packages.lock.json
+++ b/MTGOSDK/lib/ScubaDiver/packages.lock.json
@@ -4,27 +4,21 @@
".NETStandard,Version=v2.0": {
"DotNet.ReproducibleBuilds": {
"type": "Direct",
- "requested": "[1.1.1, )",
- "resolved": "1.1.1",
- "contentHash": "+H2t/t34h6mhEoUvHi8yGXyuZ2GjSovcGYehJrS2MDm2XgmPfZL2Sdxg+uL2lKgZ4M6tTwKHIlxOob2bgh0NRQ==",
- "dependencies": {
- "Microsoft.SourceLink.AzureRepos.Git": "1.1.1",
- "Microsoft.SourceLink.Bitbucket.Git": "1.1.1",
- "Microsoft.SourceLink.GitHub": "1.1.1",
- "Microsoft.SourceLink.GitLab": "1.1.1"
- }
+ "requested": "[1.2.4, )",
+ "resolved": "1.2.4",
+ "contentHash": "Ch9U74tQA2fQH+U0hcYH7WyIFUfAq7jrjgSHVu2FAcYiMBtbrCMyq2nGA/ZZnB2jSaUeOOYiCjxeaDVB7Ssbdw=="
},
"ILRepack.Lib.MSBuild.Task": {
"type": "Direct",
- "requested": "[2.0.33, )",
- "resolved": "2.0.33",
- "contentHash": "mbWjnc+7Z9qdJTNWubqCbU8pWgUxkgIJOny3lnejF7RVdZVWZyH9FhFoz+p9qJ9Z7tim5kE2um/NCGUdKBNSfA=="
+ "requested": "[2.0.34.1, )",
+ "resolved": "2.0.34.1",
+ "contentHash": "QHqCm+bTalw4/Jl+k0l0llUJmsv7ftkxZN+CnXdbJ4Fy6Xr2I+OGq4qY5rma+vRAy1veGQCCBcUr+VFVNwBi5A=="
},
"Meziantou.Polyfill": {
"type": "Direct",
- "requested": "[1.0.37, )",
- "resolved": "1.0.37",
- "contentHash": "kFlH4Ye5lEK9AjViSWm32JrH3Ee42jd15FNnv4X4fK4JzdSlkMR7mqzsgRCBgZzszWLr0ioIA78dsMNxRoouSw=="
+ "requested": "[1.0.39, )",
+ "resolved": "1.0.39",
+ "contentHash": "hqeFk63pjJvovNTl1fnTDcIB7XTt7uiD1O9bNHRi94JIZ31ILkKsb/Gv32U0RGMScddQUp4KZGYkgSpw1zozog=="
},
"Microsoft.Diagnostics.Runtime": {
"type": "Direct",
@@ -151,38 +145,11 @@
"resolved": "1.1.0",
"contentHash": "aOZA3BWfz9RXjpzt0sRJJMjAscAUm3Hoa4UWAfceV9UTYxgwZ1lZt5nO2myFf+/jetYQo4uTP7zS8sJY67BBxg=="
},
- "Microsoft.SourceLink.AzureRepos.Git": {
- "type": "Transitive",
- "resolved": "1.1.1",
- "contentHash": "qB5urvw9LO2bG3eVAkuL+2ughxz2rR7aYgm2iyrB8Rlk9cp2ndvGRCvehk3rNIhRuNtQaeKwctOl1KvWiklv5w==",
- "dependencies": {
- "Microsoft.Build.Tasks.Git": "1.1.1",
- "Microsoft.SourceLink.Common": "1.1.1"
- }
- },
- "Microsoft.SourceLink.Bitbucket.Git": {
- "type": "Transitive",
- "resolved": "1.1.1",
- "contentHash": "cDzxXwlyWpLWaH0em4Idj0H3AmVo3L/6xRXKssYemx+7W52iNskj/SQ4FOmfCb8YQt39otTDNMveCZzYtMoucQ==",
- "dependencies": {
- "Microsoft.Build.Tasks.Git": "1.1.1",
- "Microsoft.SourceLink.Common": "1.1.1"
- }
- },
"Microsoft.SourceLink.Common": {
"type": "Transitive",
"resolved": "8.0.0",
"contentHash": "dk9JPxTCIevS75HyEQ0E4OVAFhB2N+V9ShCXf8Q6FkUQZDkgLI12y679Nym1YqsiSysuQskT7Z+6nUf3yab6Vw=="
},
- "Microsoft.SourceLink.GitLab": {
- "type": "Transitive",
- "resolved": "1.1.1",
- "contentHash": "tvsg47DDLqqedlPeYVE2lmiTpND8F0hkrealQ5hYltSmvruy/Gr5nHAKSsjyw5L3NeM/HLMI5ORv7on/M4qyZw==",
- "dependencies": {
- "Microsoft.Build.Tasks.Git": "1.1.1",
- "Microsoft.SourceLink.Common": "1.1.1"
- }
- },
"System.Buffers": {
"type": "Transitive",
"resolved": "4.5.1",
diff --git a/MTGOSDK/lib/ScubaDiver/src/Diver.cs b/MTGOSDK/lib/ScubaDiver/src/Diver.cs
index 41701611..824ef52b 100644
--- a/MTGOSDK/lib/ScubaDiver/src/Diver.cs
+++ b/MTGOSDK/lib/ScubaDiver/src/Diver.cs
@@ -1,7 +1,7 @@
/** @file
Copyright (c) 2021, Xappy.
Copyright (c) 2024, Cory Bennett. All rights reserved.
- SPDX-License-Identifier: Apache-2.0 and MIT
+ SPDX-License-Identifier: Apache-2.0
**/
using System;
@@ -21,17 +21,18 @@
using Microsoft.Diagnostics.Runtime;
using Newtonsoft.Json;
+using MTGOSDK.Core.Compiler;
+using MTGOSDK.Core.Compiler.Extensions;
+using MTGOSDK.Core.Compiler.Snapshot;
using MTGOSDK.Core.Reflection;
-using MTGOSDK.Core.Reflection.Snapshot;
-using MTGOSDK.Core.Reflection.Emit;
+using MTGOSDK.Core.Reflection.Extensions;
+using MTGOSDK.Core.Reflection.Types;
using MTGOSDK.Core.Remoting.Interop;
-using MTGOSDK.Core.Remoting.Interop.Extensions;
using MTGOSDK.Core.Remoting.Interop.Interactions;
using MTGOSDK.Core.Remoting.Interop.Interactions.Callbacks;
using MTGOSDK.Core.Remoting.Interop.Interactions.Client;
using MTGOSDK.Core.Remoting.Interop.Interactions.Dumps;
using MTGOSDK.Core.Remoting.Interop.Interactions.Object;
-using MTGOSDK.Core.Remoting.Interop.Utils;
using MTGOSDK.Win32.API;
@@ -165,6 +166,7 @@ private void HandleDispatchedRequest(HttpListenerContext requestContext)
}
catch (Exception ex)
{
+ Logger.Debug("[Diver] Exception in handler: " + ex.ToString());
body = QuickError(ex.Message, ex.StackTrace);
}
}
@@ -218,8 +220,7 @@ void ListenerCallback(IAsyncResult result)
}
catch (Exception e)
{
- Logger.Debug("[Diver] Task faulted! Exception:");
- Logger.Debug(e.ToString());
+ Logger.Debug("[Diver] Task faulted! Exception: " + e.ToString());
}
}
IAsyncResult asyncOperation = listener.BeginGetContext(ListenerCallback, listener);
@@ -316,31 +317,21 @@ private string MakeUnregisterClientResponse(HttpListenerRequest arg)
private string MakeDomainsResponse(HttpListenerRequest req)
{
- List available = new();
+ // Extract the names of all available modules from the current AppDomain.
+ List modules = new();
+ string currentDomain = AppDomain.CurrentDomain.FriendlyName;
lock (_runtime.clrLock)
{
- foreach (ClrAppDomain clrAppDomain in _runtime.GetClrAppDomains())
- {
- var modules = clrAppDomain.Modules
- .Select(m => Path.GetFileNameWithoutExtension(m.Name))
- .Where(m => !string.IsNullOrWhiteSpace(m))
- .ToList();
- var dom = new DomainsDump.AvailableDomain()
- {
- Name = clrAppDomain.Name,
- AvailableModules = modules
- };
- available.Add(dom);
- }
+ ClrAppDomain clrAppDomain = _runtime.GetClrAppDomains()
+ .FirstOrDefault(ad => ad.Name == currentDomain);
+ modules = clrAppDomain.Modules
+ .Select(m => Path.GetFileNameWithoutExtension(m.Name))
+ .Where(m => !string.IsNullOrWhiteSpace(m))
+ .ToList();
}
- DomainsDump dd = new()
- {
- Current = AppDomain.CurrentDomain.FriendlyName,
- AvailableDomains = available
- };
-
- return JsonConvert.SerializeObject(dd);
+ DomainDump domainDump = new(currentDomain, modules);
+ return JsonConvert.SerializeObject(domainDump);
}
private string MakeTypesResponse(HttpListenerRequest req)
@@ -789,7 +780,7 @@ private string MakeInvokeResponse(HttpListenerRequest arg)
dumpedObjType = _runtime.ResolveType(clrObj.Type.Name);
try
{
- instance = _runtime.DereferenceObject(clrObj.Address, mt);
+ instance = _runtime.Compile(clrObj.Address, mt);
}
catch (Exception)
{
@@ -817,7 +808,7 @@ private string MakeInvokeResponse(HttpListenerRequest arg)
// Infer parameter types from received parameters.
// Note that for 'null' arguments we don't know the type so we use a "Wild Card" type
- Type[] argumentTypes = paramsList.Select(p => p?.GetType() ?? new WildCardType()).ToArray();
+ Type[] argumentTypes = paramsList.Select(p => p?.GetType() ?? new TypeStub()).ToArray();
// Get types of generic arguments
Type[] genericArgumentTypes = request.GenericArgsTypeFullNames.Select(typeFullName => _runtime.ResolveType(typeFullName)).ToArray();
@@ -1035,7 +1026,7 @@ private string MakeSetFieldResponse(HttpListenerRequest arg)
clrObj = _runtime.GetClrObject(request.ObjAddress);
if (clrObj.Type == null)
{
- return QuickError("'address' points at an invalid address");
+ return QuickError($"The invalid address for '${request.TypeFullName}'.");
}
// Make sure it's still in place
@@ -1045,14 +1036,14 @@ private string MakeSetFieldResponse(HttpListenerRequest arg)
if (clrObj.Type == null)
{
return
- QuickError("Object moved since last refresh. 'address' now points at an invalid address.");
+ QuickError($"The address for '${request.TypeFullName}' moved since last refresh.");
}
ulong mt = clrObj.Type.MethodTable;
dumpedObjType = _runtime.ResolveType(clrObj.Type.Name);
try
{
- instance = _runtime.DereferenceObject(clrObj.Address, mt);
+ instance = _runtime.Compile(clrObj.Address, mt);
}
catch (Exception)
{
diff --git a/MTGOSDK/lib/ScubaDiver/src/DllEntry.cs b/MTGOSDK/lib/ScubaDiver/src/DllEntry.cs
index f3288c08..35313def 100644
--- a/MTGOSDK/lib/ScubaDiver/src/DllEntry.cs
+++ b/MTGOSDK/lib/ScubaDiver/src/DllEntry.cs
@@ -1,7 +1,7 @@
/** @file
Copyright (c) 2021, Xappy.
Copyright (c) 2024, Cory Bennett. All rights reserved.
- SPDX-License-Identifier: Apache-2.0 and MIT
+ SPDX-License-Identifier: Apache-2.0
**/
using System;
diff --git a/MTGOSDK/lib/ScubaDiver/src/Logger.cs b/MTGOSDK/lib/ScubaDiver/src/Logger.cs
index 993e59ff..e8cad1f0 100644
--- a/MTGOSDK/lib/ScubaDiver/src/Logger.cs
+++ b/MTGOSDK/lib/ScubaDiver/src/Logger.cs
@@ -1,7 +1,7 @@
/** @file
Copyright (c) 2021, Xappy.
Copyright (c) 2024, Cory Bennett. All rights reserved.
- SPDX-License-Identifier: Apache-2.0 and MIT
+ SPDX-License-Identifier: Apache-2.0
**/
using System;
@@ -37,7 +37,7 @@ internal class Logger
#endregion
-#if DEBUG
+#if DEBUG && ENABLE_DEBUG_CONSOLE
public static bool IsDebug = true;
#else
public static bool IsDebug = false;
@@ -60,7 +60,8 @@ internal static void Debug(string s)
{
if (IsDebug || Debugger.IsAttached)
{
- System.Diagnostics.Debug.WriteLine(s);
+ Console.WriteLine(s);
+ // System.Diagnostics.Debug.WriteLine(s);
}
}
}
diff --git a/MTGOSDK/packages.lock.json b/MTGOSDK/packages.lock.json
index b193b61f..105aa06f 100644
--- a/MTGOSDK/packages.lock.json
+++ b/MTGOSDK/packages.lock.json
@@ -4,15 +4,9 @@
".NETStandard,Version=v2.0": {
"DotNet.ReproducibleBuilds": {
"type": "Direct",
- "requested": "[1.1.1, )",
- "resolved": "1.1.1",
- "contentHash": "+H2t/t34h6mhEoUvHi8yGXyuZ2GjSovcGYehJrS2MDm2XgmPfZL2Sdxg+uL2lKgZ4M6tTwKHIlxOob2bgh0NRQ==",
- "dependencies": {
- "Microsoft.SourceLink.AzureRepos.Git": "1.1.1",
- "Microsoft.SourceLink.Bitbucket.Git": "1.1.1",
- "Microsoft.SourceLink.GitHub": "1.1.1",
- "Microsoft.SourceLink.GitLab": "1.1.1"
- }
+ "requested": "[1.2.4, )",
+ "resolved": "1.2.4",
+ "contentHash": "Ch9U74tQA2fQH+U0hcYH7WyIFUfAq7jrjgSHVu2FAcYiMBtbrCMyq2nGA/ZZnB2jSaUeOOYiCjxeaDVB7Ssbdw=="
},
"ImpromptuInterface": {
"type": "Direct",
@@ -27,9 +21,9 @@
},
"Meziantou.Polyfill": {
"type": "Direct",
- "requested": "[1.0.37, )",
- "resolved": "1.0.37",
- "contentHash": "kFlH4Ye5lEK9AjViSWm32JrH3Ee42jd15FNnv4X4fK4JzdSlkMR7mqzsgRCBgZzszWLr0ioIA78dsMNxRoouSw=="
+ "requested": "[1.0.39, )",
+ "resolved": "1.0.39",
+ "contentHash": "hqeFk63pjJvovNTl1fnTDcIB7XTt7uiD1O9bNHRi94JIZ31ILkKsb/Gv32U0RGMScddQUp4KZGYkgSpw1zozog=="
},
"Microsoft.CSharp": {
"type": "Direct",
@@ -190,38 +184,11 @@
"resolved": "1.1.0",
"contentHash": "aOZA3BWfz9RXjpzt0sRJJMjAscAUm3Hoa4UWAfceV9UTYxgwZ1lZt5nO2myFf+/jetYQo4uTP7zS8sJY67BBxg=="
},
- "Microsoft.SourceLink.AzureRepos.Git": {
- "type": "Transitive",
- "resolved": "1.1.1",
- "contentHash": "qB5urvw9LO2bG3eVAkuL+2ughxz2rR7aYgm2iyrB8Rlk9cp2ndvGRCvehk3rNIhRuNtQaeKwctOl1KvWiklv5w==",
- "dependencies": {
- "Microsoft.Build.Tasks.Git": "1.1.1",
- "Microsoft.SourceLink.Common": "1.1.1"
- }
- },
- "Microsoft.SourceLink.Bitbucket.Git": {
- "type": "Transitive",
- "resolved": "1.1.1",
- "contentHash": "cDzxXwlyWpLWaH0em4Idj0H3AmVo3L/6xRXKssYemx+7W52iNskj/SQ4FOmfCb8YQt39otTDNMveCZzYtMoucQ==",
- "dependencies": {
- "Microsoft.Build.Tasks.Git": "1.1.1",
- "Microsoft.SourceLink.Common": "1.1.1"
- }
- },
"Microsoft.SourceLink.Common": {
"type": "Transitive",
"resolved": "8.0.0",
"contentHash": "dk9JPxTCIevS75HyEQ0E4OVAFhB2N+V9ShCXf8Q6FkUQZDkgLI12y679Nym1YqsiSysuQskT7Z+6nUf3yab6Vw=="
},
- "Microsoft.SourceLink.GitLab": {
- "type": "Transitive",
- "resolved": "1.1.1",
- "contentHash": "tvsg47DDLqqedlPeYVE2lmiTpND8F0hkrealQ5hYltSmvruy/Gr5nHAKSsjyw5L3NeM/HLMI5ORv7on/M4qyZw==",
- "dependencies": {
- "Microsoft.Build.Tasks.Git": "1.1.1",
- "Microsoft.SourceLink.Common": "1.1.1"
- }
- },
"System.Buffers": {
"type": "Transitive",
"resolved": "4.5.1",
@@ -345,21 +312,15 @@
"net8.0-windows7.0": {
"DotNet.ReproducibleBuilds": {
"type": "Direct",
- "requested": "[1.1.1, )",
- "resolved": "1.1.1",
- "contentHash": "+H2t/t34h6mhEoUvHi8yGXyuZ2GjSovcGYehJrS2MDm2XgmPfZL2Sdxg+uL2lKgZ4M6tTwKHIlxOob2bgh0NRQ==",
- "dependencies": {
- "Microsoft.SourceLink.AzureRepos.Git": "1.1.1",
- "Microsoft.SourceLink.Bitbucket.Git": "1.1.1",
- "Microsoft.SourceLink.GitHub": "1.1.1",
- "Microsoft.SourceLink.GitLab": "1.1.1"
- }
+ "requested": "[1.2.4, )",
+ "resolved": "1.2.4",
+ "contentHash": "Ch9U74tQA2fQH+U0hcYH7WyIFUfAq7jrjgSHVu2FAcYiMBtbrCMyq2nGA/ZZnB2jSaUeOOYiCjxeaDVB7Ssbdw=="
},
"ILRepack.Lib.MSBuild.Task": {
"type": "Direct",
- "requested": "[2.0.33, )",
- "resolved": "2.0.33",
- "contentHash": "mbWjnc+7Z9qdJTNWubqCbU8pWgUxkgIJOny3lnejF7RVdZVWZyH9FhFoz+p9qJ9Z7tim5kE2um/NCGUdKBNSfA=="
+ "requested": "[2.0.34.1, )",
+ "resolved": "2.0.34.1",
+ "contentHash": "QHqCm+bTalw4/Jl+k0l0llUJmsv7ftkxZN+CnXdbJ4Fy6Xr2I+OGq4qY5rma+vRAy1veGQCCBcUr+VFVNwBi5A=="
},
"ImpromptuInterface": {
"type": "Direct",
@@ -416,9 +377,9 @@
},
"Nerdbank.GitVersioning": {
"type": "Direct",
- "requested": "[3.6.133, )",
- "resolved": "3.6.133",
- "contentHash": "VZWMd5YAeDxpjWjAP/X6bAxnRMiEf6tES/ITN0X5CHJgkWLLeHGmEALivmTAfYM6P+P/3Szy6VCITUAkqjcHVw=="
+ "requested": "[3.6.139, )",
+ "resolved": "3.6.139",
+ "contentHash": "rq0Ub/Jik7PtMtZtLn0tHuJ01Yt36RQ+eeBe+S7qnJ/EFOX6D4T9zuYD3vQPYKGI6Ro4t2iWgFm3fGDgjBrMfg=="
},
"Newtonsoft.Json": {
"type": "Direct",
@@ -499,38 +460,11 @@
"resolved": "1.1.0",
"contentHash": "aOZA3BWfz9RXjpzt0sRJJMjAscAUm3Hoa4UWAfceV9UTYxgwZ1lZt5nO2myFf+/jetYQo4uTP7zS8sJY67BBxg=="
},
- "Microsoft.SourceLink.AzureRepos.Git": {
- "type": "Transitive",
- "resolved": "1.1.1",
- "contentHash": "qB5urvw9LO2bG3eVAkuL+2ughxz2rR7aYgm2iyrB8Rlk9cp2ndvGRCvehk3rNIhRuNtQaeKwctOl1KvWiklv5w==",
- "dependencies": {
- "Microsoft.Build.Tasks.Git": "1.1.1",
- "Microsoft.SourceLink.Common": "1.1.1"
- }
- },
- "Microsoft.SourceLink.Bitbucket.Git": {
- "type": "Transitive",
- "resolved": "1.1.1",
- "contentHash": "cDzxXwlyWpLWaH0em4Idj0H3AmVo3L/6xRXKssYemx+7W52iNskj/SQ4FOmfCb8YQt39otTDNMveCZzYtMoucQ==",
- "dependencies": {
- "Microsoft.Build.Tasks.Git": "1.1.1",
- "Microsoft.SourceLink.Common": "1.1.1"
- }
- },
"Microsoft.SourceLink.Common": {
"type": "Transitive",
"resolved": "8.0.0",
"contentHash": "dk9JPxTCIevS75HyEQ0E4OVAFhB2N+V9ShCXf8Q6FkUQZDkgLI12y679Nym1YqsiSysuQskT7Z+6nUf3yab6Vw=="
},
- "Microsoft.SourceLink.GitLab": {
- "type": "Transitive",
- "resolved": "1.1.1",
- "contentHash": "tvsg47DDLqqedlPeYVE2lmiTpND8F0hkrealQ5hYltSmvruy/Gr5nHAKSsjyw5L3NeM/HLMI5ORv7on/M4qyZw==",
- "dependencies": {
- "Microsoft.Build.Tasks.Git": "1.1.1",
- "Microsoft.SourceLink.Common": "1.1.1"
- }
- },
"System.ComponentModel": {
"type": "Transitive",
"resolved": "4.3.0",
diff --git a/MTGOSDK/src/API/Chat/Channel.cs b/MTGOSDK/src/API/Chat/Channel.cs
index 0093aed5..5dd9edc0 100644
--- a/MTGOSDK/src/API/Chat/Channel.cs
+++ b/MTGOSDK/src/API/Chat/Channel.cs
@@ -73,7 +73,7 @@ public sealed class Channel(dynamic chatChannel)
///
/// The log of messages in this channel.
///
- public IList Messages => Map(ChatLog);
+ public IList Messages => Map(ChatLog, proxy: true);
///
/// The number of messages sent in this channel.
diff --git a/MTGOSDK/src/API/Client.cs b/MTGOSDK/src/API/Client.cs
index e8b336de..d55dd1f6 100644
--- a/MTGOSDK/src/API/Client.cs
+++ b/MTGOSDK/src/API/Client.cs
@@ -134,7 +134,7 @@ public sealed class Client : DLRWrapper, IDisposable
/// and should be instantiated once per application instance and prior to
/// invoking with other API classes.
///
- ///
+ ///
/// Thrown when the client process fails to finish installation or start.
///
///
@@ -167,7 +167,7 @@ public Client(
throw new ExternalErrorException("MTGO servers are currently offline.");
if (!await RemoteClient.StartProcess())
- throw new SetupFailedException("Failed to start the MTGO process.");
+ throw new SetupFailureException("Failed to start the MTGO process.");
}
// Sets the client's disposal policy.
diff --git a/MTGOSDK/src/API/Collection/Binder.cs b/MTGOSDK/src/API/Collection/Binder.cs
index 1ed54544..6fb6f67b 100644
--- a/MTGOSDK/src/API/Collection/Binder.cs
+++ b/MTGOSDK/src/API/Collection/Binder.cs
@@ -16,6 +16,7 @@ public sealed class Binder(dynamic binder) : CardGrouping
///
/// The internal reference for the binding type for the wrapped object.
///
+ [RuntimeInternal]
internal override Type type => typeof(IBinder);
///
diff --git a/MTGOSDK/src/API/Collection/Card.cs b/MTGOSDK/src/API/Collection/Card.cs
index 08f302f0..3ea5c5bd 100644
--- a/MTGOSDK/src/API/Collection/Card.cs
+++ b/MTGOSDK/src/API/Collection/Card.cs
@@ -15,6 +15,7 @@ public sealed class Card(dynamic card) : CollectionItem
///
/// The internal reference for the binding type for the wrapped object.
///
+ [RuntimeInternal]
internal override Type type => typeof(ICardDefinition);
///
diff --git a/MTGOSDK/src/API/Collection/Collection.cs b/MTGOSDK/src/API/Collection/Collection.cs
index 3455316a..a893b7fb 100644
--- a/MTGOSDK/src/API/Collection/Collection.cs
+++ b/MTGOSDK/src/API/Collection/Collection.cs
@@ -17,6 +17,7 @@ public sealed class Collection(ICollectionGrouping collection)
///
/// The internal reference for the binding type for the wrapped object.
///
+ [RuntimeInternal]
internal override Type type => typeof(ICollectionGrouping);
///
diff --git a/MTGOSDK/src/API/Collection/Deck.cs b/MTGOSDK/src/API/Collection/Deck.cs
index f0d1b1bf..70d136a5 100644
--- a/MTGOSDK/src/API/Collection/Deck.cs
+++ b/MTGOSDK/src/API/Collection/Deck.cs
@@ -19,6 +19,7 @@ public sealed class Deck(dynamic deck) : CardGrouping
///
/// The internal reference for the binding type for the wrapped object.
///
+ [RuntimeInternal]
internal override Type type => typeof(IDeck);
///
@@ -51,7 +52,7 @@ public sealed class Deck(dynamic deck) : CardGrouping
///
/// Proxy type for the client's DeckRegion class.
///
- private static readonly Proxy s_DeckRegion =
+ private static readonly TypeProxy s_DeckRegion =
new(typeof(WotC.MTGO.Common.DeckRegion));
public dynamic GetRegionRef(DeckRegion region)
diff --git a/MTGOSDK/src/API/Interface/ViewModels/BasicToastViewModel.cs b/MTGOSDK/src/API/Interface/ViewModels/BasicToastViewModel.cs
index eba754bd..862779a4 100644
--- a/MTGOSDK/src/API/Interface/ViewModels/BasicToastViewModel.cs
+++ b/MTGOSDK/src/API/Interface/ViewModels/BasicToastViewModel.cs
@@ -23,7 +23,7 @@ public sealed class BasicToastViewModel(dynamic basicToastViewModel)
///
/// Creates a new remote instance of the BasicToastViewModel class.
///
- internal static BasicToastViewModel NewInstance(params dynamic[] args) =>
+ private static BasicToastViewModel NewInstance(params dynamic[] args) =>
new(RemoteClient.CreateInstance(
"Shiny.Toast.ViewModels.BasicToastViewModel",
Unbind(args)
diff --git a/MTGOSDK/src/API/Interface/ViewModels/GenericDialogViewModel.cs b/MTGOSDK/src/API/Interface/ViewModels/GenericDialogViewModel.cs
index 77a83a4b..b76f1f46 100644
--- a/MTGOSDK/src/API/Interface/ViewModels/GenericDialogViewModel.cs
+++ b/MTGOSDK/src/API/Interface/ViewModels/GenericDialogViewModel.cs
@@ -22,7 +22,7 @@ public sealed class GenericDialogViewModel(dynamic genericDialogViewModel)
///
/// Creates a new remote instance of the GenericDialogViewModel class.
///
- internal static GenericDialogViewModel NewInstance() =>
+ private static GenericDialogViewModel NewInstance() =>
new(RemoteClient.CreateInstance("Shiny.ViewModels.GenericDialogViewModel"));
public GenericDialogViewModel(
diff --git a/MTGOSDK/src/API/Interface/WindowUtilities.cs b/MTGOSDK/src/API/Interface/WindowUtilities.cs
index 9a2429a5..ec6ddd0c 100644
--- a/MTGOSDK/src/API/Interface/WindowUtilities.cs
+++ b/MTGOSDK/src/API/Interface/WindowUtilities.cs
@@ -52,7 +52,7 @@ public static ICollection GetWindows()
try
{
var collection = RemoteClient
- .GetInstances(new Proxy())
+ .GetInstances(new TypeProxy())
.LastOrDefault() ?? throw null;
return Bind>(collection);
diff --git a/MTGOSDK/src/API/ObjectProvider.cs b/MTGOSDK/src/API/ObjectProvider.cs
index 17f3d397..f1d1a758 100644
--- a/MTGOSDK/src/API/ObjectProvider.cs
+++ b/MTGOSDK/src/API/ObjectProvider.cs
@@ -9,7 +9,8 @@
using MTGOSDK.Core.Reflection;
using MTGOSDK.Core.Remoting;
using static MTGOSDK.Core.Reflection.DLRWrapper;
-using static MTGOSDK.Core.Remoting.LazyRemoteObject;
+using MTGOSDK.Core.Remoting.Reflection;
+using TResetter = MTGOSDK.Core.Remoting.Reflection.LazyRemoteObject.TResetter;
namespace MTGOSDK.API;
@@ -22,7 +23,7 @@ public static class ObjectProvider
///
/// Proxy type for the client's static ObjectProvider class.
///
- private static readonly Proxy s_proxy =
+ private static readonly TypeProxy s_proxy =
new(typeof(WotC.MtGO.Client.Common.ServiceLocation.ObjectProvider));
private static readonly ConcurrentDictionary s_instances = new();
@@ -177,7 +178,7 @@ public static dynamic Get(
public static dynamic Get(bool bindTypes = true) where T : class
{
// Create a proxy type for the given generic type
- Proxy proxy = new();
+ TypeProxy proxy = new();
//
// If not binding types, return an instance leaving open all binding flags.
@@ -198,7 +199,7 @@ public static dynamic Get(bool bindTypes = true) where T : class
// Late bind the interface type to the proxy value
if (bindTypes && (@interface != null || proxy.IsInterface))
- obj = Proxy.As(obj, @interface ?? proxy.Class);
+ obj = TypeProxy.As(obj, @interface ?? proxy.Class);
return obj;
}
diff --git a/MTGOSDK/src/API/Play/Games/Game.cs b/MTGOSDK/src/API/Play/Games/Game.cs
index 7c04d82a..3b50a268 100644
--- a/MTGOSDK/src/API/Play/Games/Game.cs
+++ b/MTGOSDK/src/API/Play/Games/Game.cs
@@ -39,7 +39,7 @@ public sealed class Game(dynamic game) : DLRWrapper
Optional(
// TODO: Use a more efficient method of retrieving view model objects
// without traversing the client's managed heap.
- RemoteClient.GetInstances(new Proxy())
+ RemoteClient.GetInstances(new TypeProxy())
.FirstOrDefault(vm => Try(() => vm.GameId == this.Id))
);
diff --git a/MTGOSDK/src/API/Play/History/HistoricalItem.cs b/MTGOSDK/src/API/Play/History/HistoricalItem.cs
index d27ed800..c3c9e0eb 100644
--- a/MTGOSDK/src/API/Play/History/HistoricalItem.cs
+++ b/MTGOSDK/src/API/Play/History/HistoricalItem.cs
@@ -19,6 +19,7 @@ public abstract class HistoricalItem : DLRWrapper
///
/// The internal reference for the binding type for the wrapped object.
///
+ [RuntimeInternal]
internal override Type type => typeof(I);
public sealed class Default(dynamic historicalItem)
diff --git a/MTGOSDK/src/API/Play/Leagues/League.cs b/MTGOSDK/src/API/Play/Leagues/League.cs
index 6e83beb5..5727cdc3 100644
--- a/MTGOSDK/src/API/Play/Leagues/League.cs
+++ b/MTGOSDK/src/API/Play/Leagues/League.cs
@@ -19,6 +19,7 @@ public sealed class League(dynamic league) : Event
///
/// The internal reference for the binding type for the wrapped object.
///
+ [RuntimeInternal]
internal override Type type => typeof(ILeague);
///
@@ -69,7 +70,7 @@ public sealed class League(dynamic league) : Event
/// The league's current leaderboard entries.
///
public IList Leaderboard =>
- Map(@base.Leaderboard);
+ Map(@base.Leaderboard, proxy: true);
///
/// The total number of matches playable in the league.
diff --git a/MTGOSDK/src/API/Play/Match.cs b/MTGOSDK/src/API/Play/Match.cs
index c5179488..32534893 100644
--- a/MTGOSDK/src/API/Play/Match.cs
+++ b/MTGOSDK/src/API/Play/Match.cs
@@ -21,6 +21,7 @@ public sealed class Match(dynamic match) : Event
///
/// The internal reference for the binding type for the wrapped object.
///
+ [RuntimeInternal]
internal override Type type => typeof(IMatch);
///
diff --git a/MTGOSDK/src/API/Play/Queue.cs b/MTGOSDK/src/API/Play/Queue.cs
index bc0c4403..a2b7efbc 100644
--- a/MTGOSDK/src/API/Play/Queue.cs
+++ b/MTGOSDK/src/API/Play/Queue.cs
@@ -16,6 +16,7 @@ public sealed class Queue(dynamic queue) : Event
///
/// The internal reference for the binding type for the wrapped object.
///
+ [RuntimeInternal]
internal override Type type => typeof(IQueue);
///
diff --git a/MTGOSDK/src/API/Play/Tournaments/Tournament.cs b/MTGOSDK/src/API/Play/Tournaments/Tournament.cs
index 7835e7eb..92a5b40c 100644
--- a/MTGOSDK/src/API/Play/Tournaments/Tournament.cs
+++ b/MTGOSDK/src/API/Play/Tournaments/Tournament.cs
@@ -18,6 +18,7 @@ public sealed class Tournament(dynamic tournament) : Event
///
/// The internal reference for the binding type for the wrapped object.
///
+ [RuntimeInternal]
internal override Type type => typeof(ITournament);
///
@@ -59,7 +60,9 @@ public sealed class Tournament(dynamic tournament) : Event
/// The time remaining in the current round or tournament phase.
///
public TimeSpan TimeRemaining =>
- Cast(Unbind(@base).TimeRemaining);
+ State == TournamentState.BetweenRounds
+ ? TimeSpan.Zero
+ : Cast(Unbind(@base).TimeRemaining);
///
/// The current round of the tournament.
diff --git a/MTGOSDK/src/Core/Reflection/Emit/Converter.cs b/MTGOSDK/src/Core/Compiler/Converter.cs
similarity index 96%
rename from MTGOSDK/src/Core/Reflection/Emit/Converter.cs
rename to MTGOSDK/src/Core/Compiler/Converter.cs
index 20d44335..ea3ba016 100644
--- a/MTGOSDK/src/Core/Reflection/Emit/Converter.cs
+++ b/MTGOSDK/src/Core/Compiler/Converter.cs
@@ -1,16 +1,16 @@
/** @file
Copyright (c) 2021, Xappy.
Copyright (c) 2024, Cory Bennett. All rights reserved.
- SPDX-License-Identifier: Apache-2.0 and MIT
+ SPDX-License-Identifier: Apache-2.0
**/
using System;
using System.Reflection.Emit;
-using MTGOSDK.Core.Remoting.Interop.Extensions;
+using MTGOSDK.Core.Compiler.Extensions;
-namespace MTGOSDK.Core.Reflection.Emit;
+namespace MTGOSDK.Core.Compiler;
///
/// A class that converts an IntPtr to an object reference.
diff --git a/MTGOSDK/src/Core/Compiler/Extensions/CallerExtensions.cs b/MTGOSDK/src/Core/Compiler/Extensions/CallerExtensions.cs
new file mode 100644
index 00000000..daf03419
--- /dev/null
+++ b/MTGOSDK/src/Core/Compiler/Extensions/CallerExtensions.cs
@@ -0,0 +1,81 @@
+/** @file
+ Copyright (c) 2024, Cory Bennett. All rights reserved.
+ SPDX-License-Identifier: Apache-2.0
+**/
+
+using System.Diagnostics;
+using System.Reflection;
+using System.Runtime.CompilerServices;
+
+using MTGOSDK.Core.Reflection.Attributes;
+using MTGOSDK.Core.Reflection.Extensions;
+
+
+namespace MTGOSDK.Core.Compiler.Extensions;
+
+///
+/// Provides methods for parsing stack frames and tracing callers.
+///
+public static class CallerExtensions
+{
+ ///
+ /// Gets the name of the caller.
+ ///
+ /// The stack frame depth.
+ /// The name of the caller.
+ public static string GetCallerName(int depth) =>
+ new StackFrame(depth).GetMethod().Name
+ .Replace("get_", "")
+ .Replace("set_", "");
+
+ ///
+ /// Gets the parent type of the caller.
+ ///
+ /// The stack frame depth.
+ /// The type of the caller.
+ public static Type GetCallerType(int depth) =>
+ new StackFrame(depth).GetMethod().ReflectedType;
+
+ ///
+ /// Gets the stack frame depth of the caller.
+ ///
+ /// The starting stack frame depth.
+ /// The caller's stack frame depth.
+ public static int GetCallerDepth(int depth = 3)
+ {
+ Type wrapperType = GetCallerType(depth);
+ while(GetCallerType(depth).Name == wrapperType.Name && depth < 50) depth++;
+
+ return depth;
+ }
+
+ ///
+ /// Gets all members of the caller that have a specific attribute.
+ ///
+ private static MemberAttributePair[] GetCallerAttributes(int depth = 2)
+ where T : Attribute =>
+ GetCallerType(depth).GetMemberAttributes();
+
+ ///
+ /// Gets a specific attribute of the caller, if it exists.
+ ///
+ /// The type of attribute.
+ /// The stack frame depth.
+ /// The attribute, or null if it does not exist.
+ public static T? GetCallerAttribute(int depth = 2) where T : Attribute
+ {
+ string name = GetCallerName(depth);
+ try
+ {
+ foreach (var memberAttributePair in GetCallerAttributes(depth+1))
+ {
+ if (memberAttributePair.Member.Name == name)
+ return memberAttributePair.Attribute;
+ }
+ }
+ // Invalid member access (or otherwise doesn't have any attributes)
+ catch (NullReferenceException) { }
+
+ return null;
+ }
+}
diff --git a/MTGOSDK/src/Core/Reflection/Snapshot/ClrExtensions.cs b/MTGOSDK/src/Core/Compiler/Extensions/ClrExtensions.cs
similarity index 59%
rename from MTGOSDK/src/Core/Reflection/Snapshot/ClrExtensions.cs
rename to MTGOSDK/src/Core/Compiler/Extensions/ClrExtensions.cs
index 096fd22f..56228742 100644
--- a/MTGOSDK/src/Core/Reflection/Snapshot/ClrExtensions.cs
+++ b/MTGOSDK/src/Core/Compiler/Extensions/ClrExtensions.cs
@@ -1,7 +1,7 @@
/** @file
Copyright (c) 2021, Xappy.
Copyright (c) 2024, Cory Bennett. All rights reserved.
- SPDX-License-Identifier: Apache-2.0 and MIT
+ SPDX-License-Identifier: Apache-2.0
**/
using System;
@@ -9,17 +9,21 @@
using System.Collections.Generic;
using Microsoft.Diagnostics.Runtime;
+using MTGOSDK.Core.Compiler.Structs;
-namespace MTGOSDK.Core.Reflection.Snapshot;
-public static class ClrExt
-{
- public struct TypeDefToMethod
- {
- public ulong MethodTable { get; set; }
- public int Token { get; set; }
- }
+namespace MTGOSDK.Core.Compiler.Extensions;
+public static class ClrExtensions
+{
+ ///
+ /// Converts a ClrArray to a raw byte array.
+ ///
+ /// The ClrArray to convert.
+ /// The byte array.
+ ///
+ /// Thrown if the ClrArray is not a byte array.
+ ///
public static byte[] ToByteArray(this ClrArray arr)
{
try
@@ -40,11 +44,24 @@ public static byte[] ToByteArray(this ClrArray arr)
return res;
}
+ ///
+ /// Converts a ClrObject to a raw byte array.
+ ///
+ /// The ClrObject to convert.
+ /// The byte array.
+ ///
+ /// Thrown if the ClrObject is not an array.
+ ///
public static byte[] ToByteArray(this ClrObject obj)
{
return obj.AsArray().ToByteArray();
}
+ ///
+ /// Enumerates the TypeDefToMethodTableMap for a ClrModule object.
+ ///
+ /// The ClrModule object.
+ /// An IEnumerable of TypeDefToMethod objects.
public static IEnumerable EnumerateTypeDefToMethodTableMap(this ClrModule mod)
{
// EnumerateTypeDefToMethodTableMap wants to return an IEnumerable<(ulong,int)>
diff --git a/MTGOSDK/src/Core/Remoting/Interop/Extensions/IntPtrExt.cs b/MTGOSDK/src/Core/Compiler/Extensions/IntPtrExtensions.cs
similarity index 59%
rename from MTGOSDK/src/Core/Remoting/Interop/Extensions/IntPtrExt.cs
rename to MTGOSDK/src/Core/Compiler/Extensions/IntPtrExtensions.cs
index bc945c37..ea40b806 100644
--- a/MTGOSDK/src/Core/Remoting/Interop/Extensions/IntPtrExt.cs
+++ b/MTGOSDK/src/Core/Compiler/Extensions/IntPtrExtensions.cs
@@ -1,15 +1,15 @@
/** @file
Copyright (c) 2021, Xappy.
Copyright (c) 2024, Cory Bennett. All rights reserved.
- SPDX-License-Identifier: Apache-2.0 and MIT
+ SPDX-License-Identifier: Apache-2.0
**/
using System.Runtime.InteropServices;
-namespace MTGOSDK.Core.Remoting.Interop.Extensions;
+namespace MTGOSDK.Core.Compiler.Extensions;
-public static class IntPtrExt
+public static class IntPtrExtensions
{
public static IntPtr GetMethodTable(this IntPtr o)
{
@@ -20,7 +20,8 @@ public static IntPtr GetMethodTable(this IntPtr o)
}
catch (Exception e)
{
- throw new AccessViolationException("Failed to read MethodTable at the object's address.", e);
+ throw new AccessViolationException(
+ "Failed to read MethodTable at the object's address.", e);
}
}
}
diff --git a/MTGOSDK/src/Core/Compiler/Extensions/TypeExtensions.cs b/MTGOSDK/src/Core/Compiler/Extensions/TypeExtensions.cs
new file mode 100644
index 00000000..ba49b3c9
--- /dev/null
+++ b/MTGOSDK/src/Core/Compiler/Extensions/TypeExtensions.cs
@@ -0,0 +1,41 @@
+/** @file
+ Copyright (c) 2023, Cory Bennett. All rights reserved.
+ SPDX-License-Identifier: Apache-2.0
+**/
+
+using System.Diagnostics;
+using System.Reflection;
+using System.Runtime.CompilerServices;
+
+
+namespace MTGOSDK.Core.Compiler.Extensions;
+
+public static class TypeExtensions
+{
+ ///
+ /// Determines whether the type is compiler-generated.
+ ///
+ public static bool IsCompilerGenerated(this Type t)
+ {
+ if (t == null) return false;
+
+ return t.IsDefined(typeof(CompilerGeneratedAttribute), false)
+ || IsCompilerGenerated(t.DeclaringType);
+ }
+
+ ///
+ /// Extracts the base type from a compiler-generated type.
+ ///
+ /// The type to extract the base type from.
+ /// The base type of the given type.
+ public static Type GetBaseType(this Type t)
+ {
+ if (!t.IsCompilerGenerated()) return t;
+
+ string fullName = t.FullName;
+ string baseName = fullName.Substring(0, fullName.IndexOf("+<"));
+ Type baseType = t.DeclaringType.Assembly.GetType(baseName);
+
+ return baseType;
+ }
+}
diff --git a/MTGOSDK/src/Core/Reflection/Emit/FreezeFuncsFactory.cs b/MTGOSDK/src/Core/Compiler/FreezeFuncsFactory.cs
similarity index 80%
rename from MTGOSDK/src/Core/Reflection/Emit/FreezeFuncsFactory.cs
rename to MTGOSDK/src/Core/Compiler/FreezeFuncsFactory.cs
index 1aae8439..735f125a 100644
--- a/MTGOSDK/src/Core/Reflection/Emit/FreezeFuncsFactory.cs
+++ b/MTGOSDK/src/Core/Compiler/FreezeFuncsFactory.cs
@@ -1,7 +1,7 @@
/** @file
Copyright (c) 2021, Xappy.
Copyright (c) 2024, Cory Bennett. All rights reserved.
- SPDX-License-Identifier: Apache-2.0 and MIT
+ SPDX-License-Identifier: Apache-2.0
**/
using System;
@@ -10,19 +10,38 @@
using System.Runtime.CompilerServices;
using System.Threading;
+using MTGOSDK.Core.Compiler.Structs;
-namespace MTGOSDK.Core.Reflection.Emit;
+namespace MTGOSDK.Core.Compiler;
+
+///
+/// A factory for generating dynamic freeze functions to pin objects in memory.
+///
public static class FreezeFuncsFactory
{
+ ///
+ /// Represents a dynamic freeze function for pinning objects in memory.
+ ///
public delegate void FreezeFunc(
object[] objects,
ulong[] addresses,
ManualResetEvent frozenFeedback,
ManualResetEvent unfreezeRequested);
+ ///
+ /// A local cache of generated freeze functions for each parameter count.
+ ///
private static Dictionary _dict = new();
+ ///
+ /// Generates a dynamic freeze function with the specified number of arguments.
+ ///
+ /// The number of arguments to freeze.
+ /// A delegate to the generated freeze function.
+ ///
+ /// This method caches the generated freeze functions for each parameter count.
+ ///
public static FreezeFunc Generate(int numArguments)
{
if (!_dict.TryGetValue(numArguments, out FreezeFunc func))
@@ -33,11 +52,16 @@ public static FreezeFunc Generate(int numArguments)
return func;
}
+ ///
+ /// Generates a dynamic freeze function with the specified number of arguments.
+ ///
+ /// The number of arguments to freeze.
+ /// A delegate to the generated freeze function.
private static FreezeFunc GenerateInternal(int numArguments)
{
// Create a dynamic method with the desired number of parameters
var freezeMethod = new DynamicMethod(
- "FreezeInternal_"+numArguments,
+ "FreezeInternal_" + numArguments,
typeof(void),
new[]
{
@@ -92,7 +116,7 @@ private static FreezeFunc GenerateInternal(int numArguments)
typeof(Unsafe).GetMethod("As", new Type[1] { typeof(object) })
.MakeGenericMethod(typeof(Pinnable)));
- // Load addess of fake "Data" field
+ // Load address of fake "Data" field
// `c = &b.Data`
il.Emit(OpCodes.Ldflda, typeof(Pinnable).GetField("Data"));
diff --git a/MTGOSDK/src/Core/Reflection/Emit/FrozenObjectCollection.cs b/MTGOSDK/src/Core/Compiler/FrozenObjectCollection.cs
similarity index 73%
rename from MTGOSDK/src/Core/Reflection/Emit/FrozenObjectCollection.cs
rename to MTGOSDK/src/Core/Compiler/FrozenObjectCollection.cs
index e29c89e7..a4db5103 100644
--- a/MTGOSDK/src/Core/Reflection/Emit/FrozenObjectCollection.cs
+++ b/MTGOSDK/src/Core/Compiler/FrozenObjectCollection.cs
@@ -1,7 +1,7 @@
/** @file
Copyright (c) 2021, Xappy.
Copyright (c) 2024, Cory Bennett. All rights reserved.
- SPDX-License-Identifier: Apache-2.0 and MIT
+ SPDX-License-Identifier: Apache-2.0
**/
using System.Collections.Generic;
@@ -10,8 +10,17 @@
using System.Threading.Tasks;
-namespace MTGOSDK.Core.Reflection.Emit;
+namespace MTGOSDK.Core.Compiler;
+///
+/// A class that manages a collection of frozen (pinned) objects.
+///
+///
+/// This class pins objects in memory and keeps track of their addresses as
+/// they are pinned. This is useful for preventing the garbage collector from
+/// moving objects in memory or for preserving pointers or references to objects
+/// that are passed to unmanaged code.
+///
public class FrozenObjectsCollection
{
private object _lock = new object();
@@ -31,6 +40,9 @@ public bool TryGetPinningAddress(object o, out ulong addr)
}
}
+ ///
+ /// Pins a collection of objects.
+ ///
private void PinInternal(object[] newfrozenObjects)
{
lock (_lock)
@@ -70,23 +82,33 @@ private void PinInternal(object[] newfrozenObjects)
}
}
+ ///
+ /// Pins an object and returns the address where it is pinned.
+ ///
+ /// The address where the object is pinned.
+ ///
+ /// If an object is already pinned, this method will simply return the address
+ /// where the object is pinned.
+ ///
public ulong Pin(object o)
{
lock (_lock)
{
- if (_frozenObjects.TryGetValue(o, out ulong addr))
- return addr;
+ // If the object is already pinned, return it's address.
+ if (_frozenObjects.TryGetValue(o, out ulong addr)) return addr;
// Prepare parameters
object[] objs = _frozenObjects.Keys.Concat(new object[] { o }).ToArray();
PinInternal(objs);
- // Logger.Debug($"[{nameof(FrozenObjectsCollection)}] Pinned another object. Num Pinned: {_frozenObjects.Count}");
-
return _frozenObjects[o];
}
}
+ ///
+ /// Tries to get the object that is pinned at the given address.
+ ///
+ /// True if the object was found, false if not.
public bool TryGetPinnedObject(ulong addr, out object? o)
{
lock (_lock)
@@ -106,7 +128,7 @@ public bool TryGetPinnedObject(ulong addr, out object? o)
}
///
- /// Unpins an object
+ /// Unpins an object from the collection.
///
/// True if it was pinned, false if not.
public bool Unpin(ulong objAddress)
@@ -132,6 +154,9 @@ public bool Unpin(ulong objAddress)
}
}
+ ///
+ /// Unpins all objects that are currently pinned.
+ ///
public void UnpinAll()
{
lock (_lock)
diff --git a/MTGOSDK/src/Core/Reflection/InstanceFactory.cs b/MTGOSDK/src/Core/Compiler/InstanceFactory.cs
similarity index 86%
rename from MTGOSDK/src/Core/Reflection/InstanceFactory.cs
rename to MTGOSDK/src/Core/Compiler/InstanceFactory.cs
index 988bbb50..cda58bfe 100644
--- a/MTGOSDK/src/Core/Reflection/InstanceFactory.cs
+++ b/MTGOSDK/src/Core/Compiler/InstanceFactory.cs
@@ -6,8 +6,10 @@
using System.Collections.Concurrent;
using System.Linq.Expressions;
+using MTGOSDK.Core.Reflection.Types;
-namespace MTGOSDK.Core.Reflection;
+
+namespace MTGOSDK.Core.Compiler;
///
/// A factory for creating instances of types with up to three constructor arguments.
@@ -29,17 +31,17 @@ object arg3
public static object CreateInstance(Type type)
{
- return InstanceFactoryGeneric.CreateInstance(type, null, null, null);
+ return InstanceFactoryGeneric.CreateInstance(type, null, null, null);
}
public static object CreateInstance(Type type, TArg1 arg1)
{
- return InstanceFactoryGeneric.CreateInstance(type, arg1, null, null);
+ return InstanceFactoryGeneric.CreateInstance(type, arg1, null, null);
}
public static object CreateInstance(Type type, TArg1 arg1, TArg2 arg2)
{
- return InstanceFactoryGeneric.CreateInstance(type, arg1, arg2, null);
+ return InstanceFactoryGeneric.CreateInstance(type, arg1, arg2, null);
}
public static object CreateInstance(Type type, TArg1 arg1, TArg2 arg2, TArg3 arg3)
@@ -66,9 +68,9 @@ public static object CreateInstance(Type type, params object[] args)
var key = Tuple.Create(
type,
- arg0?.GetType() ?? typeof(TypeToIgnore),
- arg1?.GetType() ?? typeof(TypeToIgnore),
- arg2?.GetType() ?? typeof(TypeToIgnore));
+ arg0?.GetType() ?? typeof(TypeStub),
+ arg1?.GetType() ?? typeof(TypeStub),
+ arg2?.GetType() ?? typeof(TypeStub));
if (cachedFuncs.TryGetValue(key, out CreateDelegate func))
return func(type, arg0, arg1, arg2);
@@ -123,11 +125,11 @@ private static Func CacheFunc(
TArg3 arg3)
{
var constructorTypes = new List();
- if (typeof(TArg1) != typeof(TypeToIgnore))
+ if (typeof(TArg1) != typeof(TypeStub))
constructorTypes.Add(typeof(TArg1));
- if (typeof(TArg2) != typeof(TypeToIgnore))
+ if (typeof(TArg2) != typeof(TypeStub))
constructorTypes.Add(typeof(TArg2));
- if (typeof(TArg3) != typeof(TypeToIgnore))
+ if (typeof(TArg3) != typeof(TypeStub))
constructorTypes.Add(typeof(TArg3));
var parameters = new List()
@@ -147,5 +149,3 @@ private static Func CacheFunc(
return func;
}
}
-
-public class TypeToIgnore { }
diff --git a/MTGOSDK/src/Core/Reflection/Snapshot/SnapshotRuntime.cs b/MTGOSDK/src/Core/Compiler/Snapshot/SnapshotRuntime.cs
similarity index 97%
rename from MTGOSDK/src/Core/Reflection/Snapshot/SnapshotRuntime.cs
rename to MTGOSDK/src/Core/Compiler/Snapshot/SnapshotRuntime.cs
index 48bf5131..eb3a7fe1 100644
--- a/MTGOSDK/src/Core/Reflection/Snapshot/SnapshotRuntime.cs
+++ b/MTGOSDK/src/Core/Compiler/Snapshot/SnapshotRuntime.cs
@@ -1,7 +1,7 @@
/** @file
Copyright (c) 2021, Xappy.
Copyright (c) 2024, Cory Bennett. All rights reserved.
- SPDX-License-Identifier: Apache-2.0 and MIT
+ SPDX-License-Identifier: Apache-2.0
**/
using System;
@@ -13,15 +13,15 @@
using System.Reflection;
using Microsoft.Diagnostics.Runtime;
-using MTGOSDK.Core.Reflection.Emit;
+using MTGOSDK.Core.Compiler;
+using MTGOSDK.Core.Compiler.Extensions;
using MTGOSDK.Core.Remoting.Interop;
using MTGOSDK.Core.Remoting.Interop.Interactions.Dumps;
-using MTGOSDK.Core.Remoting.Interop.Utils;
using MTGOSDK.Win32.API;
-namespace MTGOSDK.Core.Reflection.Snapshot;
+namespace MTGOSDK.Core.Compiler.Snapshot;
///
/// The snapshot runtime used to interact with the ClrMD runtime and snapshot
@@ -31,6 +31,8 @@ public class SnapshotRuntime : IDisposable
{
internal static readonly object _clrMdLock = new(); // static
public virtual object clrLock => SnapshotRuntime._clrMdLock; // non-static
+
+ // Internal ClrMD runtime and data target objects to manage the snapshot.
private DataTarget _dt;
private ClrRuntime _runtime;
@@ -142,10 +144,10 @@ public bool UnpinObject(ulong objAddress)
// IL.Emit runtime converter methods
//
- public object DereferenceObject(IntPtr pObj, IntPtr expectedMethodTable) =>
+ public object Compile(IntPtr pObj, IntPtr expectedMethodTable) =>
_converter.ConvertFromIntPtr(pObj, expectedMethodTable);
- public object DereferenceObject(ulong pObj, ulong expectedMethodTable) =>
+ public object Compile(ulong pObj, ulong expectedMethodTable) =>
_converter.ConvertFromIntPtr(pObj, expectedMethodTable);
//
@@ -414,7 +416,7 @@ public ImmutableArray GetClrAppDomains()
object instance;
try
{
- instance = DereferenceObject(finalObjAddress, methodTable);
+ instance = Compile(finalObjAddress, methodTable);
}
catch (ArgumentException)
{
@@ -505,7 +507,7 @@ public ImmutableArray GetClrAppDomains()
object instance = null;
try
{
- instance = DereferenceObject(clrObj.Address, mt);
+ instance = Compile(clrObj.Address, mt);
}
catch (Exception)
{
diff --git a/MTGOSDK/src/Core/Reflection/Snapshot/UnifiedAppDomain.cs b/MTGOSDK/src/Core/Compiler/Snapshot/UnifiedAppDomain.cs
similarity index 94%
rename from MTGOSDK/src/Core/Reflection/Snapshot/UnifiedAppDomain.cs
rename to MTGOSDK/src/Core/Compiler/Snapshot/UnifiedAppDomain.cs
index 3dad044c..cdc72d38 100644
--- a/MTGOSDK/src/Core/Reflection/Snapshot/UnifiedAppDomain.cs
+++ b/MTGOSDK/src/Core/Compiler/Snapshot/UnifiedAppDomain.cs
@@ -1,7 +1,7 @@
/** @file
Copyright (c) 2021, Xappy.
Copyright (c) 2024, Cory Bennett. All rights reserved.
- SPDX-License-Identifier: Apache-2.0 and MIT
+ SPDX-License-Identifier: Apache-2.0
**/
using System;
@@ -12,7 +12,7 @@
using MTGOSDK.Core.Remoting.Interop.Interactions.Dumps;
-namespace MTGOSDK.Core.Reflection.Snapshot;
+namespace MTGOSDK.Core.Compiler.Snapshot;
///
/// Encapsulates access to all AppDomains in the process
@@ -84,9 +84,9 @@ public Type ResolveType(string typeFullName, string assemblyName = null)
typeFullName = $"{nonGenericPart}`{numOfParams}";
}
- foreach (Assembly assm in _domains.SelectMany(d => d.GetAssemblies()))
+ foreach (Assembly asm in _domains.SelectMany(d => d.GetAssemblies()))
{
- Type t = assm.GetType(typeFullName, throwOnError: false);
+ Type t = asm.GetType(typeFullName, throwOnError: false);
if (t != null)
return t;
}
diff --git a/MTGOSDK/src/Core/Reflection/Emit/Pinnable.cs b/MTGOSDK/src/Core/Compiler/Structs/Pinnable.cs
similarity index 79%
rename from MTGOSDK/src/Core/Reflection/Emit/Pinnable.cs
rename to MTGOSDK/src/Core/Compiler/Structs/Pinnable.cs
index 5516c1cf..fe5766b1 100644
--- a/MTGOSDK/src/Core/Reflection/Emit/Pinnable.cs
+++ b/MTGOSDK/src/Core/Compiler/Structs/Pinnable.cs
@@ -1,13 +1,13 @@
/** @file
Copyright (c) 2021, Xappy.
Copyright (c) 2024, Cory Bennett. All rights reserved.
- SPDX-License-Identifier: Apache-2.0 and MIT
+ SPDX-License-Identifier: Apache-2.0
**/
using System.Runtime.InteropServices;
-namespace MTGOSDK.Core.Reflection.Emit;
+namespace MTGOSDK.Core.Compiler.Structs;
///
/// This class is used to make arbitrary objects "Pinnable" in the .NET
@@ -15,7 +15,7 @@ namespace MTGOSDK.Core.Reflection.Emit;
/// first field's address overlaps with this class's field.
///
[StructLayout(LayoutKind.Sequential)]
-internal sealed class Pinnable
+public sealed class Pinnable
{
public byte Data;
}
diff --git a/MTGOSDK/src/Core/Compiler/Structs/TypeDefToMethod.cs b/MTGOSDK/src/Core/Compiler/Structs/TypeDefToMethod.cs
new file mode 100644
index 00000000..5ee3ff53
--- /dev/null
+++ b/MTGOSDK/src/Core/Compiler/Structs/TypeDefToMethod.cs
@@ -0,0 +1,14 @@
+/** @file
+ Copyright (c) 2021, Xappy.
+ Copyright (c) 2024, Cory Bennett. All rights reserved.
+ SPDX-License-Identifier: Apache-2.0
+**/
+
+
+namespace MTGOSDK.Core.Compiler.Structs;
+
+public struct TypeDefToMethod
+{
+ public ulong MethodTable { get; set; }
+ public int Token { get; set; }
+}
diff --git a/MTGOSDK/src/Core/DiagnosticOptions.cs b/MTGOSDK/src/Core/DiagnosticOptions.cs
new file mode 100644
index 00000000..87e394bb
--- /dev/null
+++ b/MTGOSDK/src/Core/DiagnosticOptions.cs
@@ -0,0 +1,69 @@
+/** @file
+ Copyright (c) 2024, Cory Bennett. All rights reserved.
+ SPDX-License-Identifier: Apache-2.0
+**/
+
+
+namespace MTGOSDK.Core;
+
+///
+/// Configurable options for diagnostic features throughout the SDK.
+///
+public struct DiagnosticOptions()
+{
+ ///
+ /// Whether to enable logging throughout the SDK.
+ ///
+ ///
+ /// By default, the SDK will use a NullLogger which will not log to any
+ /// output. To enable logging, provide a logger factory through the
+ /// method or
+ /// through the constructor.
+ ///
+ ///
+ /// true to enable logging, false otherwise.
+ ///
+ public bool EnableLogging { get; init; } = true;
+
+ ///
+ /// Whether to enable performance metrics throughout the SDK.
+ ///
+ ///
+ /// Enabling this option will allow the SDK to collect performance metrics
+ /// throughout its execution. These metrics can be useful for identifying
+ /// performance bottlenecks and other regression issues between client and
+ /// SDK versions.
+ ///
+ ///
+ /// true to enable performance metrics, false otherwise.
+ ///
+ public bool EnablePerformanceMetrics { get; init; } = false;
+
+ ///
+ /// Whether to enable snapshot debugging throughout the SDK.
+ ///
+ ///
+ /// As the SDK takes continuous snapshots of the MTGO client, enabling this
+ /// option will reserve the past few snapshots to inspect previous states of
+ /// the client. This is managed through the
+ /// class to
+ /// provide several copy-on-write snapshots of the client's state.
+ /// Enabling snapshots can be useful for debugging behaviors intricately
+ /// dependent on the client's state that are not easily reproducible.
+ ///
+ ///
+ /// true to enable snapshot debugging, false otherwise.
+ ///
+ public bool EnableSnapshotDebugging { get; init; } = false;
+
+ ///
+ /// The interval at which to take snapshots of the client.
+ ///
+ ///
+ /// This option is only relevant if is
+ /// set to true. The SDK will take a snapshot of the client at the
+ /// specified interval, allowing for a history of the client's state to be
+ /// inspected.
+ ///
+ public TimeSpan SnapshotInterval { get; init; } = TimeSpan.FromSeconds(30);
+}
diff --git a/MTGOSDK/src/Core/Remoting/Interop/Exceptions/RemoteException.cs b/MTGOSDK/src/Core/Exceptions/RemoteException.cs
similarity index 86%
rename from MTGOSDK/src/Core/Remoting/Interop/Exceptions/RemoteException.cs
rename to MTGOSDK/src/Core/Exceptions/RemoteException.cs
index b530b5d9..c8cff412 100644
--- a/MTGOSDK/src/Core/Remoting/Interop/Exceptions/RemoteException.cs
+++ b/MTGOSDK/src/Core/Exceptions/RemoteException.cs
@@ -1,11 +1,11 @@
/** @file
Copyright (c) 2021, Xappy.
Copyright (c) 2024, Cory Bennett. All rights reserved.
- SPDX-License-Identifier: Apache-2.0 and MIT
+ SPDX-License-Identifier: Apache-2.0
**/
-namespace MTGOSDK.Core.Remoting.Interop.Exceptions;
+namespace MTGOSDK.Core.Exceptions;
///
/// Encapsulates an exception that was thrown in the remote object and catched by the Diver.
diff --git a/MTGOSDK/src/Core/Exceptions/RemoteObjectMovedException.cs b/MTGOSDK/src/Core/Exceptions/RemoteObjectMovedException.cs
new file mode 100644
index 00000000..b682b984
--- /dev/null
+++ b/MTGOSDK/src/Core/Exceptions/RemoteObjectMovedException.cs
@@ -0,0 +1,14 @@
+/** @file
+ Copyright (c) 2021, Xappy.
+ Copyright (c) 2024, Cory Bennett. All rights reserved.
+ SPDX-License-Identifier: Apache-2.0
+**/
+
+
+namespace MTGOSDK.Core.Exceptions;
+
+public class RemoteObjectMovedException(ulong testedAddress, string msg)
+ : Exception(msg)
+{
+ public ulong TestedAddress { get; private set; } = testedAddress;
+}
diff --git a/MTGOSDK/src/Core/Exceptions/SetupFailedException.cs b/MTGOSDK/src/Core/Exceptions/SetupFailureException.cs
similarity index 59%
rename from MTGOSDK/src/Core/Exceptions/SetupFailedException.cs
rename to MTGOSDK/src/Core/Exceptions/SetupFailureException.cs
index 05b3e0f8..7e8db676 100644
--- a/MTGOSDK/src/Core/Exceptions/SetupFailedException.cs
+++ b/MTGOSDK/src/Core/Exceptions/SetupFailureException.cs
@@ -9,13 +9,13 @@ namespace MTGOSDK.Core.Exceptions;
///
/// Exception thrown when the setup of the SDK fails.
///
-public class SetupFailedException : Exception
+public class SetupFailureException : Exception
{
- public SetupFailedException() { }
+ public SetupFailureException() { }
- public SetupFailedException(string message)
+ public SetupFailureException(string message)
: base(message) { }
- public SetupFailedException(string message, Exception inner)
+ public SetupFailureException(string message, Exception inner)
: base(message, inner) { }
}
diff --git a/MTGOSDK/src/Core/GlobalUsings.cs b/MTGOSDK/src/Core/GlobalUsings.cs
new file mode 100644
index 00000000..cb7781b7
--- /dev/null
+++ b/MTGOSDK/src/Core/GlobalUsings.cs
@@ -0,0 +1,7 @@
+/** @file
+ Copyright (c) 2024, Cory Bennett. All rights reserved.
+ SPDX-License-Identifier: Apache-2.0
+**/
+
+global using static MTGOSDK.Core.Compiler.Extensions.CallerExtensions;
+global using MTGOSDK.Core.Reflection.Attributes;
diff --git a/MTGOSDK/src/Core/Logging/Log.cs b/MTGOSDK/src/Core/Logging/Log.cs
index 9e72a354..52faf289 100644
--- a/MTGOSDK/src/Core/Logging/Log.cs
+++ b/MTGOSDK/src/Core/Logging/Log.cs
@@ -14,35 +14,10 @@ namespace MTGOSDK.Core.Logging;
///
public class Log : LoggerBase
{
- private class SuppressionContext : IDisposable
- {
- private static Type baseType;
-
- public SuppressionContext(Type callerType, LogLevel logLevel)
- {
- baseType = GetBaseType(callerType);
- s_suppressedCallerTypes.TryAdd(baseType, logLevel);
- Debug("Suppressed logging for {type} below loglevel {level}", baseType, logLevel);
- }
-
- public void Dispose()
- {
- s_suppressedCallerTypes.TryRemove(baseType, out _);
- }
- }
-
///
/// Suppresses all logging in the current context.
///
- public static IDisposable Suppress()
- {
- Type callerType;
- int depth = 2;
- do { callerType = GetCallerType(depth); depth++; }
- while (callerType.FullName.StartsWith("System.") ||
- callerType.FullName.StartsWith("MTGOSDK.Core.Logging."));
- return new SuppressionContext(callerType, LogLevel.None);
- }
+ public static IDisposable Suppress() => new SuppressionContext(LogLevel.None);
//
// ILogger Extensions
diff --git a/MTGOSDK/src/Core/Logging/LogOptionsProvider.cs b/MTGOSDK/src/Core/Logging/LogOptionsProvider.cs
index 9f0ce1d8..c4a8843d 100644
--- a/MTGOSDK/src/Core/Logging/LogOptionsProvider.cs
+++ b/MTGOSDK/src/Core/Logging/LogOptionsProvider.cs
@@ -9,7 +9,7 @@
using Microsoft.Extensions.Logging;
using Microsoft.Extensions.Options;
-using MTGOSDK.Core.Reflection;
+using MTGOSDK.Core.Compiler;
namespace MTGOSDK.Core.Logging;
diff --git a/MTGOSDK/src/Core/Logging/LoggerBase.cs b/MTGOSDK/src/Core/Logging/LoggerBase.cs
index cab3868d..7004e276 100644
--- a/MTGOSDK/src/Core/Logging/LoggerBase.cs
+++ b/MTGOSDK/src/Core/Logging/LoggerBase.cs
@@ -8,6 +8,7 @@
using Microsoft.Extensions.Logging;
using Microsoft.Extensions.Logging.Abstractions;
+using MTGOSDK.Core.Compiler.Extensions;
using MTGOSDK.Core.Reflection;
@@ -47,11 +48,6 @@ public class LoggerBase : DLRWrapper, ILogger
///
private static readonly ConcurrentDictionary s_callerTypes = new();
- ///
- /// A concurrent bag of caller types that have been suppressed from logging.
- ///
- internal static readonly ConcurrentDictionary s_suppressedCallerTypes = new();
-
///
/// Represents a type used to perform logging.
///
@@ -62,7 +58,8 @@ internal static ILogger s_logger
{
// If logging is suppressed and the caller type is a suppressed type,
// return a null logger to prevent logging from suppressed sources.
- if (IsSuppressedCallerType()) return s_nulllogger;
+ if (SuppressionContext.IsSuppressedCallerType())
+ return s_nulllogger;
// Get the caller type of the calling method or class.
Type callerType;
@@ -73,10 +70,10 @@ internal static ILogger s_logger
// Fetch the base type if the caller is a compiler-generated type
// (e.g. lambda expressions, async state machines, etc.).
- if (!s_loggers.ContainsKey(callerType) && IsCompilerGenerated(callerType))
+ if (!s_loggers.ContainsKey(callerType) && callerType.IsCompilerGenerated())
{
if (!s_callerTypes.TryGetValue(callerType, out Type? baseType))
- baseType = GetBaseType(callerType);
+ baseType = callerType.GetBaseType();
callerType = baseType;
}
@@ -85,27 +82,6 @@ internal static ILogger s_logger
}
}
- public static bool IsSuppressedCallerType(int depth = 3)
- {
- if (s_suppressedCallerTypes.Count == 0)
- return false;
-
- // Search the entire call stack for a suppressed caller type.
- try
- {
- for (int i = depth; i < 50; i++)
- {
- Type callerType = GetCallerType(i);
- Type baseType = GetBaseType(callerType);
- if (s_suppressedCallerTypes.ContainsKey(callerType))
- return true;
- }
- }
- catch { /* Have fetched past the current depth of the call stack. */ }
-
- return false;
- }
-
///
/// Creates a logger instance for the given caller type.
///
diff --git a/MTGOSDK/src/Core/Logging/SuppressionContext.cs b/MTGOSDK/src/Core/Logging/SuppressionContext.cs
new file mode 100644
index 00000000..5c68679b
--- /dev/null
+++ b/MTGOSDK/src/Core/Logging/SuppressionContext.cs
@@ -0,0 +1,75 @@
+/** @file
+ Copyright (c) 2024, Cory Bennett. All rights reserved.
+ SPDX-License-Identifier: Apache-2.0
+**/
+
+using System;
+using System.Collections.Concurrent;
+using Microsoft.Extensions.Logging;
+
+
+using MTGOSDK.Core.Compiler.Extensions;
+
+
+namespace MTGOSDK.Core.Logging;
+
+///
+/// A class that suppresses logging for a specific caller type.
+///
+///
+/// This class creates a context that suppresses all logging invoked from the
+/// caller type (and all functions called by the caller type) until the context
+/// is disposed. This is useful for suppressing logging for a specific caller
+/// type without having to modify the logging configuration or log level.
+///
+public class SuppressionContext : IDisposable
+{
+ private static Type baseType;
+
+ ///
+ /// A concurrent bag of caller types that have been suppressed from logging.
+ ///
+ private static readonly ConcurrentDictionary s_suppressedCallerTypes = new();
+
+ public SuppressionContext(LogLevel logLevel = LogLevel.None)
+ {
+ // Get the type of the original caller creating the suppression context.
+ Type callerType;
+ int depth = 3;
+ do { callerType = GetCallerType(depth); depth++; }
+ while (callerType.FullName.StartsWith("System.") ||
+ callerType.FullName.StartsWith("MTGOSDK.Core.Logging."));
+
+ baseType = callerType.GetBaseType();
+ s_suppressedCallerTypes.TryAdd(baseType, logLevel);
+ Log.Debug("Suppressed logging for {type} below loglevel {level}", baseType, logLevel);
+ }
+
+ public static bool IsSuppressedCallerType()
+ {
+ // If there are no suppressed caller types, return false.
+ if (s_suppressedCallerTypes.Count == 0)
+ return false;
+
+ // Search the entire call stack for a suppressed caller type.
+ try
+ {
+ int depth = 3; // Default stack depth to begin searching.
+ for (int i = depth; i < 50; i++)
+ {
+ Type callerType = GetCallerType(i);
+ Type baseType = callerType.GetBaseType();
+ if (s_suppressedCallerTypes.ContainsKey(callerType))
+ return true;
+ }
+ }
+ catch { /* Have fetched past the current depth of the call stack. */ }
+
+ return false;
+ }
+
+ public void Dispose()
+ {
+ s_suppressedCallerTypes.TryRemove(baseType, out _);
+ }
+}
diff --git a/MTGOSDK/src/Core/Reflection/Attributes.cs b/MTGOSDK/src/Core/Reflection/Attributes.cs
deleted file mode 100644
index 59c10b82..00000000
--- a/MTGOSDK/src/Core/Reflection/Attributes.cs
+++ /dev/null
@@ -1,103 +0,0 @@
-/** @file
- Copyright (c) 2023, Cory Bennett. All rights reserved.
- SPDX-License-Identifier: Apache-2.0
-**/
-
-using System.Diagnostics;
-using System.Reflection;
-
-
-namespace MTGOSDK.Core.Reflection;
-
-public static class Attributes
-{
- //
- // Member Attribute Reflection
- //
-
- ///
- /// A struct that contains a member and its attribute.
- ///
- /// The type of attribute.
- public struct MemberAttributePair() where T : Attribute
- {
- public MemberInfo Member { get; init; }
- public T Attribute { get; init; }
- }
-
- ///
- /// Gets all members of a type that have a specific attribute.
- ///
- /// The type of attribute.
- /// The type to get members from.
- /// The binding flags to use.
- /// An array of member attribute pairs.
- public static MemberAttributePair[] GetMemberAttributes(
- this Type type,
- BindingFlags bindingFlags = BindingFlags.Public
- | BindingFlags.NonPublic
- | BindingFlags.Instance
- | BindingFlags.Static
- | BindingFlags.FlattenHierarchy
- ) where T : Attribute =>
- type.GetMembers(bindingFlags)
- .Where(p => p.IsDefined(typeof(T), false))
- .Select(p => new MemberAttributePair()
- {
- Member = p,
-#pragma warning disable CS8601 // Possible null reference argument.
- Attribute = p.GetCustomAttributes(typeof(T), false).Single() as T
-#pragma warning restore CS8601
- })
- .ToArray();
-
- //
- // StackFrame Property Reflection
- //
-
- ///
- /// Gets the name of the caller.
- ///
- /// The stack frame depth.
- /// The name of the caller.
- public static string GetCallerName(int depth) =>
- new StackFrame(depth).GetMethod().Name
- .Replace("get_", "")
- .Replace("set_", "");
-
- ///
- /// Gets the parent type of the caller.
- ///
- /// The stack frame depth.
- /// The type of the caller.
- public static Type GetCallerType(int depth) =>
- new StackFrame(depth).GetMethod().ReflectedType;
-
- public static MemberAttributePair[] GetMemberAttributes(
- int depth = 2
- ) where T : Attribute =>
- GetCallerType(depth).GetMemberAttributes();
-
- ///
- /// Gets a specific attribute of the caller, if it exists.
- ///
- /// The type of attribute.
- /// The stack frame depth.
- /// The attribute, or null if it does not exist.
- public static T? GetCallerAttribute(int depth = 2) where T : Attribute
- {
- string name = GetCallerName(depth);
- try
- {
- foreach (var memberAttributePair in GetMemberAttributes(depth+1))
- {
- if (memberAttributePair.Member.Name == name)
- return memberAttributePair.Attribute;
- }
- }
- // Invalid member access (or otherwise doesn't have any attributes)
- catch (NullReferenceException) { }
-
- return null;
- }
-}
diff --git a/MTGOSDK/src/Core/Reflection/Attributes/CallerAttribute.cs b/MTGOSDK/src/Core/Reflection/Attributes/CallerAttribute.cs
new file mode 100644
index 00000000..cc1f39f2
--- /dev/null
+++ b/MTGOSDK/src/Core/Reflection/Attributes/CallerAttribute.cs
@@ -0,0 +1,27 @@
+/** @file
+ Copyright (c) 2024, Cory Bennett. All rights reserved.
+ SPDX-License-Identifier: Apache-2.0
+**/
+
+using System.Runtime.CompilerServices;
+
+
+namespace MTGOSDK.Core.Reflection.Attributes;
+
+///
+/// A wrapper attribute that allows for a default value to fallback to.
+///
+/// The default value.
+public class CallerAttribute : Attribute where T : Attribute
+{
+ ///
+ /// Attempts to get the caller attribute from the outer caller.
+ ///
+ /// The caller attribute (if present).
+ /// True if the caller attribute was found.
+ public static bool TryGetCallerAttribute(out T? attribute)
+ {
+ attribute = GetCallerAttribute(depth: GetCallerDepth());
+ return attribute != null;
+ }
+}
diff --git a/MTGOSDK/src/Core/Reflection/Attributes/DefaultAttribute.cs b/MTGOSDK/src/Core/Reflection/Attributes/DefaultAttribute.cs
new file mode 100644
index 00000000..2a1645d1
--- /dev/null
+++ b/MTGOSDK/src/Core/Reflection/Attributes/DefaultAttribute.cs
@@ -0,0 +1,15 @@
+/** @file
+ Copyright (c) 2024, Cory Bennett. All rights reserved.
+ SPDX-License-Identifier: Apache-2.0
+**/
+
+
+namespace MTGOSDK.Core.Reflection.Attributes;
+///
+/// A wrapper attribute that allows for a default value to fallback to.
+///
+/// The default value.
+public class DefaultAttribute(object value) : CallerAttribute
+{
+ public readonly object Value = value;
+}
diff --git a/MTGOSDK/src/Core/Reflection/Attributes/MemberAttributePair.cs b/MTGOSDK/src/Core/Reflection/Attributes/MemberAttributePair.cs
new file mode 100644
index 00000000..abf6d171
--- /dev/null
+++ b/MTGOSDK/src/Core/Reflection/Attributes/MemberAttributePair.cs
@@ -0,0 +1,20 @@
+/** @file
+ Copyright (c) 2023, Cory Bennett. All rights reserved.
+ SPDX-License-Identifier: Apache-2.0
+**/
+
+using System.Diagnostics;
+using System.Reflection;
+
+
+namespace MTGOSDK.Core.Reflection.Attributes;
+
+///
+/// A struct that contains a member and its attribute.
+///
+/// The type of attribute.
+public struct MemberAttributePair() where T : Attribute
+{
+ public MemberInfo Member { get; init; }
+ public T Attribute { get; init; }
+}
diff --git a/MTGOSDK/src/Core/Reflection/Attributes/RuntimeInternalAttribute.cs b/MTGOSDK/src/Core/Reflection/Attributes/RuntimeInternalAttribute.cs
new file mode 100644
index 00000000..f00055b1
--- /dev/null
+++ b/MTGOSDK/src/Core/Reflection/Attributes/RuntimeInternalAttribute.cs
@@ -0,0 +1,17 @@
+/** @file
+ Copyright (c) 2024, Cory Bennett. All rights reserved.
+ SPDX-License-Identifier: Apache-2.0
+**/
+
+
+namespace MTGOSDK.Core.Reflection.Attributes;
+
+///
+/// A wrapper attribute that indicates that a field is internal to the runtime.
+///
+/// The default value.
+public class RuntimeInternalAttribute(Type? baseType = null)
+ : CallerAttribute
+{
+ public readonly Type BaseType = baseType ?? typeof(object);
+}
diff --git a/MTGOSDK/src/Core/Reflection/DLRWrapper.cs b/MTGOSDK/src/Core/Reflection/DLRWrapper.cs
index 7a637160..34c82850 100644
--- a/MTGOSDK/src/Core/Reflection/DLRWrapper.cs
+++ b/MTGOSDK/src/Core/Reflection/DLRWrapper.cs
@@ -9,12 +9,15 @@
using System.Reflection;
using System.Runtime.CompilerServices;
+using MTGOSDK.Core.Compiler;
+using MTGOSDK.Core.Compiler.Extensions;
using MTGOSDK.Core.Logging;
+using MTGOSDK.Core.Reflection.Attributes;
+using MTGOSDK.Core.Reflection.Extensions;
using MTGOSDK.Core.Remoting;
namespace MTGOSDK.Core.Reflection;
-using static Attributes;
///
/// A wrapper for dynamic objects that implement an interface at runtime.
@@ -61,6 +64,10 @@ public DLRWrapper(Func? factory = null) : this()
if (factory != null) factory.Invoke().Wait();
}
+ //
+ // Internal fields and properties for the wrapped object.
+ //
+
///
/// The internal reference for the binding type for the wrapped object.
///
@@ -94,10 +101,10 @@ internal virtual dynamic @base
// Attempt to extract the base object from the derived class.
var baseObj = Try(() => obj is DLRWrapper ? obj.obj : obj);
- // Return a ProxyObject wrapper with a default value, if present.
- if (TryGetDefaultAttribute(out var defaultAttribute))
+ // Return a DynamicProxy wrapper with a default value, if present.
+ if (DefaultAttribute.TryGetCallerAttribute(out var defaultAttribute))
{
- return new ProxyObject(baseObj, @default: defaultAttribute.Value);
+ return new DynamicProxy(baseObj, @default: defaultAttribute.Value);
}
else if (baseObj is null)
{
@@ -109,43 +116,6 @@ internal virtual dynamic @base
}
}
- ///
- /// Marks the retrieval of a DLRWrapper's instance as optional.
- ///
- /// The class type to instantiate.
- /// The object to wrap.
- /// The condition to check before wrapping.
- /// The wrapped object or null if the object is null.
- public static T? Optional(
- dynamic obj,
- Func condition = null) where T : class
- {
- // Return null if the condition is not met
- if (condition != null && !condition(obj))
- return null;
-
- // Return null if the underlying object is null
- if (Try(() => (obj == null || Unbind(obj) == null)))
- return null;
-
- if (typeof(T).IsSubclassOf(typeof(DLRWrapper)))
- return (T)(InstanceFactory.CreateInstance(typeof(T), obj));
- else
- return Cast(obj);
- }
-
- ///
- /// Marks the retrieval of a DLRWrapper's instance as optional.
- ///
- /// The class type to instantiate.
- /// The object to wrap.
- /// The condition to check before wrapping.
- /// The wrapped object or null if the object is null.
- public static T? Optional(dynamic obj, bool condition) where T : class
- {
- return Optional(obj, new Func(_ => condition));
- }
-
//
// Wrapper methods for type casting and dynamic dispatching.
//
@@ -161,7 +131,7 @@ internal virtual dynamic @base
///
public static T Bind(dynamic obj) where T : class
{
- return Proxy.As(obj)
+ return TypeProxy.As(obj)
?? throw new InvalidOperationException(
$"Unable to bind {obj.GetType().Name} to {typeof(T).Name}.");
}
@@ -176,15 +146,16 @@ public static T Bind(dynamic obj) where T : class
///
public static dynamic Unbind(dynamic obj)
{
- Func isProxy = (o) => o.GetType().Name.StartsWith("ActLike_");
- if (!isProxy(obj)) return obj;
+ if (!TypeProxy.IsProxy(obj))
+ return obj;
- var unbound_obj = Proxy.From(obj)
+ var unbound_obj = TypeProxy.From(obj)
?? throw new InvalidOperationException(
$"Unable to unbind types from {obj.GetType().Name}.");
// Recursively unbind any nested interface types.
- if (isProxy(unbound_obj)) return Unbind(unbound_obj);
+ if (TypeProxy.IsProxy(unbound_obj))
+ return Unbind(unbound_obj);
return unbound_obj;
}
@@ -260,7 +231,7 @@ public static T Cast(dynamic obj)
///
/// Provides a default type mapper based on the given reference type.
///
- private static dynamic UseTypeMapper()
+ internal static dynamic UseTypeMapper()
where T1 : notnull
where T2 : notnull
{
@@ -324,18 +295,15 @@ public static IEnumerable Map(dynamic obj, Func? func = null)
/// The item type to cast to.
/// The object or enumerable to iterate over.
/// The function to run for each item (optional).
+ /// Whether to return a proxy instance (optional).
/// A list of the function's output.
- public static IList Map(dynamic obj, Func? func = null)
- where L : IList
- where T : notnull
+ public static IList Map(
+ dynamic obj,
+ Func? func = null,
+ bool proxy = false)
+ where L : IList
+ where T : notnull
{
- IList newList = Try(
- // Attempt to create a new instance of the 'L' list type.
- () => InstanceFactory.CreateInstance(typeof(L)),
- // Otherwise fallback to a generic list implementation
- // (i.e. when the provided type is abstract or has no constructor).
- () => new List());
-
dynamic innerList = Try(
// Attempt to cast the object to a list type.
() => Cast(obj),
@@ -343,6 +311,26 @@ public static IList Map(dynamic obj, Func? func = null)
// Otherwise fallback to a dynamic list implementation.
() => obj as dynamic);
+ // // If `T` is a DLRWrapper type and the object is a dynamic remote object,
+ // // then simply return a ListProxy instance wrapping the remote list object.
+ // if (typeof(T).IsOpenSubtypeOf(typeof(DLRWrapper<>)))
+ // {
+ // IList proxy = new ListProxyinnerList, func);
+ //
+ // // If the instance has a well-defined count property, return the instance.
+ // if (Try(() => proxy.Count >= 0))
+ // return proxy;
+ // }
+ if (proxy) return new ListProxy(innerList, func);
+
+ // Otherwise allocate a local list object and map the items to the new type.
+ IList newList = Try(
+ // Attempt to create a new instance of the 'L' list type.
+ () => InstanceFactory.CreateInstance(typeof(L)),
+ // Otherwise fallback to a generic list implementation
+ // (i.e. when the provided type is abstract or has no constructor).
+ () => new List());
+
foreach (var item in Map(innerList, func)) newList.Add(item);
return newList;
}
@@ -453,80 +441,40 @@ public static T Retry(
}
}
- //
- // Wrapper Attributes
- //
-
- ///
- /// A wrapper attribute that allows for a default value to fallback to.
- ///
- /// The default value.
- public class DefaultAttribute(object value) : Attribute
- {
- public object Value { get; set; } = value;
- }
-
///
- /// Determines whether the type is compiler-generated.
- ///
- public static bool IsCompilerGenerated(Type t)
- {
- if (t == null) return false;
-
- return t.IsDefined(typeof(CompilerGeneratedAttribute), false)
- || IsCompilerGenerated(t.DeclaringType);
- }
-
- //
- // Attribute wrapper helpers.
- //
-
- ///
- /// Extracts the base type from a compiler-generated type.
+ /// Marks the retrieval of a DLRWrapper's instance as optional.
///
- /// The type to extract the base type from.
- /// The base type of the given type.
- public static Type GetBaseType(Type callerType)
+ /// The class type to instantiate.
+ /// The object to wrap.
+ /// The condition to check before wrapping.
+ /// The wrapped object or null if the object is null.
+ public static T? Optional(
+ dynamic obj,
+ Func condition = null) where T : class
{
- if (!IsCompilerGenerated(callerType))
- return callerType;
-
- string fullName = callerType.FullName;
- string baseName = fullName.Substring(0, fullName.IndexOf("+<"));
- Type baseType = callerType.DeclaringType.Assembly.GetType(baseName);
-
- return baseType;
- }
-
- ///
- /// Gets the parent type of the caller.
- ///
- /// The stack frame depth.
- /// The type of the caller.
- public static Type GetCallerType(int depth = 4) =>
- new StackFrame(depth).GetMethod().ReflectedType;
+ // Return null if the condition is not met
+ if (condition != null && !condition(obj))
+ return null;
- ///
- /// Gets the stack frame depth of the (outer) DLRWrapper caller.
- ///
- /// The starting stack frame depth.
- /// The caller's stack frame depth.
- private static int GetCallerDepth(int depth = 3)
- {
- Type wrapperType = GetCallerType(depth);
- while(GetCallerType(depth).Name == wrapperType.Name && depth < 50) depth++;
+ // Return null if the underlying object is null
+ if (Try(() => (obj == null || Unbind(obj) == null)))
+ return null;
- return depth;
+ if (typeof(T).IsSubclassOf(typeof(DLRWrapper)))
+ return (T)(InstanceFactory.CreateInstance(typeof(T), obj));
+ else
+ return Cast(obj);
}
///
- /// Attempts to get the default attribute from the (outer) DLRWrapper caller.
+ /// Marks the retrieval of a DLRWrapper's instance as optional.
///
- /// The default attribute (if present).
- /// True if the default attribute was found.
- private static bool TryGetDefaultAttribute(out DefaultAttribute? attribute)
+ /// The class type to instantiate.
+ /// The object to wrap.
+ /// The condition to check before wrapping.
+ /// The wrapped object or null if the object is null.
+ public static T? Optional(dynamic obj, bool condition) where T : class
{
- attribute = GetCallerAttribute(depth: GetCallerDepth());
- return attribute != null;
+ return Optional(obj, new Func(_ => condition));
}
}
diff --git a/MTGOSDK/src/Core/Reflection/ProxyObject.cs b/MTGOSDK/src/Core/Reflection/DynamicProxy.cs
similarity index 98%
rename from MTGOSDK/src/Core/Reflection/ProxyObject.cs
rename to MTGOSDK/src/Core/Reflection/DynamicProxy.cs
index cab04974..3f876e0d 100644
--- a/MTGOSDK/src/Core/Reflection/ProxyObject.cs
+++ b/MTGOSDK/src/Core/Reflection/DynamicProxy.cs
@@ -13,7 +13,7 @@ namespace MTGOSDK.Core.Reflection;
///
/// Provides a dynamic object that can be used to wrap a static value.
///
-public class ProxyObject(
+public class DynamicProxy(
dynamic @base,
dynamic @default = null,
dynamic fallback = null,
diff --git a/MTGOSDK/src/Core/Remoting/Internal/Utils/MethodBaseExtensions.cs b/MTGOSDK/src/Core/Reflection/Extensions/MethodBaseExtensions.cs
similarity index 95%
rename from MTGOSDK/src/Core/Remoting/Internal/Utils/MethodBaseExtensions.cs
rename to MTGOSDK/src/Core/Reflection/Extensions/MethodBaseExtensions.cs
index 9f61cf27..74327710 100644
--- a/MTGOSDK/src/Core/Remoting/Internal/Utils/MethodBaseExtensions.cs
+++ b/MTGOSDK/src/Core/Reflection/Extensions/MethodBaseExtensions.cs
@@ -1,13 +1,13 @@
/** @file
Copyright (c) 2021, Xappy.
Copyright (c) 2024, Cory Bennett. All rights reserved.
- SPDX-License-Identifier: Apache-2.0 and MIT
+ SPDX-License-Identifier: Apache-2.0
**/
using System.Reflection;
-namespace MTGOSDK.Core.Remoting.Internal.Utils;
+namespace MTGOSDK.Core.Reflection.Extensions;
public static class MethodBaseExtensions
{
diff --git a/MTGOSDK/src/Core/Remoting/Interop/Utils/TypeExt.cs b/MTGOSDK/src/Core/Reflection/Extensions/TypeExtensions.cs
similarity index 72%
rename from MTGOSDK/src/Core/Remoting/Interop/Utils/TypeExt.cs
rename to MTGOSDK/src/Core/Reflection/Extensions/TypeExtensions.cs
index 97bbd980..b5f2413b 100644
--- a/MTGOSDK/src/Core/Remoting/Interop/Utils/TypeExt.cs
+++ b/MTGOSDK/src/Core/Reflection/Extensions/TypeExtensions.cs
@@ -1,31 +1,46 @@
/** @file
Copyright (c) 2021, Xappy.
Copyright (c) 2024, Cory Bennett. All rights reserved.
- SPDX-License-Identifier: Apache-2.0 and MIT
+ SPDX-License-Identifier: Apache-2.0
**/
using System.Reflection;
-using MTGOSDK.Core.Remoting.Interop.Extensions;
+using MTGOSDK.Core.Reflection.Attributes;
+using MTGOSDK.Core.Reflection.Types;
-namespace MTGOSDK.Core.Remoting.Interop.Utils;
+namespace MTGOSDK.Core.Reflection.Extensions;
-public static class TypeExt
+public static class TypeExtensions
{
- public class WildCardEnabledTypesComparer : IEqualityComparer
- {
- public bool Equals(Type x, Type y)
- {
- if (x is WildCardType || y is WildCardType)
- return true;
- return x.IsAssignableFrom(y);
- }
-
- public int GetHashCode(Type obj) => obj.GetHashCode();
- }
+ private static readonly TypeComparer _wildCardTypesComparer = new();
- private static readonly WildCardEnabledTypesComparer _wildCardTypesComparer = new();
+ ///
+ /// Gets all members of a type that have a specific attribute.
+ ///
+ /// The type of attribute.
+ /// The type to get members from.
+ /// The binding flags to use.
+ /// An array of member attribute pairs.
+ public static MemberAttributePair[] GetMemberAttributes(
+ this Type type,
+ BindingFlags bindingFlags = BindingFlags.Public
+ | BindingFlags.NonPublic
+ | BindingFlags.Instance
+ | BindingFlags.Static
+ | BindingFlags.FlattenHierarchy
+ ) where T : Attribute =>
+ type.GetMembers(bindingFlags)
+ .Where(p => p.IsDefined(typeof(T), false))
+ .Select(p => new MemberAttributePair()
+ {
+ Member = p,
+#pragma warning disable CS8601 // Possible null reference argument.
+ Attribute = p.GetCustomAttributes(typeof(T), false).Single() as T
+#pragma warning restore CS8601
+ })
+ .ToArray();
///
/// Searches a type for a specific method. If not found searches its ancestors.
@@ -132,7 +147,7 @@ public static ConstructorInfo GetConstructor(
m.GetParameters()
.Select(pi => pi.ParameterType)
.SequenceEqual(parameterTypes,
- new TypeExt.WildCardEnabledTypesComparer()));
+ new TypeComparer()));
}
return ctorInfo;
@@ -186,12 +201,29 @@ public static bool IsStringCoercible(this Type realType)
public static Type GetType(this AppDomain domain, string typeFullName)
{
var assemblies = domain.GetAssemblies();
- foreach (Assembly assm in assemblies)
+ foreach (Assembly asm in assemblies)
{
- Type t = assm.GetType(typeFullName);
- if (t != null)
- return t;
+ Type t = asm.GetType(typeFullName);
+ if (t != null) return t;
}
return null;
}
+
+ ///
+ /// Determines if a type is a subtype of another open generic type.
+ ///
+ /// The type to check.
+ /// The open generic type to check against.
+ /// True if the type is a subtype of the open generic type.
+ public static bool IsOpenSubtypeOf(this Type type, Type baseType)
+ {
+ try
+ {
+ return type.BaseType.GetGenericTypeDefinition().IsAssignableFrom(baseType);
+ }
+ catch
+ {
+ return false;
+ }
+ }
}
diff --git a/MTGOSDK/src/Core/Reflection/ListProxy.cs b/MTGOSDK/src/Core/Reflection/ListProxy.cs
new file mode 100644
index 00000000..df9fd44f
--- /dev/null
+++ b/MTGOSDK/src/Core/Reflection/ListProxy.cs
@@ -0,0 +1,75 @@
+/** @file
+ Copyright (c) 2024, Cory Bennett. All rights reserved.
+ SPDX-License-Identifier: Apache-2.0
+**/
+
+using System.Collections;
+using System.Collections.Generic;
+
+
+namespace MTGOSDK.Core.Reflection;
+
+///
+/// Represents a proxy object for a remote list object.
+///
+public class ListProxy(
+ dynamic list,
+ Func? func = null)
+ : DLRWrapper>, IList where T : notnull
+{
+ ///
+ /// Stores an internal reference to the remote list object.
+ ///
+ internal override dynamic obj => Bind(list);
+
+ private dynamic s_typeMapper = func ?? UseTypeMapper();
+
+ //
+ // IList wrapper properties
+ //
+
+ public int Count => @base.Count;
+
+ public bool IsReadOnly => @base.IsReadOnly;
+
+ public T this[int index]
+ {
+ get => s_typeMapper(Unbind(@base)[index]);
+ set => Unbind(@base)[index] = value;
+ }
+
+ //
+ // IList wrapper methods
+ //
+
+ public void Add(T item) => @base.Add(item);
+
+ public void Clear() => @base.Clear();
+
+ public bool Contains(T item) => @base.Contains(item);
+
+ public void CopyTo(T[] array, int arrayIndex)
+ {
+ var baseRef = Unbind(@base);
+ for (int i = 0; i < this.Count; i++)
+ {
+ array[arrayIndex + i] = s_typeMapper(baseRef[i]);
+ }
+ }
+
+ public IEnumerator GetEnumerator() => Map(@base, func);
+
+ public int IndexOf(T item) => @base.IndexOf(item);
+
+ public void Insert(int index, T item) => @base.Insert(index, item);
+
+ public bool Remove(T item) => @base.Remove(item);
+
+ public void RemoveAt(int index) => @base.RemoveAt(index);
+
+ //
+ // IEnumerable wrapper methods
+ //
+
+ IEnumerator IEnumerable.GetEnumerator() => GetEnumerator();
+}
diff --git a/MTGOSDK/src/Core/Reflection/Proxy.cs b/MTGOSDK/src/Core/Reflection/TypeProxy.cs
similarity index 87%
rename from MTGOSDK/src/Core/Reflection/Proxy.cs
rename to MTGOSDK/src/Core/Reflection/TypeProxy.cs
index 5d82d8bd..e527391e 100644
--- a/MTGOSDK/src/Core/Reflection/Proxy.cs
+++ b/MTGOSDK/src/Core/Reflection/TypeProxy.cs
@@ -8,7 +8,7 @@
namespace MTGOSDK.Core.Reflection;
-public class Proxy(Type? @type=null) where T : class
+public class TypeProxy(Type? @type=null) where T : class
{
//
// BuilderProxy methods
@@ -43,6 +43,14 @@ public static dynamic As(dynamic? obj=null, params Type[] interfaces) =>
public static dynamic From(dynamic? obj=null) =>
Impromptu.UndoActLike(obj);
+ ///
+ /// Check whether an instance implements a proxy interface.
+ ///
+ /// The object to check
+ /// True if the object is a proxy
+ public static bool IsProxy(dynamic? obj=null) =>
+ typeof(IActLikeProxy).IsAssignableFrom(obj.GetType());
+
//
// Derived class properties
//
@@ -102,6 +110,6 @@ public override string ToString() => Class.FullName
?? throw new TypeInitializationException(
$"The Proxied type is not a valid type.", innerException: null);
- public static implicit operator string(Proxy proxy) =>
+ public static implicit operator string(TypeProxy proxy) =>
proxy.ToString();
}
diff --git a/MTGOSDK/src/Core/Remoting/Internal/Reflection/TypesResolver.cs b/MTGOSDK/src/Core/Reflection/TypeResolver.cs
similarity index 78%
rename from MTGOSDK/src/Core/Remoting/Internal/Reflection/TypesResolver.cs
rename to MTGOSDK/src/Core/Reflection/TypeResolver.cs
index 3d9f1e44..390537d4 100644
--- a/MTGOSDK/src/Core/Remoting/Internal/Reflection/TypesResolver.cs
+++ b/MTGOSDK/src/Core/Reflection/TypeResolver.cs
@@ -1,25 +1,27 @@
/** @file
Copyright (c) 2021, Xappy.
Copyright (c) 2024, Cory Bennett. All rights reserved.
- SPDX-License-Identifier: Apache-2.0 and MIT
+ SPDX-License-Identifier: Apache-2.0
**/
using System.Reflection;
+using MTGOSDK.Core.Remoting.Types;
-namespace MTGOSDK.Core.Remoting.Internal.Reflection;
+
+namespace MTGOSDK.Core.Reflection;
///
/// Resolves local and remote types. Contains a cache so the same TypeFullName
/// object is returned for different resolutions for the same remote type.
///
-public class TypesResolver()
+public class TypeResolver()
{
+ private readonly Dictionary, Type> _cache = new();
+
// Since the resolver works with a cache that should be global we make the
// whole class a singleton
- public static TypesResolver Instance = new TypesResolver();
-
- internal readonly Dictionary, Type> _cache = new();
+ public static TypeResolver Instance = new TypeResolver();
public void RegisterType(Type type)
=> RegisterType(type.Assembly.GetName().Name, type.FullName, type);
@@ -44,8 +46,9 @@ public Type Resolve(string assemblyName, string typeFullName)
// Filter assemblies but avoid filtering for "mscorlib"
if(assemblyName?.Equals("mscorlib") == false)
{
- assemblies = assemblies.Where(assm => assm.FullName.Contains(assemblyName ?? ""));
+ assemblies = assemblies.Where(asm => asm.FullName.Contains(assemblyName ?? ""));
}
+
foreach (Assembly assembly in assemblies)
{
resolvedType = assembly.GetType(typeFullName);
@@ -53,10 +56,7 @@ public Type Resolve(string assemblyName, string typeFullName)
{
// Found the type!
// But retreat if it's an enum (and get remote proxy of it instead)
- if(resolvedType.IsEnum)
- {
- resolvedType = null;
- }
+ if(resolvedType.IsEnum) resolvedType = null;
break;
}
}
@@ -68,4 +68,9 @@ public Type Resolve(string assemblyName, string typeFullName)
return resolvedType;
}
+
+ public void ClearCache()
+ {
+ _cache.Clear();
+ }
}
diff --git a/MTGOSDK/src/Core/Reflection/Types/ConstructorInfoStub.cs b/MTGOSDK/src/Core/Reflection/Types/ConstructorInfoStub.cs
new file mode 100644
index 00000000..6533b218
--- /dev/null
+++ b/MTGOSDK/src/Core/Reflection/Types/ConstructorInfoStub.cs
@@ -0,0 +1,69 @@
+/** @file
+ Copyright (c) 2024, Cory Bennett. All rights reserved.
+ SPDX-License-Identifier: Apache-2.0
+**/
+
+using System.Globalization;
+using System.Reflection;
+
+
+namespace MTGOSDK.Core.Reflection.Types;
+
+public class ConstructorInfoStub : ConstructorInfo
+{
+ public override string Name => throw new NotImplementedException();
+ public override Type DeclaringType => throw new NotImplementedException();
+ public override Type ReflectedType => throw new NotImplementedException();
+
+ public override MethodAttributes Attributes =>
+ throw new NotImplementedException();
+
+ public override RuntimeMethodHandle MethodHandle =>
+ throw new NotImplementedException();
+ public override object[] GetCustomAttributes(bool inherit)
+ {
+ throw new NotImplementedException();
+ }
+
+ public override object[] GetCustomAttributes(Type attributeType, bool inherit)
+ {
+ throw new NotImplementedException();
+ }
+
+ public override MethodImplAttributes GetMethodImplementationFlags()
+ {
+ throw new NotImplementedException();
+ }
+
+ public override ParameterInfo[] GetParameters() =>
+ throw new NotImplementedException();
+
+ public override object Invoke(
+ BindingFlags invokeAttr,
+ Binder binder,
+ object[] parameters,
+ CultureInfo culture)
+ {
+ throw new NotImplementedException();
+ }
+
+ public override object Invoke(
+ object obj,
+ BindingFlags invokeAttr,
+ Binder binder,
+ object[] parameters,
+ CultureInfo culture)
+ {
+ throw new NotImplementedException();
+ }
+
+ public override bool IsDefined(Type attributeType, bool inherit)
+ {
+ throw new NotImplementedException();
+ }
+
+ public override string ToString()
+ {
+ throw new NotImplementedException();
+ }
+}
diff --git a/MTGOSDK/src/Core/Reflection/Types/FieldInfoStub.cs b/MTGOSDK/src/Core/Reflection/Types/FieldInfoStub.cs
new file mode 100644
index 00000000..cb24e19e
--- /dev/null
+++ b/MTGOSDK/src/Core/Reflection/Types/FieldInfoStub.cs
@@ -0,0 +1,52 @@
+/** @file
+ Copyright (c) 2024, Cory Bennett. All rights reserved.
+ SPDX-License-Identifier: Apache-2.0
+**/
+
+using System.Globalization;
+using System.Reflection;
+
+
+namespace MTGOSDK.Core.Reflection.Types;
+
+public class FieldInfoStub : FieldInfo
+{
+ public override string Name => throw new NotImplementedException();
+ public override Type FieldType => throw new NotImplementedException();
+ public override Type DeclaringType => throw new NotImplementedException();
+ public override Type ReflectedType => throw new NotImplementedException();
+ public override FieldAttributes Attributes => throw new NotImplementedException();
+ public override RuntimeFieldHandle FieldHandle => throw new NotImplementedException();
+
+ public override object[] GetCustomAttributes(bool inherit)
+ {
+ throw new NotImplementedException();
+ }
+
+ public override object[] GetCustomAttributes(Type attributeType, bool inherit)
+ {
+ throw new NotImplementedException();
+ }
+
+ public override bool IsDefined(Type attributeType, bool inherit)
+ {
+ throw new NotImplementedException();
+ }
+
+ public override object GetValue(object obj)
+ {
+ throw new NotImplementedException();
+ }
+
+ public override void SetValue(
+ object obj,
+ object value,
+ BindingFlags invokeAttr,
+ Binder binder,
+ CultureInfo culture)
+ {
+ throw new NotImplementedException();
+ }
+
+ public override string ToString() => throw new NotImplementedException();
+}
diff --git a/MTGOSDK/src/Core/Reflection/Types/MethodInfoStub.cs b/MTGOSDK/src/Core/Reflection/Types/MethodInfoStub.cs
new file mode 100644
index 00000000..38c89ac3
--- /dev/null
+++ b/MTGOSDK/src/Core/Reflection/Types/MethodInfoStub.cs
@@ -0,0 +1,98 @@
+/** @file
+ Copyright (c) 2024, Cory Bennett. All rights reserved.
+ SPDX-License-Identifier: Apache-2.0
+**/
+
+using System.Globalization;
+using System.Reflection;
+
+
+namespace MTGOSDK.Core.Reflection.Types;
+
+public class MethodInfoStub : MethodInfo
+{
+ public override string Name =>
+ throw new NotImplementedException();
+
+ public override Type DeclaringType =>
+ throw new NotImplementedException();
+
+ public override Type ReturnType =>
+ throw new NotImplementedException();
+
+ public override Type ReflectedType =>
+ throw new NotImplementedException();
+
+ public override RuntimeMethodHandle MethodHandle =>
+ throw new NotImplementedException();
+
+ public override MethodAttributes Attributes =>
+ throw new NotImplementedException();
+
+ public override ICustomAttributeProvider ReturnTypeCustomAttributes =>
+ throw new NotImplementedException();
+
+ public override bool IsGenericMethod =>
+ throw new NotImplementedException();
+
+ public override bool IsGenericMethodDefinition =>
+ throw new NotImplementedException();
+
+ public override bool ContainsGenericParameters =>
+ throw new NotImplementedException();
+
+ public override Type[] GetGenericArguments()
+ {
+ throw new NotImplementedException();
+ }
+
+ public override MethodInfo MakeGenericMethod(params Type[] typeArguments)
+ {
+ throw new NotImplementedException();
+ }
+
+ public override object[] GetCustomAttributes(bool inherit)
+ {
+ throw new NotImplementedException();
+ }
+
+ public override bool IsDefined(Type attributeType, bool inherit)
+ {
+ throw new NotImplementedException();
+ }
+
+ public override ParameterInfo[] GetParameters()
+ {
+ throw new NotImplementedException();
+ }
+
+ public override MethodImplAttributes GetMethodImplementationFlags()
+ {
+ throw new NotImplementedException();
+ }
+
+ public override object Invoke(
+ object obj,
+ BindingFlags invokeAttr,
+ Binder binder,
+ object[] parameters,
+ CultureInfo culture)
+ {
+ throw new NotImplementedException();
+ }
+
+ public override MethodInfo GetBaseDefinition()
+ {
+ throw new NotImplementedException();
+ }
+
+ public override object[] GetCustomAttributes(Type attributeType, bool inherit)
+ {
+ throw new NotImplementedException();
+ }
+
+ public override string ToString()
+ {
+ throw new NotImplementedException();
+ }
+}
diff --git a/MTGOSDK/src/Core/Reflection/Types/ParameterInfoStub.cs b/MTGOSDK/src/Core/Reflection/Types/ParameterInfoStub.cs
new file mode 100644
index 00000000..79d6613d
--- /dev/null
+++ b/MTGOSDK/src/Core/Reflection/Types/ParameterInfoStub.cs
@@ -0,0 +1,18 @@
+/** @file
+ Copyright (c) 2024, Cory Bennett. All rights reserved.
+ SPDX-License-Identifier: Apache-2.0
+**/
+
+using System.Reflection;
+
+
+namespace MTGOSDK.Core.Reflection.Types;
+
+public class ParameterInfoStub : ParameterInfo
+{
+ public override string Name => throw new NotImplementedException();
+
+ public override Type ParameterType => throw new NotImplementedException();
+
+ public override string ToString() => throw new NotImplementedException();
+}
diff --git a/MTGOSDK/src/Core/Reflection/Types/PropertyInfoStub.cs b/MTGOSDK/src/Core/Reflection/Types/PropertyInfoStub.cs
new file mode 100644
index 00000000..f1ad0f3f
--- /dev/null
+++ b/MTGOSDK/src/Core/Reflection/Types/PropertyInfoStub.cs
@@ -0,0 +1,91 @@
+/** @file
+ Copyright (c) 2024, Cory Bennett. All rights reserved.
+ SPDX-License-Identifier: Apache-2.0
+**/
+
+using System.Globalization;
+using System.Reflection;
+
+
+namespace MTGOSDK.Core.Reflection.Types;
+
+public class PropertyInfoStub : PropertyInfo
+{
+ public override string Name =>
+ throw new NotImplementedException();
+
+ public override Type DeclaringType =>
+ throw new NotImplementedException();
+
+ public override Type PropertyType =>
+ throw new NotImplementedException();
+
+ public override PropertyAttributes Attributes =>
+ throw new NotImplementedException();
+
+ public override Type ReflectedType => throw new NotImplementedException();
+
+ public override MethodInfo GetMethod => throw new NotImplementedException();
+ public override MethodInfo SetMethod => throw new NotImplementedException();
+
+ public override bool CanRead => throw new NotImplementedException();
+ public override bool CanWrite => throw new NotImplementedException();
+
+ public override MethodInfo[] GetAccessors(bool nonPublic)
+ {
+ throw new NotImplementedException();
+ }
+
+ public override object[] GetCustomAttributes(bool inherit)
+ {
+ throw new NotImplementedException();
+ }
+
+ public override object[] GetCustomAttributes(Type attributeType, bool inherit)
+ {
+ throw new NotImplementedException();
+ }
+
+ public override MethodInfo GetGetMethod(bool nonPublic)
+ {
+ throw new NotImplementedException();
+ }
+
+ public override MethodInfo GetSetMethod(bool nonPublic)
+ {
+ throw new NotImplementedException();
+ }
+
+ public override ParameterInfo[] GetIndexParameters()
+ {
+ throw new NotImplementedException();
+ }
+
+ public override object GetValue(
+ object obj,
+ BindingFlags invokeAttr,
+ Binder binder,
+ object[] index,
+ CultureInfo culture)
+ {
+ throw new NotImplementedException();
+ }
+
+ public override bool IsDefined(Type attributeType, bool inherit)
+ {
+ throw new NotImplementedException();
+ }
+
+ public override void SetValue(
+ object obj,
+ object value,
+ BindingFlags invokeAttr,
+ Binder binder,
+ object[] index,
+ CultureInfo culture)
+ {
+ throw new NotImplementedException();
+ }
+
+ public override string ToString() => throw new NotImplementedException();
+}
diff --git a/MTGOSDK/src/Core/Reflection/Types/TypeComparer.cs b/MTGOSDK/src/Core/Reflection/Types/TypeComparer.cs
new file mode 100644
index 00000000..46e4b18e
--- /dev/null
+++ b/MTGOSDK/src/Core/Reflection/Types/TypeComparer.cs
@@ -0,0 +1,22 @@
+/** @file
+ Copyright (c) 2021, Xappy.
+ Copyright (c) 2024, Cory Bennett. All rights reserved.
+ SPDX-License-Identifier: Apache-2.0
+**/
+
+using System.Reflection;
+
+
+namespace MTGOSDK.Core.Reflection.Types;
+
+public class TypeComparer : IEqualityComparer
+{
+ public bool Equals(Type x, Type y)
+ {
+ if (x is TypeStub || y is TypeStub)
+ return true;
+ return x.IsAssignableFrom(y);
+ }
+
+ public int GetHashCode(Type obj) => obj.GetHashCode();
+}
diff --git a/MTGOSDK/src/Core/Remoting/Interop/Extensions/WildCardType.cs b/MTGOSDK/src/Core/Reflection/Types/TypeStub.cs
similarity index 80%
rename from MTGOSDK/src/Core/Remoting/Interop/Extensions/WildCardType.cs
rename to MTGOSDK/src/Core/Reflection/Types/TypeStub.cs
index cda2f086..68c9daf8 100644
--- a/MTGOSDK/src/Core/Remoting/Interop/Extensions/WildCardType.cs
+++ b/MTGOSDK/src/Core/Reflection/Types/TypeStub.cs
@@ -1,20 +1,35 @@
-/** @file
- Copyright (c) 2021, Xappy.
+/** @file
Copyright (c) 2024, Cory Bennett. All rights reserved.
- SPDX-License-Identifier: Apache-2.0 and MIT
+ SPDX-License-Identifier: Apache-2.0
**/
using System.Globalization;
using System.Reflection;
-namespace MTGOSDK.Core.Remoting.Interop.Extensions;
+namespace MTGOSDK.Core.Reflection.Types;
-///
-/// This fake type is used when the type of a parameter is unknown (if NULL was passed)
-///
-public class WildCardType : Type
+public class TypeStub(
+ string name = nameof(TypeStub),
+ Guid guid = default,
+ Module module = default,
+ Assembly assembly = default,
+ string fullName = default,
+ string @namespace = default,
+ string assemblyQualifiedName = default,
+ Type baseType = default,
+ Type underlyingSystemType = default) : Type
{
+ public override string Name => name;
+ public override Guid GUID => guid;
+ public override Module Module => module;
+ public override Assembly Assembly => assembly;
+ public override string FullName => fullName;
+ public override string Namespace => @namespace;
+ public override string AssemblyQualifiedName => assemblyQualifiedName;
+ public override Type BaseType => baseType;
+ public override Type UnderlyingSystemType => underlyingSystemType;
+
public override object[] GetCustomAttributes(bool inherit)
{
throw new NotImplementedException();
@@ -160,8 +175,6 @@ public override object InvokeMember(
throw new NotImplementedException();
}
- public override Type UnderlyingSystemType { get; }
-
protected override ConstructorInfo GetConstructorImpl(
BindingFlags bindingAttr,
Binder binder,
@@ -172,15 +185,6 @@ protected override ConstructorInfo GetConstructorImpl(
throw new NotImplementedException();
}
- public override string Name { get; }
- public override Guid GUID { get; }
- public override Module Module { get; }
- public override Assembly Assembly { get; }
- public override string FullName { get; }
- public override string Namespace { get; }
- public override string AssemblyQualifiedName { get; }
- public override Type BaseType { get; }
-
public override object[] GetCustomAttributes(Type attributeType, bool inherit)
{
throw new NotImplementedException();
diff --git a/MTGOSDK/src/Core/Remoting/CandidateObject.cs b/MTGOSDK/src/Core/Remoting/CandidateObject.cs
deleted file mode 100644
index 28268e2e..00000000
--- a/MTGOSDK/src/Core/Remoting/CandidateObject.cs
+++ /dev/null
@@ -1,19 +0,0 @@
-/** @file
- Copyright (c) 2021, Xappy.
- Copyright (c) 2024, Cory Bennett. All rights reserved.
- SPDX-License-Identifier: Apache-2.0 and MIT
-**/
-
-
-namespace MTGOSDK.Core.Remoting;
-
-///
-/// A candidate for a remote object.
-/// Holding this item does not mean having a meaningful hold of the remote object. To gain one use
-///
-public struct CandidateObject(ulong address, string typeFullName, int hashCode)
-{
- public ulong Address = address;
- public string TypeFullName = typeFullName;
- public int HashCode = hashCode;
-}
diff --git a/MTGOSDK/src/Core/Remoting/CandidateType.cs b/MTGOSDK/src/Core/Remoting/CandidateType.cs
deleted file mode 100644
index 11bfa1b6..00000000
--- a/MTGOSDK/src/Core/Remoting/CandidateType.cs
+++ /dev/null
@@ -1,14 +0,0 @@
-/** @file
- Copyright (c) 2021, Xappy.
- Copyright (c) 2024, Cory Bennett. All rights reserved.
- SPDX-License-Identifier: Apache-2.0 and MIT
-**/
-
-
-namespace MTGOSDK.Core.Remoting;
-
-public struct CandidateType(string typeName, string assembly)
-{
- public string TypeFullName = typeName;
- public string Assembly = assembly;
-}
diff --git a/MTGOSDK/src/Core/Remoting/Internal/DiverDiscovery.cs b/MTGOSDK/src/Core/Remoting/Internal/DiverDiscovery.cs
deleted file mode 100644
index 5556462d..00000000
--- a/MTGOSDK/src/Core/Remoting/Internal/DiverDiscovery.cs
+++ /dev/null
@@ -1,83 +0,0 @@
-/** @file
- Copyright (c) 2021, Xappy.
- Copyright (c) 2024, Cory Bennett. All rights reserved.
- SPDX-License-Identifier: Apache-2.0 and MIT
-**/
-
-using System.Diagnostics;
-using System.Net;
-using System.Net.Sockets;
-
-using MTGOSDK.Core.Remoting.Interop;
-
-using MTGOSDK.Win32.Extensions;
-
-
-namespace MTGOSDK.Core.Remoting.Internal;
-
-public enum DiverState
-{
- NoDiver,
- Alive,
- Corpse,
- HollowSnapshot
-}
-
-public static class DiverDiscovery
-{
- public static DiverState QueryStatus(
- Process target,
- string diverAddr,
- ushort diverPort)
- {
- DiverCommunicator com = new DiverCommunicator(diverAddr, diverPort);
-
- // We WANT to check liveness of the diver using HTTP but this might take a
- // LOT of time if it is dead (Trying to TCP SYN several times, with a
- // timeout between each). So a simple circuit-breaker is implemented
- // before that: If we manage to bind to the expected diver endpoint, we
- // assume it's not alive
-
- bool diverPortIsFree = false;
- try
- {
- IPAddress localAddr = IPAddress.Parse(diverAddr);
- TcpListener server = new TcpListener(localAddr, diverPort);
- server.Start();
- diverPortIsFree = true;
- server.Stop();
- }
- catch
- {
- // Had some issues, perhaps it's the diver holding that port.
- }
-
- if (!diverPortIsFree && com.CheckAliveness())
- {
- return DiverState.Alive;
- }
-
- // // Check if this is a snapshot created by the diver.
- // if (target.Threads.Count == 0)
- // return DiverState.HollowSnapshot;
-
- // Diver isn't alive. It's possible that it was never injected or it was
- // injected and killed
- bool containsToolkitDll = false;
- try
- {
- containsToolkitDll |= target.Modules.AsEnumerable()
- .Any(module => module.ModuleName.Contains("Bootstrapper"));
- }
- catch
- {
- // Sometimes this happens because of x32 vs x64 process interaction
- }
- if (containsToolkitDll)
- {
- return DiverState.Corpse;
- }
-
- return DiverState.NoDiver;
- }
-}
diff --git a/MTGOSDK/src/Core/Remoting/Internal/DynamicRemoteObjectFactory.cs b/MTGOSDK/src/Core/Remoting/Internal/DynamicRemoteObjectFactory.cs
deleted file mode 100644
index 2128c418..00000000
--- a/MTGOSDK/src/Core/Remoting/Internal/DynamicRemoteObjectFactory.cs
+++ /dev/null
@@ -1,24 +0,0 @@
-/** @file
- Copyright (c) 2021, Xappy.
- Copyright (c) 2024, Cory Bennett. All rights reserved.
- SPDX-License-Identifier: Apache-2.0 and MIT
-**/
-
-using MTGOSDK.Core.Remoting.Interop.Interactions.Dumps;
-
-
-namespace MTGOSDK.Core.Remoting.Internal;
-
-public class DynamicRemoteObjectFactory
-{
- private RemoteHandle _app;
-
- public DynamicRemoteObject Create(
- RemoteHandle rApp,
- RemoteObject remoteObj,
- TypeDump typeDump)
- {
- _app = rApp;
- return new DynamicRemoteObject(rApp, remoteObj);
- }
-}
diff --git a/MTGOSDK/src/Core/Remoting/Internal/ProxiedReflection/IProxiedMember.cs b/MTGOSDK/src/Core/Remoting/Internal/ProxiedReflection/IProxiedMember.cs
deleted file mode 100644
index f0bf988f..00000000
--- a/MTGOSDK/src/Core/Remoting/Internal/ProxiedReflection/IProxiedMember.cs
+++ /dev/null
@@ -1,12 +0,0 @@
-/** @file
- Copyright (c) 2021, Xappy.
- Copyright (c) 2024, Cory Bennett. All rights reserved.
- SPDX-License-Identifier: Apache-2.0 and MIT
-**/
-
-namespace MTGOSDK.Core.Remoting.Internal.ProxiedReflection;
-
-public interface IProxiedMember
-{
- public ProxiedMemberType Type { get; }
-}
diff --git a/MTGOSDK/src/Core/Remoting/Internal/ProxiedReflection/ProxiedEventInfo.cs b/MTGOSDK/src/Core/Remoting/Internal/ProxiedReflection/ProxiedEventInfo.cs
deleted file mode 100644
index baab496b..00000000
--- a/MTGOSDK/src/Core/Remoting/Internal/ProxiedReflection/ProxiedEventInfo.cs
+++ /dev/null
@@ -1,46 +0,0 @@
-/** @file
- Copyright (c) 2021, Xappy.
- Copyright (c) 2024, Cory Bennett. All rights reserved.
- SPDX-License-Identifier: Apache-2.0 and MIT
-**/
-
-using System.Reflection;
-
-
-namespace MTGOSDK.Core.Remoting.Internal.ProxiedReflection;
-
-public class ProxiedEventInfo(RemoteObject ro, string name, List args)
- : IProxiedMember
-{
- public ProxiedMemberType Type => ProxiedMemberType.Event;
-
- private readonly RemoteObject _ro = ro;
- private string Name { get; set; } = name;
- private List ArgumentsTypes { get; set; } = args;
-
- public static ProxiedEventInfo operator +(ProxiedEventInfo c1, Delegate x)
- {
- ParameterInfo[] parameters = x.Method.GetParameters();
-
- if (parameters.Length != c1.ArgumentsTypes.Count)
- {
- throw new Exception($"The '{c1.Name}' event expects {c1.ArgumentsTypes.Count} parameters, " +
- $"the callback that was being registered have {parameters.Length}");
- }
-
- if (parameters.Any(p => p.GetType().IsAssignableFrom(typeof(DynamicRemoteObject))))
- {
- throw new Exception("A Remote event's local callback must have only 'dynamic' parameters");
- }
-
- c1._ro.EventSubscribe(c1.Name, x);
-
- return c1;
- }
-
- public static ProxiedEventInfo operator -(ProxiedEventInfo c1, Delegate x)
- {
- c1._ro.EventUnsubscribe(c1.Name, x);
- return c1;
- }
-}
diff --git a/MTGOSDK/src/Core/Remoting/Internal/ProxiedReflection/ProxiedMemberType.cs b/MTGOSDK/src/Core/Remoting/Internal/ProxiedReflection/ProxiedMemberType.cs
deleted file mode 100644
index ead0b9e4..00000000
--- a/MTGOSDK/src/Core/Remoting/Internal/ProxiedReflection/ProxiedMemberType.cs
+++ /dev/null
@@ -1,16 +0,0 @@
-/** @file
- Copyright (c) 2021, Xappy.
- Copyright (c) 2024, Cory Bennett. All rights reserved.
- SPDX-License-Identifier: Apache-2.0 and MIT
-**/
-
-namespace MTGOSDK.Core.Remoting.Internal.ProxiedReflection;
-
-public enum ProxiedMemberType
-{
- Unknown,
- Field,
- Property,
- Method,
- Event
-}
diff --git a/MTGOSDK/src/Core/Remoting/Internal/ProxiedReflection/ProxiedMethodGroup.cs b/MTGOSDK/src/Core/Remoting/Internal/ProxiedReflection/ProxiedMethodGroup.cs
deleted file mode 100644
index 211e350a..00000000
--- a/MTGOSDK/src/Core/Remoting/Internal/ProxiedReflection/ProxiedMethodGroup.cs
+++ /dev/null
@@ -1,13 +0,0 @@
-/** @file
- Copyright (c) 2021, Xappy.
- Copyright (c) 2024, Cory Bennett. All rights reserved.
- SPDX-License-Identifier: Apache-2.0 and MIT
-**/
-
-using MTGOSDK.Core.Remoting.Internal.Reflection;
-
-
-namespace MTGOSDK.Core.Remoting.Internal.ProxiedReflection;
-
-public class ProxiedMethodGroup : List
-{ }
diff --git a/MTGOSDK/src/Core/Remoting/Internal/ProxiedReflection/ProxiedMethodOverload.cs b/MTGOSDK/src/Core/Remoting/Internal/ProxiedReflection/ProxiedMethodOverload.cs
deleted file mode 100644
index 10a5f53f..00000000
--- a/MTGOSDK/src/Core/Remoting/Internal/ProxiedReflection/ProxiedMethodOverload.cs
+++ /dev/null
@@ -1,20 +0,0 @@
-/** @file
- Copyright (c) 2021, Xappy.
- Copyright (c) 2024, Cory Bennett. All rights reserved.
- SPDX-License-Identifier: Apache-2.0 and MIT
-**/
-
-using MTGOSDK.Core.Remoting.Internal.Reflection;
-
-
-namespace MTGOSDK.Core.Remoting.Internal.ProxiedReflection;
-
-public class ProxiedMethodOverload
-{
- public Type ReturnType { get; set; }
- public List Parameters { get; set; }
- public Func