2002-12-18 19:00:25 +03:00
|
|
|
/*
|
2005-03-11 16:56:44 +03:00
|
|
|
Copyright (C) 2002, 2003, 2004, 2005 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.IO;
|
|
|
|
using System.Reflection;
|
|
|
|
using System.Reflection.Emit;
|
|
|
|
using System.Collections;
|
2004-09-15 17:35:44 +04:00
|
|
|
using System.Collections.Specialized;
|
2002-12-29 19:27:00 +03:00
|
|
|
using System.Text;
|
2004-09-09 15:17:55 +04:00
|
|
|
using IKVM.Internal;
|
2004-09-15 17:35:44 +04:00
|
|
|
using IKVM.Runtime;
|
2002-12-18 19:00:25 +03:00
|
|
|
|
|
|
|
using java.lang.reflect;
|
|
|
|
using java.net;
|
|
|
|
using java.util.jar;
|
|
|
|
using java.io;
|
|
|
|
|
|
|
|
public class Starter
|
|
|
|
{
|
|
|
|
private class Timer
|
|
|
|
{
|
|
|
|
private static Timer t;
|
|
|
|
private DateTime now = DateTime.Now;
|
|
|
|
|
|
|
|
internal Timer()
|
|
|
|
{
|
|
|
|
t = this;
|
|
|
|
}
|
|
|
|
|
|
|
|
~Timer()
|
|
|
|
{
|
|
|
|
Console.WriteLine(DateTime.Now - now);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2002-12-27 12:01:16 +03:00
|
|
|
public class PathClassLoader : URLClassLoader
|
|
|
|
{
|
|
|
|
private static URL[] GetClassPath(string classpath)
|
|
|
|
{
|
2003-02-15 20:06:47 +03:00
|
|
|
string[] s = classpath.Split(Path.PathSeparator);
|
2002-12-27 12:01:16 +03:00
|
|
|
URL[] urls = new URL[s.Length];
|
|
|
|
for(int i = 0; i < urls.Length; i++)
|
|
|
|
{
|
|
|
|
// TODO non-existing file/dir is treated as current directory, this obviously isn't correct
|
|
|
|
urls[i] = new java.io.File(s[i]).toURL();
|
|
|
|
}
|
|
|
|
return urls;
|
|
|
|
}
|
|
|
|
|
|
|
|
public PathClassLoader(string classpath, java.lang.ClassLoader parent)
|
|
|
|
: base(GetClassPath(classpath), parent)
|
|
|
|
{
|
|
|
|
}
|
|
|
|
|
|
|
|
protected override java.lang.Class loadClass(string name, bool resolve)
|
|
|
|
{
|
|
|
|
java.lang.Class c = findClass(name);
|
|
|
|
if(resolve)
|
|
|
|
{
|
|
|
|
resolveClass(c);
|
|
|
|
}
|
|
|
|
return c;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2003-12-20 01:19:18 +03:00
|
|
|
private class SaveAssemblyShutdownHook : java.lang.Thread
|
|
|
|
{
|
|
|
|
private java.lang.Class clazz;
|
|
|
|
|
|
|
|
internal SaveAssemblyShutdownHook(java.lang.Class clazz)
|
2004-09-27 14:17:34 +04:00
|
|
|
: base("SaveAssemblyShutdownHook")
|
2003-12-20 01:19:18 +03:00
|
|
|
{
|
|
|
|
this.clazz = clazz;
|
|
|
|
}
|
|
|
|
|
|
|
|
public override void run()
|
|
|
|
{
|
2004-08-17 13:05:21 +04:00
|
|
|
System.Threading.Thread.CurrentThread.Priority = System.Threading.ThreadPriority.AboveNormal;
|
2003-12-24 14:51:41 +03:00
|
|
|
Console.Error.WriteLine("Saving dynamic assembly...");
|
|
|
|
try
|
|
|
|
{
|
|
|
|
JVM.SaveDebugImage(clazz);
|
2004-04-23 18:21:43 +04:00
|
|
|
Console.Error.WriteLine("Saving done.");
|
2003-12-24 14:51:41 +03:00
|
|
|
}
|
|
|
|
catch(Exception x)
|
|
|
|
{
|
|
|
|
Console.Error.WriteLine(x);
|
2004-03-20 16:25:08 +03:00
|
|
|
Console.Error.WriteLine(new System.Diagnostics.StackTrace(x, true));
|
2003-12-24 14:51:41 +03:00
|
|
|
System.Diagnostics.Debug.Assert(false, x.ToString());
|
|
|
|
}
|
2003-12-20 01:19:18 +03:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2004-04-02 12:13:01 +04:00
|
|
|
private class WaitShutdownHook : java.lang.Thread
|
|
|
|
{
|
2004-09-27 14:17:34 +04:00
|
|
|
internal WaitShutdownHook()
|
|
|
|
: base("WaitShutdownHook")
|
|
|
|
{
|
|
|
|
}
|
|
|
|
|
2004-04-02 12:13:01 +04:00
|
|
|
public override void run()
|
|
|
|
{
|
|
|
|
Console.Error.WriteLine("IKVM runtime terminated. Waiting for Ctrl+C...");
|
|
|
|
for(;;)
|
|
|
|
{
|
|
|
|
System.Threading.Thread.Sleep(System.Threading.Timeout.Infinite);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2002-12-18 19:00:25 +03:00
|
|
|
[STAThread] // NOTE this is here because otherwise SWT's RegisterDragDrop (a COM thing) doesn't work
|
2004-10-19 17:43:55 +04:00
|
|
|
[IKVM.Attributes.HideFromJava]
|
2002-12-18 19:00:25 +03:00
|
|
|
static int Main(string[] args)
|
|
|
|
{
|
2004-03-08 18:18:47 +03:00
|
|
|
Tracer.EnableTraceForDebug();
|
2004-10-19 17:43:55 +04:00
|
|
|
Hashtable props = new Hashtable();
|
2002-12-18 19:00:25 +03:00
|
|
|
bool jar = false;
|
|
|
|
bool saveAssembly = false;
|
2004-09-05 13:37:58 +04:00
|
|
|
bool saveAssemblyX = false;
|
2004-04-02 12:13:01 +04:00
|
|
|
bool waitOnExit = false;
|
2002-12-18 19:00:25 +03:00
|
|
|
string mainClass = null;
|
|
|
|
string[] vmargs = null;
|
2004-08-17 13:05:21 +04:00
|
|
|
string bootclasspath = null;
|
2002-12-18 19:00:25 +03:00
|
|
|
for(int i = 0; i < args.Length; i++)
|
|
|
|
{
|
|
|
|
if(args[i][0] == '-')
|
|
|
|
{
|
2003-03-17 17:02:46 +03:00
|
|
|
if(args[i] == "-help" || args[i] == "-?")
|
2002-12-18 19:00:25 +03:00
|
|
|
{
|
|
|
|
break;
|
|
|
|
}
|
2003-03-17 17:02:46 +03:00
|
|
|
else if(args[i] == "-Xsave")
|
2002-12-18 19:00:25 +03:00
|
|
|
{
|
|
|
|
saveAssembly = true;
|
2003-05-10 15:43:12 +04:00
|
|
|
JVM.PrepareForSaveDebugImage();
|
2002-12-18 19:00:25 +03:00
|
|
|
}
|
2004-09-05 13:37:58 +04:00
|
|
|
else if(args[i] == "-XXsave")
|
|
|
|
{
|
|
|
|
saveAssemblyX = true;
|
|
|
|
JVM.PrepareForSaveDebugImage();
|
|
|
|
}
|
2003-03-17 17:02:46 +03:00
|
|
|
else if(args[i] == "-Xtime")
|
2002-12-18 19:00:25 +03:00
|
|
|
{
|
|
|
|
new Timer();
|
|
|
|
}
|
2004-04-02 12:13:01 +04:00
|
|
|
else if(args[i] == "-Xwait")
|
|
|
|
{
|
|
|
|
waitOnExit = true;
|
|
|
|
}
|
2004-07-10 11:19:42 +04:00
|
|
|
else if(args[i] == "-Xbreak")
|
|
|
|
{
|
|
|
|
System.Diagnostics.Debugger.Break();
|
|
|
|
}
|
2002-12-18 19:00:25 +03:00
|
|
|
else if(args[i] == "-jar")
|
|
|
|
{
|
|
|
|
jar = true;
|
|
|
|
}
|
2003-07-21 16:12:40 +04:00
|
|
|
else if(args[i] == "-version")
|
|
|
|
{
|
2005-03-11 16:56:44 +03:00
|
|
|
Console.WriteLine("CLR version: {0} ({1} bit)", Environment.Version, IntPtr.Size * 8);
|
2003-07-21 16:12:40 +04:00
|
|
|
foreach(Assembly asm in AppDomain.CurrentDomain.GetAssemblies())
|
|
|
|
{
|
|
|
|
Console.WriteLine("{0}: {1}", asm.GetName().Name, asm.GetName().Version);
|
|
|
|
}
|
|
|
|
return 0;
|
|
|
|
}
|
2002-12-18 19:00:25 +03:00
|
|
|
else if(args[i].StartsWith("-D"))
|
|
|
|
{
|
|
|
|
string[] keyvalue = args[i].Substring(2).Split('=');
|
|
|
|
if(keyvalue.Length != 2)
|
|
|
|
{
|
|
|
|
keyvalue = new string[] { keyvalue[0], "" };
|
|
|
|
}
|
2004-09-05 13:37:58 +04:00
|
|
|
props[keyvalue[0]] = keyvalue[1];
|
2002-12-18 19:00:25 +03:00
|
|
|
}
|
2004-10-04 23:30:53 +04:00
|
|
|
else if(args[i] == "-ea" || args[i] == "-enableassertions")
|
|
|
|
{
|
|
|
|
props["ikvm.assert.default"] = "true";
|
|
|
|
}
|
|
|
|
else if(args[i] == "-da" || args[i] == "-disableassertions")
|
|
|
|
{
|
|
|
|
props["ikvm.assert.default"] = "false";
|
|
|
|
}
|
|
|
|
else if(args[i].StartsWith("-ea:") || args[i].StartsWith("-enableassertions:"))
|
|
|
|
{
|
|
|
|
props["ikvm.assert.enable"] = args[i].Substring(args[i].IndexOf(':') + 1);
|
|
|
|
}
|
|
|
|
else if(args[i].StartsWith("-da:") || args[i].StartsWith("-disableassertions:"))
|
|
|
|
{
|
|
|
|
props["ikvm.assert.disable"] = args[i].Substring(args[i].IndexOf(':') + 1);
|
|
|
|
}
|
2002-12-18 19:00:25 +03:00
|
|
|
else if(args[i] == "-cp" || args[i] == "-classpath")
|
|
|
|
{
|
2004-09-05 13:37:58 +04:00
|
|
|
props["java.class.path"] = args[++i];
|
2002-12-18 19:00:25 +03:00
|
|
|
}
|
2002-12-27 12:01:16 +03:00
|
|
|
else if(args[i].StartsWith("-Xbootclasspath:"))
|
|
|
|
{
|
2004-08-17 13:05:21 +04:00
|
|
|
bootclasspath = args[i].Substring(16);
|
2002-12-27 12:01:16 +03:00
|
|
|
}
|
2004-03-08 18:18:47 +03:00
|
|
|
else if(args[i].StartsWith("-Xtrace:"))
|
2003-03-17 17:02:46 +03:00
|
|
|
{
|
2004-03-08 18:18:47 +03:00
|
|
|
Tracer.SetTraceLevel(args[i].Substring(8));
|
|
|
|
}
|
|
|
|
else if(args[i].StartsWith("-Xmethodtrace:"))
|
|
|
|
{
|
|
|
|
Tracer.HandleMethodTrace(args[i].Substring(14));
|
2003-03-17 17:02:46 +03:00
|
|
|
}
|
2002-12-18 19:00:25 +03:00
|
|
|
else
|
|
|
|
{
|
|
|
|
Console.Error.WriteLine("{0}: illegal argument", args[i]);
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
mainClass = args[i];
|
2004-09-15 17:35:44 +04:00
|
|
|
vmargs = Startup.Glob(i + 2);
|
2002-12-18 19:00:25 +03:00
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
if(mainClass == null)
|
|
|
|
{
|
|
|
|
Console.Error.WriteLine("usage: ikvm [-options] <class> [args...]");
|
|
|
|
Console.Error.WriteLine(" (to execute a class)");
|
|
|
|
Console.Error.WriteLine(" or ikvm -jar [-options] <jarfile> [args...]");
|
|
|
|
Console.Error.WriteLine(" (to execute a jar file)");
|
|
|
|
Console.Error.WriteLine();
|
|
|
|
Console.Error.WriteLine("where options include:");
|
2004-09-15 17:35:44 +04:00
|
|
|
Console.Error.WriteLine(" -? -help Display this message");
|
2004-10-04 23:30:53 +04:00
|
|
|
Console.Error.WriteLine(" -version Display IKVM and runtime version");
|
2003-03-17 17:02:46 +03:00
|
|
|
Console.Error.WriteLine(" -cp -classpath <directories and zip/jar files separated by {0}>", Path.PathSeparator);
|
2004-09-15 17:35:44 +04:00
|
|
|
Console.Error.WriteLine(" Set search path for application classes and resources");
|
|
|
|
Console.Error.WriteLine(" -D<name>=<value> Set a system property");
|
2004-10-04 23:30:53 +04:00
|
|
|
Console.Error.WriteLine(" -ea[:<packagename>...|:<classname>]");
|
|
|
|
Console.Error.WriteLine(" -enableassertions[:<packagename>...|:<classname>]");
|
|
|
|
Console.Error.WriteLine(" Enable assertions.");
|
|
|
|
Console.Error.WriteLine(" -da[:<packagename>...|:<classname>]");
|
|
|
|
Console.Error.WriteLine(" -disableassertions[:<packagename>...|:<classname>]");
|
|
|
|
Console.Error.WriteLine(" Disable assertions");
|
2004-09-15 17:35:44 +04:00
|
|
|
Console.Error.WriteLine(" -Xsave Save the generated assembly (for debugging)");
|
|
|
|
Console.Error.WriteLine(" -Xtime Time the execution");
|
2003-03-17 17:02:46 +03:00
|
|
|
Console.Error.WriteLine(" -Xbootclasspath:<directories and zip/jar files separated by {0}>", Path.PathSeparator);
|
2004-09-15 17:35:44 +04:00
|
|
|
Console.Error.WriteLine(" Set search path for bootstrap classes and resources");
|
2004-03-08 18:18:47 +03:00
|
|
|
Console.Error.WriteLine(" -Xtrace:<string> Displays all tracepoints with the given name");
|
2004-10-04 23:30:53 +04:00
|
|
|
Console.Error.WriteLine(" -Xmethodtrace:<string>");
|
|
|
|
Console.Error.WriteLine(" Builds method trace into the specified output methods");
|
2004-04-02 12:13:01 +04:00
|
|
|
Console.Error.WriteLine(" -Xwait Keep process hanging around after exit");
|
2004-10-04 23:30:53 +04:00
|
|
|
Console.Error.WriteLine(" -Xbreak Trigger a user defined breakpoint at startup");
|
2002-12-18 19:00:25 +03:00
|
|
|
return 1;
|
|
|
|
}
|
|
|
|
try
|
|
|
|
{
|
2004-09-27 14:17:34 +04:00
|
|
|
if(jar)
|
|
|
|
{
|
|
|
|
props["java.class.path"] = mainClass;
|
|
|
|
}
|
2004-09-15 17:35:44 +04:00
|
|
|
Startup.SetProperties(props);
|
2004-09-27 14:17:34 +04:00
|
|
|
Startup.EnterMainThread();
|
2002-12-18 19:00:25 +03:00
|
|
|
if(jar)
|
|
|
|
{
|
|
|
|
JarFile jf = new JarFile(mainClass);
|
|
|
|
try
|
|
|
|
{
|
2004-11-04 15:50:28 +03:00
|
|
|
mainClass = jf.getManifest().getMainAttributes().getValue(Attributes.Name.MAIN_CLASS).Replace('/', '.');
|
2002-12-18 19:00:25 +03:00
|
|
|
}
|
|
|
|
finally
|
|
|
|
{
|
|
|
|
jf.close();
|
|
|
|
}
|
|
|
|
if(mainClass == null)
|
|
|
|
{
|
|
|
|
Console.Error.WriteLine("Manifest doesn't contain a Main-Class.");
|
|
|
|
return 1;
|
|
|
|
}
|
|
|
|
}
|
2004-08-17 13:05:21 +04:00
|
|
|
if(bootclasspath != null)
|
2002-12-27 12:01:16 +03:00
|
|
|
{
|
2004-08-17 13:05:21 +04:00
|
|
|
JVM.SetBootstrapClassLoader(new PathClassLoader(bootclasspath, null));
|
2002-12-27 12:01:16 +03:00
|
|
|
}
|
2004-08-17 13:05:21 +04:00
|
|
|
java.lang.Class clazz = java.lang.Class.forName(mainClass, true, java.lang.ClassLoader.getSystemClassLoader());
|
2003-12-20 01:19:18 +03:00
|
|
|
Method method = FindMainMethod(clazz);
|
|
|
|
if(method == null)
|
2003-08-29 14:14:08 +04:00
|
|
|
{
|
|
|
|
throw new java.lang.NoSuchMethodError("main");
|
|
|
|
}
|
2003-12-20 01:19:18 +03:00
|
|
|
else if(!Modifier.isPublic(method.getModifiers()))
|
|
|
|
{
|
|
|
|
Console.Error.WriteLine("Main method not public.");
|
|
|
|
}
|
|
|
|
else
|
2002-12-18 19:00:25 +03:00
|
|
|
{
|
2004-06-17 13:14:51 +04:00
|
|
|
// if clazz isn't public, we can still call main
|
|
|
|
method.setAccessible(true);
|
2004-03-16 20:10:09 +03:00
|
|
|
if(saveAssembly)
|
|
|
|
{
|
|
|
|
java.lang.Runtime.getRuntime().addShutdownHook(new SaveAssemblyShutdownHook(clazz));
|
|
|
|
}
|
2004-04-02 12:13:01 +04:00
|
|
|
if(waitOnExit)
|
|
|
|
{
|
|
|
|
java.lang.Runtime.getRuntime().addShutdownHook(new WaitShutdownHook());
|
|
|
|
}
|
2002-12-18 19:00:25 +03:00
|
|
|
try
|
|
|
|
{
|
|
|
|
method.invoke(null, new object[] { vmargs });
|
2003-12-20 01:19:18 +03:00
|
|
|
return 0;
|
2002-12-18 19:00:25 +03:00
|
|
|
}
|
2003-12-20 01:19:18 +03:00
|
|
|
catch(InvocationTargetException x)
|
2002-12-18 19:00:25 +03:00
|
|
|
{
|
2003-12-20 01:19:18 +03:00
|
|
|
throw x.getCause();
|
2002-12-18 19:00:25 +03:00
|
|
|
}
|
2004-09-05 13:37:58 +04:00
|
|
|
finally
|
|
|
|
{
|
|
|
|
if(saveAssemblyX)
|
|
|
|
{
|
|
|
|
JVM.SaveDebugImage(clazz);
|
|
|
|
}
|
|
|
|
}
|
2002-12-18 19:00:25 +03:00
|
|
|
}
|
|
|
|
}
|
|
|
|
catch(System.Exception x)
|
|
|
|
{
|
|
|
|
java.lang.Thread thread = java.lang.Thread.currentThread();
|
2004-11-29 16:58:21 +03:00
|
|
|
thread.getThreadGroup().uncaughtException(thread, IKVM.Runtime.Util.MapException(x));
|
2002-12-18 19:00:25 +03:00
|
|
|
}
|
2003-12-20 01:19:18 +03:00
|
|
|
finally
|
|
|
|
{
|
2004-09-27 14:17:34 +04:00
|
|
|
Startup.ExitMainThread();
|
2003-12-20 01:19:18 +03:00
|
|
|
}
|
|
|
|
return 1;
|
|
|
|
}
|
|
|
|
|
|
|
|
private static Method FindMainMethod(java.lang.Class clazz)
|
|
|
|
{
|
2005-02-23 15:56:15 +03:00
|
|
|
// HACK without this hack, clazz.getDeclaredMethods would throw a NoClassDefFoundError if any
|
|
|
|
// of the methods in the class had an unloadable parameter type, but we don't want that.
|
|
|
|
JVM.EnableReflectionOnMethodsWithUnloadableTypeParameters = true;
|
|
|
|
try
|
2003-12-20 01:19:18 +03:00
|
|
|
{
|
2005-02-23 15:56:15 +03:00
|
|
|
while(clazz != null)
|
2003-12-20 01:19:18 +03:00
|
|
|
{
|
2005-02-23 15:56:15 +03:00
|
|
|
foreach(Method m in clazz.getDeclaredMethods())
|
2003-12-20 01:19:18 +03:00
|
|
|
{
|
2005-02-23 15:56:15 +03:00
|
|
|
if(m.getName() == "main" && m.getReturnType() == java.lang.Void.TYPE)
|
2003-12-20 01:19:18 +03:00
|
|
|
{
|
2005-02-23 15:56:15 +03:00
|
|
|
java.lang.Class[] parameters = m.getParameterTypes();
|
|
|
|
if(parameters.Length == 1 && parameters[0] == java.lang.Class.forName("[Ljava.lang.String;"))
|
|
|
|
{
|
|
|
|
return m;
|
|
|
|
}
|
2003-12-20 01:19:18 +03:00
|
|
|
}
|
|
|
|
}
|
2005-02-23 15:56:15 +03:00
|
|
|
clazz = clazz.getSuperclass();
|
2003-12-20 01:19:18 +03:00
|
|
|
}
|
2005-02-23 15:56:15 +03:00
|
|
|
return null;
|
|
|
|
}
|
|
|
|
finally
|
|
|
|
{
|
|
|
|
JVM.EnableReflectionOnMethodsWithUnloadableTypeParameters = false;
|
2003-12-20 01:19:18 +03:00
|
|
|
}
|
2002-12-18 19:00:25 +03:00
|
|
|
}
|
|
|
|
}
|