IKVM11  11
Java SE 11 Virtual Machine for .NET
Loading...
Searching...
No Matches
AssemblyResolver.cs
Go to the documentation of this file.
1/*
2 Copyright (C) 2010-2013 Jeroen Frijters
3
4 This software is provided 'as-is', without any express or implied
5 warranty. In no event will the authors be held liable for any damages
6 arising from the use of this software.
7
8 Permission is granted to anyone to use this software for any purpose,
9 including commercial applications, and to alter it and redistribute it
10 freely, subject to the following restrictions:
11
12 1. The origin of this software must not be misrepresented; you must not
13 claim that you wrote the original software. If you use this software
14 in a product, an acknowledgment in the product documentation would be
15 appreciated but is not required.
16 2. Altered source versions must be plainly marked as such, and must not be
17 misrepresented as being the original software.
18 3. This notice may not be removed or altered from any source distribution.
19
20 Jeroen Frijters
21 jeroen@frijters.net
22
23*/
24using System;
25using System.Collections.Generic;
26using System.IO;
27
28using IKVM.Reflection;
29using IKVM.Runtime;
30
32{
33
34 sealed class AssemblyResolver
35 {
36
37 readonly List<string> libpath = new List<string>();
38 Universe universe;
39 Version coreLibVersion;
40
41 internal enum WarningId
42 {
43 HigherVersion = 1,
44 LocationIgnored = 2,
45 InvalidLibDirectoryOption = 3,
46 InvalidLibDirectoryEnvironment = 4,
47 LegacySearchRule = 5,
48 }
49
50 internal delegate void WarningEvent(WarningId warning, string message, string[] parameters);
51 internal event WarningEvent Warning;
52
53 private void EmitWarning(WarningId warning, string message, params string[] parameters)
54 {
55 if (Warning != null)
56 {
57 Warning(warning, message, parameters);
58 }
59 else
60 {
61 Console.Error.WriteLine("Warning: " + message, parameters);
62 }
63 }
64
65 internal void Init(Universe universe, bool nostdlib, IList<string> references, IList<string> userLibPaths)
66 {
67 this.universe = universe;
68
69 // like the C# compiler, the references are loaded from:
70 // current directory, CLR directory, -lib: option, %LIB% environment
71 // (note that, unlike the C# compiler, we don't add the CLR directory if -nostdlib has been specified)
72 libpath.Add(Environment.CurrentDirectory);
73
74 if (nostdlib == false)
75 libpath.Add(System.Runtime.InteropServices.RuntimeEnvironment.GetRuntimeDirectory());
76
77 foreach (var str in userLibPaths)
78 AddLibraryPaths(str, true);
79
80 AddLibraryPaths(Environment.GetEnvironmentVariable("LIB") ?? "", false);
81
82 if (nostdlib)
83 {
84 coreLibVersion = LoadCoreLib(references).GetName().Version;
85 }
86 else
87 {
88 coreLibVersion = universe.Load(universe.CoreLibName).GetName().Version;
89 }
90
91#if IMPORTER
92 universe.AssemblyResolve += AssemblyResolve;
93#else
94 universe.AssemblyResolve += LegacyAssemblyResolve;
95#endif
96 }
97
98 internal Assembly LoadFile(string path)
99 {
100 string ex = null;
101 try
102 {
103 using (RawModule module = universe.OpenRawModule(path))
104 {
105 if (coreLibVersion != null)
106 {
107 // to avoid problems (i.e. weird exceptions), we don't allow assemblies to load that reference a newer version of mscorlib
108 foreach (AssemblyName asmref in module.GetReferencedAssemblies())
109 {
110 if (asmref.Name == universe.CoreLibName && asmref.Version > coreLibVersion)
111 {
112 Console.Error.WriteLine("Error: unable to load assembly '{0}' as it depends on a higher version of mscorlib than the one currently loaded", path);
113 Environment.Exit(1);
114 }
115 }
116 }
117 Assembly asm = universe.LoadAssembly(module);
118 if (asm.Location != module.Location && CanonicalizePath(asm.Location) != CanonicalizePath(module.Location))
119 {
120 EmitWarning(WarningId.LocationIgnored, "assembly \"{0}\" is ignored as previously loaded assembly \"{1}\" has the same identity \"{2}\"", path, asm.Location, asm.FullName);
121 }
122 return asm;
123 }
124 }
125 catch (IOException x)
126 {
127 ex = x.Message;
128 }
129 catch (UnauthorizedAccessException x)
130 {
131 ex = x.Message;
132 }
134 {
135 ex = x.Message;
136 }
137 Console.Error.WriteLine("Error: unable to load assembly '{0}'" + Environment.NewLine + " ({1})", path, ex);
138 Environment.Exit(1);
139 return null;
140 }
141
142 private static string CanonicalizePath(string path)
143 {
144 try
145 {
146 System.IO.FileInfo fi = new System.IO.FileInfo(path);
147 if (fi.DirectoryName == null)
148 {
149 return path.Length > 1 && path[1] == ':' ? path.ToUpper() : path;
150 }
151 string dir = CanonicalizePath(fi.DirectoryName);
152 string name = fi.Name;
153 try
154 {
155 string[] arr = System.IO.Directory.GetFileSystemEntries(dir, name);
156 if (arr.Length == 1)
157 {
158 name = arr[0];
159 }
160 }
161 catch (System.UnauthorizedAccessException)
162 {
163 }
164 catch (System.IO.IOException)
165 {
166 }
167 return System.IO.Path.Combine(dir, name);
168 }
169 catch (System.UnauthorizedAccessException)
170 {
171 }
172 catch (System.IO.IOException)
173 {
174 }
175 catch (System.Security.SecurityException)
176 {
177 }
178 catch (System.NotSupportedException)
179 {
180 }
181 return path;
182 }
183
184 internal Assembly LoadWithPartialName(string name)
185 {
186 foreach (string path in FindAssemblyPath(name + ".dll"))
187 return LoadFile(path);
188
189 return null;
190 }
191
192 internal bool ResolveReference(Dictionary<string, Assembly> cache, ref Assembly[] references, string reference)
193 {
194 string[] files = new string[0];
195 try
196 {
197 string path = Path.GetDirectoryName(reference);
198 files = Directory.GetFiles(path == "" ? "." : path, Path.GetFileName(reference));
199 }
200 catch (ArgumentException)
201 {
202 }
203 catch (IOException)
204 {
205 }
206 if (files.Length == 0)
207 {
208 Assembly asm = null;
209 cache.TryGetValue(reference, out asm);
210 if (asm == null)
211 {
212 foreach (string found in FindAssemblyPath(reference))
213 {
214 asm = LoadFile(found);
215 cache.Add(reference, asm);
216 break;
217 }
218 }
219 if (asm == null)
220 {
221 return false;
222 }
223 ArrayAppend(ref references, asm);
224 }
225 else
226 {
227 foreach (string file in files)
228 {
229 Assembly asm;
230 if (!cache.TryGetValue(file, out asm))
231 {
232 asm = LoadFile(file);
233 }
234 ArrayAppend(ref references, asm);
235 }
236 }
237 return true;
238 }
239
240 private static void ArrayAppend<T>(ref T[] array, T element)
241 {
242 if (array == null)
243 {
244 array = new T[] { element };
245 }
246 else
247 {
248 array = ArrayUtil.Concat(array, element);
249 }
250 }
251
252 Assembly AssemblyResolve(object sender, IKVM.Reflection.ResolveEventArgs args)
253 {
254 var name = new AssemblyName(args.Name);
255
256 foreach (var asm in universe.GetAssemblies())
257 if (Match(asm.GetName(), name))
258 return asm;
259
260 if (args.RequestingAssembly != null)
261 {
262 return universe.CreateMissingAssembly(args.Name);
263 }
264 else
265 {
266 return null;
267 }
268 }
269
270 private Assembly LegacyAssemblyResolve(object sender, IKVM.Reflection.ResolveEventArgs args)
271 {
272 return LegacyLoad(new AssemblyName(args.Name), args.RequestingAssembly);
273 }
274
275 internal Assembly LegacyLoad(AssemblyName name, Assembly requestingAssembly)
276 {
277 foreach (Assembly asm in universe.GetAssemblies())
278 if (Match(asm.GetName(), name))
279 return asm;
280
281 foreach (string file in FindAssemblyPath(name.Name + ".dll"))
282 if (Match(AssemblyName.GetAssemblyName(file), name))
283 return LoadFile(file);
284
285 if (requestingAssembly != null)
286 {
287 var path = Path.Combine(Path.GetDirectoryName(requestingAssembly.Location), name.Name + ".dll");
288 if (File.Exists(path) && Match(AssemblyName.GetAssemblyName(path), name))
289 return LoadFile(path);
290 }
291
292#if EXPORTER
293 return universe.CreateMissingAssembly(name.FullName);
294#else
295 Console.Error.WriteLine("Error: unable to find assembly '{0}'", name.FullName);
296 if (requestingAssembly != null)
297 Console.Error.WriteLine(" (a dependency of '{0}')", requestingAssembly.FullName);
298#endif
299
300 throw new InvalidOperationException();
301 }
302
303 private bool Match(AssemblyName assemblyDef, AssemblyName assemblyRef)
304 {
305 return assemblyRef.Name == assemblyDef.Name;
306 }
307
308 private void AddLibraryPaths(string str, bool option)
309 {
310 foreach (string dir in str.Split(Path.PathSeparator))
311 {
312 if (Directory.Exists(dir))
313 {
314 libpath.Add(dir);
315 }
316 else if (dir != "")
317 {
318 if (option)
319 {
320 EmitWarning(WarningId.InvalidLibDirectoryOption, "directory \"{0}\" specified in -lib option is not valid", dir);
321 }
322 else
323 {
324 EmitWarning(WarningId.InvalidLibDirectoryEnvironment, "directory \"{0}\" specified in LIB environment is not valid", dir);
325 }
326 }
327 }
328 }
329
330 Assembly LoadCoreLib(IList<string> references)
331 {
332 if (references != null)
333 {
334 foreach (var r in references)
335 {
336 try
337 {
338 if (AssemblyName.GetAssemblyName(r).Name == universe.CoreLibName)
339 {
340 return LoadFile(r);
341 }
342 }
343 catch
344 {
345
346 }
347 }
348 }
349
350 foreach (var coreLib in FindAssemblyPath(universe.CoreLibName + ".dll"))
351 return LoadFile(coreLib);
352
353 Console.Error.WriteLine($"Error: unable to find '{universe.CoreLibName}.dll'.");
354 Environment.Exit(1);
355 return null;
356 }
357
358 IEnumerable<string> FindAssemblyPath(string file)
359 {
360 if (Path.IsPathRooted(file))
361 {
362 if (File.Exists(file))
363 {
364 yield return file;
365 }
366 }
367 else
368 {
369 foreach (string dir in libpath)
370 {
371 var path = Path.Combine(dir, file);
372 if (File.Exists(path))
373 yield return path;
374
375 // for legacy compat, we try again after appending .dll
376 path = Path.Combine(dir, file + ".dll");
377 if (File.Exists(path))
378 {
379 EmitWarning(WarningId.LegacySearchRule, "found assembly \"{0}\" using legacy search rule, please append '.dll' to the reference", file);
380 yield return path;
381 }
382 }
383 }
384 }
385
386 }
387
388}
IKVM.Reflection.AssemblyName AssemblyName