diff --git a/ikvmc/CompilerClassLoader.cs b/ikvmc/CompilerClassLoader.cs index 4f418e01..131878c4 100644 --- a/ikvmc/CompilerClassLoader.cs +++ b/ikvmc/CompilerClassLoader.cs @@ -422,15 +422,20 @@ namespace IKVM.Internal ((DynamicClassLoader)this.GetTypeWrapperFactory()).FinishAll(); ModuleBuilder mb = GetTypeWrapperFactory().ModuleBuilder; - // HACK force all referenced assemblies to end up as references in the assembly - // (even if they are otherwise unused), to make sure that the assembly class loader - // delegates to them at runtime. - for(int i = 0;i < referencedAssemblies.Length; i++) + if(targetIsModule) { - Type[] types = referencedAssemblies[i].MainAssembly.GetExportedTypes(); - if(types.Length > 0) + // HACK force all referenced assemblies to end up as references in the assembly + // (even if they are otherwise unused), to make sure that the assembly class loader + // delegates to them at runtime. + // NOTE now we only do this for modules, when we're an assembly we store the exported + // assemblies in the ikvm.exports resource. + for(int i = 0;i < referencedAssemblies.Length; i++) { - mb.GetTypeToken(types[0]); + Type[] types = referencedAssemblies[i].MainAssembly.GetExportedTypes(); + if(types.Length > 0) + { + mb.GetTypeToken(types[0]); + } } } mb.CreateGlobalFunctions(); @@ -454,18 +459,19 @@ namespace IKVM.Internal mb.SetCustomAttribute(cab); } - // add a package list + // add a package list and export map if(options.sharedclassloader == null || options.sharedclassloader[0] == this) { string[] list = new string[packages.Count]; packages.Keys.CopyTo(list, 0); mb.SetCustomAttribute(new CustomAttributeBuilder(JVM.LoadType(typeof(PackageListAttribute)).GetConstructor(new Type[] { typeof(string[]) }), new object[] { list })); - } - - // if we're the main assembly in a shared class loader group, add a resource that lists all the types available in the other assemblies - if(options.sharedclassloader != null && options.sharedclassloader.Count > 1 && options.sharedclassloader[0] == this) - { - WriteExportMap(); + // We can't add the resource when we're a module, because a multi-module assembly has a single resource namespace + // and since you cannot combine -target:module with -sharedclassloader we don't need an export map + // (the wildcard exports have already been added above, by making sure that we statically reference the assemblies). + if(!targetIsModule) + { + WriteExportMap(); + } } if(targetIsModule) @@ -482,40 +488,51 @@ namespace IKVM.Internal } } - private static void AddExportMapEntry(Dictionary> map, CompilerClassLoader ccl, string name) + private static void AddExportMapEntry(Dictionary> map, CompilerClassLoader ccl, string name) { + AssemblyName asm = ccl.assemblyBuilder.GetName(); List list; - if (!map.TryGetValue(ccl, out list)) + if (!map.TryGetValue(asm, out list)) { list = new List(); - map.Add(ccl, list); + map.Add(asm, list); + } + if (list != null) // if list is null, we already have a wildcard export for this assembly + { + list.Add(name); } - list.Add(name); } private void WriteExportMap() { - Dictionary> exportedNamesPerAssembly = new Dictionary>(); + Dictionary> exportedNamesPerAssembly = new Dictionary>(); + foreach (AssemblyClassLoader acl in referencedAssemblies) + { + exportedNamesPerAssembly.Add(acl.MainAssembly.GetName(), null); + } foreach (TypeWrapper tw in dynamicallyImportedTypes) { AddExportMapEntry(exportedNamesPerAssembly, (CompilerClassLoader)tw.GetClassLoader(), tw.Name); } - foreach (CompilerClassLoader ccl in options.sharedclassloader) + if (options.sharedclassloader != null) { - if (ccl != this) + foreach (CompilerClassLoader ccl in options.sharedclassloader) { - if (ccl.options.resources != null) + if (ccl != this) { - foreach (string name in ccl.options.resources.Keys) + if (ccl.options.resources != null) { - AddExportMapEntry(exportedNamesPerAssembly, ccl, name); + foreach (string name in ccl.options.resources.Keys) + { + AddExportMapEntry(exportedNamesPerAssembly, ccl, name); + } } - } - if (ccl.options.externalResources != null) - { - foreach (string name in ccl.options.externalResources.Keys) + if (ccl.options.externalResources != null) { - AddExportMapEntry(exportedNamesPerAssembly, ccl, name); + foreach (string name in ccl.options.externalResources.Keys) + { + AddExportMapEntry(exportedNamesPerAssembly, ccl, name); + } } } } @@ -523,13 +540,22 @@ namespace IKVM.Internal MemoryStream ms = new MemoryStream(); BinaryWriter bw = new BinaryWriter(ms); bw.Write(exportedNamesPerAssembly.Count); - foreach (KeyValuePair> kv in exportedNamesPerAssembly) + foreach (KeyValuePair> kv in exportedNamesPerAssembly) { - bw.Write(kv.Key.assemblyBuilder.GetName().FullName); - bw.Write(kv.Value.Count); - foreach (string name in kv.Value) + bw.Write(kv.Key.FullName); + if (kv.Value == null) { - bw.Write(JVM.PersistableHash(name)); + // wildcard export + bw.Write(0); + } + else + { + Debug.Assert(kv.Value.Count != 0); + bw.Write(kv.Value.Count); + foreach (string name in kv.Value) + { + bw.Write(JVM.PersistableHash(name)); + } } } ms.Position = 0; @@ -2332,10 +2358,6 @@ namespace IKVM.Internal { compiler.EmitRemappedTypes2ndPass(); } - if (!compilingCoreAssembly) - { - ClassLoaderWrapper.SetBootstrapClassLoader(ClassLoaderWrapper.GetAssemblyClassLoader(JVM.CoreAssembly)); - } Dictionary mainAssemblyCabs = new Dictionary(); foreach (CompilerClassLoader compiler in compilers) { diff --git a/runtime/ClassLoaderWrapper.cs b/runtime/ClassLoaderWrapper.cs index 6643809a..18490970 100644 --- a/runtime/ClassLoaderWrapper.cs +++ b/runtime/ClassLoaderWrapper.cs @@ -71,6 +71,7 @@ namespace IKVM.Internal #if STATIC_COMPILER // HACK this is used by the ahead-of-time compiler to overrule the bootstrap classloader + // when we're compiling the core class libraries internal static void SetBootstrapClassLoader(ClassLoaderWrapper bootstrapClassLoader) { Debug.Assert(ClassLoaderWrapper.bootstrapClassLoader == null); @@ -1057,11 +1058,15 @@ namespace IKVM.Internal return loader; } } -#if !STATIC_COMPILER && !FIRST_PASS if(assembly == JVM.CoreAssembly) { - return GetBootstrapClassLoader(); + // This cast is necessary for ikvmc and a no-op for the runtime. + // Note that the cast cannot fail, because ikvmc will only return a non AssemblyClassLoader + // from GetBootstrapClassLoader() when compiling the core assembly and in that case JVM.CoreAssembly + // will be null. + return (AssemblyClassLoader)GetBootstrapClassLoader(); } +#if !STATIC_COMPILER && !FIRST_PASS if(!assembly.ReflectionOnly) { Type customClassLoaderClass = null; @@ -1678,20 +1683,20 @@ namespace IKVM.Internal } internal AssemblyClassLoader(Assembly assembly, object javaClassLoader, bool hasCustomClassLoader) - : this(assembly, assembly.GetReferencedAssemblies(), javaClassLoader, hasCustomClassLoader) + : this(assembly, null, javaClassLoader, hasCustomClassLoader) { } - internal AssemblyClassLoader(Assembly assembly, AssemblyName[] references, object javaClassLoader, bool hasCustomClassLoader) + internal AssemblyClassLoader(Assembly assembly, AssemblyName[] fixedReferences, object javaClassLoader, bool hasCustomClassLoader) : base(CodeGenOptions.None, javaClassLoader) { this.assemblyLoader = new AssemblyLoader(assembly); - this.references = references; + this.references = fixedReferences; this.isReflectionOnly = assembly.ReflectionOnly; this.hasCustomClassLoader = hasCustomClassLoader; - delegates = new AssemblyClassLoader[references.Length]; - if(assembly.GetType("__") != null) + if(assembly.GetManifestResourceInfo("ikvm.exports") != null) { + List wildcardExports = new List(); using(Stream stream = assembly.GetManifestResourceStream("ikvm.exports")) { BinaryReader rdr = new BinaryReader(stream); @@ -1704,6 +1709,10 @@ namespace IKVM.Internal { exportedAssemblyNames[i] = new AssemblyName(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(); @@ -1717,7 +1726,16 @@ namespace IKVM.Internal } } } + if(references == null) + { + references = wildcardExports.ToArray(); + } } + else + { + references = assembly.GetReferencedAssemblies(); + } + delegates = new AssemblyClassLoader[references.Length]; } internal Assembly MainAssembly