diff --git a/ikvmc.8.csproj b/ikvmc.8.csproj index 06b643f0..a00c8623 100644 --- a/ikvmc.8.csproj +++ b/ikvmc.8.csproj @@ -71,6 +71,7 @@ Code + Code diff --git a/ikvmc/AssemblyResolver.cs b/ikvmc/AssemblyResolver.cs new file mode 100644 index 00000000..85556d45 --- /dev/null +++ b/ikvmc/AssemblyResolver.cs @@ -0,0 +1,294 @@ +/* + Copyright (C) 2010 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.Collections.Generic; +using System.Text; +using System.IO; +using IKVM.Reflection; + +namespace IKVM.Internal +{ + sealed class AssemblyResolver + { + private readonly List libpath = new List(); + private Universe universe; + + internal int Init(Universe universe, bool nostdlib, IList references, IList userLibPaths) + { + this.universe = universe; + // like the C# compiler, the references are loaded from: + // current directory, CLR directory, -lib: option, %LIB% environment + // (note that, unlike the C# compiler, we don't add the CLR directory if -nostdlib has been specified) + libpath.Add(Environment.CurrentDirectory); + if (!nostdlib) + { + libpath.Add(System.Runtime.InteropServices.RuntimeEnvironment.GetRuntimeDirectory()); + } + foreach (string str in userLibPaths) + { + AddLibraryPaths(str, "-lib option"); + } + AddLibraryPaths(Environment.GetEnvironmentVariable("LIB") ?? "", "LIB environment"); + int rc = 0; + if (nostdlib) + { + rc = LoadMscorlib(references); + } + if (rc == 0) + { + universe.AssemblyResolve += new IKVM.Reflection.ResolveEventHandler(universe_AssemblyResolve); + } + return rc; + } + + internal Assembly LoadWithPartialName(string name) + { + foreach (string path in FindAssemblyPath(name + ".dll")) + { + return universe.LoadFile(path); + } + return null; + } + + internal int ResolveReference(Dictionary cache, ref Assembly[] references, string reference) + { + string[] files = new string[0]; + try + { + string path = Path.GetDirectoryName(reference); + files = Directory.GetFiles(path == "" ? "." : path, Path.GetFileName(reference)); + } + catch (ArgumentException) + { + } + catch (IOException) + { + } + if (files.Length == 0) + { + Assembly asm = null; + cache.TryGetValue(reference, out asm); + try + { + if (asm == null) + { + foreach (string found in FindAssemblyPath(reference)) + { + asm = StaticCompiler.LoadFile(found); + cache.Add(reference, asm); + break; + } + } + } + catch (FileLoadException) + { + } + if (asm == null) + { + Console.Error.WriteLine("Error: reference not found: {0}", reference); + return 1; + } + ArrayAppend(ref references, asm); + } + else + { + foreach (string file in files) + { + try + { + Assembly asm; + if (!cache.TryGetValue(file, out asm)) + { + asm = StaticCompiler.LoadFile(file); + } + ArrayAppend(ref references, asm); + } + catch (FileLoadException) + { + Console.Error.WriteLine("Error: reference not found: {0}", file); + return 1; + } + } + } + return 0; + } + + private static void ArrayAppend(ref T[] array, T element) + { + if (array == null) + { + array = new T[] { element }; + } + else + { + T[] temp = new T[array.Length + 1]; + Array.Copy(array, 0, temp, 0, array.Length); + temp[temp.Length - 1] = element; + array = temp; + } + } + + private Assembly universe_AssemblyResolve(object sender, IKVM.Reflection.ResolveEventArgs args) + { + // to support Universe.GetType("System.Object, mscorlib"), we have to support partial names + // (the map.xml file contains such type names) + bool partialName = !args.Name.Contains(","); + AssemblyName name = new AssemblyName(args.Name); + foreach (string file in FindAssemblyPath(name.Name + ".dll")) + { + Assembly asm = StaticCompiler.LoadFile(file); + if (Matches(asm.GetName(), name) || partialName) + { + return asm; + } + } + if (args.RequestingAssembly != null) + { + string path = Path.Combine(Path.GetDirectoryName(args.RequestingAssembly.Location), name.Name + ".dll"); + if (File.Exists(path) && Matches(AssemblyName.GetAssemblyName(path), name)) + { + return StaticCompiler.LoadFile(path); + } + } + Console.Error.WriteLine("Error: unable to find assembly '{0}'", args.Name); + if (args.RequestingAssembly != null) + { + Console.Error.WriteLine(" (a dependency of '{0}')", args.RequestingAssembly.FullName); + } + Environment.Exit(1); + return null; + } + + private static bool Matches(AssemblyName assemblyDef, AssemblyName assemblyRef) + { + if (assemblyDef.Name != assemblyRef.Name) + { + return false; + } + bool strongNamed = IsStrongNamed(assemblyDef); + if (strongNamed != IsStrongNamed(assemblyRef)) + { + return false; + } + if (strongNamed) + { + return IsEqual(assemblyDef.GetPublicKeyToken(), assemblyRef.GetPublicKeyToken()) && assemblyDef.Version >= assemblyRef.Version; + } + return true; + } + + private static bool IsStrongNamed(AssemblyName name) + { + byte[] key = name.GetPublicKeyToken(); + return key != null && key.Length != 0; + } + + private static bool IsEqual(byte[] b1, byte[] b2) + { + if (b1.Length != b2.Length) + { + return false; + } + for (int i = 0; i < b1.Length; i++) + { + if (b1[i] != b2[i]) + { + return false; + } + } + return true; + } + + private void AddLibraryPaths(string str, string msg) + { + foreach (string dir in str.Split(Path.PathSeparator)) + { + if (Directory.Exists(dir)) + { + libpath.Add(dir); + } + else if (dir != "") + { + Console.Error.WriteLine("Warning: directory '{0}' specified in {1} is not valid", dir, msg); + } + } + } + + private int LoadMscorlib(IList references) + { + Universe dummy = new Universe(); + foreach (string r in references) + { + try + { + Assembly asm = dummy.LoadFile(r); + if (asm.GetType("System.Object") != null) + { + StaticCompiler.Universe.LoadMscorlib(r); + return 0; + } + } + catch + { + } + } + foreach (string mscorlib in FindAssemblyPath("mscorlib.dll")) + { + StaticCompiler.Universe.LoadMscorlib(mscorlib); + return 0; + } + Console.Error.WriteLine("Error: unable to find mscorlib.dll"); + return 1; + } + + private IEnumerable FindAssemblyPath(string file) + { + if (Path.IsPathRooted(file)) + { + if (File.Exists(file)) + { + yield return file; + } + } + else + { + foreach (string dir in libpath) + { + string path = Path.Combine(dir, file); + if (File.Exists(path)) + { + yield return path; + } + // for legacy compat, we try again after appending .dll + path = Path.Combine(dir, file + ".dll"); + if (File.Exists(path)) + { + Console.WriteLine("Warning: Found assembly '{0}' using legacy search rule. Please append '.dll' to the reference.", file); + yield return path; + } + } + } + } + } +} diff --git a/ikvmc/Compiler.cs b/ikvmc/Compiler.cs index 52f0ba46..2d35fad6 100644 --- a/ikvmc/Compiler.cs +++ b/ikvmc/Compiler.cs @@ -44,6 +44,9 @@ class IkvmcCompiler private List classesToExclude = new List(); private static bool time; private static string runtimeAssembly; + private static bool nostdlib; + private static readonly List libpaths = new List(); + private static readonly AssemblyResolver loader = new AssemblyResolver(); private static List GetArgs(string[] args) { @@ -76,7 +79,6 @@ class IkvmcCompiler static int Main(string[] args) { DateTime start = DateTime.Now; - StaticCompiler.Universe.AssemblyResolve += new ResolveEventHandler(CurrentDomain_AssemblyResolve); System.Threading.Thread.CurrentThread.Name = "compiler"; Tracer.EnableTraceConsoleListener(); Tracer.EnableTraceForDebug(); @@ -88,7 +90,12 @@ class IkvmcCompiler } IkvmcCompiler comp = new IkvmcCompiler(); List targets = new List(); - int rc = comp.ParseCommandLine(argList.GetEnumerator(), targets); + CompilerOptions toplevel = new CompilerOptions(); + int rc = comp.ParseCommandLine(argList.GetEnumerator(), targets, toplevel); + if (rc == 0) + { + loader.Init(StaticCompiler.Universe, nostdlib, toplevel.unresolvedReferences, libpaths); + } if (rc == 0) { rc = ResolveReferences(targets); @@ -207,11 +214,12 @@ class IkvmcCompiler Console.Error.WriteLine(" class loader"); Console.Error.WriteLine(" -baseaddress:
Base address for the library to be built"); Console.Error.WriteLine(" -nopeercrossreference Do not automatically cross reference all peers"); + Console.Error.WriteLine(" -nostdlib Do not reference standard libraries"); + Console.Error.WriteLine(" -lib: Additional directories to search for references"); } - int ParseCommandLine(IEnumerator arglist, List targets) + int ParseCommandLine(IEnumerator arglist, List targets, CompilerOptions options) { - CompilerOptions options = new CompilerOptions(); options.target = PEFileKinds.ConsoleApplication; options.guessFileKind = true; options.version = new Version(0, 0, 0, 0); @@ -613,6 +621,16 @@ class IkvmcCompiler { options.crossReferenceAllPeers = false; } + else if(s=="-nostdlib") + { + // this is a global option + nostdlib = true; + } + else if(s.StartsWith("-lib:")) + { + // this is a global option + libpaths.Add(s.Substring(5)); + } else { Console.Error.WriteLine("Warning: unrecognized option: {0}", s); @@ -778,7 +796,7 @@ class IkvmcCompiler goto next_reference; } } - int rc = ResolveReferences(cache, ref target.references, reference); + int rc = loader.ResolveReference(cache, ref target.references, reference); if (rc != 0) { return rc; @@ -790,72 +808,6 @@ class IkvmcCompiler return 0; } - private static int ResolveReferences(Dictionary cache, ref Assembly[] references, string r) - { - string[] files = new string[0]; - try - { - string path = Path.GetDirectoryName(r); - files = Directory.GetFiles(path == "" ? "." : path, Path.GetFileName(r)); - } - catch (ArgumentException) - { - } - catch (IOException) - { - } - if (files.Length == 0) - { - Assembly asm = null; - cache.TryGetValue(r, out asm); - try - { - if (asm == null) - { -#pragma warning disable 618 - // Assembly.LoadWithPartialName is obsolete - System.Reflection.Assembly found = System.Reflection.Assembly.LoadWithPartialName(r); -#pragma warning restore - if (found != null) - { - asm = StaticCompiler.LoadFile(found.Location); - cache.Add(r, asm); - } - } - } - catch (FileLoadException) - { - } - if (asm == null) - { - Console.Error.WriteLine("Error: reference not found: {0}", r); - return 1; - } - ArrayAppend(ref references, asm); - } - else - { - foreach (string file in files) - { - try - { - Assembly asm; - if (!cache.TryGetValue(file, out asm)) - { - asm = StaticCompiler.LoadFile(file); - } - ArrayAppend(ref references, asm); - } - catch (FileLoadException) - { - Console.Error.WriteLine("Error: reference not found: {0}", file); - return 1; - } - } - } - return 0; - } - private static void ArrayAppend(ref T[] array, T element) { if (array == null) @@ -1092,77 +1044,4 @@ class IkvmcCompiler Console.Error.WriteLine("Warning: could not find exclusion file '{0}'", filename); } } - - // this method checks if the assembly was loaded from a CLR probe location - // (in that case we can also resolve its dependencies via the CLR) - private static bool IsLoadedFromCurrentClrProbeLocation(Assembly asm) - { - try - { - // we have to use StringComparison.OrdinalIgnoreCase, because it the CLR sometimes appends ".dll" - // and other times ".DLL" (when the assembly is loaded from DEVPATH) - return System.Reflection.Assembly.ReflectionOnlyLoad(asm.FullName).Location.Equals(asm.Location, StringComparison.OrdinalIgnoreCase); - } - catch - { - return false; - } - } - - private static Assembly CurrentDomain_AssemblyResolve(object sender, ResolveEventArgs args) - { - if (args.RequestingAssembly == null || IsLoadedFromCurrentClrProbeLocation(args.RequestingAssembly)) - { - System.Reflection.Assembly asm = null; - try - { - asm = System.Reflection.Assembly.ReflectionOnlyLoad(args.Name); - } - catch - { - } - if (asm != null) - { - return StaticCompiler.LoadFile(asm.Location); - } - } - else - { - // apply unification and policy - try - { - string name = System.Reflection.Assembly.ReflectionOnlyLoad(args.Name).FullName; - if (name != args.Name) - { - return StaticCompiler.Load(name); - } - } - catch - { - } - // HACK support loading additional assemblies from a multi assembly group from the same location as the main assembly - Type main = args.RequestingAssembly.GetType("__"); - if (main != null) - { - try - { - string path = Path.Combine(Path.GetDirectoryName(main.Assembly.Location), new AssemblyName(args.Name).Name + ".dll"); - if (AssemblyName.GetAssemblyName(path).FullName == args.Name) - { - return StaticCompiler.LoadFile(path); - } - } - catch - { - } - } - } - Console.Error.WriteLine("Error: unable to find assembly '{0}'", args.Name); - if (args.RequestingAssembly != null) - { - Console.Error.WriteLine(" (a dependency of '{0}')", args.RequestingAssembly.FullName); - } - Environment.Exit(1); - return null; - } } diff --git a/ikvmc/ikvmc.build b/ikvmc/ikvmc.build index 9470185a..78455869 100644 --- a/ikvmc/ikvmc.build +++ b/ikvmc/ikvmc.build @@ -13,6 +13,7 @@ + diff --git a/ikvmstub/ikvmstub.8.csproj b/ikvmstub/ikvmstub.8.csproj index 5b8be175..74ac0956 100644 --- a/ikvmstub/ikvmstub.8.csproj +++ b/ikvmstub/ikvmstub.8.csproj @@ -73,6 +73,7 @@ Code + Code diff --git a/ikvmstub/ikvmstub.build b/ikvmstub/ikvmstub.build index eb1f5b1e..387837a7 100644 --- a/ikvmstub/ikvmstub.build +++ b/ikvmstub/ikvmstub.build @@ -8,6 +8,7 @@ + diff --git a/ikvmstub/ikvmstub.cs b/ikvmstub/ikvmstub.cs index b6de9510..6b5236e9 100644 --- a/ikvmstub/ikvmstub.cs +++ b/ikvmstub/ikvmstub.cs @@ -29,8 +29,6 @@ using IKVM.Attributes; using IKVM.Internal; using IKVM.Reflection; using Type = IKVM.Reflection.Type; -using ResolveEventArgs = IKVM.Reflection.ResolveEventArgs; -using ResolveEventHandler = IKVM.Reflection.ResolveEventHandler; static class NetExp { @@ -48,6 +46,9 @@ static class NetExp string assemblyNameOrPath = null; bool continueOnError = false; bool autoLoadSharedClassLoaderAssemblies = false; + List references = new List(); + List libpaths = new List(); + bool nostdlib = false; foreach(string s in args) { if(s.StartsWith("-") || assemblyNameOrPath != null) @@ -66,17 +67,15 @@ static class NetExp } else if(s.StartsWith("-r:") || s.StartsWith("-reference:")) { - string path = s.Substring(s.IndexOf(':') + 1); - try - { - StaticCompiler.Universe.LoadFile(path); - } - catch (Exception x) - { - Console.Error.WriteLine("Error: unable to load reference {0}", path); - Console.Error.WriteLine(" ({0})", x.Message); - return 1; - } + references.Add(s.Substring(s.IndexOf(':') + 1)); + } + else if(s == "-nostdlib") + { + nostdlib = true; + } + else if(s.StartsWith("-lib:")) + { + libpaths.Add(s.Substring(5)); } else { @@ -94,9 +93,21 @@ static class NetExp { Console.Error.WriteLine(GetVersionAndCopyrightInfo()); Console.Error.WriteLine(); - Console.Error.WriteLine("usage: ikvmstub [-serialver] [-skiperror] "); + Console.Error.WriteLine("usage: ikvmstub [-serialver] [-skiperror] [-reference:] [-lib:] "); return 1; } + AssemblyResolver resolver = new AssemblyResolver(); + resolver.Init(StaticCompiler.Universe, nostdlib, references, libpaths); + Dictionary cache = new Dictionary(); + foreach (string reference in references) + { + Assembly[] dummy = null; + int rc1 = resolver.ResolveReference(cache, ref dummy, reference); + if (rc1 != 0) + { + return rc1; + } + } Assembly assembly = null; try { @@ -109,19 +120,11 @@ static class NetExp } if(file != null && file.Exists) { - StaticCompiler.Universe.AssemblyResolve += new ResolveEventHandler(Universe_AssemblyResolve); assembly = StaticCompiler.LoadFile(assemblyNameOrPath); } else { -#pragma warning disable 618 - // Assembly.LoadWithPartialName is obsolete - System.Reflection.Assembly asm = System.Reflection.Assembly.LoadWithPartialName(assemblyNameOrPath); -#pragma warning restore - if (asm != null) - { - assembly = StaticCompiler.Universe.LoadFile(asm.Location); - } + assembly = resolver.LoadWithPartialName(assemblyNameOrPath); } int rc = 0; if(assembly == null) @@ -244,29 +247,6 @@ static class NetExp } } - private static Assembly Universe_AssemblyResolve(object sender, ResolveEventArgs args) - { - string path = args.Name; - int index = path.IndexOf(','); - if (index > 0) - { - path = path.Substring(0, index); - } - path = file.DirectoryName + Path.DirectorySeparatorChar + path + ".dll"; - if (File.Exists(path)) - { - return StaticCompiler.LoadFile(path); - } - try - { - return StaticCompiler.LoadFile(System.Reflection.Assembly.Load(args.Name).Location); - } - catch - { - } - return null; - } - private static void WriteClass(TypeWrapper tw) { string name = tw.Name.Replace('.', '/'); diff --git a/openjdk/openjdk.build b/openjdk/openjdk.build index 9b03f944..3823f7e6 100644 --- a/openjdk/openjdk.build +++ b/openjdk/openjdk.build @@ -47,7 +47,7 @@ - + @@ -74,7 +74,7 @@ - + @@ -165,6 +165,7 @@ + @@ -183,7 +184,6 @@ -