ikvm-fork/ikvmc/CompilerClassLoader.cs

4183 строки
132 KiB
C#

/*
Copyright (C) 2002-2014 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.Resources;
using System.IO;
using System.Collections.Generic;
using System.Xml;
using System.Diagnostics;
using System.Text.RegularExpressions;
using System.Text;
using System.Threading;
using System.Security.Permissions;
using System.Security;
using System.Runtime.CompilerServices;
using ICSharpCode.SharpZipLib.Zip;
using IKVM.Attributes;
using IKVM.Reflection;
using IKVM.Reflection.Emit;
using Type = IKVM.Reflection.Type;
namespace IKVM.Internal
{
sealed class CompilerClassLoader : ClassLoaderWrapper
{
private Dictionary<string, Jar.Item> classes;
private Dictionary<string, RemapperTypeWrapper> remapped = new Dictionary<string, RemapperTypeWrapper>();
private string assemblyName;
private string assemblyFile;
private string assemblyDir;
private bool targetIsModule;
private AssemblyBuilder assemblyBuilder;
private IKVM.Internal.MapXml.Attribute[] assemblyAttributes;
private CompilerOptions options;
private AssemblyClassLoader[] referencedAssemblies;
private Dictionary<string, string> nameMappings = new Dictionary<string, string>();
private Packages packages;
private Dictionary<string, List<TypeWrapper>> ghosts;
private TypeWrapper[] mappedExceptions;
private bool[] mappedExceptionsAllSubClasses;
private Dictionary<string, IKVM.Internal.MapXml.Class> mapxml_Classes;
private Dictionary<MethodKey, IKVM.Internal.MapXml.InstructionList> mapxml_MethodBodies;
private Dictionary<MethodKey, IKVM.Internal.MapXml.ReplaceMethodCall[]> mapxml_ReplacedMethods;
private Dictionary<MethodKey, IKVM.Internal.MapXml.InstructionList> mapxml_MethodPrologues;
private IKVM.Internal.MapXml.Root map;
private List<string> classesToCompile;
private List<CompilerClassLoader> peerReferences = new List<CompilerClassLoader>();
private Dictionary<string, string> peerLoading = new Dictionary<string, string>();
private List<ClassLoaderWrapper> internalsVisibleTo = new List<ClassLoaderWrapper>();
private List<TypeWrapper> dynamicallyImportedTypes = new List<TypeWrapper>();
private List<string> jarList = new List<string>();
private List<TypeWrapper> allwrappers;
internal CompilerClassLoader(AssemblyClassLoader[] referencedAssemblies, CompilerOptions options, FileInfo assemblyPath, bool targetIsModule, string assemblyName, Dictionary<string, Jar.Item> classes)
: base(options.codegenoptions, null)
{
this.referencedAssemblies = referencedAssemblies;
this.options = options;
this.classes = classes;
this.assemblyName = assemblyName;
this.assemblyFile = assemblyPath.Name;
this.assemblyDir = assemblyPath.DirectoryName;
this.targetIsModule = targetIsModule;
Tracer.Info(Tracer.Compiler, "Instantiate CompilerClassLoader for {0}", assemblyName);
}
internal bool ReserveName(string javaName)
{
return !classes.ContainsKey(javaName) && GetTypeWrapperFactory().ReserveName(javaName);
}
internal void AddNameMapping(string javaName, string typeName)
{
nameMappings.Add(javaName, typeName);
}
internal void AddReference(AssemblyClassLoader acl)
{
referencedAssemblies = ArrayUtil.Concat(referencedAssemblies, acl);
}
internal void AddReference(CompilerClassLoader ccl)
{
peerReferences.Add(ccl);
}
internal AssemblyName GetAssemblyName()
{
return assemblyBuilder.GetName();
}
private static PermissionSet Combine(PermissionSet p1, PermissionSet p2)
{
if (p1 == null)
{
return p2;
}
if (p2 == null)
{
return p1;
}
return p1.Union(p2);
}
internal ModuleBuilder CreateModuleBuilder()
{
AssemblyName name = new AssemblyName();
name.Name = assemblyName;
if (options.keyPair != null)
{
name.KeyPair = options.keyPair;
}
else if (options.publicKey != null)
{
name.SetPublicKey(options.publicKey);
}
name.Version = options.version;
assemblyBuilder =
StaticCompiler.Universe
.DefineDynamicAssembly(name, AssemblyBuilderAccess.ReflectionOnly, assemblyDir);
ModuleBuilder moduleBuilder;
moduleBuilder = assemblyBuilder.DefineDynamicModule(assemblyName, assemblyFile, this.EmitDebugInfo);
if(this.EmitStackTraceInfo)
{
AttributeHelper.SetSourceFile(moduleBuilder, null);
}
if(this.EmitDebugInfo || this.EmitStackTraceInfo)
{
CustomAttributeBuilder debugAttr = new CustomAttributeBuilder(JVM.Import(typeof(DebuggableAttribute)).GetConstructor(new Type[] { Types.Boolean, Types.Boolean }), new object[] { true, this.EmitDebugInfo });
assemblyBuilder.SetCustomAttribute(debugAttr);
}
AttributeHelper.SetRuntimeCompatibilityAttribute(assemblyBuilder);
if(options.baseAddress != 0)
{
moduleBuilder.__ImageBase = options.baseAddress;
}
if(options.fileAlignment != 0)
{
moduleBuilder.__FileAlignment = options.fileAlignment;
}
if(options.highentropyva)
{
moduleBuilder.__DllCharacteristics |= DllCharacteristics.HighEntropyVA;
}
// allow the runtime to "inject" dynamic classes into the assembly
string mainAssemblyName = options.sharedclassloader != null && options.sharedclassloader[0] != this
? options.sharedclassloader[0].assemblyName
: assemblyName;
if(!DisableDynamicBinding)
{
AttributeHelper.SetInternalsVisibleToAttribute(assemblyBuilder, mainAssemblyName + DynamicClassLoader.DynamicAssemblySuffixAndPublicKey);
}
return moduleBuilder;
}
public override string ToString()
{
return "CompilerClassLoader:" + options.assembly;
}
protected override TypeWrapper LoadClassImpl(string name, bool throwClassNotFoundException)
{
foreach(AssemblyClassLoader acl in referencedAssemblies)
{
TypeWrapper tw = acl.DoLoad(name);
if(tw != null)
{
return tw;
}
}
if(!peerLoading.ContainsKey(name))
{
peerLoading.Add(name, null);
try
{
foreach(CompilerClassLoader ccl in peerReferences)
{
TypeWrapper tw = ccl.PeerLoad(name);
if(tw != null)
{
return tw;
}
}
if(options.sharedclassloader != null && options.sharedclassloader[0] != this)
{
TypeWrapper tw = options.sharedclassloader[0].PeerLoad(name);
if(tw != null)
{
return tw;
}
}
}
finally
{
peerLoading.Remove(name);
}
}
TypeWrapper tw1 = GetTypeWrapperCompilerHook(name);
if(tw1 != null)
{
return tw1;
}
// HACK the peer loading mess above may have indirectly loaded the classes without returning it,
// so we try once more here
tw1 = FindLoadedClass(name);
if(tw1 != null)
{
return tw1;
}
return FindOrLoadGenericClass(name, false);
}
private TypeWrapper PeerLoad(string name)
{
// To keep the performance acceptable in cases where we're compiling many targets, we first check if the load can
// possibly succeed on this class loader, otherwise we'll end up doing a lot of futile recursive loading attempts.
if(classes.ContainsKey(name) || remapped.ContainsKey(name) || FindLoadedClass(name) != null)
{
TypeWrapper tw = LoadClassByDottedNameFast(name);
// HACK we don't want to load classes referenced by peers, hence the "== this" check
if(tw != null && tw.GetClassLoader() == this)
{
return tw;
}
}
if(options.sharedclassloader != null && options.sharedclassloader[0] == this)
{
foreach(CompilerClassLoader ccl in options.sharedclassloader)
{
if(ccl != this)
{
TypeWrapper tw = ccl.PeerLoad(name);
if(tw != null)
{
return tw;
}
}
}
}
return null;
}
private TypeWrapper GetTypeWrapperCompilerHook(string name)
{
RemapperTypeWrapper rtw;
if(remapped.TryGetValue(name, out rtw))
{
return rtw;
}
else
{
Jar.Item itemRef;
if(classes.TryGetValue(name, out itemRef))
{
classes.Remove(name);
ClassFile f;
try
{
byte[] buf = itemRef.GetData();
f = new ClassFile(buf, 0, buf.Length, name, ClassFileParseOptions, null);
}
catch(ClassFormatError x)
{
StaticCompiler.SuppressWarning(options, Message.ClassNotFound, name);
StaticCompiler.IssueMessage(options, Message.ClassFormatError, name, x.Message);
return null;
}
if(f.Name != name)
{
StaticCompiler.SuppressWarning(options, Message.ClassNotFound, name);
StaticCompiler.IssueMessage(options, Message.WrongClassName, name, f.Name);
return null;
}
if(options.removeUnusedFields)
{
f.RemoveUnusedFields();
}
if(f.IsPublic && options.privatePackages != null)
{
foreach(string p in options.privatePackages)
{
if(f.Name.StartsWith(p))
{
f.SetInternal();
break;
}
}
}
if(f.IsPublic && options.publicPackages != null)
{
bool found = false;
foreach(string package in options.publicPackages)
{
if(f.Name.StartsWith(package))
{
found = true;
break;
}
}
if(!found)
{
f.SetInternal();
}
}
if(f.SourceFileAttribute != null)
{
FileInfo path = itemRef.Path;
if(path != null)
{
string sourceFile = Path.GetFullPath(Path.Combine(path.DirectoryName, f.SourceFileAttribute));
if(File.Exists(sourceFile))
{
f.SourcePath = sourceFile;
}
}
if(f.SourcePath == null)
{
if (options.sourcepath != null)
{
string package = f.Name;
int index = package.LastIndexOf('.');
package = index == -1 ? "" : package.Substring(0, index).Replace('.', '/');
f.SourcePath = Path.GetFullPath(Path.Combine(options.sourcepath + "/" + package, f.SourceFileAttribute));
}
else
{
f.SourcePath = f.SourceFileAttribute;
}
}
}
try
{
TypeWrapper tw = DefineClass(f, null);
// we successfully created the type, so we don't need to include the class as a resource
if (options.nojarstubs)
{
itemRef.Remove();
}
else
{
itemRef.MarkAsStub();
}
int pos = f.Name.LastIndexOf('.');
if (pos != -1)
{
string manifestJar = options.IsClassesJar(itemRef.Jar) ? null : itemRef.Jar.Name;
packages.DefinePackage(f.Name.Substring(0, pos), manifestJar);
}
return tw;
}
catch (ClassFormatError x)
{
StaticCompiler.IssueMessage(options, Message.ClassFormatError, name, x.Message);
}
catch (IllegalAccessError x)
{
StaticCompiler.IssueMessage(options, Message.IllegalAccessError, name, x.Message);
}
catch (VerifyError x)
{
StaticCompiler.IssueMessage(options, Message.VerificationError, name, x.Message);
}
catch (NoClassDefFoundError x)
{
if ((options.codegenoptions & CodeGenOptions.DisableDynamicBinding) != 0)
{
StaticCompiler.IssueMessage(options, Message.NoClassDefFoundError, name, x.Message);
}
StaticCompiler.IssueMessage(options, Message.ClassNotFound, x.Message);
}
catch (RetargetableJavaException x)
{
StaticCompiler.IssueMessage(options, Message.GenericUnableToCompileError, name, x.GetType().Name, x.Message);
}
StaticCompiler.SuppressWarning(options, Message.ClassNotFound, name);
return null;
}
else
{
return null;
}
}
}
// HACK when we're compiling multiple targets with -sharedclassloader, each target will have its own CompilerClassLoader,
// so we need to consider them equivalent (because they represent the same class loader).
internal bool IsEquivalentTo(ClassLoaderWrapper other)
{
if (this == other)
{
return true;
}
CompilerClassLoader ccl = other as CompilerClassLoader;
if (ccl != null && options.sharedclassloader != null && options.sharedclassloader.Contains(ccl))
{
if (!internalsVisibleTo.Contains(ccl))
{
AddInternalsVisibleToAttribute(ccl);
}
return true;
}
return false;
}
internal override bool InternalsVisibleToImpl(TypeWrapper wrapper, TypeWrapper friend)
{
Debug.Assert(wrapper.GetClassLoader() == this);
ClassLoaderWrapper other = friend.GetClassLoader();
// TODO ideally we should also respect InternalsVisibleToAttribute.Annotation here
if (this == other || internalsVisibleTo.Contains(other))
{
return true;
}
CompilerClassLoader ccl = other as CompilerClassLoader;
if (ccl != null)
{
AddInternalsVisibleToAttribute(ccl);
return true;
}
return false;
}
private void AddInternalsVisibleToAttribute(CompilerClassLoader ccl)
{
internalsVisibleTo.Add(ccl);
AssemblyBuilder asm = ccl.assemblyBuilder;
AssemblyName asmName = asm.GetName();
string name = asmName.Name;
byte[] pubkey = asmName.GetPublicKey();
if (pubkey == null && asmName.KeyPair != null)
{
pubkey = asmName.KeyPair.PublicKey;
}
if (pubkey != null && pubkey.Length != 0)
{
StringBuilder sb = new StringBuilder(name);
sb.Append(", PublicKey=");
foreach (byte b in pubkey)
{
sb.AppendFormat("{0:X2}", b);
}
name = sb.ToString();
}
AttributeHelper.SetInternalsVisibleToAttribute(this.assemblyBuilder, name);
}
internal void SetMain(MethodInfo m, PEFileKinds target, Dictionary<string, string> props, bool noglobbing, Type apartmentAttributeType)
{
MethodBuilder mainStub = this.GetTypeWrapperFactory().ModuleBuilder.DefineGlobalMethod("main", MethodAttributes.Public | MethodAttributes.Static, Types.Int32, new Type[] { Types.String.MakeArrayType() });
if(apartmentAttributeType != null)
{
mainStub.SetCustomAttribute(new CustomAttributeBuilder(apartmentAttributeType.GetConstructor(Type.EmptyTypes), new object[0]));
}
CodeEmitter ilgen = CodeEmitter.Create(mainStub);
CodeEmitterLocal rc = ilgen.DeclareLocal(Types.Int32);
TypeWrapper startupType = LoadClassByDottedName("ikvm.runtime.Startup");
if(props.Count > 0)
{
ilgen.Emit(OpCodes.Newobj, JVM.Import(typeof(System.Collections.Generic.Dictionary<string, string>)).GetConstructor(Type.EmptyTypes));
foreach(KeyValuePair<string, string> kv in props)
{
ilgen.Emit(OpCodes.Dup);
ilgen.Emit(OpCodes.Ldstr, kv.Key);
ilgen.Emit(OpCodes.Ldstr, kv.Value);
if(kv.Value.IndexOf('%') < kv.Value.LastIndexOf('%'))
{
ilgen.Emit(OpCodes.Call, JVM.Import(typeof(Environment)).GetMethod("ExpandEnvironmentVariables", new Type[] { Types.String }));
}
ilgen.Emit(OpCodes.Callvirt, JVM.Import(typeof(System.Collections.Generic.Dictionary<string, string>)).GetMethod("Add"));
}
startupType.GetMethodWrapper("setProperties", "(Lcli.System.Collections.IDictionary;)V", false).EmitCall(ilgen);
}
ilgen.BeginExceptionBlock();
startupType.GetMethodWrapper("enterMainThread", "()V", false).EmitCall(ilgen);
ilgen.Emit(OpCodes.Ldarg_0);
if (!noglobbing)
{
ilgen.Emit(OpCodes.Ldc_I4_0);
startupType.GetMethodWrapper("glob", "([Ljava.lang.String;I)[Ljava.lang.String;", false).EmitCall(ilgen);
}
ilgen.Emit(OpCodes.Call, m);
CodeEmitterLabel label = ilgen.DefineLabel();
ilgen.EmitLeave(label);
ilgen.BeginCatchBlock(Types.Exception);
LoadClassByDottedName("ikvm.runtime.Util").GetMethodWrapper("mapException", "(Ljava.lang.Throwable;)Ljava.lang.Throwable;", false).EmitCall(ilgen);
CodeEmitterLocal exceptionLocal = ilgen.DeclareLocal(Types.Exception);
ilgen.Emit(OpCodes.Stloc, exceptionLocal);
TypeWrapper threadTypeWrapper = ClassLoaderWrapper.LoadClassCritical("java.lang.Thread");
CodeEmitterLocal threadLocal = ilgen.DeclareLocal(threadTypeWrapper.TypeAsLocalOrStackType);
threadTypeWrapper.GetMethodWrapper("currentThread", "()Ljava.lang.Thread;", false).EmitCall(ilgen);
ilgen.Emit(OpCodes.Stloc, threadLocal);
ilgen.Emit(OpCodes.Ldloc, threadLocal);
threadTypeWrapper.GetMethodWrapper("getThreadGroup", "()Ljava.lang.ThreadGroup;", false).EmitCallvirt(ilgen);
ilgen.Emit(OpCodes.Ldloc, threadLocal);
ilgen.Emit(OpCodes.Ldloc, exceptionLocal);
ClassLoaderWrapper.LoadClassCritical("java.lang.ThreadGroup").GetMethodWrapper("uncaughtException", "(Ljava.lang.Thread;Ljava.lang.Throwable;)V", false).EmitCallvirt(ilgen);
ilgen.Emit(OpCodes.Ldc_I4_1);
ilgen.Emit(OpCodes.Stloc, rc);
ilgen.EmitLeave(label);
ilgen.BeginFinallyBlock();
startupType.GetMethodWrapper("exitMainThread", "()V", false).EmitCall(ilgen);
ilgen.Emit(OpCodes.Endfinally);
ilgen.EndExceptionBlock();
ilgen.MarkLabel(label);
ilgen.Emit(OpCodes.Ldloc, rc);
ilgen.Emit(OpCodes.Ret);
ilgen.DoEmit();
assemblyBuilder.SetEntryPoint(mainStub, target);
}
private void PrepareSave()
{
((DynamicClassLoader)this.GetTypeWrapperFactory()).FinishAll();
}
private void Save()
{
ModuleBuilder mb = GetTypeWrapperFactory().ModuleBuilder;
if(targetIsModule)
{
// 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++)
{
Type[] types = referencedAssemblies[i].MainAssembly.GetExportedTypes();
if(types.Length > 0)
{
mb.GetTypeToken(types[0]);
}
}
}
mb.CreateGlobalFunctions();
AddJavaModuleAttribute(mb);
// add a package list and export map
if(options.sharedclassloader == null || options.sharedclassloader[0] == this)
{
ConstructorInfo packageListAttributeCtor = JVM.LoadType(typeof(PackageListAttribute)).GetConstructor(new Type[] { Types.String, Types.String.MakeArrayType() });
foreach(object[] args in packages.ToArray())
{
mb.SetCustomAttribute(new CustomAttributeBuilder(packageListAttributeCtor, args));
}
// 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)
{
Tracer.Info(Tracer.Compiler, "CompilerClassLoader saving {0} in {1}", assemblyFile, assemblyDir);
try
{
GetTypeWrapperFactory().ModuleBuilder.__Save(options.pekind, options.imageFileMachine);
}
catch(IOException x)
{
throw new FatalCompilerErrorException(Message.ErrorWritingFile, GetTypeWrapperFactory().ModuleBuilder.FullyQualifiedName, x.Message);
}
catch(UnauthorizedAccessException x)
{
throw new FatalCompilerErrorException(Message.ErrorWritingFile, GetTypeWrapperFactory().ModuleBuilder.FullyQualifiedName, x.Message);
}
}
else
{
Tracer.Info(Tracer.Compiler, "CompilerClassLoader saving {0} in {1}", assemblyFile, assemblyDir);
try
{
assemblyBuilder.Save(assemblyFile, options.pekind, options.imageFileMachine);
}
catch(IOException x)
{
throw new FatalCompilerErrorException(Message.ErrorWritingFile, Path.Combine(assemblyDir, assemblyFile), x.Message);
}
catch(UnauthorizedAccessException x)
{
throw new FatalCompilerErrorException(Message.ErrorWritingFile, Path.Combine(assemblyDir, assemblyFile), x.Message);
}
}
}
private void AddJavaModuleAttribute(ModuleBuilder mb)
{
Type typeofJavaModuleAttribute = JVM.LoadType(typeof(JavaModuleAttribute));
PropertyInfo[] propInfos = new PropertyInfo[] {
typeofJavaModuleAttribute.GetProperty("Jars")
};
object[] propValues = new object[] {
jarList.ToArray()
};
if (nameMappings.Count > 0)
{
string[] list = new string[nameMappings.Count * 2];
int i = 0;
foreach (KeyValuePair<string, string> kv in nameMappings)
{
list[i++] = kv.Key;
list[i++] = kv.Value;
}
CustomAttributeBuilder cab = new CustomAttributeBuilder(typeofJavaModuleAttribute.GetConstructor(new Type[] { JVM.Import(typeof(string[])) }), new object[] { list }, propInfos, propValues);
mb.SetCustomAttribute(cab);
}
else
{
CustomAttributeBuilder cab = new CustomAttributeBuilder(typeofJavaModuleAttribute.GetConstructor(Type.EmptyTypes), new object[0], propInfos, propValues);
mb.SetCustomAttribute(cab);
}
}
private static void AddExportMapEntry(Dictionary<string, List<string>> map, CompilerClassLoader ccl, string name)
{
string assemblyName = ccl.assemblyBuilder.FullName;
List<string> list;
if (!map.TryGetValue(assemblyName, out list))
{
list = new List<string>();
map.Add(assemblyName, list);
}
if (list != null) // if list is null, we already have a wildcard export for this assembly
{
list.Add(name);
}
}
private void AddWildcardExports(Dictionary<string, List<string>> exportedNamesPerAssembly)
{
foreach (AssemblyClassLoader acl in referencedAssemblies)
{
exportedNamesPerAssembly[acl.MainAssembly.FullName] = null;
}
}
private void WriteExportMap()
{
Dictionary<string, List<string>> exportedNamesPerAssembly = new Dictionary<string, List<string>>();
AddWildcardExports(exportedNamesPerAssembly);
foreach (TypeWrapper tw in dynamicallyImportedTypes)
{
AddExportMapEntry(exportedNamesPerAssembly, (CompilerClassLoader)tw.GetClassLoader(), tw.Name);
}
if (options.sharedclassloader == null)
{
foreach (CompilerClassLoader ccl in peerReferences)
{
exportedNamesPerAssembly[ccl.assemblyBuilder.FullName] = null;
}
}
else
{
foreach (CompilerClassLoader ccl in options.sharedclassloader)
{
if (ccl != this)
{
ccl.AddWildcardExports(exportedNamesPerAssembly);
foreach (Jar jar in ccl.options.jars)
{
foreach (Jar.Item item in jar)
{
if (!item.IsStub)
{
AddExportMapEntry(exportedNamesPerAssembly, ccl, item.Name);
}
}
}
if (ccl.options.externalResources != null)
{
foreach (string name in ccl.options.externalResources.Keys)
{
AddExportMapEntry(exportedNamesPerAssembly, ccl, name);
}
}
}
}
}
MemoryStream ms = new MemoryStream();
BinaryWriter bw = new BinaryWriter(ms);
bw.Write(exportedNamesPerAssembly.Count);
foreach (KeyValuePair<string, List<string>> kv in exportedNamesPerAssembly)
{
bw.Write(kv.Key);
if (kv.Value == null)
{
// 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;
this.GetTypeWrapperFactory().ModuleBuilder.DefineManifestResource("ikvm.exports", ms, ResourceAttributes.Public);
}
private void WriteResources()
{
Tracer.Info(Tracer.Compiler, "CompilerClassLoader adding resources...");
// BUG we need to call GetTypeWrapperFactory() to make sure that the assemblyBuilder is created (when building an empty target)
ModuleBuilder moduleBuilder = this.GetTypeWrapperFactory().ModuleBuilder;
for (int i = 0; i < options.jars.Count; i++)
{
bool hasEntries = false;
MemoryStream mem = new MemoryStream();
using (ZipOutputStream zip = new ZipOutputStream(mem))
{
if (!string.IsNullOrEmpty(options.jars[i].Comment))
{
zip.SetComment(options.jars[i].Comment);
}
zip.SetLevel(9);
List<string> stubs = new List<string>();
foreach (Jar.Item item in options.jars[i])
{
if (item.IsStub)
{
// we don't want stub class pseudo resources for classes loaded from the file system
if (i != options.classesJar)
{
stubs.Add(item.Name);
}
continue;
}
ZipEntry zipEntry = item.ZipEntry;
if (options.compressedResources || zipEntry.CompressionMethod != CompressionMethod.Stored)
{
zipEntry.CompressionMethod = CompressionMethod.Deflated;
}
zip.PutNextEntry(zipEntry);
byte[] data = item.GetData();
zip.Write(data, 0, data.Length);
zip.CloseEntry();
hasEntries = true;
}
if (stubs.Count != 0)
{
// generate the --ikvm-classes-- file in the jar
ZipEntry zipEntry = new ZipEntry(JVM.JarClassList);
zipEntry.CompressionMethod = CompressionMethod.Deflated;
zip.PutNextEntry(zipEntry);
BinaryWriter bw = new BinaryWriter(zip);
bw.Write(stubs.Count);
foreach (string classFile in stubs)
{
bw.Write(classFile);
}
bw.Flush();
zip.CloseEntry();
hasEntries = true;
}
}
// don't include empty classes.jar
if (i != options.classesJar || hasEntries)
{
mem = new MemoryStream(mem.ToArray());
string name = options.jars[i].Name;
if (options.targetIsModule)
{
name = Path.GetFileNameWithoutExtension(name) + "-" + moduleBuilder.ModuleVersionId.ToString("N") + Path.GetExtension(name);
}
jarList.Add(name);
moduleBuilder.DefineManifestResource(name, mem, ResourceAttributes.Public);
}
}
}
private static MethodAttributes MapMethodAccessModifiers(IKVM.Internal.MapXml.MapModifiers mod)
{
const IKVM.Internal.MapXml.MapModifiers access = IKVM.Internal.MapXml.MapModifiers.Public | IKVM.Internal.MapXml.MapModifiers.Protected | IKVM.Internal.MapXml.MapModifiers.Private;
switch(mod & access)
{
case IKVM.Internal.MapXml.MapModifiers.Public:
return MethodAttributes.Public;
case IKVM.Internal.MapXml.MapModifiers.Protected:
return MethodAttributes.FamORAssem;
case IKVM.Internal.MapXml.MapModifiers.Private:
return MethodAttributes.Private;
default:
return MethodAttributes.Assembly;
}
}
private static FieldAttributes MapFieldAccessModifiers(IKVM.Internal.MapXml.MapModifiers mod)
{
const IKVM.Internal.MapXml.MapModifiers access = IKVM.Internal.MapXml.MapModifiers.Public | IKVM.Internal.MapXml.MapModifiers.Protected | IKVM.Internal.MapXml.MapModifiers.Private;
switch(mod & access)
{
case IKVM.Internal.MapXml.MapModifiers.Public:
return FieldAttributes.Public;
case IKVM.Internal.MapXml.MapModifiers.Protected:
return FieldAttributes.FamORAssem;
case IKVM.Internal.MapXml.MapModifiers.Private:
return FieldAttributes.Private;
default:
return FieldAttributes.Assembly;
}
}
private sealed class RemapperTypeWrapper : TypeWrapper
{
private CompilerClassLoader classLoader;
private TypeBuilder typeBuilder;
private TypeBuilder helperTypeBuilder;
private Type shadowType;
private IKVM.Internal.MapXml.Class classDef;
private TypeWrapper baseTypeWrapper;
private TypeWrapper[] interfaceWrappers;
internal override ClassLoaderWrapper GetClassLoader()
{
return classLoader;
}
internal override bool IsRemapped
{
get
{
return true;
}
}
private static TypeWrapper GetBaseWrapper(IKVM.Internal.MapXml.Class c)
{
if((c.Modifiers & IKVM.Internal.MapXml.MapModifiers.Interface) != 0)
{
return null;
}
if(c.Name == "java.lang.Object")
{
return null;
}
return CoreClasses.java.lang.Object.Wrapper;
}
internal RemapperTypeWrapper(CompilerClassLoader classLoader, IKVM.Internal.MapXml.Class c, IKVM.Internal.MapXml.Root map)
: base(TypeFlags.None, (Modifiers)c.Modifiers, c.Name)
{
this.classLoader = classLoader;
this.baseTypeWrapper = GetBaseWrapper(c);
classDef = c;
bool baseIsSealed = false;
shadowType = StaticCompiler.Universe.GetType(c.Shadows, true);
classLoader.SetRemappedType(shadowType, this);
Type baseType = shadowType;
Type baseInterface = null;
if(baseType.IsInterface)
{
baseInterface = baseType;
}
TypeAttributes attrs = TypeAttributes.Public;
if((c.Modifiers & IKVM.Internal.MapXml.MapModifiers.Interface) == 0)
{
attrs |= TypeAttributes.Class;
if(baseType.IsSealed)
{
baseIsSealed = true;
attrs |= TypeAttributes.Abstract | TypeAttributes.Sealed;
}
}
else
{
attrs |= TypeAttributes.Interface | TypeAttributes.Abstract;
baseType = null;
}
if((c.Modifiers & IKVM.Internal.MapXml.MapModifiers.Abstract) != 0)
{
attrs |= TypeAttributes.Abstract;
}
string name = c.Name.Replace('/', '.');
typeBuilder = classLoader.GetTypeWrapperFactory().ModuleBuilder.DefineType(name, attrs, baseIsSealed ? Types.Object : baseType);
if(c.Attributes != null)
{
foreach(IKVM.Internal.MapXml.Attribute custattr in c.Attributes)
{
AttributeHelper.SetCustomAttribute(classLoader, typeBuilder, custattr);
}
}
if(baseInterface != null)
{
typeBuilder.AddInterfaceImplementation(baseInterface);
}
if(classLoader.EmitStackTraceInfo)
{
AttributeHelper.SetSourceFile(typeBuilder, classLoader.options.remapfile.Name);
}
if(baseIsSealed)
{
AttributeHelper.SetModifiers(typeBuilder, (Modifiers)c.Modifiers, false);
}
if(c.scope == IKVM.Internal.MapXml.Scope.Public)
{
// FXBUG we would like to emit an attribute with a Type argument here, but that doesn't work because
// of a bug in SetCustomAttribute that causes type arguments to be serialized incorrectly (if the type
// is in the same assembly). Normally we use AttributeHelper.FreezeDry to get around this, but that doesn't
// work in this case (no attribute is emitted at all). So we work around by emitting a string instead
AttributeHelper.SetRemappedClass(classLoader.assemblyBuilder, name, shadowType);
AttributeHelper.SetRemappedType(typeBuilder, shadowType);
}
List<MethodWrapper> methods = new List<MethodWrapper>();
if(c.Constructors != null)
{
foreach(IKVM.Internal.MapXml.Constructor m in c.Constructors)
{
methods.Add(new RemappedConstructorWrapper(this, m));
}
}
if(c.Methods != null)
{
foreach(IKVM.Internal.MapXml.Method m in c.Methods)
{
methods.Add(new RemappedMethodWrapper(this, m, map, false));
}
}
// add methods from our super classes (e.g. Throwable should have Object's methods)
if(!this.IsFinal && !this.IsInterface && this.BaseTypeWrapper != null)
{
foreach(MethodWrapper mw in BaseTypeWrapper.GetMethods())
{
RemappedMethodWrapper rmw = mw as RemappedMethodWrapper;
if(rmw != null && (rmw.IsPublic || rmw.IsProtected))
{
if(!FindMethod(methods, rmw.Name, rmw.Signature))
{
methods.Add(new RemappedMethodWrapper(this, rmw.XmlMethod, map, true));
}
}
}
}
SetMethods(methods.ToArray());
}
internal sealed override TypeWrapper BaseTypeWrapper
{
get { return baseTypeWrapper; }
}
internal void LoadInterfaces(IKVM.Internal.MapXml.Class c)
{
if (c.Interfaces != null)
{
interfaceWrappers = new TypeWrapper[c.Interfaces.Length];
for (int i = 0; i < c.Interfaces.Length; i++)
{
interfaceWrappers[i] = classLoader.LoadClassByDottedName(c.Interfaces[i].Name);
}
}
else
{
interfaceWrappers = TypeWrapper.EmptyArray;
}
}
private static bool FindMethod(List<MethodWrapper> methods, string name, string sig)
{
foreach(MethodWrapper mw in methods)
{
if(mw.Name == name && mw.Signature == sig)
{
return true;
}
}
return false;
}
abstract class RemappedMethodBaseWrapper : MethodWrapper
{
internal RemappedMethodBaseWrapper(RemapperTypeWrapper typeWrapper, string name, string sig, Modifiers modifiers)
: base(typeWrapper, name, sig, null, null, null, modifiers, MemberFlags.None)
{
}
internal abstract MethodBase DoLink();
internal abstract void Finish();
}
sealed class RemappedConstructorWrapper : RemappedMethodBaseWrapper
{
private IKVM.Internal.MapXml.Constructor m;
private MethodBuilder mbHelper;
internal RemappedConstructorWrapper(RemapperTypeWrapper typeWrapper, IKVM.Internal.MapXml.Constructor m)
: base(typeWrapper, "<init>", m.Sig, (Modifiers)m.Modifiers)
{
this.m = m;
}
internal override void EmitCall(CodeEmitter ilgen)
{
ilgen.Emit(OpCodes.Call, GetMethod());
}
internal override void EmitNewobj(CodeEmitter ilgen)
{
if(mbHelper != null)
{
ilgen.Emit(OpCodes.Call, mbHelper);
}
else
{
ilgen.Emit(OpCodes.Newobj, GetMethod());
}
}
internal override MethodBase DoLink()
{
MethodAttributes attr = MapMethodAccessModifiers(m.Modifiers);
RemapperTypeWrapper typeWrapper = (RemapperTypeWrapper)DeclaringType;
Type[] paramTypes = typeWrapper.GetClassLoader().ArgTypeListFromSig(m.Sig);
MethodBuilder cbCore = null;
if(typeWrapper.shadowType.IsSealed)
{
mbHelper = typeWrapper.typeBuilder.DefineMethod("newhelper", attr | MethodAttributes.Static, CallingConventions.Standard, typeWrapper.shadowType, paramTypes);
if(m.Attributes != null)
{
foreach(IKVM.Internal.MapXml.Attribute custattr in m.Attributes)
{
AttributeHelper.SetCustomAttribute(DeclaringType.GetClassLoader(), mbHelper, custattr);
}
}
SetParameters(DeclaringType.GetClassLoader(), mbHelper, m.Params);
AttributeHelper.SetModifiers(mbHelper, (Modifiers)m.Modifiers, false);
AttributeHelper.SetNameSig(mbHelper, "<init>", m.Sig);
AddDeclaredExceptions(mbHelper, m.throws);
}
else
{
cbCore = ReflectUtil.DefineConstructor(typeWrapper.typeBuilder, attr, paramTypes);
if(m.Attributes != null)
{
foreach(IKVM.Internal.MapXml.Attribute custattr in m.Attributes)
{
AttributeHelper.SetCustomAttribute(DeclaringType.GetClassLoader(), cbCore, custattr);
}
}
SetParameters(DeclaringType.GetClassLoader(), cbCore, m.Params);
AddDeclaredExceptions(cbCore, m.throws);
}
return cbCore;
}
internal override void Finish()
{
// TODO we should insert method tracing (if enabled)
Type[] paramTypes = this.GetParametersForDefineMethod();
MethodBuilder cbCore = GetMethod() as MethodBuilder;
if(cbCore != null)
{
CodeEmitter ilgen = CodeEmitter.Create(cbCore);
// TODO we need to support ghost (and other funky?) parameter types
if(m.body != null)
{
// TODO do we need return type conversion here?
m.body.Emit(DeclaringType.GetClassLoader(), ilgen);
}
else
{
ilgen.Emit(OpCodes.Ldarg_0);
for(int i = 0; i < paramTypes.Length; i++)
{
ilgen.EmitLdarg(i + 1);
}
if(m.redirect != null)
{
throw new NotImplementedException();
}
else
{
ConstructorInfo baseCon = DeclaringType.TypeAsTBD.GetConstructor(paramTypes);
if(baseCon == null)
{
// TODO better error handling
throw new InvalidOperationException("base class constructor not found: " + DeclaringType.Name + ".<init>" + m.Sig);
}
ilgen.Emit(OpCodes.Call, baseCon);
}
ilgen.Emit(OpCodes.Ret);
}
ilgen.DoEmit();
if(this.DeclaringType.GetClassLoader().EmitStackTraceInfo)
{
ilgen.EmitLineNumberTable(cbCore);
}
}
if(mbHelper != null)
{
CodeEmitter ilgen = CodeEmitter.Create(mbHelper);
if(m.redirect != null)
{
m.redirect.Emit(DeclaringType.GetClassLoader(), ilgen);
}
else if(m.alternateBody != null)
{
m.alternateBody.Emit(DeclaringType.GetClassLoader(), ilgen);
}
else if(m.body != null)
{
// <body> doesn't make sense for helper constructors (which are actually factory methods)
throw new InvalidOperationException();
}
else
{
ConstructorInfo baseCon = DeclaringType.TypeAsTBD.GetConstructor(paramTypes);
if(baseCon == null)
{
// TODO better error handling
throw new InvalidOperationException("constructor not found: " + DeclaringType.Name + ".<init>" + m.Sig);
}
for(int i = 0; i < paramTypes.Length; i++)
{
ilgen.EmitLdarg(i);
}
ilgen.Emit(OpCodes.Newobj, baseCon);
ilgen.Emit(OpCodes.Ret);
}
ilgen.DoEmit();
if(this.DeclaringType.GetClassLoader().EmitStackTraceInfo)
{
ilgen.EmitLineNumberTable(mbHelper);
}
}
}
}
sealed class RemappedMethodWrapper : RemappedMethodBaseWrapper
{
private IKVM.Internal.MapXml.Method m;
private IKVM.Internal.MapXml.Root map;
private MethodBuilder mbHelper;
private List<RemapperTypeWrapper> overriders = new List<RemapperTypeWrapper>();
private bool inherited;
internal RemappedMethodWrapper(RemapperTypeWrapper typeWrapper, IKVM.Internal.MapXml.Method m, IKVM.Internal.MapXml.Root map, bool inherited)
: base(typeWrapper, m.Name, m.Sig, (Modifiers)m.Modifiers)
{
this.m = m;
this.map = map;
this.inherited = inherited;
}
internal IKVM.Internal.MapXml.Method XmlMethod
{
get
{
return m;
}
}
internal override void EmitCall(CodeEmitter ilgen)
{
if(!IsStatic && IsFinal)
{
// When calling a final instance method on a remapped type from a class derived from a .NET class (i.e. a cli.System.Object or cli.System.Exception derived base class)
// then we can't call the java.lang.Object or java.lang.Throwable methods and we have to go through the instancehelper_ method. Note that since the method
// is final, this won't affect the semantics.
EmitCallvirt(ilgen);
}
else
{
ilgen.Emit(OpCodes.Call, (MethodInfo)GetMethod());
}
}
internal override void EmitCallvirt(CodeEmitter ilgen)
{
EmitCallvirtImpl(ilgen, this.IsProtected && !mbHelper.IsPublic);
}
private void EmitCallvirtImpl(CodeEmitter ilgen, bool cloneOrFinalizeHack)
{
if(mbHelper != null && !cloneOrFinalizeHack)
{
ilgen.Emit(OpCodes.Call, mbHelper);
}
else
{
ilgen.Emit(OpCodes.Callvirt, (MethodInfo)GetMethod());
}
}
internal override MethodBase DoLink()
{
RemapperTypeWrapper typeWrapper = (RemapperTypeWrapper)DeclaringType;
if(typeWrapper.IsInterface)
{
if(m.@override == null)
{
throw new InvalidOperationException(typeWrapper.Name + "." + m.Name + m.Sig);
}
MethodInfo interfaceMethod = typeWrapper.shadowType.GetMethod(m.@override.Name, typeWrapper.GetClassLoader().ArgTypeListFromSig(m.Sig));
if(interfaceMethod == null)
{
throw new InvalidOperationException(typeWrapper.Name + "." + m.Name + m.Sig);
}
// if any of the remapped types has a body for this interface method, we need a helper method
// to special invocation through this interface for that type
List<IKVM.Internal.MapXml.Class> specialCases = null;
foreach(IKVM.Internal.MapXml.Class c in map.assembly.Classes)
{
if(c.Methods != null)
{
foreach(IKVM.Internal.MapXml.Method mm in c.Methods)
{
if(mm.Name == m.Name && mm.Sig == m.Sig && mm.body != null)
{
if(specialCases == null)
{
specialCases = new List<IKVM.Internal.MapXml.Class>();
}
specialCases.Add(c);
break;
}
}
}
}
string[] throws;
if (m.throws == null)
{
throws = new string[0];
}
else
{
throws = new string[m.throws.Length];
for (int i = 0; i < throws.Length; i++)
{
throws[i] = m.throws[i].Class;
}
}
AttributeHelper.SetRemappedInterfaceMethod(typeWrapper.typeBuilder, m.Name, m.@override.Name, throws);
MethodBuilder helper = null;
if(specialCases != null)
{
CodeEmitter ilgen;
Type[] argTypes = ArrayUtil.Concat(typeWrapper.shadowType, typeWrapper.GetClassLoader().ArgTypeListFromSig(m.Sig));
if(typeWrapper.helperTypeBuilder == null)
{
typeWrapper.helperTypeBuilder = typeWrapper.typeBuilder.DefineNestedType("__Helper", TypeAttributes.NestedPublic | TypeAttributes.Class | TypeAttributes.Sealed | TypeAttributes.Abstract);
AttributeHelper.HideFromJava(typeWrapper.helperTypeBuilder);
}
helper = typeWrapper.helperTypeBuilder.DefineMethod(m.Name, MethodAttributes.HideBySig | MethodAttributes.Public | MethodAttributes.Static, typeWrapper.GetClassLoader().RetTypeWrapperFromSig(m.Sig).TypeAsSignatureType, argTypes);
if(m.Attributes != null)
{
foreach(IKVM.Internal.MapXml.Attribute custattr in m.Attributes)
{
AttributeHelper.SetCustomAttribute(DeclaringType.GetClassLoader(), helper, custattr);
}
}
SetParameters(DeclaringType.GetClassLoader(), helper, m.Params);
ilgen = CodeEmitter.Create(helper);
foreach(IKVM.Internal.MapXml.Class c in specialCases)
{
TypeWrapper tw = typeWrapper.GetClassLoader().LoadClassByDottedName(c.Name);
ilgen.Emit(OpCodes.Ldarg_0);
ilgen.Emit(OpCodes.Isinst, tw.TypeAsTBD);
ilgen.Emit(OpCodes.Dup);
CodeEmitterLabel label = ilgen.DefineLabel();
ilgen.EmitBrfalse(label);
for(int i = 1; i < argTypes.Length; i++)
{
ilgen.EmitLdarg(i);
}
MethodWrapper mw = tw.GetMethodWrapper(m.Name, m.Sig, false);
mw.Link();
mw.EmitCallvirt(ilgen);
ilgen.Emit(OpCodes.Ret);
ilgen.MarkLabel(label);
ilgen.Emit(OpCodes.Pop);
}
for(int i = 0; i < argTypes.Length; i++)
{
ilgen.EmitLdarg(i);
}
ilgen.Emit(OpCodes.Callvirt, interfaceMethod);
ilgen.Emit(OpCodes.Ret);
ilgen.DoEmit();
}
mbHelper = helper;
return interfaceMethod;
}
else
{
MethodBuilder mbCore = null;
Type[] paramTypes = typeWrapper.GetClassLoader().ArgTypeListFromSig(m.Sig);
Type retType = typeWrapper.GetClassLoader().RetTypeWrapperFromSig(m.Sig).TypeAsSignatureType;
if(typeWrapper.shadowType.IsSealed && (m.Modifiers & IKVM.Internal.MapXml.MapModifiers.Static) == 0)
{
// skip instance methods in sealed types, but we do need to add them to the overriders
if(typeWrapper.BaseTypeWrapper != null && (m.Modifiers & IKVM.Internal.MapXml.MapModifiers.Private) == 0)
{
RemappedMethodWrapper baseMethod = typeWrapper.BaseTypeWrapper.GetMethodWrapper(m.Name, m.Sig, true) as RemappedMethodWrapper;
if(baseMethod != null &&
!baseMethod.IsFinal &&
!baseMethod.IsPrivate &&
(baseMethod.m.@override != null ||
baseMethod.m.redirect != null ||
baseMethod.m.body != null ||
baseMethod.m.alternateBody != null))
{
baseMethod.overriders.Add(typeWrapper);
}
}
}
else
{
MethodInfo overrideMethod = null;
MethodAttributes attr = m.MethodAttributes | MapMethodAccessModifiers(m.Modifiers) | MethodAttributes.HideBySig;
if((m.Modifiers & IKVM.Internal.MapXml.MapModifiers.Static) != 0)
{
attr |= MethodAttributes.Static;
}
else if((m.Modifiers & IKVM.Internal.MapXml.MapModifiers.Private) == 0 && (m.Modifiers & IKVM.Internal.MapXml.MapModifiers.Final) == 0)
{
attr |= MethodAttributes.Virtual | MethodAttributes.NewSlot | MethodAttributes.CheckAccessOnOverride;
if(!typeWrapper.shadowType.IsSealed)
{
MethodInfo autoOverride = typeWrapper.shadowType.GetMethod(m.Name, BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic, null, paramTypes, null);
if(autoOverride != null && autoOverride.ReturnType == retType && !autoOverride.IsFinal)
{
// the method we're processing is overriding a method in its shadowType (which is the actual base type)
attr &= ~MethodAttributes.NewSlot;
}
}
if(typeWrapper.BaseTypeWrapper != null)
{
RemappedMethodWrapper baseMethod = typeWrapper.BaseTypeWrapper.GetMethodWrapper(m.Name, m.Sig, true) as RemappedMethodWrapper;
if(baseMethod != null)
{
baseMethod.overriders.Add(typeWrapper);
if(baseMethod.m.@override != null)
{
overrideMethod = typeWrapper.BaseTypeWrapper.TypeAsTBD.GetMethod(baseMethod.m.@override.Name, BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic, null, paramTypes, null);
if(overrideMethod == null)
{
throw new InvalidOperationException();
}
}
}
}
}
mbCore = GetDefineMethodHelper().DefineMethod(DeclaringType.GetClassLoader().GetTypeWrapperFactory(), typeWrapper.typeBuilder, m.Name, attr);
if(m.Attributes != null)
{
foreach(IKVM.Internal.MapXml.Attribute custattr in m.Attributes)
{
AttributeHelper.SetCustomAttribute(DeclaringType.GetClassLoader(), mbCore, custattr);
}
}
SetParameters(DeclaringType.GetClassLoader(), mbCore, m.Params);
if(overrideMethod != null && !inherited)
{
typeWrapper.typeBuilder.DefineMethodOverride(mbCore, overrideMethod);
}
if(inherited)
{
AttributeHelper.HideFromReflection(mbCore);
}
AddDeclaredExceptions(mbCore, m.throws);
}
if((m.Modifiers & IKVM.Internal.MapXml.MapModifiers.Static) == 0 && !IsHideFromJava(m))
{
// instance methods must have an instancehelper method
MethodAttributes attr = MapMethodAccessModifiers(m.Modifiers) | MethodAttributes.HideBySig | MethodAttributes.Static;
// NOTE instancehelpers for protected methods are made internal
// and special cased in DotNetTypeWrapper.LazyPublishMembers
if((m.Modifiers & IKVM.Internal.MapXml.MapModifiers.Protected) != 0)
{
attr &= ~MethodAttributes.MemberAccessMask;
attr |= MethodAttributes.Assembly;
}
mbHelper = typeWrapper.typeBuilder.DefineMethod("instancehelper_" + m.Name, attr, CallingConventions.Standard, retType, ArrayUtil.Concat(typeWrapper.shadowType, paramTypes));
if(m.Attributes != null)
{
foreach(IKVM.Internal.MapXml.Attribute custattr in m.Attributes)
{
AttributeHelper.SetCustomAttribute(DeclaringType.GetClassLoader(), mbHelper, custattr);
}
}
IKVM.Internal.MapXml.Param[] parameters;
if(m.Params == null)
{
parameters = new IKVM.Internal.MapXml.Param[1];
}
else
{
parameters = new IKVM.Internal.MapXml.Param[m.Params.Length + 1];
m.Params.CopyTo(parameters, 1);
}
parameters[0] = new IKVM.Internal.MapXml.Param();
parameters[0].Name = "this";
SetParameters(DeclaringType.GetClassLoader(), mbHelper, parameters);
if(!typeWrapper.IsFinal)
{
AttributeHelper.SetEditorBrowsableNever(mbHelper);
}
AttributeHelper.SetModifiers(mbHelper, (Modifiers)m.Modifiers, false);
AttributeHelper.SetNameSig(mbHelper, m.Name, m.Sig);
AddDeclaredExceptions(mbHelper, m.throws);
mbHelper.SetCustomAttribute(new CustomAttributeBuilder(JVM.Import(typeof(ObsoleteAttribute)).GetConstructor(new Type[] { Types.String }), new object[] { "This function will be removed from future versions. Please use extension methods from ikvm.extensions namespace instead." }));
}
return mbCore;
}
}
private static bool IsHideFromJava(IKVM.Internal.MapXml.Method m)
{
if (m.Attributes != null)
{
foreach (MapXml.Attribute attr in m.Attributes)
{
if (attr.Type == "IKVM.Attributes.HideFromJavaAttribute")
{
return true;
}
}
}
return m.Name.StartsWith("__<", StringComparison.Ordinal);
}
internal override void Finish()
{
// TODO we should insert method tracing (if enabled)
Type[] paramTypes = this.GetParametersForDefineMethod();
MethodBuilder mbCore = GetMethod() as MethodBuilder;
// NOTE sealed types don't have instance methods (only instancehelpers)
if(mbCore != null)
{
CodeEmitter ilgen = CodeEmitter.Create(mbCore);
MethodInfo baseMethod = null;
if(m.@override != null)
{
baseMethod = DeclaringType.TypeAsTBD.GetMethod(m.@override.Name, BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic, null, paramTypes, null);
if(baseMethod == null)
{
throw new InvalidOperationException();
}
((TypeBuilder)DeclaringType.TypeAsBaseType).DefineMethodOverride(mbCore, baseMethod);
}
// TODO we need to support ghost (and other funky?) parameter types
if(m.body != null)
{
// we manually walk the instruction list, because we need to special case the ret instructions
IKVM.Internal.MapXml.CodeGenContext context = new IKVM.Internal.MapXml.CodeGenContext(DeclaringType.GetClassLoader());
foreach(IKVM.Internal.MapXml.Instruction instr in m.body.invoke)
{
if(instr is IKVM.Internal.MapXml.Ret)
{
this.ReturnType.EmitConvStackTypeToSignatureType(ilgen, null);
}
instr.Generate(context, ilgen);
}
}
else
{
if(m.redirect != null && m.redirect.LineNumber != -1)
{
ilgen.SetLineNumber((ushort)m.redirect.LineNumber);
}
int thisOffset = 0;
if((m.Modifiers & IKVM.Internal.MapXml.MapModifiers.Static) == 0)
{
thisOffset = 1;
ilgen.Emit(OpCodes.Ldarg_0);
}
for(int i = 0; i < paramTypes.Length; i++)
{
ilgen.EmitLdarg(i + thisOffset);
}
if(m.redirect != null)
{
EmitRedirect(DeclaringType.TypeAsTBD, ilgen);
}
else
{
if(baseMethod == null)
{
throw new InvalidOperationException(DeclaringType.Name + "." + m.Name + m.Sig);
}
ilgen.Emit(OpCodes.Call, baseMethod);
}
this.ReturnType.EmitConvStackTypeToSignatureType(ilgen, null);
ilgen.Emit(OpCodes.Ret);
}
ilgen.DoEmit();
if(this.DeclaringType.GetClassLoader().EmitStackTraceInfo)
{
ilgen.EmitLineNumberTable(mbCore);
}
}
// NOTE static methods don't have helpers
// NOTE for interface helpers we don't have to do anything,
// because they've already been generated in DoLink
// (currently this only applies to Comparable.compareTo).
if(mbHelper != null && !this.DeclaringType.IsInterface)
{
CodeEmitter ilgen = CodeEmitter.Create(mbHelper);
// check "this" for null
if(m.@override != null && m.redirect == null && m.body == null && m.alternateBody == null)
{
// we're going to be calling the overridden version, so we don't need the null check
}
else if(!m.NoNullCheck)
{
ilgen.Emit(OpCodes.Ldarg_0);
ilgen.EmitNullCheck();
}
if(mbCore != null &&
(m.@override == null || m.redirect != null) &&
(m.Modifiers & IKVM.Internal.MapXml.MapModifiers.Private) == 0 && (m.Modifiers & IKVM.Internal.MapXml.MapModifiers.Final) == 0)
{
// TODO we should have a way to supress this for overridden methods
ilgen.Emit(OpCodes.Ldarg_0);
ilgen.Emit(OpCodes.Isinst, DeclaringType.TypeAsBaseType);
ilgen.Emit(OpCodes.Dup);
CodeEmitterLabel skip = ilgen.DefineLabel();
ilgen.EmitBrfalse(skip);
for(int i = 0; i < paramTypes.Length; i++)
{
ilgen.EmitLdarg(i + 1);
}
ilgen.Emit(OpCodes.Callvirt, mbCore);
this.ReturnType.EmitConvStackTypeToSignatureType(ilgen, null);
ilgen.Emit(OpCodes.Ret);
ilgen.MarkLabel(skip);
ilgen.Emit(OpCodes.Pop);
}
foreach(RemapperTypeWrapper overrider in overriders)
{
RemappedMethodWrapper mw = (RemappedMethodWrapper)overrider.GetMethodWrapper(Name, Signature, false);
if(mw.m.redirect == null && mw.m.body == null && mw.m.alternateBody == null)
{
// the overridden method doesn't actually do anything special (that means it will end
// up calling the .NET method it overrides), so we don't need to special case this
}
else
{
ilgen.Emit(OpCodes.Ldarg_0);
ilgen.Emit(OpCodes.Isinst, overrider.TypeAsTBD);
ilgen.Emit(OpCodes.Dup);
CodeEmitterLabel skip = ilgen.DefineLabel();
ilgen.EmitBrfalse(skip);
for(int i = 0; i < paramTypes.Length; i++)
{
ilgen.EmitLdarg(i + 1);
}
mw.Link();
mw.EmitCallvirtImpl(ilgen, false);
this.ReturnType.EmitConvStackTypeToSignatureType(ilgen, null);
ilgen.Emit(OpCodes.Ret);
ilgen.MarkLabel(skip);
ilgen.Emit(OpCodes.Pop);
}
}
if(m.body != null || m.alternateBody != null)
{
IKVM.Internal.MapXml.InstructionList body = m.alternateBody == null ? m.body : m.alternateBody;
// we manually walk the instruction list, because we need to special case the ret instructions
IKVM.Internal.MapXml.CodeGenContext context = new IKVM.Internal.MapXml.CodeGenContext(DeclaringType.GetClassLoader());
foreach(IKVM.Internal.MapXml.Instruction instr in body.invoke)
{
if(instr is IKVM.Internal.MapXml.Ret)
{
this.ReturnType.EmitConvStackTypeToSignatureType(ilgen, null);
}
instr.Generate(context, ilgen);
}
}
else
{
if(m.redirect != null && m.redirect.LineNumber != -1)
{
ilgen.SetLineNumber((ushort)m.redirect.LineNumber);
}
Type shadowType = ((RemapperTypeWrapper)DeclaringType).shadowType;
for(int i = 0; i < paramTypes.Length + 1; i++)
{
ilgen.EmitLdarg(i);
}
if(m.redirect != null)
{
EmitRedirect(shadowType, ilgen);
}
else if(m.@override != null)
{
MethodInfo baseMethod = shadowType.GetMethod(m.@override.Name, BindingFlags.Instance | BindingFlags.Public, null, paramTypes, null);
if(baseMethod == null)
{
throw new InvalidOperationException(DeclaringType.Name + "." + m.Name + m.Sig);
}
ilgen.Emit(OpCodes.Callvirt, baseMethod);
}
else
{
RemappedMethodWrapper baseMethod = DeclaringType.BaseTypeWrapper.GetMethodWrapper(Name, Signature, true) as RemappedMethodWrapper;
if(baseMethod == null || baseMethod.m.@override == null)
{
throw new InvalidOperationException(DeclaringType.Name + "." + m.Name + m.Sig);
}
MethodInfo overrideMethod = shadowType.GetMethod(baseMethod.m.@override.Name, BindingFlags.Instance | BindingFlags.Public, null, paramTypes, null);
if(overrideMethod == null)
{
throw new InvalidOperationException(DeclaringType.Name + "." + m.Name + m.Sig);
}
ilgen.Emit(OpCodes.Callvirt, overrideMethod);
}
this.ReturnType.EmitConvStackTypeToSignatureType(ilgen, null);
ilgen.Emit(OpCodes.Ret);
}
ilgen.DoEmit();
if(this.DeclaringType.GetClassLoader().EmitStackTraceInfo)
{
ilgen.EmitLineNumberTable(mbHelper);
}
}
// do we need a helper for non-virtual reflection invocation?
if(m.nonvirtualAlternateBody != null || (m.@override != null && overriders.Count > 0))
{
RemapperTypeWrapper typeWrapper = (RemapperTypeWrapper)DeclaringType;
MethodBuilder mb = typeWrapper.typeBuilder.DefineMethod("nonvirtualhelper/" + this.Name, MethodAttributes.Private | MethodAttributes.Static,
ReturnTypeForDefineMethod, ArrayUtil.Concat(typeWrapper.TypeAsSignatureType, GetParametersForDefineMethod()));
if(m.Attributes != null)
{
foreach(IKVM.Internal.MapXml.Attribute custattr in m.Attributes)
{
AttributeHelper.SetCustomAttribute(DeclaringType.GetClassLoader(), mb, custattr);
}
}
SetParameters(DeclaringType.GetClassLoader(), mb, m.Params);
AttributeHelper.HideFromJava(mb);
CodeEmitter ilgen = CodeEmitter.Create(mb);
if(m.nonvirtualAlternateBody != null)
{
m.nonvirtualAlternateBody.Emit(DeclaringType.GetClassLoader(), ilgen);
}
else
{
Type shadowType = ((RemapperTypeWrapper)DeclaringType).shadowType;
MethodInfo baseMethod = shadowType.GetMethod(m.@override.Name, BindingFlags.Instance | BindingFlags.Public, null, paramTypes, null);
if(baseMethod == null)
{
throw new InvalidOperationException(DeclaringType.Name + "." + m.Name + m.Sig);
}
ilgen.Emit(OpCodes.Ldarg_0);
for(int i = 0; i < paramTypes.Length; i++)
{
ilgen.EmitLdarg(i + 1);
}
ilgen.Emit(OpCodes.Call, baseMethod);
ilgen.Emit(OpCodes.Ret);
}
ilgen.DoEmit();
}
}
private void EmitRedirect(Type baseType, CodeEmitter ilgen)
{
string redirName = m.redirect.Name;
string redirSig = m.redirect.Sig;
if(redirName == null)
{
redirName = m.Name;
}
if(redirSig == null)
{
redirSig = m.Sig;
}
ClassLoaderWrapper classLoader = DeclaringType.GetClassLoader();
// HACK if the class name contains a comma, we assume it is a .NET type
if(m.redirect.Class == null || m.redirect.Class.IndexOf(',') >= 0)
{
// TODO better error handling
Type type = m.redirect.Class == null ? baseType : StaticCompiler.Universe.GetType(m.redirect.Class, true);
Type[] redirParamTypes = classLoader.ArgTypeListFromSig(redirSig);
MethodInfo mi = type.GetMethod(m.redirect.Name, redirParamTypes);
if(mi == null)
{
throw new InvalidOperationException();
}
ilgen.Emit(OpCodes.Call, mi);
}
else
{
TypeWrapper tw = classLoader.LoadClassByDottedName(m.redirect.Class);
MethodWrapper mw = tw.GetMethodWrapper(redirName, redirSig, false);
if(mw == null)
{
throw new InvalidOperationException("Missing redirect method: " + tw.Name + "." + redirName + redirSig);
}
mw.Link();
mw.EmitCall(ilgen);
}
}
}
private static void SetParameters(ClassLoaderWrapper loader, MethodBuilder mb, IKVM.Internal.MapXml.Param[] parameters)
{
if(parameters != null)
{
for(int i = 0; i < parameters.Length; i++)
{
ParameterBuilder pb = mb.DefineParameter(i + 1, ParameterAttributes.None, parameters[i].Name);
if(parameters[i].Attributes != null)
{
for(int j = 0; j < parameters[i].Attributes.Length; j++)
{
AttributeHelper.SetCustomAttribute(loader, pb, parameters[i].Attributes[j]);
}
}
}
}
}
internal void Process2ndPassStep1()
{
if (!shadowType.IsSealed)
{
foreach (TypeWrapper ifaceTypeWrapper in interfaceWrappers)
{
typeBuilder.AddInterfaceImplementation(ifaceTypeWrapper.TypeAsBaseType);
}
}
AttributeHelper.SetImplementsAttribute(typeBuilder, interfaceWrappers);
}
internal void Process2ndPassStep2(IKVM.Internal.MapXml.Root map)
{
IKVM.Internal.MapXml.Class c = classDef;
TypeBuilder tb = typeBuilder;
List<FieldWrapper> fields = new List<FieldWrapper>();
// TODO fields should be moved to the RemapperTypeWrapper constructor as well
if(c.Fields != null)
{
foreach(IKVM.Internal.MapXml.Field f in c.Fields)
{
{
FieldAttributes attr = MapFieldAccessModifiers(f.Modifiers);
if(f.Constant != null)
{
attr |= FieldAttributes.Literal;
}
else if((f.Modifiers & IKVM.Internal.MapXml.MapModifiers.Final) != 0)
{
attr |= FieldAttributes.InitOnly;
}
if((f.Modifiers & IKVM.Internal.MapXml.MapModifiers.Static) != 0)
{
attr |= FieldAttributes.Static;
}
FieldBuilder fb = tb.DefineField(f.Name, GetClassLoader().FieldTypeWrapperFromSig(f.Sig).TypeAsSignatureType, attr);
if(f.Attributes != null)
{
foreach(IKVM.Internal.MapXml.Attribute custattr in f.Attributes)
{
AttributeHelper.SetCustomAttribute(classLoader, fb, custattr);
}
}
object constant;
if(f.Constant != null)
{
switch(f.Sig[0])
{
case 'J':
constant = long.Parse(f.Constant);
break;
default:
// TODO support other types
throw new NotImplementedException("remapped constant field of type: " + f.Sig);
}
fb.SetConstant(constant);
fields.Add(new ConstantFieldWrapper(this, GetClassLoader().FieldTypeWrapperFromSig(f.Sig), f.Name, f.Sig, (Modifiers)f.Modifiers, fb, constant, MemberFlags.None));
}
else
{
fields.Add(FieldWrapper.Create(this, GetClassLoader().FieldTypeWrapperFromSig(f.Sig), fb, f.Name, f.Sig, new ExModifiers((Modifiers)f.Modifiers, false)));
}
}
}
}
SetFields(fields.ToArray());
}
internal void Process3rdPass()
{
foreach(RemappedMethodBaseWrapper m in GetMethods())
{
m.Link();
}
}
internal void Process4thPass(ICollection<RemapperTypeWrapper> remappedTypes)
{
foreach(RemappedMethodBaseWrapper m in GetMethods())
{
m.Finish();
}
if(classDef.Clinit != null)
{
MethodBuilder cb = ReflectUtil.DefineTypeInitializer(typeBuilder, classLoader);
CodeEmitter ilgen = CodeEmitter.Create(cb);
// TODO emit code to make sure super class is initialized
classDef.Clinit.body.Emit(classLoader, ilgen);
ilgen.DoEmit();
}
// FXBUG because the AppDomain.TypeResolve event doesn't work correctly for inner classes,
// we need to explicitly finish the interface we implement (if they are ghosts, we need the nested __Interface type)
if(classDef.Interfaces != null)
{
foreach(IKVM.Internal.MapXml.Interface iface in classDef.Interfaces)
{
GetClassLoader().LoadClassByDottedName(iface.Name).Finish();
}
}
CreateShadowInstanceOf(remappedTypes);
CreateShadowCheckCast(remappedTypes);
if(!shadowType.IsInterface)
{
// For all inherited methods, we emit a method that hides the inherited method and
// annotate it with EditorBrowsableAttribute(EditorBrowsableState.Never) to make
// sure the inherited methods don't show up in Intellisense.
Dictionary<string, MethodBuilder> methods = new Dictionary<string, MethodBuilder>();
foreach(MethodWrapper mw in GetMethods())
{
MethodBuilder mb = mw.GetMethod() as MethodBuilder;
if(mb != null)
{
methods.Add(MakeMethodKey(mb), mb);
}
}
foreach(MethodInfo mi in typeBuilder.BaseType.GetMethods(BindingFlags.Public | BindingFlags.Instance | BindingFlags.Static | BindingFlags.FlattenHierarchy))
{
string key = MakeMethodKey(mi);
if(!methods.ContainsKey(key))
{
ParameterInfo[] paramInfo = mi.GetParameters();
Type[] paramTypes = new Type[paramInfo.Length];
for(int i = 0; i < paramInfo.Length; i++)
{
paramTypes[i] = paramInfo[i].ParameterType;
}
MethodBuilder mb = typeBuilder.DefineMethod(mi.Name, mi.Attributes & (MethodAttributes.MemberAccessMask | MethodAttributes.SpecialName | MethodAttributes.Static), mi.ReturnType, paramTypes);
AttributeHelper.HideFromJava(mb);
AttributeHelper.SetEditorBrowsableNever(mb);
CopyLinkDemands(mb, mi);
CodeEmitter ilgen = CodeEmitter.Create(mb);
for(int i = 0; i < paramTypes.Length; i++)
{
ilgen.EmitLdarg(i);
}
if(!mi.IsStatic)
{
ilgen.EmitLdarg(paramTypes.Length);
ilgen.Emit(OpCodes.Callvirt, mi);
}
else
{
ilgen.Emit(OpCodes.Call, mi);
}
ilgen.Emit(OpCodes.Ret);
ilgen.DoEmit();
methods[key] = mb;
}
}
foreach(PropertyInfo pi in typeBuilder.BaseType.GetProperties(BindingFlags.Public | BindingFlags.Instance | BindingFlags.Static))
{
ParameterInfo[] paramInfo = pi.GetIndexParameters();
Type[] paramTypes = new Type[paramInfo.Length];
for(int i = 0; i < paramInfo.Length; i++)
{
paramTypes[i] = paramInfo[i].ParameterType;
}
PropertyBuilder pb = typeBuilder.DefineProperty(pi.Name, PropertyAttributes.None, pi.PropertyType, paramTypes);
if(pi.GetGetMethod() != null)
{
pb.SetGetMethod(methods[MakeMethodKey(pi.GetGetMethod())]);
}
if(pi.GetSetMethod() != null)
{
pb.SetSetMethod(methods[MakeMethodKey(pi.GetSetMethod())]);
}
AttributeHelper.SetEditorBrowsableNever(pb);
}
}
typeBuilder.CreateType();
if(helperTypeBuilder != null)
{
helperTypeBuilder.CreateType();
}
}
private static void CopyLinkDemands(MethodBuilder mb, MethodInfo mi)
{
foreach (CustomAttributeData cad in CustomAttributeData.__GetDeclarativeSecurity(mi))
{
if (cad.ConstructorArguments.Count == 0 || (int)cad.ConstructorArguments[0].Value == (int)SecurityAction.LinkDemand)
{
mb.__AddDeclarativeSecurity(cad.__ToBuilder());
}
}
}
private static string MakeMethodKey(MethodInfo method)
{
StringBuilder sb = new StringBuilder();
sb.Append(method.ReturnType.AssemblyQualifiedName).Append(":").Append(method.Name);
ParameterInfo[] paramInfo = method.GetParameters();
Type[] paramTypes = new Type[paramInfo.Length];
for(int i = 0; i < paramInfo.Length; i++)
{
paramTypes[i] = paramInfo[i].ParameterType;
sb.Append(":").Append(paramInfo[i].ParameterType.AssemblyQualifiedName);
}
return sb.ToString();
}
private void CreateShadowInstanceOf(ICollection<RemapperTypeWrapper> remappedTypes)
{
// FXBUG .NET 1.1 doesn't allow static methods on interfaces
if(typeBuilder.IsInterface)
{
return;
}
MethodAttributes attr = MethodAttributes.SpecialName | MethodAttributes.Public | MethodAttributes.Static;
MethodBuilder mb = typeBuilder.DefineMethod("__<instanceof>", attr, Types.Boolean, new Type[] { Types.Object });
AttributeHelper.HideFromJava(mb);
AttributeHelper.SetEditorBrowsableNever(mb);
CodeEmitter ilgen = CodeEmitter.Create(mb);
ilgen.Emit(OpCodes.Ldarg_0);
ilgen.Emit(OpCodes.Isinst, shadowType);
CodeEmitterLabel retFalse = ilgen.DefineLabel();
ilgen.EmitBrfalse(retFalse);
if(!shadowType.IsSealed)
{
ilgen.Emit(OpCodes.Ldarg_0);
ilgen.Emit(OpCodes.Isinst, typeBuilder);
ilgen.EmitBrtrue(retFalse);
}
if(shadowType == Types.Object)
{
ilgen.Emit(OpCodes.Ldarg_0);
ilgen.Emit(OpCodes.Isinst, Types.Array);
ilgen.EmitBrtrue(retFalse);
}
foreach(RemapperTypeWrapper r in remappedTypes)
{
if(!r.shadowType.IsInterface && r.shadowType.IsSubclassOf(shadowType))
{
ilgen.Emit(OpCodes.Ldarg_0);
ilgen.Emit(OpCodes.Isinst, r.shadowType);
ilgen.EmitBrtrue(retFalse);
}
}
ilgen.Emit(OpCodes.Ldc_I4_1);
ilgen.Emit(OpCodes.Ret);
ilgen.MarkLabel(retFalse);
ilgen.Emit(OpCodes.Ldc_I4_0);
ilgen.Emit(OpCodes.Ret);
ilgen.DoEmit();
}
private void CreateShadowCheckCast(ICollection<RemapperTypeWrapper> remappedTypes)
{
// FXBUG .NET 1.1 doesn't allow static methods on interfaces
if(typeBuilder.IsInterface)
{
return;
}
MethodAttributes attr = MethodAttributes.SpecialName | MethodAttributes.Public | MethodAttributes.Static;
MethodBuilder mb = typeBuilder.DefineMethod("__<checkcast>", attr, shadowType, new Type[] { Types.Object });
AttributeHelper.HideFromJava(mb);
AttributeHelper.SetEditorBrowsableNever(mb);
CodeEmitter ilgen = CodeEmitter.Create(mb);
CodeEmitterLabel fail = ilgen.DefineLabel();
bool hasfail = false;
if(!shadowType.IsSealed)
{
ilgen.Emit(OpCodes.Ldarg_0);
ilgen.Emit(OpCodes.Isinst, typeBuilder);
ilgen.EmitBrtrue(fail);
hasfail = true;
}
if(shadowType == Types.Object)
{
ilgen.Emit(OpCodes.Ldarg_0);
ilgen.Emit(OpCodes.Isinst, Types.Array);
ilgen.EmitBrtrue(fail);
hasfail = true;
}
foreach(RemapperTypeWrapper r in remappedTypes)
{
if(!r.shadowType.IsInterface && r.shadowType.IsSubclassOf(shadowType))
{
ilgen.Emit(OpCodes.Ldarg_0);
ilgen.Emit(OpCodes.Isinst, r.shadowType);
ilgen.EmitBrtrue(fail);
hasfail = true;
}
}
ilgen.Emit(OpCodes.Ldarg_0);
ilgen.EmitCastclass(shadowType);
ilgen.Emit(OpCodes.Ret);
if(hasfail)
{
ilgen.MarkLabel(fail);
ilgen.ThrowException(JVM.Import(typeof(InvalidCastException)));
}
ilgen.DoEmit();
}
internal override MethodBase LinkMethod(MethodWrapper mw)
{
return ((RemappedMethodBaseWrapper)mw).DoLink();
}
internal override TypeWrapper DeclaringTypeWrapper
{
get
{
// at the moment we don't support nested remapped types
return null;
}
}
internal override void Finish()
{
if(BaseTypeWrapper != null)
{
BaseTypeWrapper.Finish();
}
foreach(TypeWrapper iface in Interfaces)
{
iface.Finish();
}
foreach(MethodWrapper m in GetMethods())
{
m.Link();
}
foreach(FieldWrapper f in GetFields())
{
f.Link();
}
}
internal override TypeWrapper[] InnerClasses
{
get
{
return TypeWrapper.EmptyArray;
}
}
internal override TypeWrapper[] Interfaces
{
get
{
return interfaceWrappers;
}
}
internal override Type TypeAsTBD
{
get
{
return shadowType;
}
}
internal override Type TypeAsBaseType
{
get
{
return typeBuilder;
}
}
internal override bool IsMapUnsafeException
{
get
{
// any remapped exceptions are automatically unsafe
return shadowType == Types.Exception || shadowType.IsSubclassOf(Types.Exception);
}
}
internal override bool IsFastClassLiteralSafe
{
get { return true; }
}
}
internal static void AddDeclaredExceptions(MethodBuilder mb, IKVM.Internal.MapXml.Throws[] throws)
{
if (throws != null)
{
string[] exceptions = new string[throws.Length];
for (int i = 0; i < exceptions.Length; i++)
{
exceptions[i] = throws[i].Class;
}
AttributeHelper.SetThrowsAttribute(mb, exceptions);
}
}
internal void EmitRemappedTypes()
{
Tracer.Info(Tracer.Compiler, "Emit remapped types");
assemblyAttributes = map.assembly.Attributes;
if(map.assembly.Classes != null)
{
// 1st pass, put all types in remapped to make them loadable
bool hasRemappedTypes = false;
foreach(IKVM.Internal.MapXml.Class c in map.assembly.Classes)
{
if(c.Shadows != null)
{
if(classes.ContainsKey(c.Name))
{
StaticCompiler.IssueMessage(Message.DuplicateClassName, c.Name);
}
remapped.Add(c.Name, new RemapperTypeWrapper(this, c, map));
hasRemappedTypes = true;
}
}
if(hasRemappedTypes)
{
SetupGhosts(map);
foreach(IKVM.Internal.MapXml.Class c in map.assembly.Classes)
{
if(c.Shadows != null)
{
remapped[c.Name].LoadInterfaces(c);
}
}
}
}
}
internal void EmitRemappedTypes2ndPass()
{
if (map != null && map.assembly != null && map.assembly.Classes != null)
{
// 2nd pass, resolve interfaces, publish methods/fields
foreach(IKVM.Internal.MapXml.Class c in map.assembly.Classes)
{
if(c.Shadows != null)
{
RemapperTypeWrapper typeWrapper = remapped[c.Name];
typeWrapper.Process2ndPassStep1();
}
}
foreach(IKVM.Internal.MapXml.Class c in map.assembly.Classes)
{
if(c.Shadows != null)
{
RemapperTypeWrapper typeWrapper = remapped[c.Name];
typeWrapper.Process2ndPassStep2(map);
}
}
}
}
internal bool IsMapUnsafeException(TypeWrapper tw)
{
if(mappedExceptions != null)
{
for(int i = 0; i < mappedExceptions.Length; i++)
{
if(mappedExceptions[i].IsSubTypeOf(tw) ||
(mappedExceptionsAllSubClasses[i] && tw.IsSubTypeOf(mappedExceptions[i])))
{
return true;
}
}
}
return false;
}
internal void LoadMappedExceptions(IKVM.Internal.MapXml.Root map)
{
if(map.exceptionMappings != null)
{
mappedExceptionsAllSubClasses = new bool[map.exceptionMappings.Length];
mappedExceptions = new TypeWrapper[map.exceptionMappings.Length];
for(int i = 0; i < mappedExceptions.Length; i++)
{
string dst = map.exceptionMappings[i].dst;
if(dst[0] == '*')
{
mappedExceptionsAllSubClasses[i] = true;
dst = dst.Substring(1);
}
mappedExceptions[i] = LoadClassByDottedName(dst);
}
// HACK we need to find the <exceptionMapping /> element and bind it
foreach(IKVM.Internal.MapXml.Class c in map.assembly.Classes)
{
if(c.Methods != null)
{
foreach(IKVM.Internal.MapXml.Method m in c.Methods)
{
if(m.body != null && m.body.invoke != null)
{
foreach(IKVM.Internal.MapXml.Instruction instr in m.body.invoke)
{
IKVM.Internal.MapXml.EmitExceptionMapping eem = instr as IKVM.Internal.MapXml.EmitExceptionMapping;
if(eem != null)
{
eem.mapping = map.exceptionMappings;
}
}
}
}
}
}
}
}
internal sealed class ExceptionMapEmitter
{
private IKVM.Internal.MapXml.ExceptionMapping[] map;
internal ExceptionMapEmitter(IKVM.Internal.MapXml.ExceptionMapping[] map)
{
this.map = map;
}
internal void Emit(IKVM.Internal.MapXml.CodeGenContext context, CodeEmitter ilgen)
{
MethodWrapper mwSuppressFillInStackTrace = CoreClasses.java.lang.Throwable.Wrapper.GetMethodWrapper("__<suppressFillInStackTrace>", "()V", false);
mwSuppressFillInStackTrace.Link();
ilgen.Emit(OpCodes.Ldarg_0);
ilgen.Emit(OpCodes.Callvirt, Compiler.getTypeMethod);
for(int i = 0; i < map.Length; i++)
{
ilgen.Emit(OpCodes.Dup);
ilgen.Emit(OpCodes.Ldtoken, StaticCompiler.Universe.GetType(map[i].src, true));
ilgen.Emit(OpCodes.Call, Compiler.getTypeFromHandleMethod);
ilgen.Emit(OpCodes.Ceq);
CodeEmitterLabel label = ilgen.DefineLabel();
ilgen.EmitBrfalse(label);
ilgen.Emit(OpCodes.Pop);
if(map[i].code != null)
{
ilgen.Emit(OpCodes.Ldarg_0);
if(map[i].code.invoke != null)
{
foreach(MapXml.Instruction instr in map[i].code.invoke)
{
MapXml.NewObj newobj = instr as MapXml.NewObj;
if(newobj != null
&& newobj.Class != null
&& context.ClassLoader.LoadClassByDottedName(newobj.Class).IsSubTypeOf(CoreClasses.java.lang.Throwable.Wrapper))
{
mwSuppressFillInStackTrace.EmitCall(ilgen);
}
instr.Generate(context, ilgen);
}
}
ilgen.Emit(OpCodes.Ret);
}
else
{
TypeWrapper tw = context.ClassLoader.LoadClassByDottedName(map[i].dst);
MethodWrapper mw = tw.GetMethodWrapper("<init>", "()V", false);
mw.Link();
mwSuppressFillInStackTrace.EmitCall(ilgen);
mw.EmitNewobj(ilgen);
ilgen.Emit(OpCodes.Ret);
}
ilgen.MarkLabel(label);
}
ilgen.Emit(OpCodes.Pop);
ilgen.Emit(OpCodes.Ldarg_0);
ilgen.Emit(OpCodes.Ret);
}
}
internal void LoadMapXml()
{
if(map.assembly.Classes != null)
{
mapxml_Classes = new Dictionary<string, IKVM.Internal.MapXml.Class>();
mapxml_MethodBodies = new Dictionary<MethodKey, IKVM.Internal.MapXml.InstructionList>();
mapxml_ReplacedMethods = new Dictionary<MethodKey, IKVM.Internal.MapXml.ReplaceMethodCall[]>();
mapxml_MethodPrologues = new Dictionary<MethodKey, IKVM.Internal.MapXml.InstructionList>();
foreach(IKVM.Internal.MapXml.Class c in map.assembly.Classes)
{
// if it is not a remapped type, it must be a container for native, patched or augmented methods
if(c.Shadows == null)
{
string className = c.Name;
mapxml_Classes.Add(className, c);
AddMapXmlMethods(className, c.Constructors);
AddMapXmlMethods(className, c.Methods);
if (c.Clinit != null)
{
AddMapXmlMethod(className, c.Clinit);
}
}
}
}
}
private void AddMapXmlMethods(string className, IKVM.Internal.MapXml.MethodBase[] methods)
{
if(methods != null)
{
foreach(IKVM.Internal.MapXml.MethodBase method in methods)
{
AddMapXmlMethod(className, method);
}
}
}
private void AddMapXmlMethod(string className, IKVM.Internal.MapXml.MethodBase method)
{
if(method.body != null)
{
mapxml_MethodBodies.Add(method.ToMethodKey(className), method.body);
}
if(method.ReplaceMethodCalls != null)
{
mapxml_ReplacedMethods.Add(method.ToMethodKey(className), method.ReplaceMethodCalls);
}
if (method.prologue != null)
{
mapxml_MethodPrologues.Add(method.ToMethodKey(className), method.prologue);
}
}
internal IKVM.Internal.MapXml.InstructionList GetMethodPrologue(MethodKey method)
{
if(mapxml_MethodPrologues == null)
{
return null;
}
IKVM.Internal.MapXml.InstructionList prologue;
mapxml_MethodPrologues.TryGetValue(method, out prologue);
return prologue;
}
internal IKVM.Internal.MapXml.ReplaceMethodCall[] GetReplacedMethodsFor(MethodWrapper mw)
{
if(mapxml_ReplacedMethods == null)
{
return null;
}
IKVM.Internal.MapXml.ReplaceMethodCall[] rmc;
mapxml_ReplacedMethods.TryGetValue(new MethodKey(mw.DeclaringType.Name, mw.Name, mw.Signature), out rmc);
return rmc;
}
internal Dictionary<string, IKVM.Internal.MapXml.Class> GetMapXmlClasses()
{
return mapxml_Classes;
}
internal Dictionary<MethodKey, IKVM.Internal.MapXml.InstructionList> GetMapXmlMethodBodies()
{
return mapxml_MethodBodies;
}
internal IKVM.Internal.MapXml.Param[] GetXmlMapParameters(string classname, string method, string sig)
{
if(mapxml_Classes != null)
{
IKVM.Internal.MapXml.Class clazz;
if(mapxml_Classes.TryGetValue(classname, out clazz))
{
if(method == "<init>" && clazz.Constructors != null)
{
for(int i = 0; i < clazz.Constructors.Length; i++)
{
if(clazz.Constructors[i].Sig == sig)
{
return clazz.Constructors[i].Params;
}
}
}
else if(clazz.Methods != null)
{
for(int i = 0; i < clazz.Methods.Length; i++)
{
if(clazz.Methods[i].Name == method && clazz.Methods[i].Sig == sig)
{
return clazz.Methods[i].Params;
}
}
}
}
}
return null;
}
internal bool IsGhost(TypeWrapper tw)
{
return ghosts != null && tw.IsInterface && ghosts.ContainsKey(tw.Name);
}
private void SetupGhosts(IKVM.Internal.MapXml.Root map)
{
ghosts = new Dictionary<string, List<TypeWrapper>>();
// find the ghost interfaces
foreach(IKVM.Internal.MapXml.Class c in map.assembly.Classes)
{
if(c.Shadows != null && c.Interfaces != null)
{
// NOTE we don't support interfaces that inherit from other interfaces
// (actually, if they are explicitly listed it would probably work)
TypeWrapper typeWrapper = FindLoadedClass(c.Name);
foreach(IKVM.Internal.MapXml.Interface iface in c.Interfaces)
{
TypeWrapper ifaceWrapper = FindLoadedClass(iface.Name);
if(ifaceWrapper == null || !ifaceWrapper.TypeAsTBD.IsAssignableFrom(typeWrapper.TypeAsTBD))
{
AddGhost(iface.Name, typeWrapper);
}
}
}
}
// we manually add the array ghost interfaces
TypeWrapper array = ClassLoaderWrapper.GetWrapperFromType(Types.Array);
AddGhost("java.io.Serializable", array);
AddGhost("java.lang.Cloneable", array);
}
private void AddGhost(string interfaceName, TypeWrapper implementer)
{
List<TypeWrapper> list;
if(!ghosts.TryGetValue(interfaceName, out list))
{
list = new List<TypeWrapper>();
ghosts[interfaceName] = list;
}
list.Add(implementer);
}
internal TypeWrapper[] GetGhostImplementers(TypeWrapper wrapper)
{
List<TypeWrapper> list;
if (!ghosts.TryGetValue(wrapper.Name, out list))
{
return TypeWrapper.EmptyArray;
}
return list.ToArray();
}
internal void FinishRemappedTypes()
{
// 3rd pass, link the methods. Note that a side effect of the linking is the
// twiddling with the overriders array in the base methods, so we need to do this
// as a separate pass before we compile the methods
foreach(RemapperTypeWrapper typeWrapper in remapped.Values)
{
typeWrapper.Process3rdPass();
}
// 4th pass, implement methods/fields and bake the type
foreach(RemapperTypeWrapper typeWrapper in remapped.Values)
{
typeWrapper.Process4thPass(remapped.Values);
}
if(assemblyAttributes != null)
{
foreach(IKVM.Internal.MapXml.Attribute attr in assemblyAttributes)
{
AttributeHelper.SetCustomAttribute(this, assemblyBuilder, attr);
}
}
}
private static bool IsSigned(Assembly asm)
{
byte[] key = asm.GetName().GetPublicKey();
return key != null && key.Length != 0;
}
internal static bool IsCoreAssembly(Assembly asm)
{
return asm.IsDefined(StaticCompiler.GetRuntimeType("IKVM.Attributes.RemappedClassAttribute"), false);
}
private bool CheckCompilingCoreAssembly()
{
if (map != null && map.assembly != null && map.assembly.Classes != null)
{
foreach (IKVM.Internal.MapXml.Class c in map.assembly.Classes)
{
if (c.Shadows != null && c.Name == "java.lang.Object")
{
return true;
}
}
}
return false;
}
internal static int Compile(string runtimeAssembly, List<CompilerOptions> optionsList)
{
try
{
if(runtimeAssembly == null)
{
// we assume that the runtime is in the same directory as the compiler
runtimeAssembly = Path.Combine(typeof(CompilerClassLoader).Assembly.Location, ".." + Path.DirectorySeparatorChar + "IKVM.Runtime.dll");
}
StaticCompiler.runtimeAssembly = StaticCompiler.LoadFile(runtimeAssembly);
StaticCompiler.runtimeJniAssembly = StaticCompiler.LoadFile(Path.Combine(StaticCompiler.runtimeAssembly.Location, ".." + Path.DirectorySeparatorChar + "IKVM.Runtime.JNI.dll"));
}
catch(FileNotFoundException)
{
if(StaticCompiler.runtimeAssembly == null)
{
throw new FatalCompilerErrorException(Message.RuntimeNotFound);
}
StaticCompiler.IssueMessage(Message.NoJniRuntime);
}
Tracer.Info(Tracer.Compiler, "Loaded runtime assembly: {0}", StaticCompiler.runtimeAssembly.FullName);
bool compilingCoreAssembly = false;
List<CompilerClassLoader> compilers = new List<CompilerClassLoader>();
foreach (CompilerOptions options in optionsList)
{
CompilerClassLoader compiler = null;
int rc = CreateCompiler(options, ref compiler, ref compilingCoreAssembly);
if(rc != 0)
{
return rc;
}
compilers.Add(compiler);
if(options.sharedclassloader != null)
{
options.sharedclassloader.Add(compiler);
}
}
foreach (CompilerClassLoader compiler1 in compilers)
{
foreach (CompilerClassLoader compiler2 in compilers)
{
if (compiler1 != compiler2
&& (compiler1.options.crossReferenceAllPeers || (compiler1.options.peerReferences != null && Array.IndexOf(compiler1.options.peerReferences, compiler2.options.assembly) != -1)))
{
compiler1.AddReference(compiler2);
}
}
}
foreach (CompilerClassLoader compiler in compilers)
{
compiler.CompilePass0();
}
Dictionary<CompilerClassLoader, Type> mainAssemblyTypes = new Dictionary<CompilerClassLoader, Type>();
foreach (CompilerClassLoader compiler in compilers)
{
if (compiler.options.sharedclassloader != null)
{
Type mainAssemblyType;
if (!mainAssemblyTypes.TryGetValue(compiler.options.sharedclassloader[0], out mainAssemblyType))
{
TypeBuilder tb = compiler.options.sharedclassloader[0].GetTypeWrapperFactory().ModuleBuilder.DefineType("__<MainAssembly>", TypeAttributes.NotPublic | TypeAttributes.Abstract | TypeAttributes.SpecialName);
AttributeHelper.HideFromJava(tb);
mainAssemblyType = tb.CreateType();
mainAssemblyTypes.Add(compiler.options.sharedclassloader[0], mainAssemblyType);
}
if (compiler.options.sharedclassloader[0] != compiler)
{
((AssemblyBuilder)compiler.GetTypeWrapperFactory().ModuleBuilder.Assembly).__AddTypeForwarder(mainAssemblyType);
}
}
compiler.CompilePass1();
}
foreach (CompilerClassLoader compiler in compilers)
{
compiler.CompilePass2();
}
if (compilingCoreAssembly)
{
RuntimeHelperTypes.Create(compilers[0]);
foreach (CompilerClassLoader compiler in compilers)
{
compiler.EmitRemappedTypes2ndPass();
}
}
foreach (CompilerClassLoader compiler in compilers)
{
int rc = compiler.CompilePass3();
if (rc != 0)
{
return rc;
}
}
Tracer.Info(Tracer.Compiler, "CompilerClassLoader.Save...");
foreach (CompilerClassLoader compiler in compilers)
{
compiler.PrepareSave();
}
if (StaticCompiler.errorCount > 0)
{
return 1;
}
foreach (CompilerClassLoader compiler in compilers)
{
compiler.Save();
}
return StaticCompiler.errorCount == 0 ? 0 : 1;
}
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);
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;
}
private static Assembly LoadReferencedAssembly(string r)
{
Assembly asm = StaticCompiler.LoadFile(r);
return asm;
}
private void CompilePass0()
{
if(options.sharedclassloader != null && options.sharedclassloader[0] != this)
{
packages = options.sharedclassloader[0].packages;
}
else
{
packages = new Packages();
}
}
private void CompilePass1()
{
Tracer.Info(Tracer.Compiler, "Compiling class files (1)");
if(CheckCompilingCoreAssembly())
{
EmitRemappedTypes();
}
// if we're compiling the core class library, generate the "fake" generic types
// that represent the not-really existing types (i.e. the Java enums that represent .NET enums,
// the Method interface for delegates and the Annotation annotation for custom attributes)
if(map != null && CheckCompilingCoreAssembly())
{
FakeTypes.Create(GetTypeWrapperFactory().ModuleBuilder, this);
}
allwrappers = new List<TypeWrapper>();
foreach(string s in classesToCompile)
{
TypeWrapper wrapper = LoadClassByDottedNameFast(s);
if(wrapper != null)
{
ClassLoaderWrapper loader = wrapper.GetClassLoader();
if(loader != this)
{
if(loader is AssemblyClassLoader)
{
StaticCompiler.IssueMessage(options, Message.SkippingReferencedClass, s, ((AssemblyClassLoader)loader).GetAssembly(wrapper).FullName);
}
continue;
}
if(options.sharedclassloader != null && options.sharedclassloader[0] != this)
{
options.sharedclassloader[0].dynamicallyImportedTypes.Add(wrapper);
}
allwrappers.Add(wrapper);
}
}
}
private void CompilePass2()
{
Tracer.Info(Tracer.Compiler, "Compiling class files (2)");
foreach(TypeWrapper tw in allwrappers)
{
DynamicTypeWrapper dtw = tw as DynamicTypeWrapper;
if(dtw != null)
{
dtw.CreateStep2();
}
}
}
private int CompilePass3()
{
Tracer.Info(Tracer.Compiler, "Compiling class files (3)");
if(map != null && CheckCompilingCoreAssembly())
{
FakeTypes.Finish(this);
}
foreach(string proxy in options.proxies)
{
ProxyGenerator.Create(this, proxy);
}
if(options.mainClass != null)
{
TypeWrapper wrapper = null;
try
{
wrapper = LoadClassByDottedNameFast(options.mainClass);
}
catch(RetargetableJavaException)
{
}
if(wrapper == null)
{
throw new FatalCompilerErrorException(Message.MainClassNotFound);
}
MethodWrapper mw = wrapper.GetMethodWrapper("main", "([Ljava.lang.String;)V", false);
if(mw == null || !mw.IsStatic)
{
throw new FatalCompilerErrorException(Message.MainMethodNotFound);
}
mw.Link();
MethodInfo method = mw.GetMethod() as MethodInfo;
if(method == null)
{
throw new FatalCompilerErrorException(Message.UnsupportedMainMethod);
}
if(!ReflectUtil.IsFromAssembly(method.DeclaringType, assemblyBuilder)
&& (!method.IsPublic || !method.DeclaringType.IsPublic))
{
throw new FatalCompilerErrorException(Message.ExternalMainNotAccessible);
}
Type apartmentAttributeType = null;
if(options.apartment == ApartmentState.STA)
{
apartmentAttributeType = JVM.Import(typeof(STAThreadAttribute));
}
else if(options.apartment == ApartmentState.MTA)
{
apartmentAttributeType = JVM.Import(typeof(MTAThreadAttribute));
}
SetMain(method, options.target, options.props, options.noglobbing, apartmentAttributeType);
}
if(map != null)
{
LoadMappedExceptions(map);
Tracer.Info(Tracer.Compiler, "Loading remapped types (2)");
try
{
FinishRemappedTypes();
}
catch (IKVM.Reflection.MissingMemberException x)
{
StaticCompiler.IssueMissingTypeMessage((Type)x.MemberInfo);
return 1;
}
}
Tracer.Info(Tracer.Compiler, "Compiling class files (2)");
WriteResources();
if(options.externalResources != null)
{
foreach(KeyValuePair<string, string> kv in options.externalResources)
{
assemblyBuilder.AddResourceFile(JVM.MangleResourceName(kv.Key), kv.Value);
}
}
if(options.fileversion != null)
{
CustomAttributeBuilder filever = new CustomAttributeBuilder(JVM.Import(typeof(System.Reflection.AssemblyFileVersionAttribute)).GetConstructor(new Type[] { Types.String }), new object[] { options.fileversion });
assemblyBuilder.SetCustomAttribute(filever);
}
if(options.assemblyAttributeAnnotations != null)
{
foreach(object[] def in options.assemblyAttributeAnnotations)
{
Annotation annotation = Annotation.LoadAssemblyCustomAttribute(this, def);
if(annotation != null)
{
annotation.Apply(this, assemblyBuilder, def);
}
}
}
if(options.classLoader != null)
{
TypeWrapper wrapper = null;
try
{
wrapper = LoadClassByDottedNameFast(options.classLoader);
}
catch(RetargetableJavaException)
{
}
if(wrapper == null)
{
throw new FatalCompilerErrorException(Message.ClassLoaderNotFound);
}
if(!wrapper.IsPublic && !ReflectUtil.IsFromAssembly(wrapper.TypeAsBaseType, assemblyBuilder))
{
throw new FatalCompilerErrorException(Message.ClassLoaderNotAccessible);
}
if(wrapper.IsAbstract)
{
throw new FatalCompilerErrorException(Message.ClassLoaderIsAbstract);
}
if(!wrapper.IsAssignableTo(ClassLoaderWrapper.LoadClassCritical("java.lang.ClassLoader")))
{
throw new FatalCompilerErrorException(Message.ClassLoaderNotClassLoader);
}
MethodWrapper mw = wrapper.GetMethodWrapper("<init>", "(Lcli.System.Reflection.Assembly;)V", false);
if(mw == null)
{
throw new FatalCompilerErrorException(Message.ClassLoaderConstructorMissing);
}
ConstructorInfo ci = JVM.LoadType(typeof(CustomAssemblyClassLoaderAttribute)).GetConstructor(BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic, null, new Type[] { Types.Type }, null);
assemblyBuilder.SetCustomAttribute(new CustomAttributeBuilder(ci, new object[] { wrapper.TypeAsTBD }));
// TODO it would be better to do this for all assemblies in a shared class loader group (because options.classloader is relevant only for the main assembly),
// but since it is probably common to specify the custom assembly class loader at the group level, it hopefully won't make much difference in practice.
MethodWrapper mwModuleInit = wrapper.GetMethodWrapper("InitializeModule", "(Lcli.System.Reflection.Module;)V", false);
if(mwModuleInit != null && !mwModuleInit.IsStatic)
{
MethodBuilder moduleInitializer = GetTypeWrapperFactory().ModuleBuilder.DefineGlobalMethod(".cctor", MethodAttributes.Private | MethodAttributes.Static | MethodAttributes.SpecialName | MethodAttributes.RTSpecialName, null, Type.EmptyTypes);
ILGenerator ilgen = moduleInitializer.GetILGenerator();
ilgen.Emit(OpCodes.Ldtoken, moduleInitializer);
ilgen.Emit(OpCodes.Call, JVM.Import(typeof(System.Reflection.MethodBase)).GetMethod("GetMethodFromHandle", new Type[] { JVM.Import(typeof(RuntimeMethodHandle)) }));
ilgen.Emit(OpCodes.Callvirt, JVM.Import(typeof(System.Reflection.MemberInfo)).GetMethod("get_Module"));
ilgen.Emit(OpCodes.Call, StaticCompiler.GetRuntimeType("IKVM.Runtime.ByteCodeHelper").GetMethod("InitializeModule"));
ilgen.Emit(OpCodes.Ret);
}
}
if (options.iconfile != null)
{
assemblyBuilder.__DefineIconResource(IkvmcCompiler.ReadAllBytes(options.iconfile));
}
if (options.manifestFile != null)
{
assemblyBuilder.__DefineManifestResource(IkvmcCompiler.ReadAllBytes(options.manifestFile));
}
assemblyBuilder.DefineVersionInfoResource();
return 0;
}
private static void ser_UnknownElement(object sender, System.Xml.Serialization.XmlElementEventArgs e)
{
StaticCompiler.IssueMessage(Message.UnknownElementInMapFile, e.Element.Name, e.LineNumber.ToString(), e.LinePosition.ToString());
}
private static void ser_UnknownAttribute(object sender, System.Xml.Serialization.XmlAttributeEventArgs e)
{
StaticCompiler.IssueMessage(Message.UnknownAttributeInMapFile, e.Attr.Name, e.LineNumber.ToString(), e.LinePosition.ToString());
}
private bool ValidateAndSetMap(IKVM.Internal.MapXml.Root map)
{
bool valid = true;
if (map.assembly != null)
{
if (map.assembly.Classes != null)
{
foreach (IKVM.Internal.MapXml.Class c in map.assembly.Classes)
{
if (c.Fields != null)
{
foreach (IKVM.Internal.MapXml.Field f in c.Fields)
{
ValidateNameSig("field", c.Name, f.Name, f.Sig, ref valid, true);
}
}
if (c.Methods != null)
{
foreach (IKVM.Internal.MapXml.Method m in c.Methods)
{
ValidateNameSig("method", c.Name, m.Name, m.Sig, ref valid, false);
}
}
if (c.Constructors != null)
{
foreach (IKVM.Internal.MapXml.Constructor ctor in c.Constructors)
{
ValidateNameSig("constructor", c.Name, "<init>", ctor.Sig, ref valid, false);
}
}
if (c.Properties != null)
{
foreach (IKVM.Internal.MapXml.Property prop in c.Properties)
{
ValidateNameSig("property", c.Name, prop.Name, prop.Sig, ref valid, false);
ValidatePropertyGetterSetter("getter", c.Name, prop.Name, prop.getter, ref valid);
ValidatePropertyGetterSetter("setter", c.Name, prop.Name, prop.setter, ref valid);
}
}
}
}
}
this.map = map;
return valid;
}
private static void ValidateNameSig(string member, string clazz, string name, string sig, ref bool valid, bool field)
{
if (!IsValidName(name))
{
valid = false;
StaticCompiler.IssueMessage(Message.InvalidMemberNameInMapFile, member, name, clazz);
}
if (!IsValidSig(sig, field))
{
valid = false;
StaticCompiler.IssueMessage(Message.InvalidMemberSignatureInMapFile, member, clazz, name, sig);
}
}
private static void ValidatePropertyGetterSetter(string getterOrSetter, string clazz, string property, IKVM.Internal.MapXml.Method method, ref bool valid)
{
if (method != null)
{
if (!IsValidName(method.Name))
{
valid = false;
StaticCompiler.IssueMessage(Message.InvalidPropertyNameInMapFile, getterOrSetter, clazz, property, method.Name);
}
if (!ClassFile.IsValidMethodSig(method.Sig))
{
valid = false;
StaticCompiler.IssueMessage(Message.InvalidPropertySignatureInMapFile, getterOrSetter, clazz, property, method.Sig);
}
}
}
private static bool IsValidName(string name)
{
return name != null && name.Length != 0;
}
private static bool IsValidSig(string sig, bool field)
{
return sig != null && (field ? ClassFile.IsValidFieldSig(sig) : ClassFile.IsValidMethodSig(sig));
}
internal Type GetTypeFromReferencedAssembly(string name)
{
foreach (AssemblyClassLoader acl in referencedAssemblies)
{
Type type = acl.MainAssembly.GetType(name, false);
if (type != null)
{
return type;
}
}
return null;
}
internal override void IssueMessage(Message msgId, params string[] values)
{
StaticCompiler.IssueMessage(options, msgId, values);
}
internal bool TryEnableUnmanagedExports()
{
// we only support -platform:x86 and -platform:x64
// (currently IKVM.Reflection doesn't support unmanaged exports for ARM)
if ((options.imageFileMachine == ImageFileMachine.I386 && (options.pekind & PortableExecutableKinds.Required32Bit) != 0)
|| options.imageFileMachine == ImageFileMachine.AMD64)
{
// when you add unmanaged exports, the ILOnly flag MUST NOT be set or the DLL will fail to load
options.pekind &= ~PortableExecutableKinds.ILOnly;
return true;
}
else
{
StaticCompiler.IssueMessage(options, Message.DllExportRequiresSupportedPlatform);
return false;
}
}
internal override bool WarningLevelHigh
{
get { return options.warningLevelHigh; }
}
internal override bool NoParameterReflection
{
get { return options.noParameterReflection; }
}
}
sealed class Jar
{
internal readonly string Name;
internal readonly string Comment;
private readonly List<JarItem> Items = new List<JarItem>();
internal Jar(string name, string comment)
{
this.Name = name;
this.Comment = comment;
}
internal Jar Copy()
{
Jar newJar = new Jar(Name, Comment);
newJar.Items.AddRange(Items);
return newJar;
}
internal void Add(ZipEntry ze, byte[] data)
{
Items.Add(new JarItem(ze, data, null));
}
internal void Add(string name, byte[] data, FileInfo fileInfo)
{
ZipEntry zipEntry = new ZipEntry(name);
zipEntry.CompressionMethod = CompressionMethod.Stored;
Items.Add(new JarItem(zipEntry, data, fileInfo));
}
private struct JarItem
{
internal readonly ZipEntry zipEntry;
internal readonly byte[] data;
internal readonly FileInfo path; // path of the original file, if it was individual file (used to construct source file path)
internal JarItem(ZipEntry zipEntry, byte[] data, FileInfo path)
{
this.zipEntry = zipEntry;
this.data = data;
this.path = path;
}
}
public struct Item
{
internal readonly Jar Jar;
private readonly int Index;
internal Item(Jar jar, int index)
{
this.Jar = jar;
this.Index = index;
}
internal string Name
{
get { return Jar.Items[Index].zipEntry.Name; }
}
internal byte[] GetData()
{
return Jar.Items[Index].data;
}
internal FileInfo Path
{
get { return Jar.Items[Index].path; }
}
internal ZipEntry ZipEntry
{
get
{
ZipEntry org = Jar.Items[Index].zipEntry;
ZipEntry zipEntry = new ZipEntry(org.Name);
zipEntry.Comment = org.Comment;
zipEntry.CompressionMethod = org.CompressionMethod;
zipEntry.DosTime = org.DosTime;
zipEntry.ExternalFileAttributes = org.ExternalFileAttributes;
zipEntry.ExtraData = org.ExtraData;
zipEntry.Flags = org.Flags;
return zipEntry;
}
}
internal void Remove()
{
Jar.Items[Index] = new JarItem();
}
internal void MarkAsStub()
{
Jar.Items[Index] = new JarItem(Jar.Items[Index].zipEntry, null, null);
}
internal bool IsStub
{
get { return Jar.Items[Index].data == null; }
}
}
internal struct JarEnumerator
{
private readonly Jar jar;
private int index;
internal JarEnumerator(Jar jar)
{
this.jar = jar;
this.index = -1;
}
public Item Current
{
get { return new Item(jar, index); }
}
public bool MoveNext()
{
while (index + 1 < jar.Items.Count)
{
if (jar.Items[++index].zipEntry != null)
{
return true;
}
}
return false;
}
}
public JarEnumerator GetEnumerator()
{
return new JarEnumerator(this);
}
}
sealed class CompilerOptions
{
internal List<Jar> jars = new List<Jar>();
private Dictionary<string, int> jarMap = new Dictionary<string, int>();
internal int classesJar = -1;
internal int resourcesJar = -1;
internal bool nojarstubs;
internal FileInfo path;
internal FileInfo keyfile;
internal string keycontainer;
internal bool delaysign;
internal byte[] publicKey;
internal StrongNameKeyPair keyPair;
internal Version version;
internal string fileversion;
internal FileInfo iconfile;
internal FileInfo manifestFile;
internal bool targetIsModule;
internal string assembly;
internal string mainClass;
internal ApartmentState apartment;
internal PEFileKinds target;
internal bool guessFileKind;
internal string[] unresolvedReferences; // only used during command line parsing
internal Dictionary<string, string> legacyStubReferences = new Dictionary<string,string>(); // only used during command line parsing
internal Assembly[] references;
internal string[] peerReferences;
internal bool crossReferenceAllPeers = true;
internal string[] classesToExclude; // only used during command line parsing
internal FileInfo remapfile;
internal Dictionary<string, string> props;
internal bool noglobbing;
internal CodeGenOptions codegenoptions;
internal bool removeUnusedFields;
internal bool compressedResources;
internal string[] privatePackages;
internal string[] publicPackages;
internal string sourcepath;
internal Dictionary<string, string> externalResources;
internal string classLoader;
internal PortableExecutableKinds pekind = PortableExecutableKinds.ILOnly;
internal ImageFileMachine imageFileMachine = ImageFileMachine.I386;
internal long baseAddress;
internal int fileAlignment;
internal bool highentropyva;
internal List<CompilerClassLoader> sharedclassloader; // should *not* be deep copied in Copy(), because we want the list of all compilers that share a class loader
internal Dictionary<string, string> suppressWarnings = new Dictionary<string, string>();
internal Dictionary<string, string> errorWarnings = new Dictionary<string, string>(); // treat specific warnings as errors
internal bool warnaserror; // treat all warnings as errors
internal FileInfo writeSuppressWarningsFile;
internal List<string> proxies = new List<string>();
internal object[] assemblyAttributeAnnotations;
internal bool warningLevelHigh;
internal bool noParameterReflection;
internal CompilerOptions Copy()
{
CompilerOptions copy = (CompilerOptions)MemberwiseClone();
copy.jars = Copy(jars);
copy.jarMap = new Dictionary<string, int>(jarMap);
if (props != null)
{
copy.props = new Dictionary<string, string>(props);
}
if (externalResources != null)
{
copy.externalResources = new Dictionary<string, string>(externalResources);
}
copy.suppressWarnings = new Dictionary<string, string>(suppressWarnings);
copy.errorWarnings = new Dictionary<string, string>(errorWarnings);
return copy;
}
private static List<Jar> Copy(List<Jar> jars)
{
List<Jar> newJars = new List<Jar>();
foreach (Jar jar in jars)
{
newJars.Add(jar.Copy());
}
return newJars;
}
internal Jar GetJar(ZipFile zf)
{
int existingJar;
if (jarMap.TryGetValue(zf.Name, out existingJar))
{
return jars[existingJar];
}
jarMap.Add(zf.Name, jars.Count);
return CreateJar(Path.GetFileName(zf.Name), zf.ZipFileComment);
}
private Jar CreateJar(string jarName, string comment)
{
int count = 0;
string name = jarName;
retry:
foreach (Jar jar in jars)
{
if (jar.Name == name)
{
name = Path.GetFileNameWithoutExtension(jarName) + "-" + (++count) + Path.GetExtension(jarName);
goto retry;
}
}
Jar newJar = new Jar(name, comment);
jars.Add(newJar);
return newJar;
}
internal Jar GetClassesJar()
{
if (classesJar == -1)
{
classesJar = jars.Count;
CreateJar("classes.jar", null);
}
return jars[classesJar];
}
internal bool IsClassesJar(Jar jar)
{
return classesJar != -1 && jars[classesJar] == jar;
}
internal Jar GetResourcesJar()
{
if (resourcesJar == -1)
{
resourcesJar = jars.Count;
CreateJar("resources.jar", null);
}
return jars[resourcesJar];
}
internal bool IsResourcesJar(Jar jar)
{
return resourcesJar != -1 && jars[resourcesJar] == jar;
}
internal bool IsExcludedClass(string className)
{
if (classesToExclude != null)
{
for (int i = 0; i < classesToExclude.Length; i++)
{
if (Regex.IsMatch(className, classesToExclude[i]))
{
return true;
}
}
}
return false;
}
}
enum Message
{
// These are the informational messages
MainMethodFound = 1,
OutputFileIs = 2,
AutoAddRef = 3,
MainMethodFromManifest = 4,
// This is were the warnings start
StartWarnings = 100,
ClassNotFound = 100,
ClassFormatError = 101,
DuplicateClassName = 102,
IllegalAccessError = 103,
VerificationError = 104,
NoClassDefFoundError = 105,
GenericUnableToCompileError = 106,
DuplicateResourceName = 107,
SkippingReferencedClass = 109,
NoJniRuntime= 110,
EmittedNoClassDefFoundError = 111,
EmittedIllegalAccessError = 112,
EmittedInstantiationError = 113,
EmittedIncompatibleClassChangeError = 114,
EmittedNoSuchFieldError = 115,
EmittedAbstractMethodError = 116,
EmittedNoSuchMethodError = 117,
EmittedLinkageError = 118,
EmittedVerificationError = 119,
EmittedClassFormatError = 120,
InvalidCustomAttribute = 121,
IgnoredCustomAttribute = 122,
AssumeAssemblyVersionMatch = 123,
InvalidDirectoryInLibOptionPath = 124,
InvalidDirectoryInLibEnvironmentPath = 125,
LegacySearchRule = 126,
AssemblyLocationIgnored = 127,
InterfaceMethodCantBeInternal = 128,
DllExportMustBeStaticMethod = 129,
DllExportRequiresSupportedPlatform = 130,
DuplicateAssemblyReference = 132,
UnableToResolveType = 133,
StubsAreDeprecated = 134,
WrongClassName = 135,
ReflectionCallerClassRequiresCallerID = 136,
LegacyAssemblyAttributesFound = 137,
UnknownWarning = 999,
// This is where the errors start
StartErrors = 4000,
UnableToCreateProxy = 4001,
DuplicateProxy = 4002,
MapXmlUnableToResolveOpCode = 4003,
MapXmlError = 4004,
InputFileNotFound = 4005,
UnknownFileType = 4006,
UnknownElementInMapFile = 4007,
UnknownAttributeInMapFile = 4008,
InvalidMemberNameInMapFile = 4009,
InvalidMemberSignatureInMapFile = 4010,
InvalidPropertyNameInMapFile = 4011,
InvalidPropertySignatureInMapFile = 4012,
NonPrimaryAssemblyReference = 4013,
MissingType = 4014,
MissingReference = 4015,
CallerSensitiveOnUnsupportedMethod = 4016,
// Fatal errors
ResponseFileDepthExceeded = 5000,
ErrorReadingFile = 5001,
NoTargetsFound = 5002,
FileFormatLimitationExceeded = 5003,
CannotSpecifyBothKeyFileAndContainer = 5004,
DelaySignRequiresKey = 5005,
InvalidStrongNameKeyPair = 5006,
ReferenceNotFound = 5007,
OptionsMustPreceedChildLevels = 5008,
UnrecognizedTargetType = 5009,
UnrecognizedPlatform = 5010,
UnrecognizedApartment = 5011,
MissingFileSpecification = 5012,
PathTooLong = 5013,
PathNotFound = 5014,
InvalidPath = 5015,
InvalidOptionSyntax = 5016,
ExternalResourceNotFound = 5017,
ExternalResourceNameInvalid = 5018,
InvalidVersionFormat = 5019,
InvalidFileAlignment = 5020,
ErrorWritingFile = 5021,
UnrecognizedOption = 5022,
NoOutputFileSpecified = 5023,
SharedClassLoaderCannotBeUsedOnModuleTarget = 5024,
RuntimeNotFound = 5025,
MainClassRequiresExe = 5026,
ExeRequiresMainClass = 5027,
PropertiesRequireExe = 5028,
ModuleCannotHaveClassLoader = 5029,
ErrorParsingMapFile = 5030,
BootstrapClassesMissing = 5031,
StrongNameRequiresStrongNamedRefs = 5032,
MainClassNotFound = 5033,
MainMethodNotFound = 5034,
UnsupportedMainMethod = 5035,
ExternalMainNotAccessible = 5036,
ClassLoaderNotFound = 5037,
ClassLoaderNotAccessible = 5038,
ClassLoaderIsAbstract = 5039,
ClassLoaderNotClassLoader = 5040,
ClassLoaderConstructorMissing = 5041,
MapFileTypeNotFound = 5042,
MapFileClassNotFound = 5043,
MaximumErrorCountReached = 5044,
LinkageError = 5045,
RuntimeMismatch = 5046,
RuntimeMismatchStrongName = 5047,
CoreClassesMissing = 5048,
CriticalClassNotFound = 5049,
AssemblyContainsDuplicateClassNames = 5050,
CallerIDRequiresHasCallerIDAnnotation = 5051,
UnableToResolveInterface = 5052,
MissingBaseType = 5053,
MissingBaseTypeReference = 5054,
FileNotFound = 5055,
RuntimeMethodMissing = 5056,
MapFileFieldNotFound = 5057,
GhostInterfaceMethodMissing = 5058,
}
static class StaticCompiler
{
internal static readonly Universe Universe = new Universe(UniverseOptions.ResolveMissingMembers | UniverseOptions.EnableFunctionPointers);
internal static Assembly runtimeAssembly;
internal static Assembly runtimeJniAssembly;
internal static CompilerOptions toplevel;
internal static int errorCount;
static StaticCompiler()
{
Universe.ResolvedMissingMember += ResolvedMissingMember;
}
private static void ResolvedMissingMember(Module requestingModule, MemberInfo member)
{
if (requestingModule != null && member is Type)
{
IssueMessage(Message.UnableToResolveType, requestingModule.Name, ((Type)member).FullName, member.Module.FullyQualifiedName);
}
}
internal static Assembly Load(string assemblyString)
{
Assembly asm = Universe.Load(assemblyString);
if (asm.__IsMissing)
{
throw new FileNotFoundException(assemblyString);
}
return asm;
}
internal static Assembly LoadFile(string path)
{
return Universe.LoadFile(path);
}
internal static Type GetRuntimeType(string name)
{
Type type = runtimeAssembly.GetType(name);
if (type != null)
{
return type;
}
if (runtimeJniAssembly != null)
{
return runtimeJniAssembly.GetType(name, true);
}
else
{
throw new TypeLoadException(name);
}
}
internal static Type GetTypeForMapXml(ClassLoaderWrapper loader, string name)
{
Type type = GetType(loader, name);
if (type == null)
{
throw new FatalCompilerErrorException(Message.MapFileTypeNotFound, name);
}
return type;
}
internal static TypeWrapper GetClassForMapXml(ClassLoaderWrapper loader, string name)
{
TypeWrapper tw = loader.LoadClassByDottedNameFast(name);
if (tw == null)
{
throw new FatalCompilerErrorException(Message.MapFileClassNotFound, name);
}
return tw;
}
internal static FieldWrapper GetFieldForMapXml(ClassLoaderWrapper loader, string clazz, string name, string sig)
{
FieldWrapper fw = GetClassForMapXml(loader, clazz).GetFieldWrapper(name, sig);
if (fw == null)
{
throw new FatalCompilerErrorException(Message.MapFileFieldNotFound, name, clazz);
}
fw.Link();
return fw;
}
internal static Type GetType(ClassLoaderWrapper loader, string name)
{
CompilerClassLoader ccl = (CompilerClassLoader)loader;
return ccl.GetTypeFromReferencedAssembly(name);
}
internal static void IssueMessage(Message msgId, params string[] values)
{
IssueMessage(toplevel, msgId, values);
}
internal static void IssueMessage(CompilerOptions options, Message msgId, params string[] values)
{
if (errorCount != 0 && msgId < Message.StartErrors && !options.warnaserror)
{
// don't display any warnings after we've emitted an error message
return;
}
string key = ((int)msgId).ToString();
for (int i = 0; ; i++)
{
if (options.suppressWarnings.ContainsKey(key))
{
return;
}
if (i == values.Length)
{
break;
}
key += ":" + values[i];
}
options.suppressWarnings.Add(key, key);
if(options.writeSuppressWarningsFile != null)
{
File.AppendAllText(options.writeSuppressWarningsFile.FullName, "-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.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, please reference primary assembly \"{1}\" instead";
break;
case Message.MissingType:
msg = "Reference to type \"{0}\" claims it is defined in \"{1}\", but it could not be found";
break;
case Message.MissingReference:
msg = "The type '{0}' is defined in an assembly that is not referenced. You must add a reference to assembly '{1}'";
break;
case Message.DuplicateAssemblyReference:
msg = "Duplicate assembly reference \"{0}\"";
break;
case Message.UnableToResolveType:
msg = "Reference in \"{0}\" to type \"{1}\" claims it is defined in \"{2}\", but it could not be found";
break;
case Message.StubsAreDeprecated:
msg = "Compiling stubs is deprecated. Please add a reference to assembly \"{0}\" instead.";
break;
case Message.WrongClassName:
msg = "Unable to compile \"{0}\" (wrong name: \"{1}\")";
break;
case Message.ReflectionCallerClassRequiresCallerID:
msg = "Reflection.getCallerClass() called from non-CallerID method" + Environment.NewLine +
" (\"{0}.{1}{2}\")";
break;
case Message.LegacyAssemblyAttributesFound:
msg = "Legacy assembly attributes container found. Please use the -assemblyattributes:<file> option.";
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;
case Message.CallerSensitiveOnUnsupportedMethod:
msg = "CallerSensitive annotation on unsupported method" + Environment.NewLine +
" (\"{0}.{1}{2}\")";
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);
}
}
}
internal static void LinkageError(string msg, TypeWrapper actualType, TypeWrapper expectedType, params object[] values)
{
object[] args = new object[values.Length + 2];
values.CopyTo(args, 2);
args[0] = AssemblyQualifiedName(actualType);
args[1] = AssemblyQualifiedName(expectedType);
string str = string.Format(msg, args);
if (actualType is UnloadableTypeWrapper && (expectedType is CompiledTypeWrapper || expectedType is DotNetTypeWrapper))
{
str += string.Format("\n\t(Please add a reference to {0})", expectedType.TypeAsBaseType.Assembly.Location);
}
throw new FatalCompilerErrorException(Message.LinkageError, str);
}
private static string AssemblyQualifiedName(TypeWrapper tw)
{
ClassLoaderWrapper loader = tw.GetClassLoader();
AssemblyClassLoader acl = loader as AssemblyClassLoader;
if(acl != null)
{
return tw.Name + ", " + acl.GetAssembly(tw).FullName;
}
CompilerClassLoader ccl = loader as CompilerClassLoader;
if(ccl != null)
{
return tw.Name + ", " + ccl.GetTypeWrapperFactory().ModuleBuilder.Assembly.FullName;
}
return tw.Name + " (unknown assembly)";
}
internal static void IssueMissingTypeMessage(Type type)
{
type = ReflectUtil.GetMissingType(type);
StaticCompiler.IssueMessage(type.Assembly.__IsMissing ? Message.MissingReference : Message.MissingType, type.FullName, type.Assembly.FullName);
}
internal static void SuppressWarning(CompilerOptions options, Message message, string name)
{
options.suppressWarnings[(int)message + ":" + name] = null;
}
}
sealed class Packages
{
private readonly List<string> packages = new List<string>();
private readonly Dictionary<string, string> packagesSet = new Dictionary<string, string>();
internal void DefinePackage(string packageName, string jar)
{
if (!packagesSet.ContainsKey(packageName))
{
packages.Add(packageName);
packagesSet.Add(packageName, jar);
}
}
// returns an array of PackageListAttribute constructor argument arrays
internal object[][] ToArray()
{
List<object[]> list = new List<object[]>();
// we use an empty string to indicate we don't yet have a jar,
// because null is used for packages that were defined from
// the file system (i.e. don't have a jar to load a manifest from)
string currentJar = "";
List<string> currentList = new List<string>();
foreach (string package in packages)
{
string jar = packagesSet[package];
if (jar != currentJar)
{
if (currentList.Count != 0)
{
list.Add(new object[] { currentJar, currentList.ToArray() });
currentList.Clear();
}
currentJar = jar;
}
currentList.Add(package);
}
if (currentList.Count != 0)
{
list.Add(new object[] { currentJar, currentList.ToArray() });
}
return list.ToArray();
}
}
}