internal CompilerClassLoader(AssemblyClassLoader[] referencedAssemblies, CompilerOptions options, string path, bool targetIsModule, string assemblyName, Dictionary<string, ClassItem> classes)
			: base(options.codegenoptions, null)
		{
			this.referencedAssemblies = referencedAssemblies;
			this.options = options;
			this.classes = classes;
			this.assemblyName = assemblyName;
			FileInfo assemblyPath = new FileInfo(path);
			this.assemblyFile = assemblyPath.Name;
			this.assemblyDir = assemblyPath.DirectoryName;
			this.targetIsModule = targetIsModule;
			Tracer.Info(Tracer.Compiler, "Instantiate CompilerClassLoader for {0}", assemblyName);
		}
		internal static void IssueMessage(CompilerOptions options, Message msgId, params string[] values)
		{
			StringBuilder sb = new StringBuilder();
			sb.Append((int)msgId);
			if(values.Length > 0)
			{
				sb.Append(':').Append(values[0]);
			}
			string key = sb.ToString();
			if(options.suppressWarnings.ContainsKey(key)
				|| options.suppressWarnings.ContainsKey(((int)msgId).ToString()))
			{
				return;
			}
			options.suppressWarnings.Add(key, key);
			if(options.writeSuppressWarningsFile != null)
			{
				File.AppendAllText(options.writeSuppressWarningsFile, "-nowarn:" + key + Environment.NewLine);
			}
			string msg;
			switch(msgId)
			{
				case Message.MainMethodFound:
					msg = "Found main method in class \"{0}\"";
					break;
				case Message.OutputFileIs:
					msg = "Output file is \"{0}\"";
					break;
				case Message.AutoAddRef:
					msg = "Automatically adding reference to \"{0}\"";
					break;
				case Message.MainMethodFromManifest:
					msg = "Using main class \"{0}\" based on jar manifest";
					break;
				case Message.ClassNotFound:
					msg = "Class \"{0}\" not found";
					break;
				case Message.ClassFormatError:
					msg = "Unable to compile class \"{0}\"" + Environment.NewLine + 
						"    (class format error \"{1}\")";
					break;
				case Message.DuplicateClassName:
					msg = "Duplicate class name: \"{0}\"";
					break;
				case Message.IllegalAccessError:
					msg = "Unable to compile class \"{0}\"" + Environment.NewLine + 
						"    (illegal access error \"{1}\")";
					break;
				case Message.VerificationError:
					msg = "Unable to compile class \"{0}\"" + Environment.NewLine + 
						"    (verification error \"{1}\")";
					break;
				case Message.NoClassDefFoundError:
					msg = "Unable to compile class \"{0}\"" + Environment.NewLine + 
						"    (missing class \"{1}\")";
					break;
				case Message.GenericUnableToCompileError:
					msg = "Unable to compile class \"{0}\"" + Environment.NewLine + 
						"    (\"{1}\": \"{2}\")";
					break;
				case Message.DuplicateResourceName:
					msg = "Skipping resource (name clash): \"{0}\"";
					break;
				case Message.NotAClassFile:
					msg = "Not a class file \"{0}\", including it as resource" + Environment.NewLine +
						"    (class format error \"{1}\")";
					break;
				case Message.SkippingReferencedClass:
					msg = "Skipping class: \"{0}\"" + Environment.NewLine +
						"    (class is already available in referenced assembly \"{1}\")";
					break;
				case Message.NoJniRuntime:
					msg = "Unable to load runtime JNI assembly";
					break;
				case Message.EmittedNoClassDefFoundError:
					msg = "Emitted java.lang.NoClassDefFoundError in \"{0}\"" + Environment.NewLine +
						"    (\"{1}\")";
					break;
				case Message.EmittedIllegalAccessError:
					msg = "Emitted java.lang.IllegalAccessError in \"{0}\"" + Environment.NewLine +
						"    (\"{1}\")";
					break;
				case Message.EmittedInstantiationError:
					msg = "Emitted java.lang.InstantiationError in \"{0}\"" + Environment.NewLine +
						"    (\"{1}\")";
					break;
				case Message.EmittedIncompatibleClassChangeError:
					msg = "Emitted java.lang.IncompatibleClassChangeError in \"{0}\"" + Environment.NewLine +
						"    (\"{1}\")";
					break;
				case Message.EmittedNoSuchFieldError:
					msg = "Emitted java.lang.NoSuchFieldError in \"{0}\"" + Environment.NewLine +
						"    (\"{1}\")";
					break;
				case Message.EmittedAbstractMethodError:
					msg = "Emitted java.lang.AbstractMethodError in \"{0}\"" + Environment.NewLine +
						"    (\"{1}\")";
					break;
				case Message.EmittedNoSuchMethodError:
					msg = "Emitted java.lang.NoSuchMethodError in \"{0}\"" + Environment.NewLine +
						"    (\"{1}\")";
					break;
				case Message.EmittedLinkageError:
					msg = "Emitted java.lang.LinkageError in \"{0}\"" + Environment.NewLine +
						"    (\"{1}\")";
					break;
				case Message.EmittedVerificationError:
					msg = "Emitted java.lang.VerificationError in \"{0}\"" + Environment.NewLine +
						"    (\"{1}\")";
					break;
				case Message.EmittedClassFormatError:
					msg = "Emitted java.lang.ClassFormatError in \"{0}\"" + Environment.NewLine +
						"    (\"{1}\")";
					break;
				case Message.InvalidCustomAttribute:
					msg = "Error emitting \"{0}\" custom attribute" + Environment.NewLine +
						"    (\"{1}\")";
					break;
				case Message.IgnoredCustomAttribute:
					msg = "Custom attribute \"{0}\" was ignored" + Environment.NewLine +
						"    (\"{1}\")";
					break;
				case Message.AssumeAssemblyVersionMatch:
					msg = "Assuming assembly reference \"{0}\" matches \"{1}\", you may need to supply runtime policy";
					break;
				case Message.InvalidDirectoryInLibOptionPath:
					msg = "Directory \"{0}\" specified in -lib option is not valid";
					break;
				case Message.InvalidDirectoryInLibEnvironmentPath:
					msg = "Directory \"{0}\" specified in LIB environment is not valid";
					break;
				case Message.LegacySearchRule:
					msg = "Found assembly \"{0}\" using legacy search rule, please append '.dll' to the reference";
					break;
				case Message.AssemblyLocationIgnored:
					msg = "Assembly \"{0}\" is ignored as previously loaded assembly \"{1}\" has the same identity \"{2}\"";
					break;
				case Message.InterfaceMethodCantBeInternal:
					msg = "Ignoring @ikvm.lang.Internal annotation on interface method" + Environment.NewLine +
						"    (\"{0}.{1}{2}\")";
					break;
				case Message.DllExportMustBeStaticMethod:
					msg = "Ignoring @ikvm.lang.DllExport annotation on non-static method" + Environment.NewLine +
						"    (\"{0}.{1}{2}\")";
					break;
				case Message.DllExportRequiresSupportedPlatform:
					msg = "Ignoring @ikvm.lang.DllExport annotation due to unsupported target platform";
					break;
				case Message.NonPrimaryAssemblyReference:
					msg = "Referenced assembly \"{0}\" is not the primary assembly of a shared class loader group, referencing primary assembly \"{1}\" instead";
					break;
				case Message.DuplicateAssemblyReference:
					msg = "Duplicate assembly reference \"{0}\"";
					break;
				case Message.UnableToCreateProxy:
					msg = "Unable to create proxy \"{0}\"" + Environment.NewLine +
						"    (\"{1}\")";
					break;
				case Message.DuplicateProxy:
					msg = "Duplicate proxy \"{0}\"";
					break;
				case Message.MapXmlUnableToResolveOpCode:
					msg = "Unable to resolve opcode in remap file: {0}";
					break;
				case Message.MapXmlError:
					msg = "Error in remap file: {0}";
					break;
				case Message.InputFileNotFound:
					msg = "Source file '{0}' not found";
					break;
				case Message.UnknownFileType:
					msg = "Unknown file type: {0}";
					break;
				case Message.UnknownElementInMapFile:
					msg = "Unknown element {0} in remap file, line {1}, column {2}";
					break;
				case Message.UnknownAttributeInMapFile:
					msg = "Unknown attribute {0} in remap file, line {1}, column {2}";
					break;
				case Message.InvalidMemberNameInMapFile:
					msg = "Invalid {0} name '{1}' in remap file in class {2}";
					break;
				case Message.InvalidMemberSignatureInMapFile:
					msg = "Invalid {0} signature '{3}' in remap file for {0} {1}.{2}";
					break;
				case Message.InvalidPropertyNameInMapFile:
					msg = "Invalid property {0} name '{3}' in remap file for property {1}.{2}";
					break;
				case Message.InvalidPropertySignatureInMapFile:
					msg = "Invalid property {0} signature '{3}' in remap file for property {1}.{2}";
					break;
				case Message.UnknownWarning:
					msg = "{0}";
					break;
				default:
					throw new InvalidProgramException();
			}
			bool error = msgId >= Message.StartErrors
				|| (options.warnaserror && msgId >= Message.StartWarnings)
				|| options.errorWarnings.ContainsKey(key)
				|| options.errorWarnings.ContainsKey(((int)msgId).ToString());
			Console.Error.Write("{0} IKVMC{1:D4}: ", error ? "error" : msgId < Message.StartWarnings ? "note" : "warning", (int)msgId);
			if (error && Message.StartWarnings <= msgId && msgId < Message.StartErrors)
			{
				Console.Error.Write("Warning as Error: ");
			}
			Console.Error.WriteLine(msg, values);
			if(options != toplevel && options.path != null)
			{
				Console.Error.WriteLine("    (in {0})", options.path);
			}
			if(error)
			{
				if (++errorCount == 100)
				{
					throw new FatalCompilerErrorException(Message.MaximumErrorCountReached);
				}
			}
		}
		private static int CreateCompiler(CompilerOptions options, ref CompilerClassLoader loader, ref bool compilingCoreAssembly)
		{
			Tracer.Info(Tracer.Compiler, "JVM.Compile path: {0}, assembly: {1}", options.path, options.assembly);
			AssemblyName runtimeAssemblyName = StaticCompiler.runtimeAssembly.GetName();
			bool allReferencesAreStrongNamed = IsSigned(StaticCompiler.runtimeAssembly);
			List<Assembly> references = new List<Assembly>();
			foreach(Assembly reference in options.references ?? new Assembly[0])
			{
				references.Add(reference);
				allReferencesAreStrongNamed &= IsSigned(reference);
				Tracer.Info(Tracer.Compiler, "Loaded reference assembly: {0}", reference.FullName);
				// if it's an IKVM compiled assembly, make sure that it was compiled
				// against same version of the runtime
				foreach(AssemblyName asmref in reference.GetReferencedAssemblies())
				{
					if(asmref.Name == runtimeAssemblyName.Name)
					{
						if(IsSigned(StaticCompiler.runtimeAssembly))
						{
							// TODO we really should support binding redirects here to allow different revisions to be mixed
							if(asmref.FullName != runtimeAssemblyName.FullName)
							{
								throw new FatalCompilerErrorException(Message.RuntimeMismatch, reference.Location, runtimeAssemblyName.FullName, asmref.FullName);
							}
						}
						else
						{
							if(asmref.GetPublicKeyToken() != null && asmref.GetPublicKeyToken().Length != 0)
							{
								throw new FatalCompilerErrorException(Message.RuntimeMismatch, reference.Location, runtimeAssemblyName.FullName, asmref.FullName);
							}
						}
					}
				}
			}
			List<object> assemblyAnnotations = new List<object>();
			Dictionary<string, string> baseClasses = new Dictionary<string, string>();
			Tracer.Info(Tracer.Compiler, "Parsing class files");
			foreach(KeyValuePair<string, ClassItem> kv in options.classes)
			{
				ClassFile f;
				try
				{
					byte[] buf = kv.Value.data;
					f = new ClassFile(buf, 0, buf.Length, null, ClassFileParseOptions.None);
					if(!f.IsInterface && f.SuperClass != null)
					{
						baseClasses[f.SuperClass] = f.SuperClass;
					}
					// NOTE the "assembly" type in the unnamed package is a magic type
					// that acts as the placeholder for assembly attributes
					if(f.Name == "assembly" && f.Annotations != null)
					{
						assemblyAnnotations.AddRange(f.Annotations);
					}
				}
				catch(ClassFormatError)
				{
					continue;
				}
				if(options.mainClass == null && (options.guessFileKind || options.target != PEFileKinds.Dll))
				{
					foreach(ClassFile.Method m in f.Methods)
					{
						if(m.IsPublic && m.IsStatic && m.Name == "main" && m.Signature == "([Ljava.lang.String;)V")
						{
							StaticCompiler.IssueMessage(Message.MainMethodFound, f.Name);
							options.mainClass = f.Name;
							break;
						}
					}
				}
			}
			Dictionary<string, ClassItem> h = new Dictionary<string, ClassItem>();
			// HACK remove "assembly" type that exists only as a placeholder for assembly attributes
			options.classes.Remove("assembly");
			foreach(KeyValuePair<string, ClassItem> kv in options.classes)
			{
				string name = kv.Key;
				bool excluded = false;
				for(int j = 0; j < options.classesToExclude.Length; j++)
				{
					if(Regex.IsMatch(name, options.classesToExclude[j]))
					{
						excluded = true;
						break;
					}
				}
				if(h.ContainsKey(name))
				{
					StaticCompiler.IssueMessage(Message.DuplicateClassName, name);
					excluded = true;
				}
				if(!excluded)
				{
					h[name] = kv.Value;
				}
			}
			options.classes = null;

			if(options.guessFileKind && options.mainClass == null)
			{
				options.target = PEFileKinds.Dll;
			}

			if(options.target == PEFileKinds.Dll && options.mainClass != null)
			{
				throw new FatalCompilerErrorException(Message.MainClassRequiresExe);
			}

			if(options.target != PEFileKinds.Dll && options.mainClass == null)
			{
				throw new FatalCompilerErrorException(Message.ExeRequiresMainClass);
			}

			if(options.target == PEFileKinds.Dll && options.props.Count != 0)
			{
				throw new FatalCompilerErrorException(Message.PropertiesRequireExe);
			}

			if(options.path == null)
			{
				if(options.target == PEFileKinds.Dll)
				{
					if(options.targetIsModule)
					{
						options.path = options.assembly + ".netmodule";
					}
					else
					{
						options.path = options.assembly + ".dll";
					}
				}
				else
				{
					options.path = options.assembly + ".exe";
				}
				StaticCompiler.IssueMessage(Message.OutputFileIs, options.path);
			}

			if(options.targetIsModule)
			{
				if(options.classLoader != null)
				{
					throw new FatalCompilerErrorException(Message.ModuleCannotHaveClassLoader);
				}
				// TODO if we're overwriting a user specified assembly name, we need to emit a warning
				options.assembly = new FileInfo(options.path).Name;
			}

			Tracer.Info(Tracer.Compiler, "Constructing compiler");
			AssemblyClassLoader[] referencedAssemblies = new AssemblyClassLoader[references.Count];
			for(int i = 0; i < references.Count; i++)
			{
				AssemblyClassLoader acl = AssemblyClassLoader.FromAssembly(references[i]);
				if (acl.MainAssembly != references[i])
				{
					StaticCompiler.IssueMessage(options, Message.NonPrimaryAssemblyReference, references[i].GetName().Name, acl.MainAssembly.GetName().Name);
				}
				if (Array.IndexOf(referencedAssemblies, acl) != -1)
				{
					StaticCompiler.IssueMessage(options, Message.DuplicateAssemblyReference, acl.MainAssembly.FullName);
				}
				referencedAssemblies[i] = acl;
			}
			loader = new CompilerClassLoader(referencedAssemblies, options, options.path, options.targetIsModule, options.assembly, h);
			loader.baseClasses = baseClasses;
			loader.assemblyAnnotations = assemblyAnnotations;
			loader.classesToCompile = new List<string>(h.Keys);
			if(options.remapfile != null)
			{
				Tracer.Info(Tracer.Compiler, "Loading remapped types (1) from {0}", options.remapfile);
				System.Xml.Serialization.XmlSerializer ser = new System.Xml.Serialization.XmlSerializer(typeof(IKVM.Internal.MapXml.Root));
				ser.UnknownElement += new System.Xml.Serialization.XmlElementEventHandler(ser_UnknownElement);
				ser.UnknownAttribute += new System.Xml.Serialization.XmlAttributeEventHandler(ser_UnknownAttribute);
				FileStream fs;
				try
				{
					fs = File.OpenRead(options.remapfile);
				}
				catch(Exception x)
				{
					throw new FatalCompilerErrorException(Message.ErrorReadingFile, options.remapfile, x.Message);
				}
				try
				{
					XmlTextReader rdr = new XmlTextReader(fs);
					IKVM.Internal.MapXml.Root.xmlReader = rdr;
					IKVM.Internal.MapXml.Root map;
					try
					{
						map = (IKVM.Internal.MapXml.Root)ser.Deserialize(rdr);
					}
					catch(InvalidOperationException x)
					{
						throw new FatalCompilerErrorException(Message.ErrorParsingMapFile, options.remapfile, x.Message);
					}
					if(!loader.ValidateAndSetMap(map))
					{
						return 1;
					}
				}
				finally
				{
					fs.Close();
				}
				if(loader.CheckCompilingCoreAssembly())
				{
					compilingCoreAssembly = true;
					ClassLoaderWrapper.SetBootstrapClassLoader(loader);
				}
			}
			// If we do not yet have a reference to the core assembly and we are not compiling the core assembly,
			// try to find the core assembly by looking at the assemblies that the runtime references
			if(JVM.CoreAssembly == null && !compilingCoreAssembly)
			{
				foreach(AssemblyName name in StaticCompiler.runtimeAssembly.GetReferencedAssemblies())
				{
					Assembly asm = null;
					try
					{
						asm = LoadReferencedAssembly(StaticCompiler.runtimeAssembly.Location + "/../" + name.Name + ".dll");
					}
					catch(FileNotFoundException)
					{
					}
					if(asm != null && IsCoreAssembly(asm))
					{
						JVM.CoreAssembly = asm;
						break;
					}
				}
				if(JVM.CoreAssembly == null)
				{
					throw new FatalCompilerErrorException(Message.BootstrapClassesMissing);
				}
				// we need to scan again for remapped types, now that we've loaded the core library
				ClassLoaderWrapper.LoadRemappedTypes();
			}

			if(!compilingCoreAssembly)
			{
				allReferencesAreStrongNamed &= IsSigned(JVM.CoreAssembly);
				loader.AddReference(AssemblyClassLoader.FromAssembly(JVM.CoreAssembly));
			}

			if((options.keyPair != null || options.publicKey != null) && !allReferencesAreStrongNamed)
			{
				throw new FatalCompilerErrorException(Message.StrongNameRequiresStrongNamedRefs);
			}

			if(loader.map != null)
			{
				loader.LoadMapXml();
			}

			if(!compilingCoreAssembly)
			{
				FakeTypes.Load(JVM.CoreAssembly);
			}
			return 0;
		}
		internal static void SuppressWarning(CompilerOptions options, Message message, string name)
		{
			options.suppressWarnings[(int)message + ":" + name] = null;
		}
		private static int CreateCompiler(CompilerOptions options, ref CompilerClassLoader loader, ref bool compilingCoreAssembly)
		{
			Tracer.Info(Tracer.Compiler, "JVM.Compile path: {0}, assembly: {1}", options.path, options.assembly);
			AssemblyName runtimeAssemblyName = StaticCompiler.runtimeAssembly.GetName();
			bool allReferencesAreStrongNamed = IsSigned(StaticCompiler.runtimeAssembly);
			List<Assembly> references = new List<Assembly>();
			foreach(Assembly reference in options.references ?? new Assembly[0])
			{
				references.Add(reference);
				allReferencesAreStrongNamed &= IsSigned(reference);
				Tracer.Info(Tracer.Compiler, "Loaded reference assembly: {0}", reference.FullName);
				// if it's an IKVM compiled assembly, make sure that it was compiled
				// against same version of the runtime
				foreach(AssemblyName asmref in reference.GetReferencedAssemblies())
				{
					if(asmref.Name == runtimeAssemblyName.Name)
					{
						if(IsSigned(StaticCompiler.runtimeAssembly))
						{
							// TODO we really should support binding redirects here to allow different revisions to be mixed
							if(asmref.FullName != runtimeAssemblyName.FullName)
							{
								throw new FatalCompilerErrorException(Message.RuntimeMismatch, reference.Location, runtimeAssemblyName.FullName, asmref.FullName);
							}
						}
						else
						{
							if(asmref.GetPublicKeyToken() != null && asmref.GetPublicKeyToken().Length != 0)
							{
								throw new FatalCompilerErrorException(Message.RuntimeMismatch, reference.Location, runtimeAssemblyName.FullName, asmref.FullName);
							}
						}
					}
				}
			}
			Tracer.Info(Tracer.Compiler, "Parsing class files");
			// map the class names to jar entries
			Dictionary<string, Jar.Item> h = new Dictionary<string, Jar.Item>();
			List<string> classNames = new List<string>();
			foreach (Jar jar in options.jars)
			{
				if (options.IsResourcesJar(jar))
				{
					continue;
				}
				foreach (Jar.Item item in jar)
				{
					string name = item.Name;
					if (name.EndsWith(".class", StringComparison.Ordinal)
						&& name.Length > 6
						&& name.IndexOf('.') == name.Length - 6)
					{
						string className = name.Substring(0, name.Length - 6).Replace('/', '.');
						if (h.ContainsKey(className))
						{
							StaticCompiler.IssueMessage(Message.DuplicateClassName, className);
							Jar.Item itemRef = h[className];
							if ((options.classesJar != -1 && itemRef.Jar == options.jars[options.classesJar]) || jar != itemRef.Jar)
							{
								// the previous class stays, because it was either in an earlier jar or we're processing the classes.jar
								// which contains the classes loaded from the file system (where the first encountered class wins)
								continue;
							}
							else
							{
								// we have a jar that contains multiple entries with the same name, the last one wins
								h.Remove(className);
								classNames.Remove(className);
							}
						}
						h.Add(className, item);
						classNames.Add(className);
					}
				}
			}

			if (options.assemblyAttributeAnnotations == null)
			{
				// look for "assembly" type that acts as a placeholder for assembly attributes
				Jar.Item assemblyType;
				if (h.TryGetValue("assembly", out assemblyType))
				{
					try
					{
						byte[] buf = assemblyType.GetData();
						ClassFile f = new ClassFile(buf, 0, buf.Length, null, ClassFileParseOptions.None, null);
						// NOTE the "assembly" type in the unnamed package is a magic type
						// that acts as the placeholder for assembly attributes
						if (f.Name == "assembly" && f.Annotations != null)
						{
							options.assemblyAttributeAnnotations = f.Annotations;
							// HACK remove "assembly" type that exists only as a placeholder for assembly attributes
							h.Remove(f.Name);
							assemblyType.Remove();
							StaticCompiler.IssueMessage(Message.LegacyAssemblyAttributesFound);
						}
					}
					catch (ClassFormatError) { }
				}
			}

			// now look for a main method
			if (options.mainClass == null && (options.guessFileKind || options.target != PEFileKinds.Dll))
			{
				foreach (string className in classNames)
				{
					try
					{
						byte[] buf = h[className].GetData();
						ClassFile f = new ClassFile(buf, 0, buf.Length, null, ClassFileParseOptions.None, null);
						if (f.Name == className)
						{
							foreach (ClassFile.Method m in f.Methods)
							{
								if (m.IsPublic && m.IsStatic && m.Name == "main" && m.Signature == "([Ljava.lang.String;)V")
								{
									StaticCompiler.IssueMessage(Message.MainMethodFound, f.Name);
									options.mainClass = f.Name;
									goto break_outer;
								}
							}
						}
					}
					catch (ClassFormatError) { }
				}
			break_outer: ;
			}

			if(options.guessFileKind && options.mainClass == null)
			{
				options.target = PEFileKinds.Dll;
			}

			if(options.target == PEFileKinds.Dll && options.mainClass != null)
			{
				throw new FatalCompilerErrorException(Message.MainClassRequiresExe);
			}

			if(options.target != PEFileKinds.Dll && options.mainClass == null)
			{
				throw new FatalCompilerErrorException(Message.ExeRequiresMainClass);
			}

			if(options.target == PEFileKinds.Dll && options.props.Count != 0)
			{
				throw new FatalCompilerErrorException(Message.PropertiesRequireExe);
			}

			if(options.path == null)
			{
				if(options.target == PEFileKinds.Dll)
				{
					if(options.targetIsModule)
					{
						options.path = IkvmcCompiler.GetFileInfo(options.assembly + ".netmodule");
					}
					else
					{
						options.path = IkvmcCompiler.GetFileInfo(options.assembly + ".dll");
					}
				}
				else
				{
					options.path = IkvmcCompiler.GetFileInfo(options.assembly + ".exe");
				}
				StaticCompiler.IssueMessage(Message.OutputFileIs, options.path.ToString());
			}

			if(options.targetIsModule)
			{
				if(options.classLoader != null)
				{
					throw new FatalCompilerErrorException(Message.ModuleCannotHaveClassLoader);
				}
				// TODO if we're overwriting a user specified assembly name, we need to emit a warning
				options.assembly = options.path.Name;
			}

			Tracer.Info(Tracer.Compiler, "Constructing compiler");
			AssemblyClassLoader[] referencedAssemblies = new AssemblyClassLoader[references.Count];
			for(int i = 0; i < references.Count; i++)
			{
				AssemblyClassLoader acl = AssemblyClassLoader.FromAssembly(references[i]);
				if (Array.IndexOf(referencedAssemblies, acl) != -1)
				{
					StaticCompiler.IssueMessage(options, Message.DuplicateAssemblyReference, acl.MainAssembly.FullName);
				}
				referencedAssemblies[i] = acl;
			}
			loader = new CompilerClassLoader(referencedAssemblies, options, options.path, options.targetIsModule, options.assembly, h, compilingCoreAssembly);
			loader.classesToCompile = new List<string>(h.Keys);
			if(options.remapfile != null)
			{
				Tracer.Info(Tracer.Compiler, "Loading remapped types (1) from {0}", options.remapfile);
				System.Xml.Serialization.XmlSerializer ser = new System.Xml.Serialization.XmlSerializer(typeof(IKVM.Internal.MapXml.Root));
				ser.UnknownElement += new System.Xml.Serialization.XmlElementEventHandler(ser_UnknownElement);
				ser.UnknownAttribute += new System.Xml.Serialization.XmlAttributeEventHandler(ser_UnknownAttribute);
				FileStream fs;
				try
				{
					fs = options.remapfile.OpenRead();
				}
				catch(Exception x)
				{
					throw new FatalCompilerErrorException(Message.ErrorReadingFile, options.remapfile, x.Message);
				}
				try
				{
					XmlTextReader rdr = new XmlTextReader(fs);
					IKVM.Internal.MapXml.Root.xmlReader = rdr;
					IKVM.Internal.MapXml.Root map;
					try
					{
						map = (IKVM.Internal.MapXml.Root)ser.Deserialize(rdr);
					}
					catch(InvalidOperationException x)
					{
						throw new FatalCompilerErrorException(Message.ErrorParsingMapFile, options.remapfile, x.Message);
					}
					if(!loader.ValidateAndSetMap(map))
					{
						return 1;
					}
				}
				finally
				{
					fs.Close();
				}
				if(loader.CheckCompilingCoreAssembly())
				{
					compilingCoreAssembly = true;
					ClassLoaderWrapper.SetBootstrapClassLoader(loader);
				}
			}
			// If we do not yet have a reference to the core assembly and we are not compiling the core assembly,
			// try to find the core assembly by looking at the assemblies that the runtime references
			if(JVM.CoreAssembly == null && !compilingCoreAssembly)
			{
				foreach(AssemblyName name in StaticCompiler.runtimeAssembly.GetReferencedAssemblies())
				{
					Assembly asm = null;
					try
					{
						asm = LoadReferencedAssembly(StaticCompiler.runtimeAssembly.Location + "/../" + name.Name + ".dll");
					}
					catch(FileNotFoundException)
					{
					}
					if(asm != null && IsCoreAssembly(asm))
					{
						AssemblyClassLoader.PreloadExportedAssemblies(asm);
						JVM.CoreAssembly = asm;
						break;
					}
				}
				if(JVM.CoreAssembly == null)
				{
					throw new FatalCompilerErrorException(Message.BootstrapClassesMissing);
				}
				// we need to scan again for remapped types, now that we've loaded the core library
				ClassLoaderWrapper.LoadRemappedTypes();
			}

			if(!compilingCoreAssembly)
			{
				allReferencesAreStrongNamed &= IsSigned(JVM.CoreAssembly);
				loader.AddReference(AssemblyClassLoader.FromAssembly(JVM.CoreAssembly));
			}

			if((options.keyPair != null || options.publicKey != null) && !allReferencesAreStrongNamed)
			{
				throw new FatalCompilerErrorException(Message.StrongNameRequiresStrongNamedRefs);
			}

			if(loader.map != null)
			{
				loader.LoadMapXml();
			}

			if(!compilingCoreAssembly)
			{
				FakeTypes.Load(JVM.CoreAssembly);
			}
			return 0;
		}
 internal static void IssueMessage(CompilerOptions options, Message msgId, params string[] values)
 {
     StringBuilder sb = new StringBuilder();
     sb.Append((int)msgId);
     if(values.Length > 0)
     {
         sb.Append(':').Append(values[0]);
     }
     string key = sb.ToString();
     if(options.suppressWarnings.ContainsKey(key)
         || options.suppressWarnings.ContainsKey(((int)msgId).ToString()))
     {
         return;
     }
     options.suppressWarnings.Add(key, key);
     if(options.writeSuppressWarningsFile != null)
     {
         File.AppendAllText(options.writeSuppressWarningsFile, "-nowarn:" + key + Environment.NewLine);
     }
     string msg;
     switch(msgId)
     {
         case Message.MainMethodFound:
             msg = "found main method in class \"{0}\"";
             break;
         case Message.OutputFileIs:
             msg = "output file is \"{0}\"";
             break;
         case Message.AutoAddRef:
             msg = "automatically adding reference to \"{0}\"";
             break;
         case Message.MainMethodFromManifest:
             msg = "using main class \"{0}\" based on jar manifest";
             break;
         case Message.ClassNotFound:
             msg = "class \"{0}\" not found";
             break;
         case Message.ClassFormatError:
             msg = "unable to compile class \"{0}\"" + Environment.NewLine +
                 "    (class format error \"{1}\")";
             break;
         case Message.DuplicateClassName:
             msg = "duplicate class name: \"{0}\"";
             break;
         case Message.IllegalAccessError:
             msg = "unable to compile class \"{0}\"" + Environment.NewLine +
                 "    (illegal access error \"{1}\")";
             break;
         case Message.VerificationError:
             msg = "unable to compile class \"{0}\"" + Environment.NewLine +
                 "    (verification error \"{1}\")";
             break;
         case Message.NoClassDefFoundError:
             msg = "unable to compile class \"{0}\"" + Environment.NewLine +
                 "    (missing class \"{1}\")";
             break;
         case Message.GenericUnableToCompileError:
             msg = "unable to compile class \"{0}\"" + Environment.NewLine +
                 "    (\"{1}\": \"{2}\")";
             break;
         case Message.DuplicateResourceName:
             msg = "skipping resource (name clash): \"{0}\"";
             break;
         case Message.NotAClassFile:
             msg = "not a class file \"{0}\", including it as resource" + Environment.NewLine +
                 "    (class format error \"{1}\")";
             break;
         case Message.SkippingReferencedClass:
             msg = "skipping class: \"{0}\"" + Environment.NewLine +
                 "    (class is already available in referenced assembly \"{1}\")";
             break;
         case Message.NoJniRuntime:
             msg = "unable to load runtime JNI assembly";
             break;
         case Message.EmittedNoClassDefFoundError:
             msg = "emitted java.lang.NoClassDefFoundError in \"{0}\"" + Environment.NewLine +
                 "    (\"{1}\")";
             break;
         case Message.EmittedIllegalAccessError:
             msg = "emitted java.lang.IllegalAccessError in \"{0}\"" + Environment.NewLine +
                 "    (\"{1}\")";
             break;
         case Message.EmittedInstantiationError:
             msg = "emitted java.lang.InstantiationError in \"{0}\"" + Environment.NewLine +
                 "    (\"{1}\")";
             break;
         case Message.EmittedIncompatibleClassChangeError:
             msg = "emitted java.lang.IncompatibleClassChangeError in \"{0}\"" + Environment.NewLine +
                 "    (\"{1}\")";
             break;
         case Message.EmittedNoSuchFieldError:
             msg = "emitted java.lang.NoSuchFieldError in \"{0}\"" + Environment.NewLine +
                 "    (\"{1}\")";
             break;
         case Message.EmittedAbstractMethodError:
             msg = "emitted java.lang.AbstractMethodError in \"{0}\"" + Environment.NewLine +
                 "    (\"{1}\")";
             break;
         case Message.EmittedNoSuchMethodError:
             msg = "emitted java.lang.NoSuchMethodError in \"{0}\"" + Environment.NewLine +
                 "    (\"{1}\")";
             break;
         case Message.EmittedLinkageError:
             msg = "emitted java.lang.LinkageError in \"{0}\"" + Environment.NewLine +
                 "    (\"{1}\")";
             break;
         case Message.EmittedVerificationError:
             msg = "emitted java.lang.VerificationError in \"{0}\"" + Environment.NewLine +
                 "    (\"{1}\")";
             break;
         case Message.EmittedClassFormatError:
             msg = "emitted java.lang.ClassFormatError in \"{0}\"" + Environment.NewLine +
                 "    (\"{1}\")";
             break;
         case Message.InvalidCustomAttribute:
             msg = "error emitting \"{0}\" custom attribute" + Environment.NewLine +
                 "    (\"{1}\")";
             break;
         case Message.IgnoredCustomAttribute:
             msg = "custom attribute \"{0}\" was ignored" + Environment.NewLine +
                 "    (\"{1}\")";
             break;
         case Message.AssumeAssemblyVersionMatch:
             msg = "assuming assembly reference \"{0}\" matches \"{1}\", you may need to supply runtime policy";
             break;
         case Message.InvalidDirectoryInLibOptionPath:
             msg = "directory \"{0}\" specified in -lib option is not valid";
             break;
         case Message.InvalidDirectoryInLibEnvironmentPath:
             msg = "directory \"{0}\" specified in LIB environment is not valid";
             break;
         case Message.LegacySearchRule:
             msg = "found assembly \"{0}\" using legacy search rule, please append '.dll' to the reference";
             break;
         case Message.AssemblyLocationIgnored:
             msg = "assembly \"{0}\" is ignored as previously loaded assembly \"{1}\" has the same identity \"{2}\"";
             break;
         case Message.InterfaceMethodCantBeInternal:
             msg = "ignoring @ikvm.lang.Internal annotation on interface method" + Environment.NewLine +
                 "    (\"{0}.{1}{2}\")";
             break;
         case Message.UnknownWarning:
             msg = "{0}";
             break;
         default:
             throw new InvalidProgramException();
     }
     if(options.errorWarnings.ContainsKey(key)
         || options.errorWarnings.ContainsKey(((int)msgId).ToString()))
     {
         Console.Error.Write("{0} IKVMC{1:D4}: ", "Error", (int)msgId);
         Console.Error.WriteLine(msg, values);
         Environment.Exit(1);
     }
     Console.Error.Write("{0} IKVMC{1:D4}: ", msgId < Message.StartWarnings ? "Note" : "Warning", (int)msgId);
     Console.Error.WriteLine(msg, values);
     if(options != toplevel && options.path != null)
     {
         Console.Error.WriteLine("    (in {0})", options.path);
     }
 }
		private static int CreateCompiler(CompilerOptions options, ref CompilerClassLoader loader, ref bool compilingCoreAssembly)
		{
			Tracer.Info(Tracer.Compiler, "JVM.Compile path: {0}, assembly: {1}", options.path, options.assembly);
			AssemblyName runtimeAssemblyName = StaticCompiler.runtimeAssembly.GetName();
			bool allReferencesAreStrongNamed = IsSigned(StaticCompiler.runtimeAssembly);
			List<Assembly> references = new List<Assembly>();
			foreach(Assembly reference in options.references ?? new Assembly[0])
			{
				try
				{
					if(IsCoreAssembly(reference))
					{
						JVM.CoreAssembly = reference;
					}
					references.Add(reference);
					allReferencesAreStrongNamed &= IsSigned(reference);
					Tracer.Info(Tracer.Compiler, "Loaded reference assembly: {0}", reference.FullName);
					// if it's an IKVM compiled assembly, make sure that it was compiled
					// against same version of the runtime
					foreach(AssemblyName asmref in reference.GetReferencedAssemblies())
					{
						if(asmref.Name == runtimeAssemblyName.Name)
						{
							if(IsSigned(StaticCompiler.runtimeAssembly))
							{
								if(asmref.FullName != runtimeAssemblyName.FullName)
								{
									Console.Error.WriteLine("Error: referenced assembly {0} was compiled with an incompatible IKVM.Runtime version ({1})", reference.Location, asmref.Version);
									Console.Error.WriteLine("   Current runtime: {0}", runtimeAssemblyName.FullName);
									Console.Error.WriteLine("   Referenced assembly runtime: {0}", asmref.FullName);
									return 1;
								}
							}
							else
							{
								if(asmref.GetPublicKeyToken() != null && asmref.GetPublicKeyToken().Length != 0)
								{
									Console.Error.WriteLine("Error: referenced assembly {0} was compiled with an incompatible (signed) IKVM.Runtime version", reference.Location);
									Console.Error.WriteLine("   Current runtime: {0}", runtimeAssemblyName.FullName);
									Console.Error.WriteLine("   Referenced assembly runtime: {0}", asmref.FullName);
									return 1;
								}
							}
						}
					}
				}
				catch(Exception x)
				{
					Console.Error.WriteLine("Error: invalid reference: {0} ({1})", reference.Location, x.Message);
					return 1;
				}
			}
			List<object> assemblyAnnotations = new List<object>();
			Dictionary<string, string> baseClasses = new Dictionary<string, string>();
			Tracer.Info(Tracer.Compiler, "Parsing class files");
			foreach(KeyValuePair<string, byte[]> kv in options.classes)
			{
				ClassFile f;
				try
				{
					byte[] buf = kv.Value;
					f = new ClassFile(buf, 0, buf.Length, null, ClassFileParseOptions.None);
					if(!f.IsInterface && f.SuperClass != null)
					{
						baseClasses[f.SuperClass] = f.SuperClass;
					}
					// NOTE the "assembly" type in the unnamed package is a magic type
					// that acts as the placeholder for assembly attributes
					if(f.Name == "assembly" && f.Annotations != null)
					{
						assemblyAnnotations.AddRange(f.Annotations);
					}
				}
				catch(ClassFormatError)
				{
					continue;
				}
				if(options.mainClass == null && (options.guessFileKind || options.target != PEFileKinds.Dll))
				{
					foreach(ClassFile.Method m in f.Methods)
					{
						if(m.IsPublic && m.IsStatic && m.Name == "main" && m.Signature == "([Ljava.lang.String;)V")
						{
							StaticCompiler.IssueMessage(Message.MainMethodFound, f.Name);
							options.mainClass = f.Name;
							break;
						}
					}
				}
			}
			Dictionary<string, byte[]> h = new Dictionary<string, byte[]>();
			// HACK remove "assembly" type that exists only as a placeholder for assembly attributes
			options.classes.Remove("assembly");
			foreach(KeyValuePair<string, byte[]> kv in options.classes)
			{
				string name = kv.Key;
				bool excluded = false;
				for(int j = 0; j < options.classesToExclude.Length; j++)
				{
					if(Regex.IsMatch(name, options.classesToExclude[j]))
					{
						excluded = true;
						break;
					}
				}
				if(h.ContainsKey(name))
				{
					StaticCompiler.IssueMessage(Message.DuplicateClassName, name);
					excluded = true;
				}
				if(!excluded)
				{
					h[name] = kv.Value;
				}
			}
			options.classes = null;

			if(options.guessFileKind && options.mainClass == null)
			{
				options.target = PEFileKinds.Dll;
			}

			if(options.target == PEFileKinds.Dll && options.mainClass != null)
			{
				Console.Error.WriteLine("Error: main class cannot be specified for library or module");
				return 1;
			}

			if(options.target != PEFileKinds.Dll && options.mainClass == null)
			{
				Console.Error.WriteLine("Error: no main method found");
				return 1;
			}

			if(options.target == PEFileKinds.Dll && options.props.Count != 0)
			{
				Console.Error.WriteLine("Error: properties cannot be specified for library or module");
				return 1;
			}

			if(options.path == null)
			{
				if(options.target == PEFileKinds.Dll)
				{
					if(options.targetIsModule)
					{
						options.path = options.assembly + ".netmodule";
					}
					else
					{
						options.path = options.assembly + ".dll";
					}
				}
				else
				{
					options.path = options.assembly + ".exe";
				}
				StaticCompiler.IssueMessage(Message.OutputFileIs, options.path);
			}

			if(options.targetIsModule)
			{
				if(options.classLoader != null)
				{
					Console.Error.WriteLine("Error: cannot specify assembly class loader for modules");
					return 1;
				}
				// TODO if we're overwriting a user specified assembly name, we need to emit a warning
				options.assembly = new FileInfo(options.path).Name;
			}

			if(options.target == PEFileKinds.Dll && !options.path.ToLower().EndsWith(".dll") && !options.targetIsModule)
			{
				Console.Error.WriteLine("Error: library output file must end with .dll");
				return 1;
			}

			if(options.target != PEFileKinds.Dll && !options.path.ToLower().EndsWith(".exe"))
			{
				Console.Error.WriteLine("Error: executable output file must end with .exe");
				return 1;
			}

			Tracer.Info(Tracer.Compiler, "Constructing compiler");
			AssemblyClassLoader[] referencedAssemblies = new AssemblyClassLoader[references.Count];
			for(int i = 0; i < references.Count; i++)
			{
				referencedAssemblies[i] = AssemblyClassLoader.FromAssembly(references[i]);
			}
			loader = new CompilerClassLoader(referencedAssemblies, options, options.path, options.targetIsModule, options.assembly, h);
			loader.baseClasses = baseClasses;
			loader.assemblyAnnotations = assemblyAnnotations;
			loader.classesToCompile = new List<string>(h.Keys);
			if(options.remapfile != null)
			{
				Tracer.Info(Tracer.Compiler, "Loading remapped types (1) from {0}", options.remapfile);
				System.Xml.Serialization.XmlSerializer ser = new System.Xml.Serialization.XmlSerializer(typeof(IKVM.Internal.MapXml.Root));
				ser.UnknownElement += new System.Xml.Serialization.XmlElementEventHandler(ser_UnknownElement);
				ser.UnknownAttribute += new System.Xml.Serialization.XmlAttributeEventHandler(ser_UnknownAttribute);
				using(FileStream fs = File.Open(options.remapfile, FileMode.Open))
				{
					XmlTextReader rdr = new XmlTextReader(fs);
					IKVM.Internal.MapXml.Root.xmlReader = rdr;
					IKVM.Internal.MapXml.Root.filename = new FileInfo(fs.Name).Name;
					if (!loader.ValidateAndSetMap((IKVM.Internal.MapXml.Root)ser.Deserialize(rdr)))
					{
						return 1;
					}
				}
				if(loader.CheckCompilingCoreAssembly())
				{
					compilingCoreAssembly = true;
					ClassLoaderWrapper.SetBootstrapClassLoader(loader);
					loader.EmitRemappedTypes();
				}
			}
			// If we do not yet have a reference to the core assembly and we are not compiling the core assembly,
			// try to find the core assembly by looking at the assemblies that the runtime references
			if(JVM.CoreAssembly == null && !compilingCoreAssembly)
			{
				foreach(AssemblyName name in StaticCompiler.runtimeAssembly.GetReferencedAssemblies())
				{
					Assembly asm = null;
					try
					{
						asm = LoadReferencedAssembly(StaticCompiler.runtimeAssembly.Location + "/../" + name.Name + ".dll");
					}
					catch(FileNotFoundException)
					{
					}
					if(asm != null && IsCoreAssembly(asm))
					{
						JVM.CoreAssembly = asm;
						break;
					}
				}
				if(JVM.CoreAssembly == null)
				{
					Console.Error.WriteLine("Error: bootstrap classes missing and core assembly not found");
					return 1;
				}
				// we need to scan again for remapped types, now that we've loaded the core library
				ClassLoaderWrapper.LoadRemappedTypes();
			}

			if(!compilingCoreAssembly)
			{
				allReferencesAreStrongNamed &= IsSigned(JVM.CoreAssembly);
				loader.AddReference(AssemblyClassLoader.FromAssembly(JVM.CoreAssembly));
			}

			if((options.keyPair != null || options.publicKey != null) && !allReferencesAreStrongNamed)
			{
				Console.Error.WriteLine("Error: all referenced assemblies must be strong named, to be able to sign the output assembly");
				return 1;
			}

			if(loader.map != null)
			{
				loader.LoadMapXml();
			}

			if(!compilingCoreAssembly)
			{
				FakeTypes.Load(JVM.CoreAssembly);
			}
			return 0;
		}