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;
|
|
|
|
using IKVM.Internal;
|
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)
|
|
|
|
{
|
2004-03-08 18:18:47 +03:00
|
|
|
Tracer.EnableTraceForDebug();
|
2004-06-07 12:28:57 +04:00
|
|
|
if(args.Length != 1)
|
|
|
|
{
|
2006-01-31 13:13:12 +03:00
|
|
|
Console.Error.WriteLine(IKVM.Runtime.Startup.GetVersionAndCopyrightInfo());
|
|
|
|
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)
|
|
|
|
{
|
|
|
|
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)
|
|
|
|
{
|
|
|
|
c = (java.lang.Class)IKVM.Runtime.Util.GetFriendlyClassFromType(t);
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
c = (java.lang.Class)IKVM.Runtime.Util.GetClassFromTypeHandle(t.TypeHandle);
|
|
|
|
}
|
|
|
|
#else
|
2006-01-31 13:13:12 +03:00
|
|
|
c = (java.lang.Class)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-04 16:39:33 +04:00
|
|
|
WriteClass(name + ".class", ikvm.@internal.stubgen.StubGenerator.generateStub(c));
|
2005-10-01 15:16:11 +04:00
|
|
|
}
|
2002-12-18 19:00:25 +03:00
|
|
|
}
|