-
Notifications
You must be signed in to change notification settings - Fork 4.7k
/
AssemblySaveTools.cs
242 lines (197 loc) · 10.9 KB
/
AssemblySaveTools.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
// Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the MIT license.
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Runtime.Loader;
using Xunit;
namespace System.Reflection.Emit.Tests
{
class TestAssemblyLoadContext : AssemblyLoadContext
{
public TestAssemblyLoadContext() : base(isCollectible: true)
{
}
protected override Assembly? Load(AssemblyName name)
{
return null;
}
}
internal static class AssemblySaveTools
{
private static readonly AssemblyName s_assemblyName = new AssemblyName("MyDynamicAssembly")
{
Version = new Version("1.2.3.4"),
};
internal static void WriteAssemblyToDisk(AssemblyName assemblyName, Type[] types, string fileLocation)
{
AssemblyBuilder assemblyBuilder = PopulateAssemblyBuilderAndSaveMethod(
assemblyName, null, typeof(string), out MethodInfo saveMethod);
ModuleBuilder mb = assemblyBuilder.DefineDynamicModule(assemblyName.Name);
PopulateMembersForModule(mb, types);
saveMethod.Invoke(assemblyBuilder, new object[] { fileLocation });
}
private static void PopulateMembersForModule(ModuleBuilder mb, Type[] types)
{
foreach (Type type in types)
{
TypeBuilder tb = mb.DefineType(type.FullName, type.Attributes, type.BaseType);
MethodInfo[] methods = type.IsInterface ? type.GetMethods() : type.GetMethods(BindingFlags.DeclaredOnly);
foreach (var method in methods)
{
ParameterInfo[] parameters = method.GetParameters();
MethodBuilder meb = tb.DefineMethod(method.Name, method.Attributes, method.CallingConvention, method.ReturnType, parameters.Select(p => p.ParameterType).ToArray());
foreach(ParameterInfo param in parameters)
{
meb.DefineParameter(param.Position + 1, param.Attributes, param.Name);
}
}
foreach (FieldInfo field in type.GetFields())
{
tb.DefineField(field.Name, field.FieldType, field.Attributes);
}
tb.CreateType();
}
}
internal static void WriteAssemblyToStream(AssemblyName assemblyName, Type[] types, Stream stream)
{
AssemblyBuilder assemblyBuilder = PopulateAssemblyBuilderAndSaveMethod(
assemblyName, null, typeof(Stream), out MethodInfo saveMethod);
ModuleBuilder mb = assemblyBuilder.DefineDynamicModule(assemblyName.Name);
PopulateMembersForModule(mb, types);
saveMethod.Invoke(assemblyBuilder, new object[] { stream });
}
internal static AssemblyBuilder PopulateAssemblyBuilderTypeBuilderAndSaveMethod(out TypeBuilder typeBuilder, out MethodInfo saveMethod)
{
AssemblyBuilder ab = PopulateAssemblyBuilderAndSaveMethod(s_assemblyName, null, typeof(string), out saveMethod);
typeBuilder = ab.DefineDynamicModule("MyModule").DefineType("MyType", TypeAttributes.Public | TypeAttributes.Class);
return ab;
}
internal static AssemblyBuilder PopulateAssemblyBuilderAndSaveMethod(AssemblyName assemblyName, out MethodInfo saveMethod) =>
PopulateAssemblyBuilderAndSaveMethod(assemblyName, null, typeof(string), out saveMethod);
internal static AssemblyBuilder PopulateAssemblyBuilderAndSaveMethod(AssemblyName assemblyName,
List<CustomAttributeBuilder>? assemblyAttributes, Type parameterType, out MethodInfo saveMethod)
{
Type assemblyType = Type.GetType("System.Reflection.Emit.AssemblyBuilderImpl, System.Reflection.Emit", throwOnError: true)!;
saveMethod = assemblyType.GetMethod("Save", BindingFlags.NonPublic | BindingFlags.Instance, new Type[] { parameterType });
MethodInfo defineDynamicAssemblyMethod = assemblyType.GetMethod("DefinePersistedAssembly", BindingFlags.NonPublic | BindingFlags.Static,
new Type[] { typeof(AssemblyName), typeof(Assembly), typeof(List<CustomAttributeBuilder>) });
return (AssemblyBuilder)defineDynamicAssemblyMethod.Invoke(null,
new object[] { assemblyName, CoreMetadataAssemblyResolver.s_coreAssembly, assemblyAttributes });
}
internal static Assembly LoadAssemblyFromPath(string filePath) =>
new MetadataLoadContext(new CoreMetadataAssemblyResolver()).LoadFromAssemblyPath(filePath);
internal static Assembly LoadAssemblyFromStream(Stream stream) =>
new MetadataLoadContext(new CoreMetadataAssemblyResolver()).LoadFromStream(stream);
internal static void AssertAssemblyNameAndModule(AssemblyName sourceAName, AssemblyName aNameFromDisk, Module moduleFromDisk)
{
// Runtime assemblies adding AssemblyNameFlags.PublicKey in Assembly.GetName() overloads
Assert.Equal(sourceAName.Flags | AssemblyNameFlags.PublicKey, aNameFromDisk.Flags);
Assert.Equal(sourceAName.Name, aNameFromDisk.Name);
Assert.Equal(sourceAName.Version, aNameFromDisk.Version);
Assert.Equal(sourceAName.CultureInfo, aNameFromDisk.CultureInfo);
Assert.Equal(sourceAName.CultureName, aNameFromDisk.CultureName);
Assert.Equal(sourceAName.ContentType, aNameFromDisk.ContentType);
Assert.NotNull(moduleFromDisk);
Assert.Equal(sourceAName.Name, moduleFromDisk.ScopeName);
Assert.Empty(moduleFromDisk.GetTypes());
}
internal static void AssertTypeProperties(Type sourceType, Type typeFromDisk)
{
Assert.Equal(sourceType.Name, typeFromDisk.Name);
Assert.Equal(sourceType.Namespace, typeFromDisk.Namespace);
Assert.Equal(sourceType.Attributes, typeFromDisk.Attributes);
Assert.Equal(sourceType.IsInterface, typeFromDisk.IsInterface);
Assert.Equal(sourceType.IsValueType, typeFromDisk.IsValueType);
}
internal static void AssertFields(FieldInfo[] declaredFields, FieldInfo[] fieldsFromDisk)
{
Assert.Equal(declaredFields.Length, fieldsFromDisk.Length);
for (int j = 0; j < declaredFields.Length; j++)
{
FieldInfo sourceField = declaredFields[j];
FieldInfo fieldFromDisk = fieldsFromDisk[j];
Assert.Equal(sourceField.Name, fieldFromDisk.Name);
Assert.Equal(sourceField.Attributes, fieldFromDisk.Attributes);
Assert.Equal(sourceField.FieldType.FullName, fieldFromDisk.FieldType.FullName);
}
}
internal static void AssertMethods(MethodInfo[] sourceMethods, MethodInfo[] methodsFromDisk)
{
Assert.Equal(sourceMethods.Length, methodsFromDisk.Length);
for (int j = 0; j < sourceMethods.Length; j++)
{
MethodInfo sourceMethod = sourceMethods[j];
MethodInfo methodFromDisk = methodsFromDisk[j];
Assert.Equal(sourceMethod.Name, methodFromDisk.Name);
Assert.Equal(sourceMethod.Attributes, methodFromDisk.Attributes);
Assert.Equal(sourceMethod.ReturnType.FullName, methodFromDisk.ReturnType.FullName);
AssertParameters(sourceMethod.GetParameters(), methodFromDisk.GetParameters());
}
}
private static void AssertParameters(ParameterInfo[] sourceParameters, ParameterInfo[] parametersLoaded)
{
Assert.Equal(sourceParameters.Length, parametersLoaded.Length);
for (int i = 0; i < sourceParameters.Length; i++)
{
Assert.Equal(sourceParameters[i].Name, parametersLoaded[i].Name);
Assert.Equal(sourceParameters[i].ParameterType.FullName, parametersLoaded[i].ParameterType.FullName);
Assert.Equal(sourceParameters[i].Attributes, parametersLoaded[i].Attributes);
Assert.Equal(sourceParameters[i].Position, parametersLoaded[i].Position);
}
}
}
// The resolver copied from MLC tests
internal sealed class CoreMetadataAssemblyResolver : MetadataAssemblyResolver
{
public static Assembly s_coreAssembly = typeof(object).Assembly;
public static Assembly s_emitAssembly = typeof(AssemblySaveTools).Assembly;
public CoreMetadataAssemblyResolver() { }
public override Assembly Resolve(MetadataLoadContext context, AssemblyName assemblyName)
{
string name = assemblyName.Name;
if (name.Equals("mscorlib", StringComparison.OrdinalIgnoreCase) ||
name.Equals("System.Private.CoreLib", StringComparison.OrdinalIgnoreCase) ||
name.Equals("System.Runtime", StringComparison.OrdinalIgnoreCase) ||
name.Equals("netstandard", StringComparison.OrdinalIgnoreCase) ||
// For interop attributes such as DllImport and Guid:
name.Equals("System.Runtime.InteropServices", StringComparison.OrdinalIgnoreCase))
{
if (_coreAssembly == null)
{
_coreAssembly = context.LoadFromStream(CreateStreamForCoreAssembly());
}
return _coreAssembly;
}
if (name.Equals("System.Reflection.Emit.Tests", StringComparison.OrdinalIgnoreCase))
{
if (_emitAssembly == null)
{
_emitAssembly = context.LoadFromStream(CreateStreamForEmitAssembly());
}
return _emitAssembly;
}
return null;
}
private Assembly _emitAssembly;
private Assembly _coreAssembly;
private Stream CreateStreamForEmitAssembly() =>
File.OpenRead(AssemblyPathHelper.GetAssemblyLocation(s_emitAssembly));
private static Stream CreateStreamForCoreAssembly()
{
// We need a core assembly in IL form. Since this version of this code is for Jitted platforms, the System.Private.Corelib
// of the underlying runtime will do just fine.
if (PlatformDetection.IsNotBrowser)
{
string assumedLocationOfCoreLibrary = typeof(object).Assembly.Location;
if (string.IsNullOrEmpty(assumedLocationOfCoreLibrary))
{
throw new Exception("Could not find a core assembly to use for tests as 'typeof(object).Assembly.Location` returned " +
"a null or empty value. The most likely cause is that you built the tests for a Jitted runtime but are running them " +
"on an AoT runtime.");
}
}
return File.OpenRead(AssemblyPathHelper.GetAssemblyLocation(s_coreAssembly));
}
}
}