/
Host.cs
264 lines (222 loc) · 9.79 KB
/
Host.cs
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
using System;
using System.Collections.Generic;
using System.IO;
using System.Reflection;
using System.Runtime.InteropServices;
using System.Runtime.Loader;
using System.Threading;
using System.Threading.Tasks;
//using Buildalyzer;
//using Buildalyzer.Workspaces;
//using Microsoft.CodeAnalysis;
namespace AltV.Net.Host
{
public class Host
{
private delegate bool ImportDelegate(string resourceName, string key, out object value);
private static readonly IDictionary<string, WeakReference> _loadContexts =
new Dictionary<string, WeakReference>();
private static readonly IDictionary<string, IDictionary<string, object>> _exports =
new Dictionary<string, IDictionary<string, object>>();
private const string DllName = "csharp-module";
private const CallingConvention NativeCallingConvention = CallingConvention.Cdecl;
internal delegate int CoreClrDelegate(IntPtr args, int argsLength);
[DllImport(DllName, CallingConvention = NativeCallingConvention)]
internal static extern void CoreClr_SetResourceLoadDelegates(CoreClrDelegate resourceExecute,
CoreClrDelegate resourceExecuteUnload);
private static CoreClrDelegate _executeResource;
private static CoreClrDelegate _executeResourceUnload;
/// <summary>
/// Main is present to execute the dll as a assembly
/// </summary>
static int Main(string[] args)
{
var semaphore = new Semaphore(0, 1);
SetDelegates();
semaphore.WaitOne();
return 0;
}
public static int Main2(IntPtr arg, int argLength)
{
SetDelegates();
return 0;
}
private static void SetDelegates()
{
_executeResource = ExecuteResource;
GCHandle.Alloc(_executeResource);
_executeResourceUnload = ExecuteResourceUnload;
GCHandle.Alloc(_executeResourceUnload);
CoreClr_SetResourceLoadDelegates(_executeResource, _executeResourceUnload);
}
private static string GetPath(string resourcePath, string resourceMain) =>
$"{resourcePath}{Path.DirectorySeparatorChar}{resourceMain}";
[StructLayout(LayoutKind.Sequential)]
public struct LibArgs
{
public IntPtr ResourcePath;
public IntPtr ResourceName;
public IntPtr ResourceMain;
public IntPtr ServerPointer;
public IntPtr ResourcePointer;
}
[StructLayout(LayoutKind.Sequential)]
public struct UnloadArgs
{
public IntPtr ResourcePath;
public IntPtr ResourceMain;
}
public static int ExecuteResource(IntPtr arg, int argLength)
{
if (argLength < Marshal.SizeOf(typeof(LibArgs)))
{
return 1;
}
var libArgs = Marshal.PtrToStructure<LibArgs>(arg);
var resourcePath = Marshal.PtrToStringUTF8(libArgs.ResourcePath);
var resourceName = Marshal.PtrToStringUTF8(libArgs.ResourceName);
var resourceMain = Marshal.PtrToStringUTF8(libArgs.ResourceMain);
var isCollectible = Environment.GetEnvironmentVariable("CSHARP_MODULE_DISABLE_COLLECTIBLE") == null;
var resourceDllPath = GetPath(resourcePath, resourceMain);
var resourceAssemblyLoadContext =
new ResourceAssemblyLoadContext(resourceDllPath, resourcePath, resourceName, isCollectible);
_loadContexts[resourceDllPath] = new WeakReference(resourceAssemblyLoadContext);
Assembly resourceAssembly;
try
{
resourceAssembly = resourceAssemblyLoadContext.LoadFromAssemblyPath(resourceDllPath);
}
catch (FileLoadException e)
{
Console.WriteLine($"Threw a exception while loading the assembly \"{resourceDllPath}\": {e}");
return 1;
}
var altVNetAssembly = resourceAssemblyLoadContext.LoadFromAssemblyName(new AssemblyName("AltV.Net"));
foreach (var type in altVNetAssembly.GetTypes())
{
switch (type.Name)
{
case "ModuleWrapper":
type.GetMethod("MainWithAssembly", BindingFlags.Public | BindingFlags.Static)?.Invoke(null,
new object[] {libArgs.ServerPointer, libArgs.ResourcePointer, resourceAssemblyLoadContext});
break;
case "HostWrapper":
try
{
type.GetMethod("SetStartTracingDelegate", BindingFlags.Public | BindingFlags.Static)
?.Invoke(
null,
new object[] {new Action<string>(StartTracing)});
type.GetMethod("SetStopTracingDelegate", BindingFlags.Public | BindingFlags.Static)?.Invoke(
null,
new object[] {new Action(StopTracing)});
type.GetMethod("SetImportDelegate", BindingFlags.Public | BindingFlags.Static)?.Invoke(
null,
new object[] {new ImportDelegate(Import)});
type.GetMethod("SetExportDelegate", BindingFlags.Public | BindingFlags.Static)?.Invoke(
null,
new object[]
{
new Action<string, object>((key, value) => { Export(resourceName, key, value); })
});
}
catch (Exception exception)
{
Console.WriteLine(
"Consider updating the AltV.Net nuget package and AltV.Net.Host.dll to be able to access all csharp-module features.");
Console.WriteLine(exception);
}
break;
}
}
return 0;
}
public static int ExecuteResourceUnload(IntPtr arg, int argLength)
{
if (argLength < Marshal.SizeOf(typeof(UnloadArgs)))
{
return 1;
}
var libArgs = Marshal.PtrToStructure<UnloadArgs>(arg);
var resourcePath = Marshal.PtrToStringUTF8(libArgs.ResourcePath);
var resourceMain = Marshal.PtrToStringUTF8(libArgs.ResourceMain);
WeakReference loadContext;
{
var resourceDllPath = GetPath(resourcePath, resourceMain);
if (!_loadContexts.Remove(resourceDllPath, out loadContext)) return 1;
if (!(loadContext.Target is AssemblyLoadContext)) return 1;
_exports.Remove(((AssemblyLoadContext) loadContext.Target).Name);
((AssemblyLoadContext) loadContext.Target).Unload();
}
for (var i = 0; loadContext.IsAlive && (i < 10); i++)
{
GC.Collect();
GC.WaitForPendingFinalizers();
}
if (loadContext.IsAlive)
{
Console.WriteLine("Resource " + resourcePath + " leaked!");
}
return 0;
}
/*public static async void CompileResource()
{
var resourceProjPath = GetPath(resourcePath, resourceMain);
var manager = new AnalyzerManager();
var analyzer = manager.GetProject(resourceProjPath);
var workspace = analyzer.GetWorkspace();
var solution = workspace.CurrentSolution;
//var dependencyGraph = solution.GetProjectDependencyGraph();
//GetTopologicallySortedProjects
foreach (var proj in solution.Projects)
{
var c = await proj.GetCompilationAsync(); .WithOptions(proj.CompilationOptions)
.AddReferences(proj.MetadataReferences);
var result = c.Emit(proj.Name + ".dll");
Console.WriteLine(result.Success);
}
}*/
private static readonly object TracingMutex = new object();
private static CollectTrace.Tracing _tracing;
private static byte _tracingState = 0;
public static void StartTracing(string traceFileName)
{
lock (TracingMutex)
{
if (_tracing != null || _tracingState == 1) return;
_tracing = new CollectTrace.Tracing();
}
Task.Run(async () => await CollectTrace.Collect(_tracing, new FileInfo(traceFileName + ".nettrace")));
lock (TracingMutex)
{
_tracingState = 1;
}
}
public static void StopTracing()
{
lock (TracingMutex)
{
if (_tracing == null || _tracingState == 0) return;
_tracing.Stop();
_tracing = null;
_tracingState = 0;
}
}
public static bool Import(string resourceName, string key, out object value)
{
if (_exports.TryGetValue(resourceName, out var resourceExports))
return resourceExports.TryGetValue(key, out value);
value = null;
return false;
}
public static void Export(string resourceName, string key, object value)
{
if (!_exports.TryGetValue(resourceName, out var resourceExports))
{
resourceExports = new Dictionary<string, object>();
_exports[resourceName] = resourceExports;
}
resourceExports[key] = value;
}
}
}