ikvm-fork/ikvmstub/ikvmstub.cs

351 строка
9.8 KiB
C#
Исходник Обычный вид История

2002-12-18 19:00:25 +03:00
/*
2006-01-20 11:35:31 +03:00
Copyright (C) 2002, 2004, 2005, 2006 Jeroen Frijters
2002-12-18 19:00:25 +03:00
This software is provided 'as-is', without any express or implied
warranty. In no event will the authors be held liable for any damages
arising from the use of this software.
Permission is granted to anyone to use this software for any purpose,
including commercial applications, and to alter it and redistribute it
freely, subject to the following restrictions:
1. The origin of this software must not be misrepresented; you must not
claim that you wrote the original software. If you use this software
in a product, an acknowledgment in the product documentation would be
appreciated but is not required.
2. Altered source versions must be plainly marked as such, and must not be
misrepresented as being the original software.
3. This notice may not be removed or altered from any source distribution.
Jeroen Frijters
jeroen@frijters.net
*/
using System;
using System.Reflection;
using System.IO;
using System.Text;
2003-10-22 20:34:22 +04:00
using System.Collections;
2003-01-08 16:35:05 +03:00
using ICSharpCode.SharpZipLib.Zip;
2004-09-09 15:17:55 +04:00
using IKVM.Attributes;
2002-12-18 19:00:25 +03:00
public class NetExp
{
private static ZipOutputStream zipFile;
2003-10-22 20:34:22 +04:00
private static Hashtable privateClasses = new Hashtable();
2005-12-19 18:12:49 +03:00
private static FileInfo file;
2002-12-18 19:00:25 +03:00
public static void Main(string[] args)
{
2006-08-04 17:13:22 +04:00
IKVM.Internal.Tracer.EnableTraceForDebug();
2004-06-07 12:28:57 +04:00
if(args.Length != 1)
{
2006-08-17 11:33:38 +04:00
Console.Error.WriteLine(ikvm.runtime.Startup.getVersionAndCopyrightInfo());
2006-01-31 13:13:12 +03:00
Console.Error.WriteLine();
2004-06-07 12:28:57 +04:00
Console.Error.WriteLine("usage: ikvmstub <assemblyNameOrPath>");
return;
}
2002-12-18 19:00:25 +03:00
Assembly assembly = null;
2004-06-07 12:28:57 +04:00
try
{
file = new FileInfo(args[0]);
}
catch(System.Exception x)
{
Console.Error.WriteLine("Error: unable to load \"{0}\"\n {1}", args[0], x.Message);
return;
}
if(file != null && file.Exists)
2002-12-18 19:00:25 +03:00
{
2005-12-07 12:06:32 +03:00
#if WHIDBEY
2005-12-19 18:12:49 +03:00
AppDomain.CurrentDomain.ReflectionOnlyAssemblyResolve += new ResolveEventHandler(CurrentDomain_ReflectionOnlyAssemblyResolve);
2005-12-07 12:06:32 +03:00
assembly = Assembly.ReflectionOnlyLoadFrom(args[0]);
#else
2003-08-26 15:24:17 +04:00
try
{
// If the same assembly can be found in the "Load" context, we prefer to use that
// http://blogs.gotdotnet.com/suzcook/permalink.aspx/d5c5e14a-3612-4af1-a9b7-0a144c8dbf16
// We use AssemblyName.FullName, because otherwise the assembly will be loaded in the
// "LoadFrom" context using the path inside the AssemblyName object.
assembly = Assembly.Load(AssemblyName.GetAssemblyName(args[0]).FullName);
Console.Error.WriteLine("Warning: Assembly loaded from {0} instead", assembly.Location);
}
catch
{
}
if(assembly == null)
{
assembly = Assembly.LoadFrom(args[0]);
}
2005-12-07 12:06:32 +03:00
#endif
2002-12-18 19:00:25 +03:00
}
2003-08-21 14:06:34 +04:00
else
{
assembly = Assembly.LoadWithPartialName(args[0]);
}
2005-02-02 18:11:26 +03:00
int rc = 0;
2002-12-18 19:00:25 +03:00
if(assembly == null)
{
Console.Error.WriteLine("Error: Assembly \"{0}\" not found", args[0]);
}
else
{
2003-01-08 16:35:05 +03:00
zipFile = new ZipOutputStream(new FileStream(assembly.GetName().Name + ".jar", FileMode.Create));
2004-12-21 13:26:51 +03:00
try
{
ProcessAssembly(assembly);
ProcessPrivateClasses(assembly);
}
2006-07-21 14:18:13 +04:00
catch(ReflectionTypeLoadException x)
{
Console.WriteLine(x);
Console.WriteLine("LoaderExceptions:");
foreach(Exception n in x.LoaderExceptions)
{
Console.WriteLine(n);
}
}
2004-12-21 13:26:51 +03:00
catch(System.Exception x)
{
2006-08-17 11:33:38 +04:00
java.lang.Throwable.instancehelper_printStackTrace(ikvm.runtime.Util.mapException(x));
2005-02-02 18:11:26 +03:00
rc = 1;
2004-12-21 13:26:51 +03:00
}
2003-08-21 14:06:34 +04:00
zipFile.Close();
2002-12-18 19:00:25 +03:00
}
2004-05-25 11:14:39 +04:00
// FXBUG if we run a static initializer that starts a thread, we would never end,
2003-08-21 14:06:34 +04:00
// so we force an exit here
2005-02-02 18:11:26 +03:00
Environment.Exit(rc);
2002-12-18 19:00:25 +03:00
}
2005-12-19 18:12:49 +03:00
#if WHIDBEY
private static Assembly CurrentDomain_ReflectionOnlyAssemblyResolve(object sender, ResolveEventArgs args)
{
2006-07-21 14:18:13 +04:00
//Console.WriteLine("Resolve: " + args.Name);
2005-12-19 18:12:49 +03:00
foreach(Assembly a in AppDomain.CurrentDomain.ReflectionOnlyGetAssemblies())
{
if(args.Name.StartsWith(a.GetName().Name + ", "))
{
return a;
}
}
2006-07-21 14:18:13 +04:00
Assembly asm = Assembly.ReflectionOnlyLoad(args.Name);
if(asm != null)
{
return asm;
}
2005-12-19 18:12:49 +03:00
string path = args.Name;
int index = path.IndexOf(',');
if(index > 0)
{
path = path.Substring(0, index);
}
path = file.DirectoryName + Path.DirectorySeparatorChar + path + ".dll";
Console.WriteLine("Loading referenced assembly: " + path);
return Assembly.ReflectionOnlyLoadFrom(path);
}
#endif
2006-08-04 16:39:33 +04:00
private static void WriteClass(string name, byte[] buf)
2002-12-18 19:00:25 +03:00
{
2003-08-21 14:06:34 +04:00
zipFile.PutNextEntry(new ZipEntry(name));
2006-08-04 16:39:33 +04:00
zipFile.Write(buf, 0, buf.Length);
2002-12-18 19:00:25 +03:00
}
2003-08-21 14:06:34 +04:00
private static void ProcessAssembly(Assembly assembly)
2002-12-18 19:00:25 +03:00
{
2003-08-21 14:06:34 +04:00
foreach(Type t in assembly.GetTypes())
2002-12-18 19:00:25 +03:00
{
2003-08-21 14:06:34 +04:00
if(t.IsPublic)
2002-12-18 19:00:25 +03:00
{
2005-02-02 18:11:26 +03:00
java.lang.Class c;
2004-10-04 23:30:53 +04:00
try
{
2006-01-31 13:13:12 +03:00
// NOTE we use GetClassFromTypeHandle instead of GetFriendlyClassFromType, to make sure
// we don't get the remapped types when we're processing System.Object, System.String,
2006-05-15 13:08:01 +04:00
// System.Throwable and System.IComparable.
// NOTE we can't use GetClassFromTypeHandle for ReflectionOnly assemblies
// (because Type.TypeHandle is not supported by ReflectionOnly types), but this
// isn't a problem because mscorlib is never loaded in the ReflectionOnly context.
#if WHIDBEY
if(assembly.ReflectionOnly)
{
2006-08-17 11:33:38 +04:00
c = ikvm.runtime.Util.getFriendlyClassFromType(t);
2006-05-15 13:08:01 +04:00
}
else
{
2006-08-17 11:33:38 +04:00
c = ikvm.runtime.Util.getClassFromTypeHandle(t.TypeHandle);
2006-05-15 13:08:01 +04:00
}
#else
2006-08-17 11:33:38 +04:00
c = ikvm.runtime.Util.getClassFromTypeHandle(t.TypeHandle);
2006-05-15 13:08:01 +04:00
#endif
2005-12-19 18:12:49 +03:00
if (c == null)
{
Console.WriteLine("Skipping: " + t.FullName);
continue;
}
2004-10-04 23:30:53 +04:00
}
2005-02-02 18:11:26 +03:00
catch(java.lang.ClassNotFoundException)
2004-10-04 23:30:53 +04:00
{
// types that IKVM doesn't support don't show up
continue;
}
2006-08-04 16:39:33 +04:00
ProcessClass(c);
2002-12-18 19:00:25 +03:00
}
}
}
2003-11-17 15:01:50 +03:00
// TODO private classes should also be done handled for interfaces, fields and method arguments/return type
2003-10-22 20:34:22 +04:00
private static void ProcessPrivateClasses(Assembly assembly)
{
Hashtable done = new Hashtable();
bool keepGoing;
do
{
Hashtable todo = privateClasses;
privateClasses = new Hashtable();
keepGoing = false;
2005-02-02 18:11:26 +03:00
foreach(java.lang.Class c in todo.Values)
2003-10-22 20:34:22 +04:00
{
if(!done.ContainsKey(c.getName()))
{
keepGoing = true;
done.Add(c.getName(), null);
2006-08-04 16:39:33 +04:00
ProcessClass(c);
2003-10-22 20:34:22 +04:00
}
}
} while(keepGoing);
}
2005-06-22 17:02:03 +04:00
private static void AddToExportList(java.lang.Class c)
{
2005-06-22 18:24:16 +04:00
while(c.isArray())
{
c = c.getComponentType();
}
2005-06-22 17:02:03 +04:00
privateClasses[c.getName()] = c;
}
private static bool IsGenericType(java.lang.Class c)
{
// HACK huge hack, we look for the backtick
2005-11-14 12:12:08 +03:00
return c.getName().IndexOf("$$0060") > 0;
2005-06-22 17:02:03 +04:00
}
2006-08-04 16:39:33 +04:00
private static void ProcessClass(java.lang.Class c)
2002-12-18 19:00:25 +03:00
{
2003-08-21 14:06:34 +04:00
string name = c.getName().Replace('.', '/');
if(c.getSuperclass() != null)
2003-04-14 13:41:58 +04:00
{
2003-10-22 20:34:22 +04:00
// if the base class isn't public, we still need to export it (!)
2005-02-02 18:11:26 +03:00
if(!java.lang.reflect.Modifier.isPublic(c.getSuperclass().getModifiers()))
2003-10-22 20:34:22 +04:00
{
2005-06-22 17:02:03 +04:00
AddToExportList(c.getSuperclass());
2003-10-22 20:34:22 +04:00
}
2003-04-14 13:41:58 +04:00
}
2005-02-02 18:11:26 +03:00
java.lang.Class[] interfaces = c.getInterfaces();
2003-11-17 15:01:50 +03:00
for(int i = 0; i < interfaces.Length; i++)
{
2006-02-21 12:26:30 +03:00
if(IsGenericType(interfaces[i])
|| !java.lang.reflect.Modifier.isPublic(interfaces[i].getModifiers()))
2003-11-17 15:01:50 +03:00
{
2006-02-21 12:26:30 +03:00
AddToExportList(interfaces[i]);
2003-11-17 15:01:50 +03:00
}
}
2005-02-02 18:11:26 +03:00
java.lang.Class[] innerClasses = c.getDeclaredClasses();
2003-08-21 14:06:34 +04:00
for(int i = 0; i < innerClasses.Length; i++)
2002-12-18 19:00:25 +03:00
{
2003-08-21 14:06:34 +04:00
Modifiers mods = (Modifiers)innerClasses[i].getModifiers();
if((mods & (Modifiers.Public | Modifiers.Protected)) != 0)
2002-12-18 19:00:25 +03:00
{
2006-08-04 16:39:33 +04:00
ProcessClass(innerClasses[i]);
2002-12-18 19:00:25 +03:00
}
}
2005-02-02 18:11:26 +03:00
java.lang.reflect.Constructor[] constructors = c.getDeclaredConstructors();
2002-12-18 19:00:25 +03:00
for(int i = 0; i < constructors.Length; i++)
{
2003-08-21 14:06:34 +04:00
Modifiers mods = (Modifiers)constructors[i].getModifiers();
if((mods & (Modifiers.Public | Modifiers.Protected)) != 0)
2002-12-18 19:00:25 +03:00
{
2003-11-17 15:01:50 +03:00
// TODO what happens if one of the argument types is non-public?
2004-02-10 12:53:56 +03:00
java.lang.Class[] args = constructors[i].getParameterTypes();
2005-06-22 18:24:16 +04:00
foreach(java.lang.Class arg in args)
{
2005-12-07 12:06:32 +03:00
// TODO if arg is not public, add it to the export list as well
2005-06-22 18:24:16 +04:00
if(IsGenericType(arg))
{
AddToExportList(arg);
}
}
2002-12-18 19:00:25 +03:00
}
}
2005-02-02 18:11:26 +03:00
java.lang.reflect.Method[] methods = c.getDeclaredMethods();
2002-12-18 19:00:25 +03:00
for(int i = 0; i < methods.Length; i++)
{
2005-10-01 15:16:11 +04:00
// FXBUG (?) .NET reflection on java.lang.Object returns toString() twice!
// I didn't want to add the work around to CompiledTypeWrapper, so it's here.
if((c.getName() == "java.lang.Object" || c.getName() == "java.lang.Throwable")
&& methods[i].getName() == "toString")
{
bool found = false;
for(int j = 0; j < i; j++)
{
if(methods[j].getName() == "toString")
{
found = true;
break;
}
}
if(found)
{
continue;
}
}
2003-08-21 14:06:34 +04:00
Modifiers mods = (Modifiers)methods[i].getModifiers();
if((mods & (Modifiers.Public | Modifiers.Protected)) != 0)
2002-12-18 19:00:25 +03:00
{
2003-11-17 15:01:50 +03:00
// TODO what happens if one of the argument types (or the return type) is non-public?
2005-06-22 18:24:16 +04:00
java.lang.Class[] args = methods[i].getParameterTypes();
foreach(java.lang.Class arg in args)
{
2005-12-07 12:06:32 +03:00
// TODO if arg is not public, add it to the export list as well
2005-06-22 18:24:16 +04:00
if(IsGenericType(arg))
{
AddToExportList(arg);
}
}
java.lang.Class retType = methods[i].getReturnType();
2005-12-07 12:06:32 +03:00
// TODO if retType is not public, add it to the export list as well
2005-06-22 18:24:16 +04:00
if(IsGenericType(retType))
{
AddToExportList(retType);
}
2002-12-18 19:00:25 +03:00
}
}
2005-02-02 18:11:26 +03:00
java.lang.reflect.Field[] fields = c.getDeclaredFields();
2003-08-21 14:06:34 +04:00
for(int i = 0; i < fields.Length; i++)
2002-12-18 19:00:25 +03:00
{
2003-08-21 14:06:34 +04:00
Modifiers mods = (Modifiers)fields[i].getModifiers();
2006-08-04 16:39:33 +04:00
if((mods & (Modifiers.Public | Modifiers.Protected)) != 0)
2002-12-18 19:00:25 +03:00
{
2005-06-22 18:24:16 +04:00
java.lang.Class fieldType = fields[i].getType();
2006-01-20 11:35:31 +03:00
if(IsGenericType(fieldType) || (fieldType.getModifiers() & (int)Modifiers.Public) == 0)
2005-06-22 18:24:16 +04:00
{
AddToExportList(fieldType);
}
2002-12-18 19:00:25 +03:00
}
2003-08-21 14:06:34 +04:00
}
2006-08-14 11:57:03 +04:00
java.io.InputStream inp = c.getResourceAsStream("/" + name + ".class");
if(inp == null)
{
Console.Error.WriteLine("Class {0} not found", name);
return;
}
byte[] buf = new byte[inp.available()];
if(inp.read(buf) != buf.Length || inp.read() != -1)
{
throw new NotImplementedException();
}
WriteClass(name + ".class", buf);
2005-10-01 15:16:11 +04:00
}
2002-12-18 19:00:25 +03:00
}