IKVM11  11
Java SE 11 Virtual Machine for .NET
Loading...
Searching...
No Matches
RuntimeAssemblyClassLoader.cs
Go to the documentation of this file.
1/*
2 Copyright (C) 2002-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.IO;
26using System.Collections.Generic;
27using System.Diagnostics;
28using System.Threading;
29using System.Runtime.Serialization;
31
33using IKVM.Attributes;
35
36#if IMPORTER || EXPORTER
37using IKVM.Reflection;
38
39using Type = IKVM.Reflection.Type;
40#else
41using System.Reflection;
42
44#endif
45
46#if IMPORTER
48#endif
49
50namespace IKVM.Runtime
51{
52
56 internal class RuntimeAssemblyClassLoader : RuntimeClassLoader
57 {
58
59 AssemblyLoader assemblyLoader;
60 string[] references;
61 RuntimeAssemblyClassLoader[] delegates;
62#if !IMPORTER && !EXPORTER && !FIRST_PASS
63 JavaClassLoaderConstructionInProgress jclcip;
64 java.security.ProtectionDomain protectionDomain;
65 byte hasCustomClassLoader; /* 0 = unknown, 1 = yes, 2 = no */
66#endif
67 Dictionary<int, List<int>> exports;
68 string[] exportedAssemblyNames;
69 AssemblyLoader[] exportedAssemblies;
70 Dictionary<Assembly, AssemblyLoader> exportedLoaders;
71
75 sealed class AssemblyLoader
76 {
77
78 readonly RuntimeAssemblyClassLoader loader;
79 readonly Assembly assembly;
80
81 bool[] isJavaModule;
82 Module[] modules;
83 Dictionary<string, string> nameMap;
84 bool hasDotNetModule;
85 AssemblyName[] internalsVisibleTo;
86 string[] jarList;
87#if !IMPORTER && !EXPORTER && !FIRST_PASS
88 sun.misc.URLClassPath urlClassPath;
89#endif
90
96 internal AssemblyLoader(RuntimeAssemblyClassLoader loader, Assembly assembly)
97 {
98 this.loader = loader ?? throw new ArgumentNullException(nameof(loader));
99 this.assembly = assembly ?? throw new ArgumentNullException(nameof(assembly));
100
101 modules = assembly.GetModules(false);
102 isJavaModule = new bool[modules.Length];
103
104 for (int i = 0; i < modules.Length; i++)
105 {
106 var attr = loader.Context.AttributeHelper.GetJavaModuleAttributes(modules[i]);
107 if (attr.Length > 0)
108 {
109 isJavaModule[i] = true;
110
111 foreach (JavaModuleAttribute jma in attr)
112 {
113 var map = jma.GetClassMap();
114 if (map != null)
115 {
116 if (nameMap == null)
117 nameMap = new Dictionary<string, string>();
118
119 for (int j = 0; j < map.Length; j += 2)
120 {
121 var key = map[j];
122 var val = map[j + 1];
123
124 // TODO if there is a name clash between modules, this will throw.
125 // Figure out how to handle that.
126 nameMap.Add(key, val);
127 }
128 }
129
130 var jars = jma.Jars;
131 if (jars != null)
132 {
133 if (jarList == null)
134 {
135 jarList = jars;
136 }
137 else
138 {
139 var newList = new string[jarList.Length + jars.Length];
140 Array.Copy(jarList, newList, jarList.Length);
141 Array.Copy(jars, 0, newList, jarList.Length, jars.Length);
142 jarList = newList;
143 }
144 }
145 }
146 }
147 else
148 {
149 hasDotNetModule = true;
150 }
151 }
152 }
153
154 internal Assembly Assembly
155 {
156 get { return assembly; }
157 }
158
159 internal bool HasJavaModule
160 {
161 get
162 {
163 for (int i = 0; i < isJavaModule.Length; i++)
164 if (isJavaModule[i])
165 return true;
166
167 return false;
168 }
169 }
170
176 Type GetType(string name)
177 {
178 try
179 {
180 return assembly.GetType(name);
181 }
182 catch (ArgumentException)
183 {
184
185 }
186 catch (FileLoadException e)
187 {
188 // this can only happen if the assembly was loaded in the ReflectionOnly
189 // context and the requested type references a type in another assembly
190 // that cannot be found in the ReflectionOnly context
191 // TODO figure out what other exceptions Assembly.GetType() can throw
192 loader.Diagnostics.GenericRuntimeInfo(e.Message);
193 }
194
195 return null;
196 }
197
204 Type GetType(Module module, string name)
205 {
206 try
207 {
208 return module.GetType(name);
209 }
210 catch (ArgumentException)
211 {
212
213 }
214 catch (FileLoadException e)
215 {
216 // this can only happen if the assembly was loaded in the ReflectionOnly
217 // context and the requested type references a type in another assembly
218 // that cannot be found in the ReflectionOnly context
219 // TODO figure out what other exceptions Assembly.GetType() can throw
220 loader.Diagnostics.GenericRuntimeInfo(e.Message);
221 }
222
223 return null;
224 }
225
226 Type GetJavaType(Module module, string name)
227 {
228 try
229 {
230 string n = null;
231 if (nameMap != null)
232 nameMap.TryGetValue(name, out n);
233
234 var t = GetType(module, n ?? name);
235 if (t == null)
236 {
237 n = name.Replace('$', '+');
238 if (!ReferenceEquals(n, name))
239 t = GetType(n);
240 }
241
242 if (t != null
243 && !loader.Context.AttributeHelper.IsHideFromJava(t)
244 && !t.IsArray
245 && !t.IsPointer
246 && !t.IsByRef)
247 return t;
248 }
249 catch (ArgumentException x)
250 {
251 // we can end up here because we replace the $ with a plus sign
252 // (or client code did a Class.forName() on an invalid name)
253 loader.Diagnostics.GenericRuntimeInfo(x.Message);
254 }
255
256 return null;
257 }
258
259 internal RuntimeJavaType DoLoad(string name)
260 {
261 for (int i = 0; i < modules.Length; i++)
262 {
263 if (isJavaModule[i])
264 {
265 var type = GetJavaType(modules[i], name);
266 if (type != null)
267 {
268 // check the name to make sure that the canonical name was used
269 if (RuntimeManagedByteCodeJavaType.GetName(loader.Context, type) == name)
270 {
271 return loader.Context.ManagedByteCodeJavaTypeFactory.newInstance(name, type);
272 }
273 }
274 }
275 else
276 {
277 var type = GetType(modules[i], RuntimeManagedJavaType.DemangleTypeName(name));
278
279 // type could be loaded from this assembly, but ended up forwarded to a different assembly
280 // this class loader isn't responsible for it
281 if (type != null && type.Assembly != assembly)
282 return null;
283
284 // type was loaded successfully
285 if (type != null && RuntimeManagedJavaType.IsAllowedOutside(type))
286 {
287 // check the name to make sure that the canonical name was used
288 if (RuntimeManagedJavaType.GetName(loader.Context, type) == name)
289 {
290 return RuntimeManagedJavaType.Create(loader.Context, type, name);
291 }
292 }
293 }
294 }
295
296 if (hasDotNetModule)
297 {
298 // for fake types, we load the declaring outer type (the real one) and
299 // let that generated the manufactured nested classes
300 // (note that for generic outer types, we need to duplicate this in ClassLoaderWrapper.LoadGenericClass)
301 RuntimeJavaType outer = null;
302 if (name.EndsWith(RuntimeManagedJavaType.DelegateInterfaceSuffix))
303 {
304 outer = DoLoad(name.Substring(0, name.Length - RuntimeManagedJavaType.DelegateInterfaceSuffix.Length));
305 }
306 else if (name.EndsWith(RuntimeManagedJavaType.AttributeAnnotationSuffix))
307 {
308 outer = DoLoad(name.Substring(0, name.Length - RuntimeManagedJavaType.AttributeAnnotationSuffix.Length));
309 }
310 else if (name.EndsWith(RuntimeManagedJavaType.AttributeAnnotationReturnValueSuffix))
311 {
312 outer = DoLoad(name.Substring(0, name.Length - RuntimeManagedJavaType.AttributeAnnotationReturnValueSuffix.Length));
313 }
314 else if (name.EndsWith(RuntimeManagedJavaType.AttributeAnnotationMultipleSuffix))
315 {
316 outer = DoLoad(name.Substring(0, name.Length - RuntimeManagedJavaType.AttributeAnnotationMultipleSuffix.Length));
317 }
318 else if (name.EndsWith(RuntimeManagedJavaType.EnumEnumSuffix))
319 {
320 outer = DoLoad(name.Substring(0, name.Length - RuntimeManagedJavaType.EnumEnumSuffix.Length));
321 }
322
323 if (outer != null && outer.IsFakeTypeContainer)
324 foreach (var tw in outer.InnerClasses)
325 if (tw.Name == name)
326 return tw;
327 }
328
329 return null;
330 }
331
338 internal JavaTypeName? GetTypeNameAndType(Type type, out bool isJavaType)
339 {
340 // find the module index of the type's module
341 var module = type.Module;
342 int moduleIndex = -1;
343 for (int i = 0; i < modules.Length; i++)
344 {
345 if (modules[i] == module)
346 {
347 moduleIndex = i;
348 break;
349 }
350 }
351
352 // if the type is associated with a Java module, the type is a Java type
353 if (isJavaModule[moduleIndex])
354 {
355 isJavaType = true;
356
357 // types which should be hidden from Java should not have Java names
358 if (loader.Context.AttributeHelper.IsHideFromJava(type))
359 return null;
360
361 return RuntimeManagedByteCodeJavaType.GetName(loader.Context, type);
362 }
363 else
364 {
365 isJavaType = false;
366
367 // type is a .NET type, but not allowed visibilty to Java
368 if (RuntimeManagedJavaType.IsAllowedOutside(type) == false)
369 return null;
370
371 return RuntimeManagedJavaType.GetName(loader.Context, type);
372 }
373 }
374
375 internal RuntimeJavaType CreateJavaTypeForAssemblyType(Type type)
376 {
377 var name = GetTypeNameAndType(type, out bool isJavaType);
378 if (name == null)
379 return null;
380
381 if (isJavaType)
382 {
383 // since this type was compiled from Java source, we have to look for our attributes
384 return loader.Context.ManagedByteCodeJavaTypeFactory.newInstance(name, type);
385 }
386 else
387 {
388 // since this type was not compiled from Java source, we don't need to
389 // look for our attributes, but we do need to filter unrepresentable
390 // stuff (and transform some other stuff)
391 return RuntimeManagedJavaType.Create(loader.Context, type, name);
392 }
393 }
394
395 internal bool InternalsVisibleTo(AssemblyName otherName)
396 {
397 if (internalsVisibleTo == null)
398 Interlocked.CompareExchange(ref internalsVisibleTo, loader.Context.AttributeHelper.GetInternalsVisibleToAttributes(assembly), null);
399
400 foreach (var name in internalsVisibleTo)
401 {
402 // we match the simple name and PublicKeyToken (because the AssemblyName constructor used
403 // by GetInternalsVisibleToAttributes() only sets the PublicKeyToken, even if a PublicKey is specified)
404 if (ReflectUtil.MatchNameAndPublicKeyToken(name, otherName))
405 {
406 return true;
407 }
408 }
409
410 return false;
411 }
412
413#if !IMPORTER && !EXPORTER && !FIRST_PASS
414
415 internal java.util.Enumeration FindResources(string name)
416 {
417 if (urlClassPath == null)
418 {
419 if (jarList == null)
420 return global::java.util.Collections.emptyEnumeration();
421
422 var urls = new List<java.net.URL>();
423 foreach (var jar in jarList)
424 urls.Add(MakeResourceURL(assembly, jar));
425
426 Interlocked.CompareExchange(ref urlClassPath, new sun.misc.URLClassPath(urls.ToArray()), null);
427 }
428
429 return urlClassPath.findResources(name, true);
430 }
431
432#endif
433 }
434
440 internal RuntimeAssemblyClassLoader(RuntimeContext context, Assembly assembly) :
441 this(context, assembly, null)
442 {
443
444 }
445
452 internal RuntimeAssemblyClassLoader(RuntimeContext context, Assembly assembly, string[] fixedReferences) :
453 base(context, CodeGenOptions.None, null)
454 {
455 this.assemblyLoader = new AssemblyLoader(this, assembly);
456 this.references = fixedReferences;
457 }
458
459#if IMPORTER
460
461 internal static void PreloadExportedAssemblies(StaticCompiler compiler, Assembly assembly)
462 {
463 if (assembly.GetManifestResourceInfo("ikvm.exports") != null)
464 {
465 using (Stream stream = assembly.GetManifestResourceStream("ikvm.exports"))
466 {
467 var rdr = new BinaryReader(stream);
468 var assemblyCount = rdr.ReadInt32();
469 for (int i = 0; i < assemblyCount; i++)
470 {
471 var assemblyName = rdr.ReadString();
472 var typeCount = rdr.ReadInt32();
473 if (typeCount != 0)
474 {
475 for (int j = 0; j < typeCount; j++)
476 rdr.ReadInt32();
477
478 try
479 {
480 compiler.LoadFile(Path.Combine(Path.GetDirectoryName(assembly.Location), new AssemblyName(assemblyName).Name + ".dll"));
481 }
482 catch
483 {
484
485 }
486 }
487 }
488 }
489 }
490 }
491
492#endif
493
494 void DoInitializeExports()
495 {
496 lock (this)
497 {
498 if (delegates == null)
499 {
500 if (ReflectUtil.IsDynamicAssembly(assemblyLoader.Assembly) == false && assemblyLoader.Assembly.GetManifestResourceInfo("ikvm.exports") != null)
501 {
502 var wildcardExports = new List<string>();
503
504 using (var stream = assemblyLoader.Assembly.GetManifestResourceStream("ikvm.exports"))
505 {
506 var rdr = new BinaryReader(stream);
507 var assemblyCount = rdr.ReadInt32();
508 exports = new Dictionary<int, List<int>>();
509 exportedAssemblies = new AssemblyLoader[assemblyCount];
510 exportedAssemblyNames = new string[assemblyCount];
511 exportedLoaders = new Dictionary<Assembly, AssemblyLoader>();
512
513 for (int i = 0; i < assemblyCount; i++)
514 {
515 exportedAssemblyNames[i] = string.Intern(rdr.ReadString());
516
517 int typeCount = rdr.ReadInt32();
518 if (typeCount == 0 && references == null)
519 wildcardExports.Add(exportedAssemblyNames[i]);
520
521 for (int j = 0; j < typeCount; j++)
522 {
523 int hash = rdr.ReadInt32();
524 if (exports.TryGetValue(hash, out List<int> assemblies) == false)
525 {
526 assemblies = new List<int>();
527 exports.Add(hash, assemblies);
528 }
529
530 assemblies.Add(i);
531 }
532 }
533 }
534
535 references ??= wildcardExports.ToArray();
536 }
537 else
538 {
539 var refNames = assemblyLoader.Assembly.GetReferencedAssemblies();
540 references = new string[refNames.Length];
541 for (int i = 0; i < references.Length; i++)
542 references[i] = refNames[i].FullName;
543 }
544
545 Interlocked.Exchange(ref delegates, new RuntimeAssemblyClassLoader[references.Length]);
546 }
547 }
548 }
549
550 void LazyInitExports()
551 {
552 if (delegates == null)
553 DoInitializeExports();
554 }
555
556 internal Assembly MainAssembly => assemblyLoader.Assembly;
557
558 internal Assembly GetAssembly(RuntimeJavaType wrapper)
559 {
560 Debug.Assert(wrapper.ClassLoader == this);
561
562 while (wrapper.IsFakeNestedType)
563 wrapper = wrapper.DeclaringTypeWrapper;
564
565 return wrapper.TypeAsBaseType.Assembly;
566 }
567
568 Assembly LoadAssemblyOrClearName(ref string name, bool exported)
569 {
570 // previous load attempt failed
571 if (name == null)
572 return null;
573
574 try
575 {
576 return Context.Resolver.ResolveAssembly(name).AsReflection();
577 }
578 catch
579 {
580 // cache failure by clearing out the name the caller uses
581 name = null;
582 // should we issue a warning error (in ikvmc)?
583 return null;
584 }
585 }
586
587 internal RuntimeJavaType DoLoad(string name)
588 {
589 var tw = assemblyLoader.DoLoad(name);
590 if (tw != null)
591 return RegisterInitiatingLoader(tw);
592
593 LazyInitExports();
594
595 if (exports != null && exports.TryGetValue(JVM.PersistableHash(name), out var assemblies))
596 {
597 foreach (int index in assemblies)
598 {
599 var loader = TryGetLoaderByIndex(index);
600 if (loader != null)
601 {
602 tw = loader.DoLoad(name);
603 if (tw != null)
604 return RegisterInitiatingLoader(tw);
605 }
606 }
607 }
608
609 return null;
610 }
611
612 internal JavaTypeName? GetTypeNameAndType(Type type, out bool isJavaType)
613 {
614 return GetLoader(type.Assembly).GetTypeNameAndType(type, out isJavaType);
615 }
616
617 AssemblyLoader TryGetLoaderByIndex(int index)
618 {
619 var loader = exportedAssemblies[index];
620 if (loader == null)
621 {
622 var asm = LoadAssemblyOrClearName(ref exportedAssemblyNames[index], true);
623 if (asm != null)
624 loader = exportedAssemblies[index] = GetLoaderForExportedAssembly(asm);
625 }
626
627 return loader;
628 }
629
630 internal List<Assembly> GetAllAvailableAssemblies()
631 {
632 var list = new List<Assembly>();
633 list.Add(assemblyLoader.Assembly);
634
635 LazyInitExports();
636
637 if (exportedAssemblies != null)
638 {
639 for (int i = 0; i < exportedAssemblies.Length; i++)
640 {
641 var loader = TryGetLoaderByIndex(i);
642 if (loader != null && Context.AssemblyClassLoaderFactory.FromAssembly(loader.Assembly) == this)
643 list.Add(loader.Assembly);
644 }
645 }
646
647 return list;
648 }
649
650 AssemblyLoader GetLoader(Assembly assembly)
651 {
652 if (assemblyLoader.Assembly == assembly)
653 return assemblyLoader;
654
655 return GetLoaderForExportedAssembly(assembly);
656 }
657
658 AssemblyLoader GetLoaderForExportedAssembly(Assembly assembly)
659 {
660 LazyInitExports();
661
662 AssemblyLoader loader;
663 lock (exportedLoaders)
664 exportedLoaders.TryGetValue(assembly, out loader);
665
666 if (loader == null)
667 {
668 loader = new AssemblyLoader(this, assembly);
669
670 lock (exportedLoaders)
671 {
672 if (exportedLoaders.TryGetValue(assembly, out AssemblyLoader existing))
673 {
674 // another thread beat us to it
675 loader = existing;
676 }
677 else
678 {
679 exportedLoaders.Add(assembly, loader);
680 }
681 }
682 }
683
684 return loader;
685 }
686
693 internal virtual RuntimeJavaType GetJavaTypeFromAssemblyType(Type type)
694 {
695 if (type.Name.EndsWith("[]"))
696 throw new InternalException();
697 if (Context.AssemblyClassLoaderFactory.FromAssembly(type.Assembly) != this)
698 throw new InternalException();
699
700 var javaType = GetLoader(type.Assembly).CreateJavaTypeForAssemblyType(type);
701 if (javaType != null)
702 {
703 if (type.IsGenericType && !type.IsGenericTypeDefinition)
704 {
705 // in the case of "magic" implementation generic type instances we'll end up here as well,
706 // but then wrapper.ClassLoader will return this anyway
707 javaType = javaType.ClassLoader.RegisterInitiatingLoader(javaType);
708 }
709 else
710 {
711 javaType = RegisterInitiatingLoader(javaType);
712 }
713
714 // this really shouldn't happen, it means that we have two different types in our assembly that both have the same Java name
715 if (javaType.TypeAsTBD != type && (!javaType.IsRemapped || javaType.TypeAsBaseType != type))
716 {
717#if IMPORTER
718 throw new FatalCompilerErrorException(DiagnosticEvent.AssemblyContainsDuplicateClassNames(type.FullName, javaType.TypeAsTBD.FullName, javaType.Name, type.Assembly.FullName));
719#else
720 throw new InternalException($"\nType \"{type.FullName}\" and \"{javaType.TypeAsTBD.FullName}\" both map to the same name \"{javaType.Name}\".");
721#endif
722 }
723
724 return javaType;
725 }
726
727 return null;
728 }
729
730 protected override RuntimeJavaType LoadClassImpl(string name, LoadMode mode)
731 {
732 var tw = FindLoadedClass(name);
733 if (tw != null)
734 return tw;
735
736#if !IMPORTER && !EXPORTER && !FIRST_PASS
737
738 while (hasCustomClassLoader != 2)
739 {
740 if (hasCustomClassLoader == 0)
741 {
742 var customClassLoader = GetCustomClassLoaderType();
743 if (customClassLoader == null)
744 {
745 hasCustomClassLoader = 2;
746 break;
747 }
748
749 WaitInitializeJavaClassLoader(customClassLoader);
750 hasCustomClassLoader = 1;
751 }
752 return base.LoadClassImpl(name, mode);
753 }
754
755#endif
756
757 return LoadBootstrapIfNonJavaAssembly(name)
758 ?? LoadDynamic(name)
759 ?? FindOrLoadGenericClass(name, LoadMode.LoadOrNull);
760 }
761
768 internal RuntimeJavaType LoadClass(string name)
769 {
770 return FindLoadedClass(name)
771 ?? LoadBootstrapIfNonJavaAssembly(name)
772 ?? LoadDynamic(name)
773 ?? FindOrLoadGenericClass(name, LoadMode.LoadOrNull);
774 }
775
776 RuntimeJavaType LoadBootstrapIfNonJavaAssembly(string name)
777 {
778 if (!assemblyLoader.HasJavaModule)
779 return Context.ClassLoaderFactory.GetBootstrapClassLoader().TryLoadClassByName(name);
780
781 return null;
782 }
783
784 RuntimeJavaType LoadDynamic(string name)
785 {
786#if !IMPORTER && !EXPORTER && !FIRST_PASS
787 var classFile = name.Replace('.', '/') + ".class";
788 foreach (var res in Context.ClassLoaderFactory.GetBootstrapClassLoader().FindDelegateResources(classFile))
789 return res.Loader.DefineDynamic(name, res.URL);
790 foreach (var res in FindDelegateResources(classFile))
791 return res.Loader.DefineDynamic(name, res.URL);
792 foreach (var url in FindResources(classFile))
793 return DefineDynamic(name, url);
794#endif
795 return null;
796 }
797
798#if !IMPORTER && !EXPORTER && !FIRST_PASS
799
800 RuntimeJavaType DefineDynamic(string name, java.net.URL url)
801 {
802 byte[] buf;
803
804 using (var inp = url.openStream())
805 {
806 buf = new byte[inp.available()];
807 for (int pos = 0; pos < buf.Length;)
808 {
809 int read = inp.read(buf, pos, buf.Length - pos);
810 if (read <= 0)
811 break;
812
813 pos += read;
814 }
815 }
816
817 // when the VM initiates a class load, it doesn't go through ClassLoader.loadClass() for non-custom Assembly class loaders (for efficiency)
818 // so when we dynamically attempt to define a class, we have to explicitly obtain the class loading lock to prevent race conditions
819 var loader = GetJavaClassLoader();
820 lock (loader == null ? this : loader.getClassLoadingLock(name))
821 {
822 // make sure the class wasn't defined since we last checked and before we acquired the lock
823 var tw = FindLoadedClass(name);
824 if (tw != null)
825 return tw;
826
827 return RuntimeJavaType.FromClass(IKVM.Java.Externs.java.lang.ClassLoader.defineClass1(loader, name, buf, 0, buf.Length, GetProtectionDomain(), null));
828 }
829 }
830#endif
831
832 RuntimeJavaType FindReferenced(string name)
833 {
834 for (int i = 0; i < delegates.Length; i++)
835 {
836 if (delegates[i] == null)
837 {
838 var asm = LoadAssemblyOrClearName(ref references[i], false);
839 if (asm != null)
841 }
842 if (delegates[i] != null)
843 {
844 var tw = delegates[i].DoLoad(name);
845 if (tw != null)
846 return RegisterInitiatingLoader(tw);
847 }
848 }
849
850 return null;
851 }
852
853#if !IMPORTER && !EXPORTER
854
855 static java.net.URL MakeResourceURL(Assembly asm, string name)
856 {
857#if FIRST_PASS
858 throw new NotImplementedException();
859#else
860 return new java.io.File(Path.Combine(VfsTable.GetAssemblyResourcesPath(JVM.Vfs.Context, asm, JVM.Properties.HomePath), name)).toURI().toURL();
861#endif
862 }
863
864 internal IEnumerable<java.net.URL> FindResources(string unmangledName)
865 {
866#if FIRST_PASS
867 throw new NotImplementedException();
868#else
869 // cannot find resources in dynamic assembly
870 if (assemblyLoader.Assembly.IsDynamic)
871 yield break;
872
873 var found = false;
874
875 var urls = assemblyLoader.FindResources(unmangledName);
876 while (urls.hasMoreElements())
877 {
878 found = true;
879 yield return (java.net.URL)urls.nextElement();
880 }
881
882 // assembly is not a Java assembly
883 if (assemblyLoader.HasJavaModule == false)
884 {
885 // attempt to find an assembly resource with the exact name
886 if (unmangledName != "" && assemblyLoader.Assembly.GetManifestResourceInfo(unmangledName) != null)
887 {
888 found = true;
889 yield return MakeResourceURL(assemblyLoader.Assembly, unmangledName);
890 }
891
892 // the JavaResourceAttribute can be used to manufacture a named Java resource
893 foreach (var res in assemblyLoader.Assembly.GetCustomAttributes<JavaResourceAttribute>())
894 {
895 if (res.JavaName == unmangledName)
896 {
897 found = true;
898 yield return MakeResourceURL(assemblyLoader.Assembly, res.ResourceName);
899 }
900 }
901 }
902
903 // find an assembly resource with the managed resource name
904 var name = JVM.MangleResourceName(unmangledName);
905 if (assemblyLoader.Assembly.GetManifestResourceInfo(name) != null)
906 {
907 found = true;
908 yield return MakeResourceURL(assemblyLoader.Assembly, name);
909 }
910
911 LazyInitExports();
912
913 if (exports != null && exports.TryGetValue(JVM.PersistableHash(unmangledName), out var assemblies))
914 {
915 foreach (int index in assemblies)
916 {
917 var loader = exportedAssemblies[index];
918 if (loader == null)
919 {
920 var asm = LoadAssemblyOrClearName(ref exportedAssemblyNames[index], true);
921 if (asm == null)
922 continue;
923
924 loader = exportedAssemblies[index] = GetLoaderForExportedAssembly(asm);
925 }
926
927 urls = loader.FindResources(unmangledName);
928 while (urls.hasMoreElements())
929 {
930 found = true;
931 yield return (java.net.URL)urls.nextElement();
932 }
933
934 if (loader.Assembly.GetManifestResourceInfo(name) != null)
935 {
936 found = true;
937 yield return MakeResourceURL(loader.Assembly, name);
938 }
939 }
940 }
941
942 // if asked for a '.class' resource, we can return the appropriate stub
943 if (found == false && unmangledName.EndsWith(".class", StringComparison.Ordinal) && unmangledName.IndexOf('.') == unmangledName.Length - 6)
944 {
945 var tw = FindLoadedClass(unmangledName.Substring(0, unmangledName.Length - 6).Replace('/', '.'));
946 if (tw != null && tw.ClassLoader == this && !tw.IsArray && !tw.IsDynamic)
947 yield return new java.io.File(Path.Combine(VfsTable.GetAssemblyClassesPath(JVM.Vfs.Context, assemblyLoader.Assembly, JVM.Properties.HomePath), unmangledName)).toURI().toURL();
948 }
949#endif
950 }
951
952 protected struct Resource
953 {
954
955 internal readonly java.net.URL URL;
956 internal readonly RuntimeAssemblyClassLoader Loader;
957
963 internal Resource(java.net.URL url, RuntimeAssemblyClassLoader loader)
964 {
965 this.URL = url;
966 this.Loader = loader;
967 }
968
969 }
970
976 protected IEnumerable<Resource> FindDelegateResources(string name)
977 {
978 LazyInitExports();
979
980 for (int i = 0; i < delegates.Length; i++)
981 {
982 if (delegates[i] == null)
983 {
984 var asm = LoadAssemblyOrClearName(ref references[i], false);
985 if (asm != null)
987 }
988
989 if (delegates[i] != null && delegates[i] != Context.ClassLoaderFactory.GetBootstrapClassLoader())
990 foreach (java.net.URL url in delegates[i].FindResources(name))
991 yield return new Resource(url, delegates[i]);
992 }
993 }
994
1001 internal virtual IEnumerable<java.net.URL> GetResources(string name)
1002 {
1003 foreach (var url in Context.ClassLoaderFactory.GetBootstrapClassLoader().GetResources(name))
1004 yield return url;
1005
1006 foreach (var res in FindDelegateResources(name))
1007 yield return res.URL;
1008
1009 foreach (var url in FindResources(name))
1010 yield return url;
1011 }
1012
1013#endif // !IMPORTER
1014
1015#if !IMPORTER && !FIRST_PASS && !EXPORTER
1016
1017 private sealed class JavaClassLoaderConstructionInProgress
1018 {
1019 internal readonly Thread Thread = Thread.CurrentThread;
1020 internal java.lang.ClassLoader javaClassLoader;
1021 internal int recursion;
1022 }
1023
1024 private java.lang.ClassLoader WaitInitializeJavaClassLoader(Type customClassLoader)
1025 {
1026 Interlocked.CompareExchange(ref jclcip, new JavaClassLoaderConstructionInProgress(), null);
1027 JavaClassLoaderConstructionInProgress curr = jclcip;
1028 if (curr != null)
1029 {
1030 if (curr.Thread == Thread.CurrentThread)
1031 {
1032 if (curr.javaClassLoader != null)
1033 {
1034 // we were recursively invoked during the class loader construction,
1035 // so we have to return the partialy constructed class loader
1036 return curr.javaClassLoader;
1037 }
1038 curr.recursion++;
1039 try
1040 {
1041 if (javaClassLoader == null)
1042 {
1043 InitializeJavaClassLoader(curr, customClassLoader);
1044 }
1045 }
1046 finally
1047 {
1048 // We only publish the class loader from the outer most invocation, otherwise
1049 // an invocation of getClassLoader in the static initializer or constructor
1050 // of the custom class loader would result in prematurely publishing it.
1051 if (--curr.recursion == 0)
1052 {
1053 lock (this)
1054 {
1055 jclcip = null;
1056 Monitor.PulseAll(this);
1057 }
1058 }
1059 }
1060 }
1061 else
1062 {
1063 lock (this)
1064 {
1065 while (jclcip != null)
1066 {
1067 Monitor.Wait(this);
1068 }
1069 }
1070 }
1071 }
1072 return javaClassLoader;
1073 }
1074
1075 internal override java.lang.ClassLoader GetJavaClassLoader()
1076 {
1077 return javaClassLoader ?? WaitInitializeJavaClassLoader(GetCustomClassLoaderType());
1078 }
1079
1080 internal virtual java.security.ProtectionDomain GetProtectionDomain()
1081 {
1082 if (protectionDomain == null)
1083 Interlocked.CompareExchange(ref protectionDomain, new java.security.ProtectionDomain(assemblyLoader.Assembly), null);
1084
1085 return protectionDomain;
1086 }
1087#endif
1088
1089 protected override RuntimeJavaType FindLoadedClassLazy(string name)
1090 {
1091 return DoLoad(name)
1092 ?? FindReferenced(name)
1093 ?? FindOrLoadGenericClass(name, LoadMode.Find);
1094 }
1095
1096 internal override bool InternalsVisibleToImpl(RuntimeJavaType wrapper, RuntimeJavaType friend)
1097 {
1098 var other = friend.ClassLoader;
1099 if (this == other)
1100 {
1101#if IMPORTER || EXPORTER
1102 return true;
1103#else
1104 // we're OK if the type being accessed (wrapper) is a dynamic type
1105 // or if the dynamic assembly has internal access
1106 return GetAssembly(wrapper).Equals(GetTypeWrapperFactory().ModuleBuilder.Assembly)
1107 || GetTypeWrapperFactory().HasInternalAccess;
1108#endif
1109 }
1110 AssemblyName otherName;
1111#if IMPORTER
1112 ImportClassLoader ccl = other as ImportClassLoader;
1113 if (ccl == null)
1114 {
1115 return false;
1116 }
1117 otherName = ccl.GetAssemblyName();
1118#else
1119 RuntimeAssemblyClassLoader acl = other as RuntimeAssemblyClassLoader;
1120 if (acl == null)
1121 {
1122 return false;
1123 }
1124 otherName = acl.GetAssembly(friend).GetName();
1125#endif
1126 return GetLoader(GetAssembly(wrapper)).InternalsVisibleTo(otherName);
1127 }
1128
1129 internal void AddDelegate(RuntimeAssemblyClassLoader acl)
1130 {
1131 LazyInitExports();
1132
1133 lock (this)
1134 delegates = ArrayUtil.Concat(delegates, acl);
1135 }
1136
1137#if !IMPORTER && !EXPORTER
1138
1139 internal List<KeyValuePair<string, string[]>> GetPackageInfo()
1140 {
1141 var list = new List<KeyValuePair<string, string[]>>();
1142 foreach (var m in assemblyLoader.Assembly.GetModules(false))
1143 {
1144 var attr = m.GetCustomAttributes<PackageListAttribute>();
1145 foreach (var p in attr)
1146 list.Add(new KeyValuePair<string, string[]>(p.jar, p.packages));
1147 }
1148
1149 return list;
1150 }
1151
1152#endif
1153
1154#if !IMPORTER && !FIRST_PASS && !EXPORTER
1155
1156 Type GetCustomClassLoaderType()
1157 {
1158 LoadCustomClassLoaderRedirects(this);
1159
1160 var assembly = assemblyLoader.Assembly;
1161 var assemblyName = assembly.FullName;
1162
1163 foreach (var kv in Context.AssemblyClassLoaderFactory.customClassLoaderRedirects)
1164 {
1165 var asm = kv.Key;
1166
1167 // FXBUG
1168 // We only support matching on the assembly's simple name,
1169 // because there appears to be no viable alternative.
1170 // There is AssemblyName.ReferenceMatchesDefinition()
1171 // but it is completely broken.
1172 if (assemblyName.StartsWith(asm + ","))
1173 {
1174 try
1175 {
1176 return Type.GetType(kv.Value, true);
1177 }
1178 catch (Exception x)
1179 {
1180 Diagnostics.GenericRuntimeError($"Unable to load custom class loader {kv.Value} specified in app.config for assembly {assembly}: {x}");
1181 }
1182
1183 break;
1184 }
1185 }
1186
1187 var attribs = assembly.GetCustomAttributes(typeof(CustomAssemblyClassLoaderAttribute), false);
1188 if (attribs.Length == 1)
1189 return ((CustomAssemblyClassLoaderAttribute)attribs[0]).Type;
1190
1191 return null;
1192 }
1193
1194 void InitializeJavaClassLoader(JavaClassLoaderConstructionInProgress jclcip, Type customClassLoaderClass)
1195 {
1196 var assembly = assemblyLoader.Assembly;
1197
1198 if (customClassLoaderClass != null)
1199 {
1200 try
1201 {
1202 if (!customClassLoaderClass.IsPublic && !customClassLoaderClass.Assembly.Equals(assembly))
1203 throw new InternalException($"Custom class loader type is not accessible: '{customClassLoaderClass}'.");
1204
1205 var customClassLoaderCtor = customClassLoaderClass.GetConstructor(BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic, null, new Type[] { typeof(Assembly) }, null);
1206 if (customClassLoaderCtor == null)
1207 throw new InternalException($"Custom class loader type has no empty constructor: '{customClassLoaderClass}'.");
1208
1209 if (!customClassLoaderCtor.IsPublic && !customClassLoaderClass.Assembly.Equals(assembly))
1210 throw new InternalException($"Custom class loader constructor is not accessible: '{customClassLoaderClass}'.");
1211
1212 // NOTE we're creating an uninitialized instance of the custom class loader here, so that getClassLoader will return the proper object
1213 // when it is called during the construction of the custom class loader later on. This still doesn't make it safe to use the custom
1214 // class loader before it is constructed, but at least the object instance is available and should anyone cache it, they will get the
1215 // right object to use later on.
1216 // Note that creating the unitialized instance will (unfortunately) trigger the static initializer. The static initializer can
1217 // trigger a call to getClassLoader(), which means we can end up here recursively.
1218 var newJavaClassLoader = (java.lang.ClassLoader)GetUninitializedObject(customClassLoaderClass);
1219
1220 // check if we weren't invoked recursively and the nested invocation already did the work
1221 if (jclcip.javaClassLoader == null)
1222 {
1223 jclcip.javaClassLoader = newJavaClassLoader;
1224 Context.ClassLoaderFactory.SetWrapperForClassLoader(jclcip.javaClassLoader, this);
1225 DoPrivileged(new CustomClassLoaderCtorCaller(customClassLoaderCtor, jclcip.javaClassLoader, assembly));
1226 Diagnostics.GenericRuntimeInfo($"Created custom assembly class loader {customClassLoaderClass.FullName} for assembly {assembly}");
1227 }
1228 else
1229 {
1230 // we didn't initialize the object, so there is no need to finalize it
1231 GC.SuppressFinalize(newJavaClassLoader);
1232 }
1233 }
1234 catch (Exception x)
1235 {
1236 Diagnostics.GenericRuntimeError($"Unable to create custom assembly class loader {customClassLoaderClass.FullName} for {assembly}: {x}");
1237 }
1238 }
1239
1240 if (jclcip.javaClassLoader == null)
1241 {
1242 jclcip.javaClassLoader = new ikvm.runtime.AssemblyClassLoader();
1243 Context.ClassLoaderFactory.SetWrapperForClassLoader(jclcip.javaClassLoader, this);
1244 }
1245
1246 // finally we publish the class loader for other threads to see
1247 Thread.MemoryBarrier();
1248 javaClassLoader = jclcip.javaClassLoader;
1249 }
1250
1251 // separate method to avoid LinkDemand killing the caller
1252 // and to bridge transparent -> critical boundary
1253 [System.Security.SecuritySafeCritical]
1254 static object GetUninitializedObject(Type type)
1255 {
1256#if NETFRAMEWORK
1257 return FormatterServices.GetUninitializedObject(type);
1258#else
1259 return RuntimeHelpers.GetUninitializedObject(type);
1260#endif
1261 }
1262
1263 static void LoadCustomClassLoaderRedirects(RuntimeClassLoader loader)
1264 {
1265 if (loader.Context.AssemblyClassLoaderFactory.customClassLoaderRedirects == null)
1266 {
1267 var dict = new Dictionary<string, string>();
1268
1269 try
1270 {
1271#if NETFRAMEWORK
1272 foreach (var key in System.Configuration.ConfigurationManager.AppSettings.AllKeys)
1273 {
1274 const string prefix = "ikvm-classloader:";
1275 if (key.StartsWith(prefix))
1276 dict[key.Substring(prefix.Length)] = System.Configuration.ConfigurationManager.AppSettings.Get(key);
1277 }
1278#endif
1279 }
1280 catch (Exception x)
1281 {
1282 loader.Diagnostics.GenericRuntimeError($"Error while reading custom class loader redirects: {x}");
1283 }
1284 finally
1285 {
1286 Interlocked.CompareExchange(ref loader.Context.AssemblyClassLoaderFactory.customClassLoaderRedirects, dict, null);
1287 }
1288 }
1289 }
1290
1294 sealed class CustomClassLoaderCtorCaller : java.security.PrivilegedAction
1295 {
1296
1297 readonly ConstructorInfo ctor;
1298 readonly object classLoader;
1299 readonly Assembly assembly;
1300
1307 internal CustomClassLoaderCtorCaller(ConstructorInfo ctor, object classLoader, Assembly assembly)
1308 {
1309 this.ctor = ctor ?? throw new ArgumentNullException(nameof(ctor));
1310 this.classLoader = classLoader ?? throw new ArgumentNullException(nameof(classLoader));
1311 this.assembly = assembly ?? throw new ArgumentNullException(nameof(assembly));
1312 }
1313
1314 public object run()
1315 {
1316 ctor.Invoke(classLoader, new object[] { assembly });
1317 return null;
1318 }
1319 }
1320
1321#endif
1322
1323 }
1324
1325}
System.Threading.Interlocked Interlocked
IKVM.Reflection.Module Module
IKVM.Reflection.Type Type
IKVM.Reflection.Assembly Assembly
IKVM.Reflection.AssemblyName AssemblyName
IKVM.Reflection.ConstructorInfo ConstructorInfo
global::java.lang.invoke.LambdaForm.Name Name
Marks an assembly such that it's types are considered to be loaded by the specified class loader type...
static global::java.lang.Class defineClass1(global::java.lang.ClassLoader self, string name, byte[] b, int off, int len, global::java.security.ProtectionDomain pd, string source)
Implements the native method 'defineClass1'.
Represents an internal error that occurred within IKVM.
Property values loaded into the JVM from various sources.
Main state of the running JVM.
RuntimeAssemblyClassLoader FromAssembly(Assembly assembly)
Obtains the RuntimeAssemblyClassLoader for the given Assembly. This method should not be used with dy...
void SetWrapperForClassLoader(java.lang.ClassLoader javaClassLoader, RuntimeClassLoader wrapper)
Runtime support for a class loader.
RuntimeContext Context
Gets a reference to the RuntimeContext that this RuntimeClassLoader is hosted within.
java.lang.ClassLoader javaClassLoader
virtual IDiagnosticHandler Diagnostics
Gets the IDiagnosticHandler events originated by this class loader should be sent to.
Maintains services relevant to an instane of the IKVM runtime.
RuntimeAssemblyClassLoaderFactory AssemblyClassLoaderFactory
Gets the RuntimeAssemblyClassLoaderFactory associated with this instance of the runtime.
ISymbolResolver Resolver
Gets the ISymbolResolver associated with this instance of the runtime.
RuntimeClassLoaderFactory ClassLoaderFactory
Gets the RuntimeClassLoaderFactory associated with this instance of the runtime.
Represents a runtime Java type derived from a .NET assembly which was the result of the IKVM compiler...
Implements a RuntimeJavaType that exposes existing managed .NET types not the result of static compil...
Implementation of RuntimeClassLoader that emits loaded Java types to an AssemblyBuilder.
void GenericRuntimeError(string arg0)
The 'GenericRuntimeError' diagnostic.
static DiagnosticEvent AssemblyContainsDuplicateClassNames(string arg0, string arg1, string arg2, string arg3, Exception? exception=null, DiagnosticLocation location=default)
The 'AssemblyContainsDuplicateClassNames' diagnostic.
Provides methods to parse a Java class name.