ikvm-fork/ikvmstub/ikvmstub.cs

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

2002-12-18 19:00:25 +03:00
/*
Copyright (C) 2002 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.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;
2003-08-21 14:06:34 +04:00
using java.lang;
using java.lang.reflect;
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();
2002-12-18 19:00:25 +03:00
public static void Main(string[] args)
{
2004-03-08 18:18:47 +03:00
Tracer.EnableTraceForDebug();
2002-12-18 19:00:25 +03:00
Assembly assembly = null;
2003-08-26 15:24:17 +04:00
FileInfo file = new FileInfo(args[0]);
if(file.Exists)
2002-12-18 19:00:25 +03:00
{
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]);
}
2002-12-18 19:00:25 +03:00
}
2003-08-21 14:06:34 +04:00
else
{
assembly = Assembly.LoadWithPartialName(args[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));
2003-08-21 14:06:34 +04:00
ProcessAssembly(assembly);
2003-10-22 20:34:22 +04:00
ProcessPrivateClasses(assembly);
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
Environment.Exit(0);
2002-12-18 19:00:25 +03:00
}
2003-08-21 14:06:34 +04:00
private static void WriteClass(string name, ClassFileWriter c)
2002-12-18 19:00:25 +03:00
{
2003-08-21 14:06:34 +04:00
zipFile.PutNextEntry(new ZipEntry(name));
c.Write(zipFile);
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
{
2003-08-26 15:24:17 +04:00
ProcessClass(assembly.FullName, Class.forName(t.AssemblyQualifiedName, false, null), null);
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;
foreach(Class c in todo.Values)
{
if(!done.ContainsKey(c.getName()))
{
keepGoing = true;
done.Add(c.getName(), null);
ProcessClass(assembly.FullName, c, c.getDeclaringClass());
}
}
} while(keepGoing);
}
2003-08-21 14:06:34 +04:00
private static void ProcessClass(string assemblyName, Class c, Class outer)
2002-12-18 19:00:25 +03:00
{
2003-08-21 14:06:34 +04:00
string name = c.getName().Replace('.', '/');
string super = null;
if(c.getSuperclass() != null)
2003-04-14 13:41:58 +04:00
{
2003-08-21 14:06:34 +04:00
super = c.getSuperclass().getName().Replace('.', '/');
2003-10-22 20:34:22 +04:00
// if the base class isn't public, we still need to export it (!)
if(!Modifier.isPublic(c.getSuperclass().getModifiers()))
{
privateClasses[c.getSuperclass().getName()] = c.getSuperclass();
}
2003-04-14 13:41:58 +04:00
}
2003-08-21 14:06:34 +04:00
if(c.isInterface())
2002-12-18 19:00:25 +03:00
{
super = "java/lang/Object";
}
2004-02-18 16:48:38 +03:00
Modifiers classmods = (Modifiers)c.getModifiers();
if(outer != null)
{
// protected inner classes are actually public and private inner classes are actually package
if((classmods & Modifiers.Protected) != 0)
{
classmods |= Modifiers.Public;
}
classmods &= ~(Modifiers.Static | Modifiers.Private | Modifiers.Protected);
}
ClassFileWriter f = new ClassFileWriter(classmods, name, super);
2003-08-21 14:06:34 +04:00
f.AddStringAttribute("IKVM.NET.Assembly", assemblyName);
InnerClassesAttribute innerClassesAttribute = null;
if(outer != null)
2002-12-18 19:00:25 +03:00
{
2003-08-21 14:06:34 +04:00
innerClassesAttribute = new InnerClassesAttribute(f);
2004-02-10 12:53:56 +03:00
string innername = name;
int idx = name.LastIndexOf('$');
if(idx >= 0)
{
innername = innername.Substring(idx + 1);
}
innerClassesAttribute.Add(name, outer.getName().Replace('.', '/'), innername, (ushort)c.getModifiers());
2002-12-18 19:00:25 +03:00
}
2003-11-17 15:01:50 +03:00
Class[] interfaces = c.getInterfaces();
for(int i = 0; i < interfaces.Length; i++)
{
if(Modifier.isPublic(interfaces[i].getModifiers()))
{
f.AddInterface(interfaces[i].getName().Replace('.', '/'));
}
}
2003-08-21 14:06:34 +04:00
Class[] innerClasses = c.getDeclaredClasses();
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
{
2003-08-21 14:06:34 +04:00
if(innerClassesAttribute == null)
2002-12-18 19:00:25 +03:00
{
2003-08-21 14:06:34 +04:00
innerClassesAttribute = new InnerClassesAttribute(f);
2002-12-18 19:00:25 +03:00
}
2003-08-21 14:06:34 +04:00
string namePart = innerClasses[i].getName();
namePart = namePart.Substring(namePart.LastIndexOf('$') + 1);
innerClassesAttribute.Add(innerClasses[i].getName().Replace('.', '/'), name, namePart, (ushort)innerClasses[i].getModifiers());
ProcessClass(assemblyName, innerClasses[i], c);
2002-12-18 19:00:25 +03:00
}
}
2003-08-21 14:06:34 +04:00
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();
FieldOrMethod m = f.AddMethod(mods, "<init>", MakeSig(args, java.lang.Void.TYPE));
CodeAttribute code = new CodeAttribute(f);
code.MaxLocals = (ushort)(args.Length + 1);
2004-02-16 20:23:03 +03:00
code.MaxStack = 3;
ushort index1 = f.AddClass("java/lang/UnsatisfiedLinkError");
ushort index2 = f.AddString("Netexp generated stubs can only be used on IKVM.NET");
ushort index3 = f.AddMethodRef("java/lang/UnsatisfiedLinkError", "<init>", "(Ljava/lang/String;)V");
code.ByteCode = new byte[] {
187, (byte)(index1 >> 8), (byte)index1, // new java/lang/UnsatisfiedLinkError
89, // dup
19, (byte)(index2 >> 8), (byte)index2, // ldc_w "..."
183, (byte)(index3 >> 8), (byte)index3, // invokespecial java/lang/UnsatisfiedLinkError/init()V
191 // athrow
};
2004-02-10 12:53:56 +03:00
m.AddAttribute(code);
2002-12-18 19:00:25 +03:00
}
}
2003-08-21 14:06:34 +04:00
Method[] methods = c.getDeclaredMethods();
2002-12-18 19:00:25 +03:00
for(int i = 0; i < methods.Length; i++)
{
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-08-21 14:06:34 +04:00
if((mods & Modifiers.Abstract) == 0)
2002-12-18 19:00:25 +03:00
{
2003-08-21 14:06:34 +04:00
mods |= Modifiers.Native;
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?
FieldOrMethod m = f.AddMethod(mods, methods[i].getName(), MakeSig(methods[i].getParameterTypes(), methods[i].getReturnType()));
Class[] exceptions = methods[i].getExceptionTypes();
if(exceptions.Length > 0)
{
ExceptionsAttribute attrib = new ExceptionsAttribute(f);
foreach(Class x in exceptions)
{
// TODO what happens if one of the exception types is non-public?
attrib.Add(x.getName().Replace('.', '/'));
}
m.AddAttribute(attrib);
}
2002-12-18 19:00:25 +03:00
}
}
2003-08-21 14:06:34 +04:00
Field[] fields = c.getDeclaredFields();
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();
if((mods & (Modifiers.Public | Modifiers.Protected)) != 0)
2002-12-18 19:00:25 +03:00
{
2003-08-21 14:06:34 +04:00
object constantValue = null;
2003-08-26 15:24:17 +04:00
// HACK we only look for constants on potential constant fields, to trigger less static initializers
if((mods & (Modifiers.Final | Modifiers.Static)) == (Modifiers.Final | Modifiers.Static) &&
(fields[i].getType().isPrimitive() || fields[i].getType() == Class.forName("java.lang.String")))
2003-08-21 14:06:34 +04:00
{
// HACK we use a non-standard API to get constant value
// NOTE we can't use Field.get() because that will run the static initializer and
// also won't allow us to see the difference between constants and blank final fields.
constantValue = NativeCode.java.lang.reflect.Field.getConstant(fields[i]);
if(constantValue != null)
{
if(constantValue is java.lang.Boolean)
{
constantValue = ((java.lang.Boolean)constantValue).booleanValue();
}
else if(constantValue is java.lang.Byte)
{
constantValue = ((java.lang.Byte)constantValue).byteValue();
}
else if(constantValue is java.lang.Short)
{
constantValue = ((java.lang.Short)constantValue).shortValue();
}
else if(constantValue is java.lang.Character)
{
constantValue = ((java.lang.Character)constantValue).charValue();
}
else if(constantValue is java.lang.Integer)
{
constantValue = ((java.lang.Integer)constantValue).intValue();
}
else if(constantValue is java.lang.Long)
{
constantValue = ((java.lang.Long)constantValue).longValue();
}
else if(constantValue is java.lang.Float)
{
constantValue = ((java.lang.Float)constantValue).floatValue();
}
else if(constantValue is java.lang.Double)
{
constantValue = ((java.lang.Double)constantValue).doubleValue();
}
else if(constantValue is string)
{
// no conversion needed
}
else
{
throw new InvalidOperationException();
}
}
}
2003-11-17 15:01:50 +03:00
// TODO what happens if the field type is non-public?
2003-08-21 14:06:34 +04:00
f.AddField(mods, fields[i].getName(), ClassToSig(fields[i].getType()), constantValue);
2002-12-18 19:00:25 +03:00
}
2003-08-21 14:06:34 +04:00
}
if(innerClassesAttribute != null)
{
f.AddAttribute(innerClassesAttribute);
2002-12-18 19:00:25 +03:00
}
WriteClass(name + ".class", f);
}
2003-08-21 14:06:34 +04:00
private static string MakeSig(Class[] args, Class ret)
2002-12-18 19:00:25 +03:00
{
2003-08-21 14:06:34 +04:00
StringBuilder sb = new StringBuilder();
sb.Append('(');
for(int i = 0; i < args.Length; i++)
{
sb.Append(ClassToSig(args[i]));
}
sb.Append(')');
sb.Append(ClassToSig(ret));
return sb.ToString();
2002-12-18 19:00:25 +03:00
}
2003-08-21 14:06:34 +04:00
private static string ClassToSig(Class c)
2002-12-18 19:00:25 +03:00
{
2003-08-21 14:06:34 +04:00
if(c.isPrimitive())
2002-12-18 19:00:25 +03:00
{
2003-08-21 14:06:34 +04:00
if(c == java.lang.Void.TYPE)
2002-12-18 19:00:25 +03:00
{
2003-08-21 14:06:34 +04:00
return "V";
2002-12-18 19:00:25 +03:00
}
2003-08-21 14:06:34 +04:00
else if(c == java.lang.Byte.TYPE)
2002-12-18 19:00:25 +03:00
{
2003-08-21 14:06:34 +04:00
return "B";
2002-12-18 19:00:25 +03:00
}
2003-08-21 14:06:34 +04:00
else if(c == java.lang.Boolean.TYPE)
2003-02-15 14:18:53 +03:00
{
2003-08-21 14:06:34 +04:00
return "Z";
2003-02-15 14:18:53 +03:00
}
2003-08-21 14:06:34 +04:00
else if(c == java.lang.Short.TYPE)
2002-12-18 19:00:25 +03:00
{
2003-08-21 14:06:34 +04:00
return "S";
2002-12-18 19:00:25 +03:00
}
2003-08-21 14:06:34 +04:00
else if(c == java.lang.Character.TYPE)
2002-12-18 19:00:25 +03:00
{
2003-08-21 14:06:34 +04:00
return "C";
2002-12-18 19:00:25 +03:00
}
2003-08-21 14:06:34 +04:00
else if(c == java.lang.Integer.TYPE)
2003-02-15 14:18:53 +03:00
{
2003-08-21 14:06:34 +04:00
return "I";
2003-02-15 14:18:53 +03:00
}
2003-08-21 14:06:34 +04:00
else if(c == java.lang.Long.TYPE)
2002-12-18 19:00:25 +03:00
{
2003-08-21 14:06:34 +04:00
return "J";
2002-12-18 19:00:25 +03:00
}
2003-08-21 14:06:34 +04:00
else if(c == java.lang.Float.TYPE)
2002-12-18 19:00:25 +03:00
{
2003-08-21 14:06:34 +04:00
return "F";
2002-12-18 19:00:25 +03:00
}
2003-08-21 14:06:34 +04:00
else if(c == java.lang.Double.TYPE)
2002-12-18 19:00:25 +03:00
{
2003-08-21 14:06:34 +04:00
return "D";
2002-12-18 19:00:25 +03:00
}
2003-08-21 14:06:34 +04:00
else
2003-03-31 18:54:35 +04:00
{
2003-08-21 14:06:34 +04:00
throw new InvalidOperationException();
2003-03-31 18:54:35 +04:00
}
2002-12-18 19:00:25 +03:00
}
2003-08-21 14:06:34 +04:00
else if(c.isArray())
2002-12-18 19:00:25 +03:00
{
2003-08-21 14:06:34 +04:00
return "[" + ClassToSig(c.getComponentType());
2002-12-18 19:00:25 +03:00
}
else
{
2003-08-21 14:06:34 +04:00
return "L" + c.getName().Replace('.', '/') + ";";
2002-12-18 19:00:25 +03:00
}
}
}