diff options
author | jfrijters <jfrijters> | 2009-09-07 08:19:04 +0400 |
---|---|---|
committer | jfrijters <jfrijters> | 2009-09-07 08:19:04 +0400 |
commit | 69d6408db6035470870a2c47bca0ebc79ad67c7d (patch) | |
tree | 44a951b0478289376fc18f31395e8c189aa9c758 | |
parent | 26096b2859fc7e7af840013a43065206ed69d9c9 (diff) |
Moved AssemblyClassLoader and BootstrapClassLoader into AssemblyClassLoader.cs.
-rw-r--r-- | ikvmc.8.csproj | 1 | ||||
-rw-r--r-- | ikvmc/ikvmc.build | 1 | ||||
-rw-r--r-- | runtime/AssemblyClassLoader.cs | 892 | ||||
-rw-r--r-- | runtime/ClassLoaderWrapper.cs | 853 | ||||
-rw-r--r-- | runtime/IKVM.Runtime.8.csproj | 1 | ||||
-rw-r--r-- | runtime/runtime.build | 1 |
6 files changed, 896 insertions, 853 deletions
diff --git a/ikvmc.8.csproj b/ikvmc.8.csproj index 6160a1cc..adec0565 100644 --- a/ikvmc.8.csproj +++ b/ikvmc.8.csproj @@ -81,6 +81,7 @@ <Compile Include="ikvmc\remapper.cs"> <SubType>Code</SubType> </Compile> + <Compile Include="runtime\AssemblyClassLoader.cs" /> <Compile Include="runtime\atomic.cs" /> <Compile Include="runtime\attributes.cs"> <SubType>Code</SubType> diff --git a/ikvmc/ikvmc.build b/ikvmc/ikvmc.build index 3f1f458f..0e19bb77 100644 --- a/ikvmc/ikvmc.build +++ b/ikvmc/ikvmc.build @@ -17,6 +17,7 @@ <include name="CompilerClassLoader.cs" /> <include name="FakeTypes.cs" /> <include name="remapper.cs" /> + <include name="../runtime/AssemblyClassLoader.cs" /> <include name="../runtime/atomic.cs" /> <include name="../runtime/attributes.cs" /> <include name="../runtime/BigEndianBinaryReader.cs" /> diff --git a/runtime/AssemblyClassLoader.cs b/runtime/AssemblyClassLoader.cs new file mode 100644 index 00000000..6b1a6e1a --- /dev/null +++ b/runtime/AssemblyClassLoader.cs @@ -0,0 +1,892 @@ +/* + Copyright (C) 2002-2009 Jeroen Frijters + + This software is provided 'as-is', without any express or implied + warranty. In no event will the authors be held liable for any damages + arising from the use of this software. + + Permission is granted to anyone to use this software for any purpose, + including commercial applications, and to alter it and redistribute it + freely, subject to the following restrictions: + + 1. The origin of this software must not be misrepresented; you must not + claim that you wrote the original software. If you use this software + in a product, an acknowledgment in the product documentation would be + appreciated but is not required. + 2. Altered source versions must be plainly marked as such, and must not be + misrepresented as being the original software. + 3. This notice may not be removed or altered from any source distribution. + + Jeroen Frijters + jeroen@frijters.net + +*/ +using System; +using System.Reflection; +#if IKVM_REF_EMIT +using IKVM.Reflection.Emit; +#else +using System.Reflection.Emit; +#endif +using System.IO; +using System.Collections.Generic; +using System.Diagnostics; +using System.Threading; +using System.Runtime.CompilerServices; +using IKVM.Attributes; + +namespace IKVM.Internal +{ + class AssemblyClassLoader : ClassLoaderWrapper + { + private AssemblyLoader assemblyLoader; + private string[] references; + private AssemblyClassLoader[] delegates; + private bool isReflectionOnly; +#if !STATIC_COMPILER + private Thread initializerThread; + private volatile object protectionDomain; +#endif + private bool hasCustomClassLoader; + private Dictionary<int, List<int>> exports; + private string[] exportedAssemblyNames; + private AssemblyLoader[] exportedAssemblies; + private Dictionary<Assembly, AssemblyLoader> exportedLoaders; + + private sealed class AssemblyLoader + { + private readonly Assembly assembly; + private bool[] isJavaModule; + private Module[] modules; + private Dictionary<string, string> nameMap; + private bool hasDotNetModule; + private AssemblyName[] internalsVisibleTo; + + internal AssemblyLoader(Assembly assembly) + { + this.assembly = assembly; + modules = assembly.GetModules(false); + isJavaModule = new bool[modules.Length]; + for (int i = 0; i < modules.Length; i++) + { + object[] attr; + try + { + attr = AttributeHelper.GetJavaModuleAttributes(modules[i]); + } + catch (Exception x) + { + // HACK we handle exceptions here, because there is at least one obfuscator that produces + // invalid assemblies that cause Module.GetCustomAttributes() to throw an exception + JVM.CriticalFailure("Unexpected exception", x); + throw null; + } + if (attr.Length > 0) + { + isJavaModule[i] = true; + foreach (JavaModuleAttribute jma in attr) + { + string[] map = jma.GetClassMap(); + if (map != null) + { + if (nameMap == null) + { + nameMap = new Dictionary<string, string>(); + } + for (int j = 0; j < map.Length; j += 2) + { + string key = map[j]; + string val = map[j + 1]; + // TODO if there is a name clash between modules, this will throw. + // Figure out how to handle that. + nameMap.Add(key, val); + } + } + } + } + else + { + hasDotNetModule = true; + } + } + } + + internal bool HasJavaModule + { + get + { + for (int i = 0; i < isJavaModule.Length; i++) + { + if (isJavaModule[i]) + { + return true; + } + } + return false; + } + } + + internal Assembly Assembly + { + get { return assembly; } + } + + private Type GetType(string name) + { + try + { + return assembly.GetType(name); + } + catch (FileLoadException x) + { + // this can only happen if the assembly was loaded in the ReflectionOnly + // context and the requested type references a type in another assembly + // that cannot be found in the ReflectionOnly context + // TODO figure out what other exceptions Assembly.GetType() can throw + Tracer.Info(Tracer.Runtime, x.Message); + } + return null; + } + + private Type GetType(Module mod, string name) + { + try + { + return mod.GetType(name); + } + catch (FileLoadException x) + { + // this can only happen if the assembly was loaded in the ReflectionOnly + // context and the requested type references a type in another assembly + // that cannot be found in the ReflectionOnly context + // TODO figure out what other exceptions Assembly.GetType() can throw + Tracer.Info(Tracer.Runtime, x.Message); + } + return null; + } + + private Type GetJavaType(Module mod, string name) + { + try + { + string n = null; + if (nameMap != null) + { + nameMap.TryGetValue(name, out n); + } + Type t = GetType(mod, n != null ? n : name); + if (t == null) + { + n = name.Replace('$', '+'); + if (!ReferenceEquals(n, name)) + { + t = GetType(n); + } + } + if (t != null + && !AttributeHelper.IsHideFromJava(t) + && !t.IsArray + && !t.IsPointer + && !t.IsByRef) + { + return t; + } + } + catch (ArgumentException x) + { + // we can end up here because we replace the $ with a plus sign + // (or client code did a Class.forName() on an invalid name) + Tracer.Info(Tracer.Runtime, x.Message); + } + return null; + } + + internal TypeWrapper DoLoad(string name) + { + for (int i = 0; i < modules.Length; i++) + { + if (isJavaModule[i]) + { + Type type = GetJavaType(modules[i], name); + if (type != null) + { + // check the name to make sure that the canonical name was used + if (CompiledTypeWrapper.GetName(type) == name) + { + return CompiledTypeWrapper.newInstance(name, type); + } + } + } + else + { + // TODO should we catch ArgumentException and prohibit array, pointer and byref here? + Type type = GetType(modules[i], DotNetTypeWrapper.DemangleTypeName(name)); + if (type != null && DotNetTypeWrapper.IsAllowedOutside(type)) + { + // check the name to make sure that the canonical name was used + if (DotNetTypeWrapper.GetName(type) == name) + { + return DotNetTypeWrapper.Create(type, name); + } + } + } + } + if (hasDotNetModule) + { + // for fake types, we load the declaring outer type (the real one) and + // let that generated the manufactured nested classes + TypeWrapper outer = null; + if (name.EndsWith(DotNetTypeWrapper.DelegateInterfaceSuffix)) + { + outer = DoLoad(name.Substring(0, name.Length - DotNetTypeWrapper.DelegateInterfaceSuffix.Length)); + } + else if (name.EndsWith(DotNetTypeWrapper.AttributeAnnotationSuffix)) + { + outer = DoLoad(name.Substring(0, name.Length - DotNetTypeWrapper.AttributeAnnotationSuffix.Length)); + } + else if (name.EndsWith(DotNetTypeWrapper.AttributeAnnotationReturnValueSuffix)) + { + outer = DoLoad(name.Substring(0, name.Length - DotNetTypeWrapper.AttributeAnnotationReturnValueSuffix.Length)); + } + else if (name.EndsWith(DotNetTypeWrapper.AttributeAnnotationMultipleSuffix)) + { + outer = DoLoad(name.Substring(0, name.Length - DotNetTypeWrapper.AttributeAnnotationMultipleSuffix.Length)); + } + else if (name.EndsWith(DotNetTypeWrapper.EnumEnumSuffix)) + { + outer = DoLoad(name.Substring(0, name.Length - DotNetTypeWrapper.EnumEnumSuffix.Length)); + } + if (outer != null && outer.IsFakeTypeContainer) + { + foreach (TypeWrapper tw in outer.InnerClasses) + { + if (tw.Name == name) + { + return tw; + } + } + } + } + return null; + } + + internal TypeWrapper CreateWrapperForAssemblyType(Type type) + { + Module mod = type.Module; + int moduleIndex = -1; + for (int i = 0; i < modules.Length; i++) + { + if (modules[i] == mod) + { + moduleIndex = i; + break; + } + } + string name; + if (isJavaModule[moduleIndex]) + { + name = CompiledTypeWrapper.GetName(type); + } + else + { + name = DotNetTypeWrapper.GetName(type); + if (name == null) + { + return null; + } + } + if (isJavaModule[moduleIndex]) + { + if (AttributeHelper.IsHideFromJava(type)) + { + return null; + } + // since this type was compiled from Java source, we have to look for our + // attributes + return CompiledTypeWrapper.newInstance(name, type); + } + else + { + if (!DotNetTypeWrapper.IsAllowedOutside(type)) + { + return null; + } + // since this type was not compiled from Java source, we don't need to + // look for our attributes, but we do need to filter unrepresentable + // stuff (and transform some other stuff) + return DotNetTypeWrapper.Create(type, name); + } + } + + internal bool InternalsVisibleTo(AssemblyName otherName) + { + if (internalsVisibleTo == null) + { + internalsVisibleTo = AttributeHelper.GetInternalsVisibleToAttributes(assembly); + } + foreach (AssemblyName name in internalsVisibleTo) + { + if (AssemblyName.ReferenceMatchesDefinition(name, otherName)) + { + return true; + } + } + return false; + } + } + + internal AssemblyClassLoader(Assembly assembly, object javaClassLoader, bool hasCustomClassLoader) + : this(assembly, null, javaClassLoader, hasCustomClassLoader) + { + } + + internal AssemblyClassLoader(Assembly assembly, string[] fixedReferences, object javaClassLoader, bool hasCustomClassLoader) + : base(CodeGenOptions.None, javaClassLoader) + { + this.assemblyLoader = new AssemblyLoader(assembly); + this.references = fixedReferences; + this.isReflectionOnly = assembly.ReflectionOnly; + this.hasCustomClassLoader = hasCustomClassLoader; + } + + private void DoInitializeExports() + { + lock (this) + { + if (delegates == null) + { + if (!(ReflectUtil.IsDynamicAssembly(assemblyLoader.Assembly)) && assemblyLoader.Assembly.GetManifestResourceInfo("ikvm.exports") != null) + { + List<string> wildcardExports = new List<string>(); + using (Stream stream = assemblyLoader.Assembly.GetManifestResourceStream("ikvm.exports")) + { + BinaryReader rdr = new BinaryReader(stream); + int assemblyCount = rdr.ReadInt32(); + exports = new Dictionary<int, List<int>>(); + exportedAssemblies = new AssemblyLoader[assemblyCount]; + exportedAssemblyNames = new string[assemblyCount]; + exportedLoaders = new Dictionary<Assembly, AssemblyLoader>(); + for (int i = 0; i < assemblyCount; i++) + { + exportedAssemblyNames[i] = rdr.ReadString(); + int typeCount = rdr.ReadInt32(); + if (typeCount == 0 && references == null) + { + wildcardExports.Add(exportedAssemblyNames[i]); + } + for (int j = 0; j < typeCount; j++) + { + int hash = rdr.ReadInt32(); + List<int> assemblies; + if (!exports.TryGetValue(hash, out assemblies)) + { + assemblies = new List<int>(); + exports.Add(hash, assemblies); + } + assemblies.Add(i); + } + } + } + if (references == null) + { + references = wildcardExports.ToArray(); + } + } + else + { + AssemblyName[] refNames = assemblyLoader.Assembly.GetReferencedAssemblies(); + references = new string[refNames.Length]; + for (int i = 0; i < references.Length; i++) + { + references[i] = refNames[i].FullName; + } + } + delegates = new AssemblyClassLoader[references.Length]; + } + } + } + + private void LazyInitExports() + { + if (delegates == null) + { + DoInitializeExports(); + } + } + + internal Assembly MainAssembly + { + get + { + return assemblyLoader.Assembly; + } + } + + internal Assembly GetAssembly(TypeWrapper wrapper) + { + Debug.Assert(wrapper.GetClassLoader() == this); + while (wrapper.IsFakeNestedType) + { + wrapper = wrapper.DeclaringTypeWrapper; + } + return wrapper.TypeAsBaseType.Assembly; + } + + internal override Type GetGenericTypeDefinition(string name) + { + try + { + // we only have to look in the main assembly, because only a .NET assembly can contain generic type definitions + // and it cannot be part of a multi assembly sharedclassloader group + Type type = assemblyLoader.Assembly.GetType(name); + if (type != null && type.IsGenericTypeDefinition) + { + return type; + } + } + catch (FileLoadException x) + { + // this can only happen if the assembly was loaded in the ReflectionOnly + // context and the requested type references a type in another assembly + // that cannot be found in the ReflectionOnly context + // TODO figure out what other exceptions Assembly.GetType() can throw + Tracer.Info(Tracer.Runtime, x.Message); + } + return null; + } + + private Assembly LoadAssemblyOrClearName(ref string name) + { + if (name == null) + { + // previous load attemp failed + return null; + } + try + { + if (isReflectionOnly) + { + return Assembly.ReflectionOnlyLoad(name); + } + else + { + return Assembly.Load(name); + } + } + catch + { + // cache failure by clearing out the name the caller uses + name = null; + // should we issue a warning error (in ikvmc)? + return null; + } + } + + internal TypeWrapper DoLoad(string name) + { + TypeWrapper tw = assemblyLoader.DoLoad(name); + if (tw != null) + { + return RegisterInitiatingLoader(tw); + } + LazyInitExports(); + if (exports != null) + { + List<int> assemblies; + if (exports.TryGetValue(JVM.PersistableHash(name), out assemblies)) + { + foreach (int index in assemblies) + { + AssemblyLoader loader = exportedAssemblies[index]; + if (loader == null) + { + Assembly asm = LoadAssemblyOrClearName(ref exportedAssemblyNames[index]); + if (asm == null) + { + continue; + } + loader = exportedAssemblies[index] = GetLoaderForExportedAssembly(asm); + } + tw = loader.DoLoad(name); + if (tw != null) + { + return RegisterInitiatingLoader(tw); + } + } + } + } + return null; + } + + private AssemblyLoader GetLoader(Assembly assembly) + { + if (assemblyLoader.Assembly == assembly) + { + return assemblyLoader; + } + return GetLoaderForExportedAssembly(assembly); + } + + private AssemblyLoader GetLoaderForExportedAssembly(Assembly assembly) + { + LazyInitExports(); + AssemblyLoader loader; + lock (exportedLoaders) + { + exportedLoaders.TryGetValue(assembly, out loader); + } + if (loader == null) + { + loader = new AssemblyLoader(assembly); + lock (exportedLoaders) + { + AssemblyLoader existing; + if (exportedLoaders.TryGetValue(assembly, out existing)) + { + // another thread beat us to it + loader = existing; + } + else + { + exportedLoaders.Add(assembly, loader); + } + } + } + return loader; + } + + internal virtual TypeWrapper GetWrapperFromAssemblyType(Type type) + { + //Tracer.Info(Tracer.Runtime, "GetWrapperFromAssemblyType: {0}", type.FullName); + Debug.Assert(!type.Name.EndsWith("[]"), "!type.IsArray", type.FullName); + Debug.Assert(ClassLoaderWrapper.GetAssemblyClassLoader(type.Assembly) == this); +#if !IKVM_REF_EMIT + Debug.Assert(!(type.Assembly is AssemblyBuilder), "!(type.Assembly is AssemblyBuilder)", type.FullName); +#endif + TypeWrapper wrapper = GetLoader(type.Assembly).CreateWrapperForAssemblyType(type); + if (wrapper != null) + { + wrapper = RegisterInitiatingLoader(wrapper); + if (wrapper.TypeAsTBD != type && (!wrapper.IsRemapped || wrapper.TypeAsBaseType != type)) + { + // this really shouldn't happen, it means that we have two different types in our assembly that both + // have the same Java name + string msg = String.Format("\nType \"{0}\" and \"{1}\" both map to the same name \"{2}\".\n", type.FullName, wrapper.TypeAsTBD.FullName, wrapper.Name); + JVM.CriticalFailure(msg, null); + } + return wrapper; + } + return null; + } + + protected override TypeWrapper LoadClassImpl(string name, bool throwClassNotFoundException) + { + TypeWrapper tw = DoLoad(name); + if (tw != null) + { + return tw; + } + if (hasCustomClassLoader) + { + return base.LoadClassImpl(name, throwClassNotFoundException); + } + else + { + tw = LoadGenericClass(name); + if (tw != null) + { + return tw; + } + return LoadReferenced(name); + } + } + + internal TypeWrapper LoadReferenced(string name) + { + LazyInitExports(); + for (int i = 0; i < delegates.Length; i++) + { + if (delegates[i] == null) + { + Assembly asm = LoadAssemblyOrClearName(ref references[i]); + if (asm != null) + { + delegates[i] = ClassLoaderWrapper.GetAssemblyClassLoader(asm); + } + } + if (delegates[i] != null) + { + TypeWrapper tw = delegates[i].DoLoad(name); + if (tw != null) + { + return tw; + } + } + } + if (!assemblyLoader.HasJavaModule) + { + return GetBootstrapClassLoader().LoadClassByDottedNameFast(name); + } + return null; + } + +#if !STATIC_COMPILER + internal Assembly FindResourceAssembliesImpl(string unmangledName, string name, bool firstOnly, ref List<Assembly> list) + { + if (ReflectUtil.IsDynamicAssembly(assemblyLoader.Assembly)) + { + return null; + } + if (assemblyLoader.Assembly.GetManifestResourceInfo(name) != null) + { + if (firstOnly) + { + return assemblyLoader.Assembly; + } + list = new List<Assembly>(); + list.Add(assemblyLoader.Assembly); + } + LazyInitExports(); + if (exports != null) + { + List<int> assemblies; + if (exports.TryGetValue(JVM.PersistableHash(unmangledName), out assemblies)) + { + foreach (int index in assemblies) + { + AssemblyLoader loader = exportedAssemblies[index]; + if (loader == null) + { + Assembly asm = LoadAssemblyOrClearName(ref exportedAssemblyNames[index]); + if (asm == null) + { + continue; + } + loader = exportedAssemblies[index] = GetLoaderForExportedAssembly(asm); + } + if (loader.Assembly.GetManifestResourceInfo(name) != null) + { + if (firstOnly) + { + return loader.Assembly; + } + if (list == null) + { + list = new List<Assembly>(); + } + list.Add(loader.Assembly); + } + } + } + } + return null; + } + + internal Assembly[] FindResourceAssemblies(string unmangledName, bool firstOnly) + { + List<Assembly> list = null; + string name = JVM.MangleResourceName(unmangledName); + Assembly first = FindResourceAssembliesImpl(unmangledName, name, firstOnly, ref list); + if (first != null) + { + return new Assembly[] { first }; + } + LazyInitExports(); + for (int i = 0; i < delegates.Length; i++) + { + if (delegates[i] == null) + { + Assembly asm = LoadAssemblyOrClearName(ref references[i]); + if (asm != null) + { + delegates[i] = ClassLoaderWrapper.GetAssemblyClassLoader(asm); + } + } + if (delegates[i] != null) + { + first = delegates[i].FindResourceAssembliesImpl(unmangledName, name, firstOnly, ref list); + if (first != null) + { + return new Assembly[] { first }; + } + } + } + if (!assemblyLoader.HasJavaModule) + { + if (firstOnly) + { + return GetBootstrapClassLoader().FindResourceAssemblies(unmangledName, firstOnly); + } + else + { + Assembly[] assemblies = GetBootstrapClassLoader().FindResourceAssemblies(unmangledName, firstOnly); + if (assemblies != null) + { + foreach (Assembly asm in assemblies) + { + if (list == null) + { + list = new List<Assembly>(); + } + if (!list.Contains(asm)) + { + list.Add(asm); + } + } + } + } + } + if (list == null) + { + return null; + } + return list.ToArray(); + } + + internal void SetInitInProgress() + { + initializerThread = Thread.CurrentThread; + } + + internal void SetInitDone() + { + lock (this) + { + initializerThread = null; + Monitor.PulseAll(this); + } + } + + internal void WaitInitDone() + { + lock (this) + { + if (initializerThread != Thread.CurrentThread) + { + while (initializerThread != null) + { + Monitor.Wait(this); + } + } + } + } +#endif // !STATIC_COMPILER + + internal virtual object GetProtectionDomain() + { +#if STATIC_COMPILER || FIRST_PASS + return null; +#else + if (protectionDomain == null) + { + java.net.URL codebase; + try + { + codebase = new java.net.URL(assemblyLoader.Assembly.CodeBase); + } + catch (NotSupportedException) + { + // dynamic assemblies don't have a codebase + codebase = null; + } + catch (java.net.MalformedURLException) + { + codebase = null; + } + java.security.Permissions permissions = new java.security.Permissions(); + permissions.add(new java.security.AllPermission()); + object pd = new java.security.ProtectionDomain(new java.security.CodeSource(codebase, (java.security.cert.Certificate[])null), permissions, (java.lang.ClassLoader)GetJavaClassLoader(), null); + lock (this) + { + if (protectionDomain == null) + { + protectionDomain = pd; + } + } + } + return protectionDomain; +#endif + } + + protected override void CheckDefineClassAllowed(string className) + { + if (DoLoad(className) != null) + { + throw new LinkageError("duplicate class definition: " + className); + } + } + + internal override TypeWrapper GetLoadedClass(string name) + { + TypeWrapper tw = base.GetLoadedClass(name); + return tw != null ? tw : DoLoad(name); + } + + internal override bool InternalsVisibleToImpl(TypeWrapper wrapper, TypeWrapper friend) + { + ClassLoaderWrapper other = friend.GetClassLoader(); + if (this == other) + { + return true; + } + AssemblyName otherName; +#if STATIC_COMPILER + CompilerClassLoader ccl = other as CompilerClassLoader; + if (ccl == null) + { + return false; + } + otherName = ccl.GetAssemblyName(); +#else + AssemblyClassLoader acl = other as AssemblyClassLoader; + if (acl == null) + { + return false; + } + otherName = acl.GetAssembly(friend).GetName(); +#endif + return GetLoaderForExportedAssembly(GetAssembly(wrapper)).InternalsVisibleTo(otherName); + } + } + + class BootstrapClassLoader : AssemblyClassLoader + { + internal BootstrapClassLoader() + : base(JVM.CoreAssembly, new string[] { + typeof(object).Assembly.FullName, // mscorlib + typeof(System.Uri).Assembly.FullName // System + }, null, false) + { + } + + internal override TypeWrapper GetWrapperFromAssemblyType(Type type) + { + // we have to special case the fake types here + if (type.IsGenericType) + { + TypeWrapper outer = ClassLoaderWrapper.GetWrapperFromType(type.GetGenericArguments()[0]); + foreach (TypeWrapper inner in outer.InnerClasses) + { + if (inner.TypeAsTBD == type) + { + return inner; + } + foreach (TypeWrapper inner2 in inner.InnerClasses) + { + if (inner2.TypeAsTBD == type) + { + return inner2; + } + } + } + return null; + } + return base.GetWrapperFromAssemblyType(type); + } + + internal override object GetProtectionDomain() + { + return null; + } + } +} diff --git a/runtime/ClassLoaderWrapper.cs b/runtime/ClassLoaderWrapper.cs index fb6bc71b..613b7883 100644 --- a/runtime/ClassLoaderWrapper.cs +++ b/runtime/ClassLoaderWrapper.cs @@ -1475,857 +1475,4 @@ namespace IKVM.Internal return sb.ToString(); } } - - class AssemblyClassLoader : ClassLoaderWrapper - { - private AssemblyLoader assemblyLoader; - private string[] references; - private AssemblyClassLoader[] delegates; - private bool isReflectionOnly; -#if !STATIC_COMPILER - private Thread initializerThread; - private volatile object protectionDomain; -#endif - private bool hasCustomClassLoader; - private Dictionary<int, List<int>> exports; - private string[] exportedAssemblyNames; - private AssemblyLoader[] exportedAssemblies; - private Dictionary<Assembly, AssemblyLoader> exportedLoaders; - - private sealed class AssemblyLoader - { - private readonly Assembly assembly; - private bool[] isJavaModule; - private Module[] modules; - private Dictionary<string, string> nameMap; - private bool hasDotNetModule; - private AssemblyName[] internalsVisibleTo; - - internal AssemblyLoader(Assembly assembly) - { - this.assembly = assembly; - modules = assembly.GetModules(false); - isJavaModule = new bool[modules.Length]; - for (int i = 0; i < modules.Length; i++) - { - object[] attr; - try - { - attr = AttributeHelper.GetJavaModuleAttributes(modules[i]); - } - catch (Exception x) - { - // HACK we handle exceptions here, because there is at least one obfuscator that produces - // invalid assemblies that cause Module.GetCustomAttributes() to throw an exception - JVM.CriticalFailure("Unexpected exception", x); - throw null; - } - if (attr.Length > 0) - { - isJavaModule[i] = true; - foreach (JavaModuleAttribute jma in attr) - { - string[] map = jma.GetClassMap(); - if (map != null) - { - if (nameMap == null) - { - nameMap = new Dictionary<string, string>(); - } - for (int j = 0; j < map.Length; j += 2) - { - string key = map[j]; - string val = map[j + 1]; - // TODO if there is a name clash between modules, this will throw. - // Figure out how to handle that. - nameMap.Add(key, val); - } - } - } - } - else - { - hasDotNetModule = true; - } - } - } - - internal bool HasJavaModule - { - get - { - for (int i = 0; i < isJavaModule.Length; i++) - { - if (isJavaModule[i]) - { - return true; - } - } - return false; - } - } - - internal Assembly Assembly - { - get { return assembly; } - } - - private Type GetType(string name) - { - try - { - return assembly.GetType(name); - } - catch (FileLoadException x) - { - // this can only happen if the assembly was loaded in the ReflectionOnly - // context and the requested type references a type in another assembly - // that cannot be found in the ReflectionOnly context - // TODO figure out what other exceptions Assembly.GetType() can throw - Tracer.Info(Tracer.Runtime, x.Message); - } - return null; - } - - private Type GetType(Module mod, string name) - { - try - { - return mod.GetType(name); - } - catch (FileLoadException x) - { - // this can only happen if the assembly was loaded in the ReflectionOnly - // context and the requested type references a type in another assembly - // that cannot be found in the ReflectionOnly context - // TODO figure out what other exceptions Assembly.GetType() can throw - Tracer.Info(Tracer.Runtime, x.Message); - } - return null; - } - - private Type GetJavaType(Module mod, string name) - { - try - { - string n = null; - if (nameMap != null) - { - nameMap.TryGetValue(name, out n); - } - Type t = GetType(mod, n != null ? n : name); - if (t == null) - { - n = name.Replace('$', '+'); - if (!ReferenceEquals(n, name)) - { - t = GetType(n); - } - } - if (t != null - && !AttributeHelper.IsHideFromJava(t) - && !t.IsArray - && !t.IsPointer - && !t.IsByRef) - { - return t; - } - } - catch (ArgumentException x) - { - // we can end up here because we replace the $ with a plus sign - // (or client code did a Class.forName() on an invalid name) - Tracer.Info(Tracer.Runtime, x.Message); - } - return null; - } - - internal TypeWrapper DoLoad(string name) - { - for (int i = 0; i < modules.Length; i++) - { - if (isJavaModule[i]) - { - Type type = GetJavaType(modules[i], name); - if (type != null) - { - // check the name to make sure that the canonical name was used - if (CompiledTypeWrapper.GetName(type) == name) - { - return CompiledTypeWrapper.newInstance(name, type); - } - } - } - else - { - // TODO should we catch ArgumentException and prohibit array, pointer and byref here? - Type type = GetType(modules[i], DotNetTypeWrapper.DemangleTypeName(name)); - if (type != null && DotNetTypeWrapper.IsAllowedOutside(type)) - { - // check the name to make sure that the canonical name was used - if (DotNetTypeWrapper.GetName(type) == name) - { - return DotNetTypeWrapper.Create(type, name); - } - } - } - } - if (hasDotNetModule) - { - // for fake types, we load the declaring outer type (the real one) and - // let that generated the manufactured nested classes - TypeWrapper outer = null; - if (name.EndsWith(DotNetTypeWrapper.DelegateInterfaceSuffix)) - { - outer = DoLoad(name.Substring(0, name.Length - DotNetTypeWrapper.DelegateInterfaceSuffix.Length)); - } - else if (name.EndsWith(DotNetTypeWrapper.AttributeAnnotationSuffix)) - { - outer = DoLoad(name.Substring(0, name.Length - DotNetTypeWrapper.AttributeAnnotationSuffix.Length)); - } - else if (name.EndsWith(DotNetTypeWrapper.AttributeAnnotationReturnValueSuffix)) - { - outer = DoLoad(name.Substring(0, name.Length - DotNetTypeWrapper.AttributeAnnotationReturnValueSuffix.Length)); - } - else if (name.EndsWith(DotNetTypeWrapper.AttributeAnnotationMultipleSuffix)) - { - outer = DoLoad(name.Substring(0, name.Length - DotNetTypeWrapper.AttributeAnnotationMultipleSuffix.Length)); - } - else if (name.EndsWith(DotNetTypeWrapper.EnumEnumSuffix)) - { - outer = DoLoad(name.Substring(0, name.Length - DotNetTypeWrapper.EnumEnumSuffix.Length)); - } - if (outer != null && outer.IsFakeTypeContainer) - { - foreach (TypeWrapper tw in outer.InnerClasses) - { - if (tw.Name == name) - { - return tw; - } - } - } - } - return null; - } - - internal TypeWrapper CreateWrapperForAssemblyType(Type type) - { - Module mod = type.Module; - int moduleIndex = -1; - for (int i = 0; i < modules.Length; i++) - { - if (modules[i] == mod) - { - moduleIndex = i; - break; - } - } - string name; - if (isJavaModule[moduleIndex]) - { - name = CompiledTypeWrapper.GetName(type); - } - else - { - name = DotNetTypeWrapper.GetName(type); - if (name == null) - { - return null; - } - } - if (isJavaModule[moduleIndex]) - { - if (AttributeHelper.IsHideFromJava(type)) - { - return null; - } - // since this type was compiled from Java source, we have to look for our - // attributes - return CompiledTypeWrapper.newInstance(name, type); - } - else - { - if (!DotNetTypeWrapper.IsAllowedOutside(type)) - { - return null; - } - // since this type was not compiled from Java source, we don't need to - // look for our attributes, but we do need to filter unrepresentable - // stuff (and transform some other stuff) - return DotNetTypeWrapper.Create(type, name); - } - } - - internal bool InternalsVisibleTo(AssemblyName otherName) - { - if (internalsVisibleTo == null) - { - internalsVisibleTo = AttributeHelper.GetInternalsVisibleToAttributes(assembly); - } - foreach (AssemblyName name in internalsVisibleTo) - { - if (AssemblyName.ReferenceMatchesDefinition(name, otherName)) - { - return true; - } - } - return false; - } - } - - internal AssemblyClassLoader(Assembly assembly, object javaClassLoader, bool hasCustomClassLoader) - : this(assembly, null, javaClassLoader, hasCustomClassLoader) - { - } - - internal AssemblyClassLoader(Assembly assembly, string[] fixedReferences, object javaClassLoader, bool hasCustomClassLoader) - : base(CodeGenOptions.None, javaClassLoader) - { - this.assemblyLoader = new AssemblyLoader(assembly); - this.references = fixedReferences; - this.isReflectionOnly = assembly.ReflectionOnly; - this.hasCustomClassLoader = hasCustomClassLoader; - } - - private void DoInitializeExports() - { - lock (this) - { - if (delegates == null) - { - if (!(ReflectUtil.IsDynamicAssembly(assemblyLoader.Assembly)) && assemblyLoader.Assembly.GetManifestResourceInfo("ikvm.exports") != null) - { - List<string> wildcardExports = new List<string>(); - using (Stream stream = assemblyLoader.Assembly.GetManifestResourceStream("ikvm.exports")) - { - BinaryReader rdr = new BinaryReader(stream); - int assemblyCount = rdr.ReadInt32(); - exports = new Dictionary<int, List<int>>(); - exportedAssemblies = new AssemblyLoader[assemblyCount]; - exportedAssemblyNames = new string[assemblyCount]; - exportedLoaders = new Dictionary<Assembly, AssemblyLoader>(); - for (int i = 0; i < assemblyCount; i++) - { - exportedAssemblyNames[i] = rdr.ReadString(); - int typeCount = rdr.ReadInt32(); - if (typeCount == 0 && references == null) - { - wildcardExports.Add(exportedAssemblyNames[i]); - } - for (int j = 0; j < typeCount; j++) - { - int hash = rdr.ReadInt32(); - List<int> assemblies; - if (!exports.TryGetValue(hash, out assemblies)) - { - assemblies = new List<int>(); - exports.Add(hash, assemblies); - } - assemblies.Add(i); - } - } - } - if (references == null) - { - references = wildcardExports.ToArray(); - } - } - else - { - AssemblyName[] refNames = assemblyLoader.Assembly.GetReferencedAssemblies(); - references = new string[refNames.Length]; - for (int i = 0; i < references.Length; i++) - { - references[i] = refNames[i].FullName; - } - } - delegates = new AssemblyClassLoader[references.Length]; - } - } - } - - private void LazyInitExports() - { - if (delegates == null) - { - DoInitializeExports(); - } - } - - internal Assembly MainAssembly - { - get - { - return assemblyLoader.Assembly; - } - } - - internal Assembly GetAssembly(TypeWrapper wrapper) - { - Debug.Assert(wrapper.GetClassLoader() == this); - while (wrapper.IsFakeNestedType) - { - wrapper = wrapper.DeclaringTypeWrapper; - } - return wrapper.TypeAsBaseType.Assembly; - } - - internal override Type GetGenericTypeDefinition(string name) - { - try - { - // we only have to look in the main assembly, because only a .NET assembly can contain generic type definitions - // and it cannot be part of a multi assembly sharedclassloader group - Type type = assemblyLoader.Assembly.GetType(name); - if (type != null && type.IsGenericTypeDefinition) - { - return type; - } - } - catch (FileLoadException x) - { - // this can only happen if the assembly was loaded in the ReflectionOnly - // context and the requested type references a type in another assembly - // that cannot be found in the ReflectionOnly context - // TODO figure out what other exceptions Assembly.GetType() can throw - Tracer.Info(Tracer.Runtime, x.Message); - } - return null; - } - - private Assembly LoadAssemblyOrClearName(ref string name) - { - if (name == null) - { - // previous load attemp failed - return null; - } - try - { - if (isReflectionOnly) - { - return Assembly.ReflectionOnlyLoad(name); - } - else - { - return Assembly.Load(name); - } - } - catch - { - // cache failure by clearing out the name the caller uses - name = null; - // should we issue a warning error (in ikvmc)? - return null; - } - } - - internal TypeWrapper DoLoad(string name) - { - TypeWrapper tw = assemblyLoader.DoLoad(name); - if (tw != null) - { - return RegisterInitiatingLoader(tw); - } - LazyInitExports(); - if (exports != null) - { - List<int> assemblies; - if (exports.TryGetValue(JVM.PersistableHash(name), out assemblies)) - { - foreach (int index in assemblies) - { - AssemblyLoader loader = exportedAssemblies[index]; - if (loader == null) - { - Assembly asm = LoadAssemblyOrClearName(ref exportedAssemblyNames[index]); - if (asm == null) - { - continue; - } - loader = exportedAssemblies[index] = GetLoaderForExportedAssembly(asm); - } - tw = loader.DoLoad(name); - if (tw != null) - { - return RegisterInitiatingLoader(tw); - } - } - } - } - return null; - } - - private AssemblyLoader GetLoader(Assembly assembly) - { - if (assemblyLoader.Assembly == assembly) - { - return assemblyLoader; - } - return GetLoaderForExportedAssembly(assembly); - } - - private AssemblyLoader GetLoaderForExportedAssembly(Assembly assembly) - { - LazyInitExports(); - AssemblyLoader loader; - lock (exportedLoaders) - { - exportedLoaders.TryGetValue(assembly, out loader); - } - if (loader == null) - { - loader = new AssemblyLoader(assembly); - lock (exportedLoaders) - { - AssemblyLoader existing; - if (exportedLoaders.TryGetValue(assembly, out existing)) - { - // another thread beat us to it - loader = existing; - } - else - { - exportedLoaders.Add(assembly, loader); - } - } - } - return loader; - } - - internal virtual TypeWrapper GetWrapperFromAssemblyType(Type type) - { - //Tracer.Info(Tracer.Runtime, "GetWrapperFromAssemblyType: {0}", type.FullName); - Debug.Assert(!type.Name.EndsWith("[]"), "!type.IsArray", type.FullName); - Debug.Assert(ClassLoaderWrapper.GetAssemblyClassLoader(type.Assembly) == this); -#if !IKVM_REF_EMIT - Debug.Assert(!(type.Assembly is AssemblyBuilder), "!(type.Assembly is AssemblyBuilder)", type.FullName); -#endif - TypeWrapper wrapper = GetLoader(type.Assembly).CreateWrapperForAssemblyType(type); - if(wrapper != null) - { - wrapper = RegisterInitiatingLoader(wrapper); - if(wrapper.TypeAsTBD != type && (!wrapper.IsRemapped || wrapper.TypeAsBaseType != type)) - { - // this really shouldn't happen, it means that we have two different types in our assembly that both - // have the same Java name - string msg = String.Format("\nType \"{0}\" and \"{1}\" both map to the same name \"{2}\".\n", type.FullName, wrapper.TypeAsTBD.FullName, wrapper.Name); - JVM.CriticalFailure(msg, null); - } - return wrapper; - } - return null; - } - - protected override TypeWrapper LoadClassImpl(string name, bool throwClassNotFoundException) - { - TypeWrapper tw = DoLoad(name); - if(tw != null) - { - return tw; - } - if(hasCustomClassLoader) - { - return base.LoadClassImpl(name, throwClassNotFoundException); - } - else - { - tw = LoadGenericClass(name); - if(tw != null) - { - return tw; - } - return LoadReferenced(name); - } - } - - internal TypeWrapper LoadReferenced(string name) - { - LazyInitExports(); - for(int i = 0; i < delegates.Length; i++) - { - if(delegates[i] == null) - { - Assembly asm = LoadAssemblyOrClearName(ref references[i]); - if(asm != null) - { - delegates[i] = ClassLoaderWrapper.GetAssemblyClassLoader(asm); - } - } - if(delegates[i] != null) - { - TypeWrapper tw = delegates[i].DoLoad(name); - if(tw != null) - { - return tw; - } - } - } - if(!assemblyLoader.HasJavaModule) - { - return GetBootstrapClassLoader().LoadClassByDottedNameFast(name); - } - return null; - } - -#if !STATIC_COMPILER - internal Assembly FindResourceAssembliesImpl(string unmangledName, string name, bool firstOnly, ref List<Assembly> list) - { - if(ReflectUtil.IsDynamicAssembly(assemblyLoader.Assembly)) - { - return null; - } - if(assemblyLoader.Assembly.GetManifestResourceInfo(name) != null) - { - if(firstOnly) - { - return assemblyLoader.Assembly; - } - list = new List<Assembly>(); - list.Add(assemblyLoader.Assembly); - } - LazyInitExports(); - if(exports != null) - { - List<int> assemblies; - if(exports.TryGetValue(JVM.PersistableHash(unmangledName), out assemblies)) - { - foreach(int index in assemblies) - { - AssemblyLoader loader = exportedAssemblies[index]; - if(loader == null) - { - Assembly asm = LoadAssemblyOrClearName(ref exportedAssemblyNames[index]); - if(asm == null) - { - continue; - } - loader = exportedAssemblies[index] = GetLoaderForExportedAssembly(asm); - } - if(loader.Assembly.GetManifestResourceInfo(name) != null) - { - if(firstOnly) - { - return loader.Assembly; - } - if(list == null) - { - list = new List<Assembly>(); - } - list.Add(loader.Assembly); - } - } - } - } - return null; - } - - internal Assembly[] FindResourceAssemblies(string unmangledName, bool firstOnly) - { - List<Assembly> list = null; - string name = JVM.MangleResourceName(unmangledName); - Assembly first = FindResourceAssembliesImpl(unmangledName, name, firstOnly, ref list); - if(first != null) - { - return new Assembly[] { first }; - } - LazyInitExports(); - for(int i = 0; i < delegates.Length; i++) - { - if(delegates[i] == null) - { - Assembly asm = LoadAssemblyOrClearName(ref references[i]); - if(asm != null) - { - delegates[i] = ClassLoaderWrapper.GetAssemblyClassLoader(asm); - } - } - if(delegates[i] != null) - { - first = delegates[i].FindResourceAssembliesImpl(unmangledName, name, firstOnly, ref list); - if(first != null) - { - return new Assembly[] { first }; - } - } - } - if(!assemblyLoader.HasJavaModule) - { - if(firstOnly) - { - return GetBootstrapClassLoader().FindResourceAssemblies(unmangledName, firstOnly); - } - else - { - Assembly[] assemblies = GetBootstrapClassLoader().FindResourceAssemblies(unmangledName, firstOnly); - if(assemblies != null) - { - foreach(Assembly asm in assemblies) - { - if(list == null) - { - list = new List<Assembly>(); - } - if(!list.Contains(asm)) - { - list.Add(asm); - } - } - } - } - } - if(list == null) - { - return null; - } - return list.ToArray(); - } - - internal void SetInitInProgress() - { - initializerThread = Thread.CurrentThread; - } - - internal void SetInitDone() - { - lock(this) - { - initializerThread = null; - Monitor.PulseAll(this); - } - } - - internal void WaitInitDone() - { - lock(this) - { - if(initializerThread != Thread.CurrentThread) - { - while(initializerThread != null) - { - Monitor.Wait(this); - } - } - } - } -#endif // !STATIC_COMPILER - - internal virtual object GetProtectionDomain() - { -#if STATIC_COMPILER || FIRST_PASS - return null; -#else - if(protectionDomain == null) - { - java.net.URL codebase; - try - { - codebase = new java.net.URL(assemblyLoader.Assembly.CodeBase); - } - catch(NotSupportedException) - { - // dynamic assemblies don't have a codebase - codebase = null; - } - catch(java.net.MalformedURLException) - { - codebase = null; - } - java.security.Permissions permissions = new java.security.Permissions(); - permissions.add(new java.security.AllPermission()); - object pd = new java.security.ProtectionDomain(new java.security.CodeSource(codebase, (java.security.cert.Certificate[])null), permissions, (java.lang.ClassLoader)GetJavaClassLoader(), null); - lock(this) - { - if(protectionDomain == null) - { - protectionDomain = pd; - } - } - } - return protectionDomain; -#endif - } - - protected override void CheckDefineClassAllowed(string className) - { - if(DoLoad(className) != null) - { - throw new LinkageError("duplicate class definition: " + className); - } - } - - internal override TypeWrapper GetLoadedClass(string name) - { - TypeWrapper tw = base.GetLoadedClass(name); - return tw != null ? tw : DoLoad(name); - } - - internal override bool InternalsVisibleToImpl(TypeWrapper wrapper, TypeWrapper friend) - { - ClassLoaderWrapper other = friend.GetClassLoader(); - if(this == other) - { - return true; - } - AssemblyName otherName; -#if STATIC_COMPILER - CompilerClassLoader ccl = other as CompilerClassLoader; - if(ccl == null) - { - return false; - } - otherName = ccl.GetAssemblyName(); -#else - AssemblyClassLoader acl = other as AssemblyClassLoader; - if(acl == null) - { - return false; - } - otherName = acl.GetAssembly(friend).GetName(); -#endif - return GetLoaderForExportedAssembly(GetAssembly(wrapper)).InternalsVisibleTo(otherName); - } - } - - class BootstrapClassLoader : AssemblyClassLoader - { - internal BootstrapClassLoader() - : base(JVM.CoreAssembly, new string[] { - typeof(object).Assembly.FullName, // mscorlib - typeof(System.Uri).Assembly.FullName // System - }, null, false) - { - } - - internal override TypeWrapper GetWrapperFromAssemblyType(Type type) - { - // we have to special case the fake types here - if(type.IsGenericType) - { - TypeWrapper outer = ClassLoaderWrapper.GetWrapperFromType(type.GetGenericArguments()[0]); - foreach(TypeWrapper inner in outer.InnerClasses) - { - if(inner.TypeAsTBD == type) - { - return inner; - } - foreach(TypeWrapper inner2 in inner.InnerClasses) - { - if(inner2.TypeAsTBD == type) - { - return inner2; - } - } - } - return null; - } - return base.GetWrapperFromAssemblyType(type); - } - - internal override object GetProtectionDomain() - { - return null; - } - } } diff --git a/runtime/IKVM.Runtime.8.csproj b/runtime/IKVM.Runtime.8.csproj index 6a6b4763..793418f9 100644 --- a/runtime/IKVM.Runtime.8.csproj +++ b/runtime/IKVM.Runtime.8.csproj @@ -79,6 +79,7 @@ <Compile Include="..\CommonAssemblyInfo.cs"> <SubType>Code</SubType> </Compile> + <Compile Include="AssemblyClassLoader.cs" /> <Compile Include="AssemblyInfo.cs"> <SubType>Code</SubType> </Compile> diff --git a/runtime/runtime.build b/runtime/runtime.build index adb12c9b..8b30c0af 100644 --- a/runtime/runtime.build +++ b/runtime/runtime.build @@ -65,6 +65,7 @@ <sources> <include name="../CommonAssemblyInfo.cs" /> <include name="AssemblyInfo.cs" /> + <include name="AssemblyClassLoader.cs" /> <include name="atomic.cs" /> <include name="attributes.cs" /> <include name="BigEndianBinaryReader.cs" /> |